]> SALOME platform Git repositories - modules/shaper.git/blob - src/SketchSolver/SketchSolver_ConstraintTangent.cpp
Salome HOME
Improve translation for Elliptic Arcs
[modules/shaper.git] / src / SketchSolver / SketchSolver_ConstraintTangent.cpp
1 // Copyright (C) 2014-2019  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include <SketchSolver_ConstraintTangent.h>
21 #include <SketchSolver_Error.h>
22
23 #include <PlaneGCSSolver_EdgeWrapper.h>
24 #include <PlaneGCSSolver_PointWrapper.h>
25 #include <PlaneGCSSolver_Storage.h>
26 #include <PlaneGCSSolver_Tools.h>
27 #include <PlaneGCSSolver_UpdateCoincidence.h>
28
29 #include <GeomAPI_Circ2d.h>
30 #include <GeomAPI_Lin2d.h>
31 #include <GeomAPI_Pnt2d.h>
32 #include <GeomAPI_Ellipse2d.h>
33
34 #include <SketchPlugin_Arc.h>
35 #include <SketchPlugin_Circle.h>
36 #include <SketchPlugin_ConstraintCoincidence.h>
37 #include <SketchPlugin_ConstraintMiddle.h>
38
39 #include <cmath>
40
41 #define GCS_EDGE_WRAPPER(x) std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(x)
42
43 /// \brief Obtain tangent features from the constraint
44 static void getTangentFeatures(const ConstraintPtr& theConstraint,
45                                FeaturePtr& theFeature1,
46                                FeaturePtr& theFeature2);
47
48 /// \brief Obtain all coincident constraints between features
49 static std::set<FeaturePtr> collectCoincidences(FeaturePtr theFeature1, FeaturePtr theFeature2);
50
51 /// \brief Check whether the entities has only one shared point or less.
52 ///        Return list of coincident points.
53 static std::set<AttributePtr> coincidentBoundaryPoints(FeaturePtr theFeature1,
54                                                        FeaturePtr theFeature2);
55
56 /// \brief Collect points coincident with each of two features
57 static std::set<AttributePtr> coincidentPoints(FeaturePtr theFeature1, FeaturePtr theFeature2);
58
59 /// \brief Check if two connected arcs have centers
60 ///        in same direction relatively to connection point
61 static bool isArcArcTangencyInternal(EntityWrapperPtr theArc1,
62                                      EntityWrapperPtr theArc2);
63
64 static ConstraintWrapperPtr
65   createArcLineTangency(EntityWrapperPtr theEntity1,
66                         EntityWrapperPtr theEntity2,
67                         EntityWrapperPtr theShapedPoint = EntityWrapperPtr(),
68                         double*          theAngle = 0);
69
70 static ConstraintWrapperPtr
71   createCurveCurveTangency(EntityWrapperPtr theEntity1,
72                            EntityWrapperPtr theEntity2,
73                            bool             theInternalTangency,
74                            EntityWrapperPtr theSharedPoint = EntityWrapperPtr(),
75                            double*          theAngle = 0);
76
77 static void calculateTangencyPoint(EntityWrapperPtr theCurve1, EntityWrapperPtr theCurve2,
78                                    GCSPointPtr& theTangencyPoint);
79
80
81 void SketchSolver_ConstraintTangent::process()
82 {
83   cleanErrorMsg();
84   if (!myBaseConstraint || !myStorage) {
85     // Not enough parameters are assigned
86     return;
87   }
88
89   EntityWrapperPtr aValue;
90   std::vector<EntityWrapperPtr> anAttributes;
91   SketchSolver_Constraint::getAttributes(aValue, anAttributes);
92   if (!myErrorMsg.empty())
93     return;
94
95   rebuild();
96   if (!myErrorMsg.empty())
97     return;
98
99   myStorage->subscribeUpdates(this, PlaneGCSSolver_UpdateCoincidence::GROUP());
100 }
101
102 void SketchSolver_ConstraintTangent::rebuild()
103 {
104   if (mySolverConstraint)
105     myStorage->removeConstraint(myBaseConstraint);
106
107   std::shared_ptr<PlaneGCSSolver_Storage> aStorage =
108       std::dynamic_pointer_cast<PlaneGCSSolver_Storage>(myStorage);
109
110   mySolverConstraint = ConstraintWrapperPtr();
111   mySharedPoint = AttributePtr();
112   if (myAuxPoint) {
113     GCS::SET_pD aParams = PlaneGCSSolver_Tools::parameters(myAuxPoint);
114     aStorage->removeParameters(aParams);
115     myAuxPoint = EntityWrapperPtr();
116   }
117
118   // Check the quantity of entities of each type and their order (arcs first)
119   int aNbLines = 0;
120   int aNbCircles = 0;
121   int aNbEllipses = 0;
122   std::list<EntityWrapperPtr>::iterator anEntIt = myAttributes.begin();
123   for (; anEntIt != myAttributes.end(); ++anEntIt) {
124     if (!(*anEntIt).get())
125       continue;
126     if ((*anEntIt)->type() == ENTITY_LINE)
127       ++aNbLines;
128     else if ((*anEntIt)->type() == ENTITY_ARC || (*anEntIt)->type() == ENTITY_CIRCLE)
129       ++aNbCircles;
130     else if ((*anEntIt)->type() == ENTITY_ELLIPSE || (*anEntIt)->type() == ENTITY_ELLIPTIC_ARC)
131       ++aNbEllipses;
132   }
133
134   if (aNbCircles + aNbEllipses < 1) {
135     myErrorMsg = SketchSolver_Error::INCORRECT_TANGENCY_ATTRIBUTE();
136     return;
137   }
138   if (aNbLines == 1 && aNbCircles == 1) {
139     myType = CONSTRAINT_TANGENT_CIRCLE_LINE;
140   }
141   else if (aNbLines + aNbCircles + aNbEllipses == 2) {
142     myType = CONSTRAINT_TANGENT_CURVE_CURVE;
143     isArcArcInternal = isArcArcTangencyInternal(myAttributes.front(), myAttributes.back());
144   }
145   else {
146     myErrorMsg = SketchSolver_Error::INCORRECT_ATTRIBUTE();
147     return;
148   }
149
150   FeaturePtr aFeature1, aFeature2;
151   getTangentFeatures(myBaseConstraint, aFeature1, aFeature2);
152
153   // check number of coincident points
154   std::set<AttributePtr> aCoincidentPoints = coincidentBoundaryPoints(aFeature1, aFeature2);
155   if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE && aCoincidentPoints.size() > 2) {
156     myErrorMsg = SketchSolver_Error::TANGENCY_FAILED();
157     return;
158   }
159
160   // Try to find non-boundary points coincident with both features.
161   // It is necesasry to create tangency with ellipse
162   if (aCoincidentPoints.empty() && aNbEllipses > 0)
163     aCoincidentPoints = coincidentPoints(aFeature1, aFeature2);
164
165   EntityWrapperPtr aSharedPointEntity;
166   std::list<GCSConstraintPtr> anAuxConstraints;
167   if (!aCoincidentPoints.empty()) {
168     mySharedPoint = *aCoincidentPoints.begin();
169     aSharedPointEntity = myStorage->entity(mySharedPoint);
170   }
171   else if (aNbEllipses > 0) {
172     // create auxiliary point
173     GCSPointPtr aPoint(new GCS::Point);
174     aPoint->x = aStorage->createParameter();
175     aPoint->y = aStorage->createParameter();
176     calculateTangencyPoint(myAttributes.front(), myAttributes.back(), aPoint);
177
178     myAuxPoint.reset(new PlaneGCSSolver_PointWrapper(aPoint));
179     aSharedPointEntity = myAuxPoint;
180
181     // create auxiliary coincident constraints for tangency with ellipse
182     EntityWrapperPtr aDummy;
183     ConstraintWrapperPtr aCoincidence = PlaneGCSSolver_Tools::createConstraint(ConstraintPtr(),
184         CONSTRAINT_PT_ON_CURVE, aDummy, aSharedPointEntity, aDummy, myAttributes.front(), aDummy);
185     anAuxConstraints = aCoincidence->constraints();
186     aCoincidence = PlaneGCSSolver_Tools::createConstraint(ConstraintPtr(),
187         CONSTRAINT_PT_ON_CURVE, aDummy, aSharedPointEntity, aDummy, myAttributes.back(), aDummy);
188     anAuxConstraints.insert(anAuxConstraints.end(),
189         aCoincidence->constraints().begin(), aCoincidence->constraints().end());
190   }
191
192   if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE) {
193     mySolverConstraint = createArcLineTangency(myAttributes.front(), myAttributes.back(),
194                                            aSharedPointEntity, &myCurveCurveAngle);
195   } else {
196     mySolverConstraint = createCurveCurveTangency(myAttributes.front(), myAttributes.back(),
197                             isArcArcInternal, aSharedPointEntity, &myCurveCurveAngle);
198   }
199
200   if (!anAuxConstraints.empty()) {
201     anAuxConstraints.insert(anAuxConstraints.end(), mySolverConstraint->constraints().begin(),
202         mySolverConstraint->constraints().end());
203     mySolverConstraint->setConstraints(anAuxConstraints);
204   }
205
206   myStorage->addConstraint(myBaseConstraint, mySolverConstraint);
207 }
208
209 void SketchSolver_ConstraintTangent::adjustConstraint()
210 {
211   if (myType == CONSTRAINT_TANGENT_CURVE_CURVE) {
212     EntityWrapperPtr anEntity1 =
213         myStorage->entity(myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
214     EntityWrapperPtr anEntity2 =
215         myStorage->entity(myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
216
217     if (isArcArcInternal != isArcArcTangencyInternal(anEntity1, anEntity2))
218       rebuild();
219   }
220 }
221
222 void SketchSolver_ConstraintTangent::notify(const FeaturePtr&      theFeature,
223                                             PlaneGCSSolver_Update* theUpdater)
224 {
225   if (theFeature->getKind() != SketchPlugin_ConstraintCoincidence::ID())
226     return;
227
228   // base constraint may become invalid (for example, during undo)
229   if (!myBaseConstraint->data() || !myBaseConstraint->data()->isValid())
230     return;
231
232   FeaturePtr aTgFeat1, aTgFeat2;
233   getTangentFeatures(myBaseConstraint, aTgFeat1, aTgFeat2);
234
235   bool isRebuild = false;
236   if (theFeature->data() && theFeature->data()->isValid()) {
237     // the constraint has been created
238     if (!mySharedPoint) {
239       // features has no shared point, check whether coincidence constraint binds these features)
240       int aNbCoincidentFeatures = 0;
241       for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
242         AttributeRefAttrPtr aRefAttr = theFeature->refattr(SketchPlugin_Constraint::ATTRIBUTE(i));
243         if (!aRefAttr)
244           continue;
245
246         ObjectPtr anObj;
247         if (aRefAttr->isObject())
248           anObj = aRefAttr->object();
249         else
250           anObj = aRefAttr->attr()->owner();
251
252         FeaturePtr aFeature = ModelAPI_Feature::feature(anObj);
253         if (aFeature == aTgFeat1 || aFeature == aTgFeat2)
254           ++aNbCoincidentFeatures;
255       }
256
257       if (aNbCoincidentFeatures == 2)
258         isRebuild = true;
259     }
260   }
261
262   if (!isRebuild) {
263     if (mySharedPoint) {
264       // The features are tangent in the shared point, but the coincidence has been removed/updated.
265       // Check if the coincidence is the same.
266       std::set<AttributePtr> aCoincidentPoints = coincidentBoundaryPoints(aTgFeat1, aTgFeat2);
267       isRebuild = true;
268       std::set<AttributePtr>::iterator anIt = aCoincidentPoints.begin();
269       for (; anIt != aCoincidentPoints.end() && isRebuild; ++anIt)
270         if (*anIt == mySharedPoint)
271           isRebuild = false; // the coincidence is still exists => nothing to change
272     }
273     else {
274       // check both features have a coincident point
275       std::set<AttributePtr> aCoincidentPoints = coincidentPoints(aTgFeat1, aTgFeat2);
276       isRebuild = (bool)(myAuxPoint.get()) == (!aCoincidentPoints.empty());
277     }
278   }
279
280   if (isRebuild)
281     rebuild();
282 }
283
284
285
286
287 // ==================   Auxiliary functions   =================================
288 void getTangentFeatures(const ConstraintPtr& theConstraint,
289                               FeaturePtr&    theFeature1,
290                               FeaturePtr&    theFeature2)
291 {
292   AttributeRefAttrPtr aRefAttr = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_A());
293   theFeature1 = ModelAPI_Feature::feature(aRefAttr->object());
294   aRefAttr = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_B());
295   theFeature2 = ModelAPI_Feature::feature(aRefAttr->object());
296 }
297
298 std::set<FeaturePtr> collectCoincidences(FeaturePtr theFeature1, FeaturePtr theFeature2)
299 {
300   const std::set<AttributePtr>& aRefs1 = theFeature1->data()->refsToMe();
301   const std::set<AttributePtr>& aRefs2 = theFeature2->data()->refsToMe();
302
303   std::set<FeaturePtr> aCoincidences;
304   std::set<AttributePtr>::const_iterator anIt;
305
306   // collect coincidences referred to the first feature
307   for (anIt = aRefs1.begin(); anIt != aRefs1.end(); ++anIt) {
308     FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
309     if (aRef && aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID())
310       aCoincidences.insert(aRef);
311   }
312
313   // leave only coincidences referred to the second feature
314   std::set<FeaturePtr> aCoincidencesBetweenFeatures;
315   for (anIt = aRefs2.begin(); anIt != aRefs2.end(); ++anIt) {
316     FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
317     if (aCoincidences.find(aRef) != aCoincidences.end())
318       aCoincidencesBetweenFeatures.insert(aRef);
319   }
320
321   return aCoincidencesBetweenFeatures;
322 }
323
324 std::set<AttributePtr> coincidentBoundaryPoints(FeaturePtr theFeature1, FeaturePtr theFeature2)
325 {
326   std::set<FeaturePtr> aCoincidences = collectCoincidences(theFeature1, theFeature2);
327   // collect points only
328   std::set<AttributePtr> aCoincidentPoints;
329   std::set<FeaturePtr>::const_iterator aCIt = aCoincidences.begin();
330   for (; aCIt != aCoincidences.end(); ++ aCIt) {
331     AttributeRefAttrPtr aRefAttrA = (*aCIt)->refattr(SketchPlugin_Constraint::ENTITY_A());
332     AttributeRefAttrPtr aRefAttrB = (*aCIt)->refattr(SketchPlugin_Constraint::ENTITY_B());
333     if (!aRefAttrA || aRefAttrA->isObject() ||
334         !aRefAttrB || aRefAttrB->isObject())
335       continue;
336
337     AttributePtr anAttrA = aRefAttrA->attr();
338     AttributePtr anAttrB = aRefAttrB->attr();
339     if (anAttrA->id() != SketchPlugin_Arc::CENTER_ID() &&
340         anAttrA->id() != SketchPlugin_Circle::CENTER_ID() &&
341         anAttrB->id() != SketchPlugin_Arc::CENTER_ID() &&
342         anAttrB->id() != SketchPlugin_Circle::CENTER_ID()) {
343       aCoincidentPoints.insert(anAttrA);
344       aCoincidentPoints.insert(anAttrB);
345     }
346   }
347   return aCoincidentPoints;
348 }
349
350 static std::set<AttributePtr> refsToFeatureAndResults(FeaturePtr theFeature)
351 {
352   std::set<AttributePtr> aRefs = theFeature->data()->refsToMe();
353   const std::list<ResultPtr>& aResults = theFeature->results();
354   for (std::list<ResultPtr>::const_iterator anIt = aResults.begin();
355       anIt != aResults.end(); ++anIt) {
356     const std::set<AttributePtr>& aResRefs = (*anIt)->data()->refsToMe();
357     aRefs.insert(aResRefs.begin(), aResRefs.end());
358   }
359   return aRefs;
360 }
361
362 // collect all points coincident with the feature
363 static std::set<AttributePtr> pointsOnFeature(FeaturePtr theFeature)
364 {
365   std::set<AttributePtr> aPoints;
366
367   std::set<AttributePtr> aRefs = refsToFeatureAndResults(theFeature);
368   for (std::set<AttributePtr>::const_iterator anIt = aRefs.begin(); anIt != aRefs.end(); ++anIt) {
369     FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
370     if (aRef && (aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID() ||
371                  aRef->getKind() == SketchPlugin_ConstraintMiddle::ID())) {
372       for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
373         AttributeRefAttrPtr aRefAttr = aRef->refattr(SketchPlugin_Constraint::ATTRIBUTE(i));
374         if (aRefAttr) {
375           AttributePtr anAttr = aRefAttr->attr();
376           if (anAttr && anAttr->id() != SketchPlugin_Arc::CENTER_ID() &&
377                         anAttr->id() != SketchPlugin_Circle::CENTER_ID())
378             aPoints.insert(anAttr);
379         }
380       }
381     }
382   }
383   return aPoints;
384 }
385
386 std::set<AttributePtr> coincidentPoints(FeaturePtr theFeature1, FeaturePtr theFeature2)
387 {
388   std::set<AttributePtr> aPointsOnF1 = pointsOnFeature(theFeature1);
389   std::set<AttributePtr> aPointsOnF2 = pointsOnFeature(theFeature2);
390
391   std::set<AttributePtr> aCommonPoints;
392   for (std::set<AttributePtr>::iterator anIt = aPointsOnF1.begin();
393        anIt != aPointsOnF1.end(); ++anIt)
394     if (aPointsOnF2.find(*anIt) != aPointsOnF2.end())
395       aCommonPoints.insert(*anIt);
396   return aCommonPoints;
397 }
398
399 bool isArcArcTangencyInternal(EntityWrapperPtr theArc1, EntityWrapperPtr theArc2)
400 {
401   std::shared_ptr<GCS::Circle> aCirc1 = std::dynamic_pointer_cast<GCS::Circle>(
402       GCS_EDGE_WRAPPER(theArc1)->entity());
403   std::shared_ptr<GCS::Circle> aCirc2 = std::dynamic_pointer_cast<GCS::Circle>(
404       GCS_EDGE_WRAPPER(theArc2)->entity());
405
406   if (!aCirc1 || !aCirc2)
407     return false;
408
409   double aDX = *(aCirc1->center.x) - *(aCirc2->center.x);
410   double aDY = *(aCirc1->center.y) - *(aCirc2->center.y);
411   double aDist = sqrt(aDX * aDX + aDY * aDY);
412
413   return (aDist < *(aCirc1->rad) || aDist < *(aCirc2->rad));
414 }
415
416 // sets angle to 0 or 180 degrees
417 static void adjustAngleBetweenCurves(const GCSCurvePtr& theCurve1,
418                                      const GCSCurvePtr& theCurve2,
419                                      const GCSPointPtr& thePoint,
420                                      double*            theAngle)
421 {
422   double anAngle = GCS::System().calculateAngleViaPoint(*theCurve1, *theCurve2, *thePoint);
423   // bring angle to [-pi..pi]
424   if (anAngle >  PI) anAngle -= 2.0 * PI;
425   if (anAngle < -PI) anAngle += 2.0 * PI;
426   // set angle value according to the current angle between curves
427   if (fabs(anAngle) <= PI / 2.)
428     *theAngle = 0.0;
429   else
430     *theAngle = PI;
431 }
432
433
434 ConstraintWrapperPtr createArcLineTangency(EntityWrapperPtr theEntity1,
435                                            EntityWrapperPtr theEntity2,
436                                            EntityWrapperPtr theSharedPoint,
437                                            double*          theAngle)
438 {
439   EdgeWrapperPtr anEntLine, anEntCirc;
440   if (theEntity1->type() == ENTITY_LINE) {
441     anEntLine = GCS_EDGE_WRAPPER(theEntity1);
442     anEntCirc = GCS_EDGE_WRAPPER(theEntity2);
443   } else {
444     anEntLine = GCS_EDGE_WRAPPER(theEntity2);
445     anEntCirc = GCS_EDGE_WRAPPER(theEntity1);
446   }
447
448   std::shared_ptr<GCS::Circle> aCirc =
449       std::dynamic_pointer_cast<GCS::Circle>(anEntCirc->entity());
450   std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(aCirc);
451
452   std::shared_ptr<GCS::Line> aLine =
453       std::dynamic_pointer_cast<GCS::Line>(anEntLine->entity());
454
455   GCSConstraintPtr aNewConstr;
456   if (theSharedPoint && anArc) { // do not process shared point between circle and line
457     GCSPointPtr aPoint =
458         std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theSharedPoint)->point();
459
460     adjustAngleBetweenCurves(anArc, aLine, aPoint, theAngle);
461     aNewConstr =
462         GCSConstraintPtr(new GCS::ConstraintAngleViaPoint(*anArc, *aLine, *aPoint, theAngle));
463   } else {
464     aNewConstr =
465       GCSConstraintPtr(new GCS::ConstraintP2LDistance(aCirc->center, *aLine, aCirc->rad));
466   }
467
468   return  ConstraintWrapperPtr(
469       new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_TANGENT_CIRCLE_LINE));
470 }
471
472 ConstraintWrapperPtr createCurveCurveTangency(EntityWrapperPtr theEntity1,
473                                               EntityWrapperPtr theEntity2,
474                                               bool             theInternalTangency,
475                                               EntityWrapperPtr theSharedPoint,
476                                               double*          theAngle)
477 {
478   GCSCurvePtr aCurve1 =
479     std::dynamic_pointer_cast<GCS::Curve>(GCS_EDGE_WRAPPER(theEntity1)->entity());
480   GCSCurvePtr aCurve2 =
481     std::dynamic_pointer_cast<GCS::Curve>(GCS_EDGE_WRAPPER(theEntity2)->entity());
482
483   GCSConstraintPtr aNewConstr;
484   if (theSharedPoint) {
485     GCSPointPtr aPoint =
486         std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theSharedPoint)->point();
487
488     adjustAngleBetweenCurves(aCurve1, aCurve2, aPoint, theAngle);
489     aNewConstr =
490         GCSConstraintPtr(new GCS::ConstraintAngleViaPoint(*aCurve1, *aCurve2, *aPoint, theAngle));
491   } else {
492     std::shared_ptr<GCS::Circle> aCirc1 = std::dynamic_pointer_cast<GCS::Circle>(aCurve1);
493     std::shared_ptr<GCS::Circle> aCirc2 = std::dynamic_pointer_cast<GCS::Circle>(aCurve2);
494     aNewConstr = GCSConstraintPtr(new GCS::ConstraintTangentCircumf(aCirc1->center, aCirc2->center,
495                                   aCirc1->rad, aCirc2->rad, theInternalTangency));
496   }
497
498   return ConstraintWrapperPtr(
499       new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_TANGENT_CURVE_CURVE));
500 }
501
502 void calculateTangencyPoint(EntityWrapperPtr theCurve1, EntityWrapperPtr theCurve2,
503                             GCSPointPtr& theTangencyPoint)
504 {
505   std::shared_ptr<GeomAPI_Ellipse2d> anEllipse = PlaneGCSSolver_Tools::ellipse(theCurve1);
506   EntityWrapperPtr aCurve2 = theCurve2;
507   if (!anEllipse) {
508     // try converting to ellipse the second curve
509     anEllipse = PlaneGCSSolver_Tools::ellipse(theCurve2);
510     if (!anEllipse)
511       return; // no one curve is ellipse
512     aCurve2 = theCurve1;
513   }
514
515   GeomPnt2dPtr aP1, aP2;
516   if (aCurve2->type() == ENTITY_LINE) {
517     std::shared_ptr<GeomAPI_Lin2d> aLine = PlaneGCSSolver_Tools::line(aCurve2);
518     anEllipse->distance(aLine, aP1, aP2);
519   }
520   else if (aCurve2->type() == ENTITY_ARC || aCurve2->type() == ENTITY_CIRCLE) {
521     std::shared_ptr<GeomAPI_Circ2d> aCircle = PlaneGCSSolver_Tools::circle(aCurve2);
522     anEllipse->distance(aCircle, aP1, aP2);
523   }
524   else if (aCurve2->type() == ENTITY_ELLIPSE || aCurve2->type() == ENTITY_ELLIPTIC_ARC) {
525     std::shared_ptr<GeomAPI_Ellipse2d> anEl2 = PlaneGCSSolver_Tools::ellipse(aCurve2);
526     anEllipse->distance(anEl2, aP1, aP2);
527   }
528
529   if (aP1 && aP2) {
530     *theTangencyPoint->x = 0.5 * (aP1->x() + aP2->x());
531     *theTangencyPoint->y = 0.5 * (aP1->y() + aP2->y());
532   }
533 }