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