Salome HOME
1c42ac69af7fdceca28e1eeb1f3de71e094165e1
[modules/shaper.git] / src / SketchSolver / SketchSolver_ConstraintTangent.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 #include <SketchSolver_ConstraintTangent.h>
4 #include <SketchSolver_Error.h>
5
6 #include <PlaneGCSSolver_EdgeWrapper.h>
7 #include <PlaneGCSSolver_PointWrapper.h>
8 #include <PlaneGCSSolver_Tools.h>
9 #include <PlaneGCSSolver_UpdateCoincidence.h>
10
11 #include <GeomAPI_Pnt2d.h>
12 #include <SketchPlugin_Circle.h>
13 #include <SketchPlugin_ConstraintCoincidence.h>
14
15 #include <cmath>
16
17 #define GCS_EDGE_WRAPPER(x) std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(x)
18
19 /// \brief Obtain tangent features from the constraint
20 static void getTangentFeatures(const ConstraintPtr& theConstraint,
21                                FeaturePtr& theFeature1,
22                                FeaturePtr& theFeature2);
23
24 /// \brief Obtain all coincident constraints between features
25 static std::set<FeaturePtr> collectCoincidences(FeaturePtr theFeature1, FeaturePtr theFeature2);
26
27 /// \brief Check whether the entities has only one shared point or less.
28 ///        Return list of coincident points.
29 static std::list<AttributePtr> coincidentPoints(FeaturePtr theFeature1, FeaturePtr theFeature2);
30
31 /// \brief Check if two connected arcs have centers
32 ///        in same direction relatively to connection point
33 static bool isArcArcTangencyInternal(EntityWrapperPtr theArc1,
34                                      EntityWrapperPtr theArc2);
35
36 static ConstraintWrapperPtr
37   createArcLineTangency(EntityWrapperPtr theEntity1,
38                         EntityWrapperPtr theEntity2,
39                         EntityWrapperPtr theShapedPoint = EntityWrapperPtr(),
40                         double*          theAngle = 0);
41
42 static ConstraintWrapperPtr
43   createArcArcTangency(EntityWrapperPtr theEntity1,
44                        EntityWrapperPtr theEntity2,
45                        bool             theInternalTangency,
46                        EntityWrapperPtr theSharedPoint = EntityWrapperPtr(),
47                        double*          theAngle = 0);
48
49
50 void SketchSolver_ConstraintTangent::process()
51 {
52   cleanErrorMsg();
53   if (!myBaseConstraint || !myStorage) {
54     // Not enough parameters are assigned
55     return;
56   }
57
58   EntityWrapperPtr aValue;
59   std::vector<EntityWrapperPtr> anAttributes;
60   SketchSolver_Constraint::getAttributes(aValue, anAttributes);
61   if (!myErrorMsg.empty())
62     return;
63
64   rebuild();
65   if (!myErrorMsg.empty())
66     return;
67
68   myStorage->subscribeUpdates(this, PlaneGCSSolver_UpdateCoincidence::GROUP());
69 }
70
71 void SketchSolver_ConstraintTangent::rebuild()
72 {
73   if (mySolverConstraint)
74     myStorage->removeConstraint(myBaseConstraint);
75
76   mySolverConstraint = ConstraintWrapperPtr();
77   mySharedPoint = AttributePtr();
78
79   // Check the quantity of entities of each type and their order (arcs first)
80   int aNbLines = 0;
81   int aNbCircles = 0;
82   std::list<EntityWrapperPtr>::iterator anEntIt = myAttributes.begin();
83   for (; anEntIt != myAttributes.end(); ++anEntIt) {
84     if (!(*anEntIt).get())
85       continue;
86     if ((*anEntIt)->type() == ENTITY_LINE)
87       ++aNbLines;
88     else if ((*anEntIt)->type() == ENTITY_ARC || (*anEntIt)->type() == ENTITY_CIRCLE)
89       ++aNbCircles;
90   }
91
92   if (aNbCircles < 1) {
93     myErrorMsg = SketchSolver_Error::INCORRECT_TANGENCY_ATTRIBUTE();
94     return;
95   }
96   if (aNbLines == 1 && aNbCircles == 1) {
97     myType = CONSTRAINT_TANGENT_CIRCLE_LINE;
98   }
99   else if (aNbCircles == 2) {
100     myType = CONSTRAINT_TANGENT_CIRCLE_CIRCLE;
101     isArcArcInternal = isArcArcTangencyInternal(myAttributes.front(), myAttributes.back());
102   }
103   else {
104     myErrorMsg = SketchSolver_Error::INCORRECT_ATTRIBUTE();
105     return;
106   }
107
108   FeaturePtr aFeature1, aFeature2;
109   getTangentFeatures(myBaseConstraint, aFeature1, aFeature2);
110
111   // check number of coincident points
112   std::list<AttributePtr> aCoincidentPoints = coincidentPoints(aFeature1, aFeature2);
113   if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE && aCoincidentPoints.size() > 1) {
114     myErrorMsg = SketchSolver_Error::TANGENCY_FAILED();
115     return;
116   }
117
118   EntityWrapperPtr aSharedPointEntity;
119   if (!aCoincidentPoints.empty()) {
120     mySharedPoint = aCoincidentPoints.front();
121     aSharedPointEntity = myStorage->entity(mySharedPoint);
122   }
123
124   if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE) {
125     mySolverConstraint = createArcLineTangency(myAttributes.front(), myAttributes.back(),
126                                            aSharedPointEntity, &myCurveCurveAngle);
127   } else {
128     mySolverConstraint = createArcArcTangency(myAttributes.front(), myAttributes.back(),
129                             isArcArcInternal, aSharedPointEntity, &myCurveCurveAngle);
130   }
131
132   myStorage->addConstraint(myBaseConstraint, mySolverConstraint);
133 }
134
135 void SketchSolver_ConstraintTangent::adjustConstraint()
136 {
137   if (myType == CONSTRAINT_TANGENT_CIRCLE_CIRCLE) {
138     EntityWrapperPtr anEntity1 =
139         myStorage->entity(myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
140     EntityWrapperPtr anEntity2 =
141         myStorage->entity(myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
142
143     if (isArcArcInternal != isArcArcTangencyInternal(anEntity1, anEntity2))
144       rebuild();
145   }
146 }
147
148 void SketchSolver_ConstraintTangent::notify(const FeaturePtr&      theFeature,
149                                             PlaneGCSSolver_Update* theUpdater)
150 {
151   if (theFeature->getKind() != SketchPlugin_ConstraintCoincidence::ID())
152     return;
153
154   // base constraint may become invalid (for example, during undo)
155   if (!myBaseConstraint->data() || !myBaseConstraint->data()->isValid())
156     return;
157
158   FeaturePtr aTgFeat1, aTgFeat2;
159   getTangentFeatures(myBaseConstraint, aTgFeat1, aTgFeat2);
160
161   bool isRebuild = false;
162   if (theFeature->data() && theFeature->data()->isValid()) {
163     // the constraint has been created
164     if (!mySharedPoint) {
165       // features has no shared point, check whether coincidence constraint binds these features)
166       int aNbCoincidentFeatures = 0;
167       for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
168         AttributeRefAttrPtr aRefAttr = theFeature->refattr(SketchPlugin_Constraint::ATTRIBUTE(i));
169         if (!aRefAttr)
170           continue;
171
172         ObjectPtr anObj;
173         if (aRefAttr->isObject())
174           anObj = aRefAttr->object();
175         else
176           anObj = aRefAttr->attr()->owner();
177
178         FeaturePtr aFeature = ModelAPI_Feature::feature(anObj);
179         if (aFeature == aTgFeat1 || aFeature == aTgFeat2)
180           ++aNbCoincidentFeatures;
181       }
182
183       if (aNbCoincidentFeatures == 2)
184         isRebuild = true;
185     }
186   } else if (mySharedPoint) {
187     // The features are tangent in the shared point, but the coincidence has been removed.
188     // Check if the coincidence is the same.
189     std::list<AttributePtr> aCoincidentPoints = coincidentPoints(aTgFeat1, aTgFeat2);
190     isRebuild = true;
191     std::list<AttributePtr>::iterator anIt = aCoincidentPoints.begin();
192     for (; anIt != aCoincidentPoints.end() && isRebuild; ++anIt)
193       if (*anIt == mySharedPoint)
194         isRebuild = false; // the coincidence is still exists => nothing to change
195   }
196
197   if (isRebuild)
198     rebuild();
199 }
200
201
202
203
204 // ==================   Auxiliary functions   =================================
205 void getTangentFeatures(const ConstraintPtr& theConstraint,
206                               FeaturePtr&    theFeature1,
207                               FeaturePtr&    theFeature2)
208 {
209   AttributeRefAttrPtr aRefAttr = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_A());
210   theFeature1 = ModelAPI_Feature::feature(aRefAttr->object());
211   aRefAttr = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_B());
212   theFeature2 = ModelAPI_Feature::feature(aRefAttr->object());
213 }
214
215 std::set<FeaturePtr> collectCoincidences(FeaturePtr theFeature1, FeaturePtr theFeature2)
216 {
217   const std::set<AttributePtr>& aRefs1 = theFeature1->data()->refsToMe();
218   const std::set<AttributePtr>& aRefs2 = theFeature2->data()->refsToMe();
219
220   std::set<FeaturePtr> aCoincidences;
221   std::set<AttributePtr>::const_iterator anIt;
222
223   // collect coincidences referred to the first feature
224   for (anIt = aRefs1.begin(); anIt != aRefs1.end(); ++anIt) {
225     FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
226     if (aRef && aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID())
227       aCoincidences.insert(aRef);
228   }
229
230   // leave only coincidences referred to the second feature
231   std::set<FeaturePtr> aCoincidencesBetweenFeatures;
232   for (anIt = aRefs2.begin(); anIt != aRefs2.end(); ++anIt) {
233     FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
234     if (aCoincidences.find(aRef) != aCoincidences.end())
235       aCoincidencesBetweenFeatures.insert(aRef);
236   }
237
238   return aCoincidencesBetweenFeatures;
239 }
240
241 std::list<AttributePtr> coincidentPoints(FeaturePtr theFeature1, FeaturePtr theFeature2)
242 {
243   std::set<FeaturePtr> aCoincidences = collectCoincidences(theFeature1, theFeature2);
244   // collect points only
245   std::list<AttributePtr> aCoincidentPoints;
246   std::set<FeaturePtr>::const_iterator aCIt = aCoincidences.begin();
247   for (; aCIt != aCoincidences.end(); ++ aCIt) {
248     for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
249       AttributeRefAttrPtr aRefAttr = (*aCIt)->refattr(SketchPlugin_Constraint::ENTITY_A());
250       if (aRefAttr && !aRefAttr->isObject()) {
251         aCoincidentPoints.push_back(aRefAttr->attr());
252         break;
253       }
254     }
255   }
256   return aCoincidentPoints;
257 }
258
259 bool isArcArcTangencyInternal(EntityWrapperPtr theArc1, EntityWrapperPtr theArc2)
260 {
261   std::shared_ptr<GCS::Circle> aCirc1 = std::dynamic_pointer_cast<GCS::Circle>(
262       GCS_EDGE_WRAPPER(theArc1)->entity());
263   std::shared_ptr<GCS::Circle> aCirc2 = std::dynamic_pointer_cast<GCS::Circle>(
264       GCS_EDGE_WRAPPER(theArc2)->entity());
265
266   if (!aCirc1 || !aCirc2)
267     return false;
268
269   double aDX = *(aCirc1->center.x) - *(aCirc2->center.x);
270   double aDY = *(aCirc1->center.y) - *(aCirc2->center.y);
271   double aDist = sqrt(aDX * aDX + aDY * aDY);
272
273   return (aDist < *(aCirc1->rad) || aDist < *(aCirc2->rad));
274 }
275
276 // sets angle to 0 or 180 degrees
277 static void adjustAngleBetweenCurves(const GCSCurvePtr& theCurve1,
278                                      const GCSCurvePtr& theCurve2,
279                                      const GCSPointPtr& thePoint,
280                                      double*            theAngle)
281 {
282   double anAngle = GCS::System().calculateAngleViaPoint(*theCurve1, *theCurve2, *thePoint);
283   // bring angle to [-pi..pi]
284   if (anAngle >  PI) anAngle -= 2.0 * PI;
285   if (anAngle < -PI) anAngle += 2.0 * PI;
286   // set angle value according to the current angle between curves
287   if (fabs(anAngle) <= PI / 2.)
288     *theAngle = 0.0;
289   else
290     *theAngle = PI;
291 }
292
293
294 ConstraintWrapperPtr createArcLineTangency(EntityWrapperPtr theEntity1,
295                                            EntityWrapperPtr theEntity2,
296                                            EntityWrapperPtr theSharedPoint,
297                                            double*          theAngle)
298 {
299   EdgeWrapperPtr anEntLine, anEntCirc;
300   if (theEntity1->type() == ENTITY_LINE) {
301     anEntLine = GCS_EDGE_WRAPPER(theEntity1);
302     anEntCirc = GCS_EDGE_WRAPPER(theEntity2);
303   } else {
304     anEntLine = GCS_EDGE_WRAPPER(theEntity2);
305     anEntCirc = GCS_EDGE_WRAPPER(theEntity1);
306   }
307
308   std::shared_ptr<GCS::Circle> aCirc =
309       std::dynamic_pointer_cast<GCS::Circle>(anEntCirc->entity());
310   std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(aCirc);
311
312   std::shared_ptr<GCS::Line> aLine =
313       std::dynamic_pointer_cast<GCS::Line>(anEntLine->entity());
314
315   GCSConstraintPtr aNewConstr;
316   if (theSharedPoint && anArc) { // do not process shared point between circle and line
317     GCSPointPtr aPoint =
318         std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theSharedPoint)->point();
319
320     adjustAngleBetweenCurves(anArc, aLine, aPoint, theAngle);
321     aNewConstr =
322         GCSConstraintPtr(new GCS::ConstraintAngleViaPoint(*anArc, *aLine, *aPoint, theAngle));
323   } else {
324     aNewConstr =
325       GCSConstraintPtr(new GCS::ConstraintP2LDistance(aCirc->center, *aLine, aCirc->rad));
326   }
327
328   return  ConstraintWrapperPtr(
329       new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_TANGENT_CIRCLE_LINE));
330 }
331
332 ConstraintWrapperPtr createArcArcTangency(EntityWrapperPtr theEntity1,
333                                           EntityWrapperPtr theEntity2,
334                                           bool             theInternalTangency,
335                                           EntityWrapperPtr theSharedPoint,
336                                           double*          theAngle)
337 {
338   std::shared_ptr<GCS::Circle> aCirc1 =
339     std::dynamic_pointer_cast<GCS::Circle>(GCS_EDGE_WRAPPER(theEntity1)->entity());
340   std::shared_ptr<GCS::Circle> aCirc2 =
341     std::dynamic_pointer_cast<GCS::Circle>(GCS_EDGE_WRAPPER(theEntity2)->entity());
342
343   GCSConstraintPtr aNewConstr;
344   if (theSharedPoint) {
345     std::shared_ptr<GCS::Arc> anArc1 = std::dynamic_pointer_cast<GCS::Arc>(aCirc1);
346     std::shared_ptr<GCS::Arc> anArc2 = std::dynamic_pointer_cast<GCS::Arc>(aCirc2);
347     GCSPointPtr aPoint =
348         std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theSharedPoint)->point();
349
350     adjustAngleBetweenCurves(anArc1, anArc2, aPoint, theAngle);
351     aNewConstr =
352         GCSConstraintPtr(new GCS::ConstraintAngleViaPoint(*anArc1, *anArc2, *aPoint, theAngle));
353   } else {
354     aNewConstr = GCSConstraintPtr(new GCS::ConstraintTangentCircumf(aCirc1->center, aCirc2->center,
355                                   aCirc1->rad, aCirc2->rad, theInternalTangency));
356   }
357
358   return ConstraintWrapperPtr(
359       new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_TANGENT_CIRCLE_CIRCLE));
360 }