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