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