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