]> SALOME platform Git repositories - modules/shaper.git/blob - src/SketchSolver/SketchSolver_ConstraintTangent.cpp
Salome HOME
7b01e15e4f250c902dc5ab2cf4a94c214dda8648
[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::set<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::set<AttributePtr> aCoincidentPoints = coincidentBoundaryPoints(aFeature1, aFeature2);
115   if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE && aCoincidentPoints.size() > 2) {
116     myErrorMsg = SketchSolver_Error::TANGENCY_FAILED();
117     return;
118   }
119
120   EntityWrapperPtr aSharedPointEntity;
121   if (!aCoincidentPoints.empty()) {
122     mySharedPoint = *aCoincidentPoints.begin();
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::set<AttributePtr> aCoincidentPoints = coincidentBoundaryPoints(aTgFeat1, aTgFeat2);
194     isRebuild = true;
195     std::set<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::set<AttributePtr> coincidentBoundaryPoints(FeaturePtr theFeature1, FeaturePtr theFeature2)
246 {
247   std::set<FeaturePtr> aCoincidences = collectCoincidences(theFeature1, theFeature2);
248   // collect points only
249   std::set<AttributePtr> aCoincidentPoints;
250   std::set<FeaturePtr>::const_iterator aCIt = aCoincidences.begin();
251   for (; aCIt != aCoincidences.end(); ++ aCIt) {
252     AttributeRefAttrPtr aRefAttrA = (*aCIt)->refattr(SketchPlugin_Constraint::ENTITY_A());
253     AttributeRefAttrPtr aRefAttrB = (*aCIt)->refattr(SketchPlugin_Constraint::ENTITY_B());
254     if (!aRefAttrA || aRefAttrA->isObject() ||
255         !aRefAttrB || aRefAttrB->isObject())
256       continue;
257
258     AttributePtr anAttrA = aRefAttrA->attr();
259     AttributePtr anAttrB = aRefAttrB->attr();
260     if (anAttrA->id() != SketchPlugin_Arc::CENTER_ID() &&
261         anAttrA->id() != SketchPlugin_Circle::CENTER_ID() &&
262         anAttrB->id() != SketchPlugin_Arc::CENTER_ID() &&
263         anAttrB->id() != SketchPlugin_Circle::CENTER_ID()) {
264       aCoincidentPoints.insert(anAttrA);
265       aCoincidentPoints.insert(anAttrB);
266     }
267   }
268   return aCoincidentPoints;
269 }
270
271 bool isArcArcTangencyInternal(EntityWrapperPtr theArc1, EntityWrapperPtr theArc2)
272 {
273   std::shared_ptr<GCS::Circle> aCirc1 = std::dynamic_pointer_cast<GCS::Circle>(
274       GCS_EDGE_WRAPPER(theArc1)->entity());
275   std::shared_ptr<GCS::Circle> aCirc2 = std::dynamic_pointer_cast<GCS::Circle>(
276       GCS_EDGE_WRAPPER(theArc2)->entity());
277
278   if (!aCirc1 || !aCirc2)
279     return false;
280
281   double aDX = *(aCirc1->center.x) - *(aCirc2->center.x);
282   double aDY = *(aCirc1->center.y) - *(aCirc2->center.y);
283   double aDist = sqrt(aDX * aDX + aDY * aDY);
284
285   return (aDist < *(aCirc1->rad) || aDist < *(aCirc2->rad));
286 }
287
288 // sets angle to 0 or 180 degrees
289 static void adjustAngleBetweenCurves(const GCSCurvePtr& theCurve1,
290                                      const GCSCurvePtr& theCurve2,
291                                      const GCSPointPtr& thePoint,
292                                      double*            theAngle)
293 {
294   double anAngle = GCS::System().calculateAngleViaPoint(*theCurve1, *theCurve2, *thePoint);
295   // bring angle to [-pi..pi]
296   if (anAngle >  PI) anAngle -= 2.0 * PI;
297   if (anAngle < -PI) anAngle += 2.0 * PI;
298   // set angle value according to the current angle between curves
299   if (fabs(anAngle) <= PI / 2.)
300     *theAngle = 0.0;
301   else
302     *theAngle = PI;
303 }
304
305
306 ConstraintWrapperPtr createArcLineTangency(EntityWrapperPtr theEntity1,
307                                            EntityWrapperPtr theEntity2,
308                                            EntityWrapperPtr theSharedPoint,
309                                            double*          theAngle)
310 {
311   EdgeWrapperPtr anEntLine, anEntCirc;
312   if (theEntity1->type() == ENTITY_LINE) {
313     anEntLine = GCS_EDGE_WRAPPER(theEntity1);
314     anEntCirc = GCS_EDGE_WRAPPER(theEntity2);
315   } else {
316     anEntLine = GCS_EDGE_WRAPPER(theEntity2);
317     anEntCirc = GCS_EDGE_WRAPPER(theEntity1);
318   }
319
320   std::shared_ptr<GCS::Circle> aCirc =
321       std::dynamic_pointer_cast<GCS::Circle>(anEntCirc->entity());
322   std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(aCirc);
323
324   std::shared_ptr<GCS::Line> aLine =
325       std::dynamic_pointer_cast<GCS::Line>(anEntLine->entity());
326
327   GCSConstraintPtr aNewConstr;
328   if (theSharedPoint && anArc) { // do not process shared point between circle and line
329     GCSPointPtr aPoint =
330         std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theSharedPoint)->point();
331
332     adjustAngleBetweenCurves(anArc, aLine, aPoint, theAngle);
333     aNewConstr =
334         GCSConstraintPtr(new GCS::ConstraintAngleViaPoint(*anArc, *aLine, *aPoint, theAngle));
335   } else {
336     aNewConstr =
337       GCSConstraintPtr(new GCS::ConstraintP2LDistance(aCirc->center, *aLine, aCirc->rad));
338   }
339
340   return  ConstraintWrapperPtr(
341       new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_TANGENT_CIRCLE_LINE));
342 }
343
344 ConstraintWrapperPtr createArcArcTangency(EntityWrapperPtr theEntity1,
345                                           EntityWrapperPtr theEntity2,
346                                           bool             theInternalTangency,
347                                           EntityWrapperPtr theSharedPoint,
348                                           double*          theAngle)
349 {
350   std::shared_ptr<GCS::Circle> aCirc1 =
351     std::dynamic_pointer_cast<GCS::Circle>(GCS_EDGE_WRAPPER(theEntity1)->entity());
352   std::shared_ptr<GCS::Circle> aCirc2 =
353     std::dynamic_pointer_cast<GCS::Circle>(GCS_EDGE_WRAPPER(theEntity2)->entity());
354
355   GCSConstraintPtr aNewConstr;
356   if (theSharedPoint) {
357     GCSPointPtr aPoint =
358         std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theSharedPoint)->point();
359
360     adjustAngleBetweenCurves(aCirc1, aCirc2, aPoint, theAngle);
361     aNewConstr =
362         GCSConstraintPtr(new GCS::ConstraintAngleViaPoint(*aCirc1, *aCirc2, *aPoint, theAngle));
363   } else {
364     aNewConstr = GCSConstraintPtr(new GCS::ConstraintTangentCircumf(aCirc1->center, aCirc2->center,
365                                   aCirc1->rad, aCirc2->rad, theInternalTangency));
366   }
367
368   return ConstraintWrapperPtr(
369       new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_TANGENT_CIRCLE_CIRCLE));
370 }