]> SALOME platform Git repositories - modules/shaper.git/blob - src/SketchSolver/SketchSolver_ConstraintTangent.cpp
Salome HOME
Task 2.4. Ability to modify the radius of circles and arcs of circle with the mouse
[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   } else if (mySharedPoint) {
189     // The features are tangent in the shared point, but the coincidence has been removed.
190     // Check if the coincidence is the same.
191     std::list<AttributePtr> aCoincidentPoints = coincidentBoundaryPoints(aTgFeat1, aTgFeat2);
192     isRebuild = true;
193     std::list<AttributePtr>::iterator anIt = aCoincidentPoints.begin();
194     for (; anIt != aCoincidentPoints.end() && isRebuild; ++anIt)
195       if (*anIt == mySharedPoint)
196         isRebuild = false; // the coincidence is still exists => nothing to change
197   }
198
199   if (isRebuild)
200     rebuild();
201 }
202
203
204
205
206 // ==================   Auxiliary functions   =================================
207 void getTangentFeatures(const ConstraintPtr& theConstraint,
208                               FeaturePtr&    theFeature1,
209                               FeaturePtr&    theFeature2)
210 {
211   AttributeRefAttrPtr aRefAttr = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_A());
212   theFeature1 = ModelAPI_Feature::feature(aRefAttr->object());
213   aRefAttr = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_B());
214   theFeature2 = ModelAPI_Feature::feature(aRefAttr->object());
215 }
216
217 std::set<FeaturePtr> collectCoincidences(FeaturePtr theFeature1, FeaturePtr theFeature2)
218 {
219   const std::set<AttributePtr>& aRefs1 = theFeature1->data()->refsToMe();
220   const std::set<AttributePtr>& aRefs2 = theFeature2->data()->refsToMe();
221
222   std::set<FeaturePtr> aCoincidences;
223   std::set<AttributePtr>::const_iterator anIt;
224
225   // collect coincidences referred to the first feature
226   for (anIt = aRefs1.begin(); anIt != aRefs1.end(); ++anIt) {
227     FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
228     if (aRef && aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID())
229       aCoincidences.insert(aRef);
230   }
231
232   // leave only coincidences referred to the second feature
233   std::set<FeaturePtr> aCoincidencesBetweenFeatures;
234   for (anIt = aRefs2.begin(); anIt != aRefs2.end(); ++anIt) {
235     FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
236     if (aCoincidences.find(aRef) != aCoincidences.end())
237       aCoincidencesBetweenFeatures.insert(aRef);
238   }
239
240   return aCoincidencesBetweenFeatures;
241 }
242
243 std::list<AttributePtr> coincidentBoundaryPoints(FeaturePtr theFeature1, FeaturePtr theFeature2)
244 {
245   std::set<FeaturePtr> aCoincidences = collectCoincidences(theFeature1, theFeature2);
246   // collect points only
247   std::list<AttributePtr> aCoincidentPoints;
248   std::set<FeaturePtr>::const_iterator aCIt = aCoincidences.begin();
249   for (; aCIt != aCoincidences.end(); ++ aCIt) {
250     for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
251       AttributeRefAttrPtr aRefAttr = (*aCIt)->refattr(SketchPlugin_Constraint::ENTITY_A());
252       if (!aRefAttr || aRefAttr->isObject())
253         continue;
254
255       AttributePtr anAttr = aRefAttr->attr();
256       if (anAttr->id() != SketchPlugin_Arc::CENTER_ID() &&
257           anAttr->id() != SketchPlugin_Circle::CENTER_ID()) {
258         aCoincidentPoints.push_back(aRefAttr->attr());
259         break;
260       }
261     }
262   }
263   return aCoincidentPoints;
264 }
265
266 bool isArcArcTangencyInternal(EntityWrapperPtr theArc1, EntityWrapperPtr theArc2)
267 {
268   std::shared_ptr<GCS::Circle> aCirc1 = std::dynamic_pointer_cast<GCS::Circle>(
269       GCS_EDGE_WRAPPER(theArc1)->entity());
270   std::shared_ptr<GCS::Circle> aCirc2 = std::dynamic_pointer_cast<GCS::Circle>(
271       GCS_EDGE_WRAPPER(theArc2)->entity());
272
273   if (!aCirc1 || !aCirc2)
274     return false;
275
276   double aDX = *(aCirc1->center.x) - *(aCirc2->center.x);
277   double aDY = *(aCirc1->center.y) - *(aCirc2->center.y);
278   double aDist = sqrt(aDX * aDX + aDY * aDY);
279
280   return (aDist < *(aCirc1->rad) || aDist < *(aCirc2->rad));
281 }
282
283 // sets angle to 0 or 180 degrees
284 static void adjustAngleBetweenCurves(const GCSCurvePtr& theCurve1,
285                                      const GCSCurvePtr& theCurve2,
286                                      const GCSPointPtr& thePoint,
287                                      double*            theAngle)
288 {
289   double anAngle = GCS::System().calculateAngleViaPoint(*theCurve1, *theCurve2, *thePoint);
290   // bring angle to [-pi..pi]
291   if (anAngle >  PI) anAngle -= 2.0 * PI;
292   if (anAngle < -PI) anAngle += 2.0 * PI;
293   // set angle value according to the current angle between curves
294   if (fabs(anAngle) <= PI / 2.)
295     *theAngle = 0.0;
296   else
297     *theAngle = PI;
298 }
299
300
301 ConstraintWrapperPtr createArcLineTangency(EntityWrapperPtr theEntity1,
302                                            EntityWrapperPtr theEntity2,
303                                            EntityWrapperPtr theSharedPoint,
304                                            double*          theAngle)
305 {
306   EdgeWrapperPtr anEntLine, anEntCirc;
307   if (theEntity1->type() == ENTITY_LINE) {
308     anEntLine = GCS_EDGE_WRAPPER(theEntity1);
309     anEntCirc = GCS_EDGE_WRAPPER(theEntity2);
310   } else {
311     anEntLine = GCS_EDGE_WRAPPER(theEntity2);
312     anEntCirc = GCS_EDGE_WRAPPER(theEntity1);
313   }
314
315   std::shared_ptr<GCS::Circle> aCirc =
316       std::dynamic_pointer_cast<GCS::Circle>(anEntCirc->entity());
317   std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(aCirc);
318
319   std::shared_ptr<GCS::Line> aLine =
320       std::dynamic_pointer_cast<GCS::Line>(anEntLine->entity());
321
322   GCSConstraintPtr aNewConstr;
323   if (theSharedPoint && anArc) { // do not process shared point between circle and line
324     GCSPointPtr aPoint =
325         std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theSharedPoint)->point();
326
327     adjustAngleBetweenCurves(anArc, aLine, aPoint, theAngle);
328     aNewConstr =
329         GCSConstraintPtr(new GCS::ConstraintAngleViaPoint(*anArc, *aLine, *aPoint, theAngle));
330   } else {
331     aNewConstr =
332       GCSConstraintPtr(new GCS::ConstraintP2LDistance(aCirc->center, *aLine, aCirc->rad));
333   }
334
335   return  ConstraintWrapperPtr(
336       new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_TANGENT_CIRCLE_LINE));
337 }
338
339 ConstraintWrapperPtr createArcArcTangency(EntityWrapperPtr theEntity1,
340                                           EntityWrapperPtr theEntity2,
341                                           bool             theInternalTangency,
342                                           EntityWrapperPtr theSharedPoint,
343                                           double*          theAngle)
344 {
345   std::shared_ptr<GCS::Circle> aCirc1 =
346     std::dynamic_pointer_cast<GCS::Circle>(GCS_EDGE_WRAPPER(theEntity1)->entity());
347   std::shared_ptr<GCS::Circle> aCirc2 =
348     std::dynamic_pointer_cast<GCS::Circle>(GCS_EDGE_WRAPPER(theEntity2)->entity());
349
350   GCSConstraintPtr aNewConstr;
351   if (theSharedPoint) {
352     std::shared_ptr<GCS::Arc> anArc1 = std::dynamic_pointer_cast<GCS::Arc>(aCirc1);
353     std::shared_ptr<GCS::Arc> anArc2 = std::dynamic_pointer_cast<GCS::Arc>(aCirc2);
354     GCSPointPtr aPoint =
355         std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theSharedPoint)->point();
356
357     adjustAngleBetweenCurves(anArc1, anArc2, aPoint, theAngle);
358     aNewConstr =
359         GCSConstraintPtr(new GCS::ConstraintAngleViaPoint(*anArc1, *anArc2, *aPoint, theAngle));
360   } else {
361     aNewConstr = GCSConstraintPtr(new GCS::ConstraintTangentCircumf(aCirc1->center, aCirc2->center,
362                                   aCirc1->rad, aCirc2->rad, theInternalTangency));
363   }
364
365   return ConstraintWrapperPtr(
366       new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_TANGENT_CIRCLE_CIRCLE));
367 }