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