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