Salome HOME
Merge remote-tracking branch 'origin/CEA_2019'
[modules/shaper.git] / src / SketchSolver / SketchSolver_ConstraintCoincidence.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_ConstraintCoincidence.h>
21 #include <SketchSolver_Error.h>
22 #include <PlaneGCSSolver_Tools.h>
23 #include <PlaneGCSSolver_UpdateCoincidence.h>
24
25 #include <GeomDataAPI_Point2D.h>
26
27 #include <SketchPlugin_Arc.h>
28 #include <SketchPlugin_ConstraintCoincidenceInternal.h>
29 #include <SketchPlugin_Ellipse.h>
30 #include <SketchPlugin_EllipticArc.h>
31 #include <SketchPlugin_Line.h>
32 #include <SketchPlugin_Point.h>
33
34 static void getCoincidentFeatureExtremities(const ConstraintPtr& theConstraint,
35                                             const StoragePtr& theStorage,
36                                             EntityWrapperPtr theExtremities[2])
37 {
38   for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
39     AttributeRefAttrPtr aRefAttr = theConstraint->refattr(SketchPlugin_Constraint::ATTRIBUTE(i));
40     if (!aRefAttr || !aRefAttr->isObject())
41       continue;
42
43     FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttr->object());
44     if (!aFeature)
45       continue;
46
47     if (aFeature->getKind() == SketchPlugin_Line::ID()) {
48       theExtremities[0] = theStorage->entity(aFeature->attribute(SketchPlugin_Line::START_ID()));
49       theExtremities[1] = theStorage->entity(aFeature->attribute(SketchPlugin_Line::END_ID()));
50     } else if (aFeature->getKind() == SketchPlugin_Arc::ID()) {
51       theExtremities[0] = theStorage->entity(aFeature->attribute(SketchPlugin_Arc::START_ID()));
52       theExtremities[1] = theStorage->entity(aFeature->attribute(SketchPlugin_Arc::END_ID()));
53     } else if (aFeature->getKind() == SketchPlugin_EllipticArc::ID()) {
54       theExtremities[0] = theStorage->entity(
55           aFeature->attribute(SketchPlugin_EllipticArc::START_POINT_ID()));
56       theExtremities[1] = theStorage->entity(
57           aFeature->attribute(SketchPlugin_EllipticArc::END_POINT_ID()));
58     }
59   }
60 }
61
62 static void getPointOwnerAndParent(const AttributeRefAttrPtr theRefAttr,
63                                    AttributePoint2DPtr& thePoint,
64                                    FeaturePtr& theOwner,
65                                    FeaturePtr& theParent)
66 {
67   AttributePtr anAttr = theRefAttr->attr();
68   if (theRefAttr->isObject()) {
69     FeaturePtr anOwner = ModelAPI_Feature::feature(theRefAttr->object());
70     if (anOwner && anOwner->getKind() == SketchPlugin_Point::ID())
71       anAttr = anOwner->attribute(SketchPlugin_Point::COORD_ID());
72     else
73       return;
74   }
75   thePoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttr);
76   if (thePoint) {
77     theOwner = ModelAPI_Feature::feature(thePoint->owner());
78     if (theOwner) {
79       std::string aParentRefID;
80       if (theOwner->getKind() == SketchPlugin_Line::ID())
81         aParentRefID = SketchPlugin_Line::PARENT_ID();
82       else if (theOwner->getKind() == SketchPlugin_Point::ID())
83         aParentRefID = SketchPlugin_Point::PARENT_ID();
84       if (!aParentRefID.empty()) {
85         AttributeReferencePtr aParentRef = theOwner->reference(aParentRefID);
86         theParent = aParentRef ? ModelAPI_Feature::feature(aParentRef->value()) : FeaturePtr();
87       }
88     }
89   }
90 }
91
92 static void ellipseDiameters(FeaturePtr theEllipse,
93                              std::pair<std::string, std::string>& theMajorAxis,
94                              std::pair<std::string, std::string>& theMinorAxis)
95 {
96   if (theEllipse->getKind() == SketchPlugin_Ellipse::ID()) {
97     theMajorAxis.first = SketchPlugin_Ellipse::MAJOR_AXIS_START_ID();
98     theMajorAxis.second = SketchPlugin_Ellipse::MAJOR_AXIS_END_ID();
99     theMinorAxis.first = SketchPlugin_Ellipse::MINOR_AXIS_START_ID();
100     theMinorAxis.second = SketchPlugin_Ellipse::MINOR_AXIS_END_ID();
101   } else if (theEllipse->getKind() == SketchPlugin_EllipticArc::ID()) {
102     theMajorAxis.first = SketchPlugin_EllipticArc::MAJOR_AXIS_START_ID();
103     theMajorAxis.second = SketchPlugin_EllipticArc::MAJOR_AXIS_END_ID();
104     theMinorAxis.first = SketchPlugin_EllipticArc::MINOR_AXIS_START_ID();
105     theMinorAxis.second = SketchPlugin_EllipticArc::MINOR_AXIS_END_ID();
106   }
107 }
108
109 static void findDiameterOnEllipse(FeaturePtr theConstruction,
110                                   FeaturePtr theEllipse,
111                                   AttributePtr& theStart,
112                                   AttributePtr& theEnd)
113 {
114   AttributePtr anEllipseAttr;
115   const std::set<AttributePtr>& aRefs = theConstruction->data()->refsToMe();
116   for (std::set<AttributePtr>::const_iterator aRefIt = aRefs.begin();
117        aRefIt != aRefs.end(); ++aRefIt) {
118     FeaturePtr anOwner = ModelAPI_Feature::feature((*aRefIt)->owner());
119     if (anOwner && anOwner->getKind() == SketchPlugin_ConstraintCoincidenceInternal::ID()) {
120       AttributeRefAttrPtr aRefAttr;
121       if ((*aRefIt)->id() == SketchPlugin_Constraint::ENTITY_A())
122         aRefAttr = anOwner->refattr(SketchPlugin_Constraint::ENTITY_B());
123       else
124         aRefAttr = anOwner->refattr(SketchPlugin_Constraint::ENTITY_A());
125       anEllipseAttr = aRefAttr->attr();
126       break;
127     }
128   }
129   if (!anEllipseAttr)
130     return;
131
132   std::pair<std::string, std::string> aMajorAxis, aMinorAxis;
133   ellipseDiameters(theEllipse, aMajorAxis, aMinorAxis);
134   if (anEllipseAttr->id() == aMajorAxis.first) {
135     theStart = anEllipseAttr;
136     theEnd = theEllipse->attribute(aMajorAxis.second);
137   }
138   else if (anEllipseAttr->id() == aMajorAxis.second) {
139     theStart = theEllipse->attribute(aMajorAxis.first);
140     theEnd = anEllipseAttr;
141   }
142   else if (anEllipseAttr->id() == aMinorAxis.first) {
143     theStart = anEllipseAttr;
144     theEnd = theEllipse->attribute(aMinorAxis.second);
145   }
146   else if (anEllipseAttr->id() == aMinorAxis.second) {
147     theStart = theEllipse->attribute(aMinorAxis.first);
148     theEnd = anEllipseAttr;
149   }
150 }
151
152 static void processEllipticArcExtremities(SketchSolver_ConstraintType& theType,
153                                           const ConstraintPtr& theConstraint,
154                                           const StoragePtr& theStorage,
155                                           std::vector<EntityWrapperPtr>& theAttributes,
156                                           EntityWrapperPtr theExtremities[2])
157 {
158   AttributePoint2DPtr aPointA, aPointB;
159   FeaturePtr anOwnerA, anOwnerB;
160   FeaturePtr aParentA, aParentB;
161   getPointOwnerAndParent(theConstraint->refattr(SketchPlugin_Constraint::ENTITY_A()),
162                          aPointA, anOwnerA, aParentA);
163   getPointOwnerAndParent(theConstraint->refattr(SketchPlugin_Constraint::ENTITY_B()),
164                          aPointB, anOwnerB, aParentB);
165
166   AttributePtr anAxisStart, anAxisEnd, aPoint;
167   FeaturePtr aConstruction, anEllipticArc;
168   if (aParentA && aParentA == anOwnerB) {
169     aPoint = aPointB;
170     aConstruction = anOwnerA;
171     anEllipticArc = anOwnerB;
172   }
173   else if (aParentB && aParentB == anOwnerA) {
174     aPoint = aPointA;
175     aConstruction = anOwnerB;
176     anEllipticArc = anOwnerA;
177   }
178
179   if (!anEllipticArc || anEllipticArc->getKind() != SketchPlugin_EllipticArc::ID() ||
180       (aPoint->id() != SketchPlugin_EllipticArc::START_POINT_ID() &&
181        aPoint->id() != SketchPlugin_EllipticArc::END_POINT_ID()))
182     return;
183
184   findDiameterOnEllipse(aConstruction, anEllipticArc, anAxisStart, anAxisEnd);
185
186   if (anAxisStart && anAxisEnd) {
187     theAttributes[0] = theStorage->entity(aPoint);
188     theAttributes[1] = theStorage->entity(anAxisStart);
189     theAttributes[2] = theStorage->entity(anAxisEnd);
190     theType = CONSTRAINT_PT_ON_CURVE;
191     getCoincidentFeatureExtremities(theConstraint, theStorage, theExtremities);
192   }
193 }
194
195
196 void SketchSolver_ConstraintCoincidence::process()
197 {
198   cleanErrorMsg();
199   if (!myBaseConstraint || !myStorage) {
200     // Not enough parameters are assigned
201     return;
202   }
203
204   EntityWrapperPtr aValue;
205   std::vector<EntityWrapperPtr> anAttributes;
206   getAttributes(aValue, anAttributes);
207   if (!myErrorMsg.empty())
208     return;
209   if (anAttributes.empty()) {
210     myErrorMsg = SketchSolver_Error::INCORRECT_ATTRIBUTE();
211     return;
212   }
213
214   mySolverConstraint = PlaneGCSSolver_Tools::createConstraint(
215       myBaseConstraint, getType(),
216       aValue, anAttributes[0], anAttributes[1], anAttributes[2], anAttributes[3]);
217
218   myStorage->subscribeUpdates(this, PlaneGCSSolver_UpdateCoincidence::GROUP());
219   myStorage->notify(myBaseConstraint);
220 }
221
222 bool SketchSolver_ConstraintCoincidence::remove()
223 {
224   myInSolver = false;
225   myFeatureExtremities[0] = EntityWrapperPtr();
226   myFeatureExtremities[1] = EntityWrapperPtr();
227   return SketchSolver_Constraint::remove();
228 }
229
230 void SketchSolver_ConstraintCoincidence::getAttributes(
231     EntityWrapperPtr& theValue,
232     std::vector<EntityWrapperPtr>& theAttributes)
233 {
234   SketchSolver_Constraint::getAttributes(theValue, theAttributes);
235   if (!myErrorMsg.empty() || !theAttributes[0]) {
236     theAttributes.clear();
237     return;
238   }
239
240   if (theAttributes[1]) {
241     myType = CONSTRAINT_PT_PT_COINCIDENT;
242     // if elliptic arc boundary point is connected with one of ellipse characteristics,
243     // it should be changed from point-point coincidence to coincidence between point
244     // and axis of the ellipse to decrease only 1 DoF instead of 2 DoF and avoid overconstraint.
245     processEllipticArcExtremities(myType, myBaseConstraint, myStorage,
246                                   theAttributes, myFeatureExtremities);
247   } else if (theAttributes[2]) {
248     myType = CONSTRAINT_PT_ON_CURVE;
249     // obtain extremity points of the coincident feature for further checking of multi-coincidence
250     getCoincidentFeatureExtremities(myBaseConstraint, myStorage, myFeatureExtremities);
251   } else
252     myErrorMsg = SketchSolver_Error::INCORRECT_ATTRIBUTE();
253 }
254
255 void SketchSolver_ConstraintCoincidence::notify(const FeaturePtr&      theFeature,
256                                                 PlaneGCSSolver_Update* theUpdater)
257 {
258   PlaneGCSSolver_UpdateCoincidence* anUpdater =
259       static_cast<PlaneGCSSolver_UpdateCoincidence*>(theUpdater);
260   bool isAccepted = anUpdater->addCoincidence(myAttributes.front(), myAttributes.back());
261   // additionally process internal coincidence, set point coincident with ellipse/elliptic arc
262   // for correct processing further coincidences set by the user
263   if (isAccepted &&
264       myBaseConstraint->getKind() == SketchPlugin_ConstraintCoincidenceInternal::ID()) {
265     AttributeRefAttrPtr aRefAttrA = myBaseConstraint->refattr(SketchPlugin_Constraint::ENTITY_A());
266     AttributeRefAttrPtr aRefAttrB = myBaseConstraint->refattr(SketchPlugin_Constraint::ENTITY_B());
267     if (aRefAttrA && aRefAttrB) {
268       AttributePoint2DPtr anAttrA, anAttrB;
269       FeaturePtr anOwnerA, anOwnerB;
270       FeaturePtr aParentA, aParentB;
271       getPointOwnerAndParent(aRefAttrA, anAttrA, anOwnerA, aParentA);
272       getPointOwnerAndParent(aRefAttrB, anAttrB, anOwnerB, aParentB);
273
274       EntityWrapperPtr aPoint, anEntity;
275       if (aParentA == anOwnerB) {
276         aPoint = myStorage->entity(anAttrA);
277         anEntity = myStorage->entity(anOwnerB);
278       }
279       else if (aParentB == anOwnerA) {
280         aPoint = myStorage->entity(anAttrB);
281         anEntity = myStorage->entity(anOwnerA);
282       }
283       if (aPoint && anEntity)
284         anUpdater->addCoincidence(aPoint, anEntity);
285     }
286   }
287
288   // additionally check the point is coincident to extremity of coincident feature
289   if (myFeatureExtremities[0] && myFeatureExtremities[1]) {
290     EntityWrapperPtr aPoint =
291         myAttributes.front()->type() == ENTITY_POINT ? myAttributes.front() : myAttributes.back();
292
293     for (int i = 0; i < 2; ++i)
294       isAccepted = isAccepted && !anUpdater->isPointOnEntity(aPoint, myFeatureExtremities[i]);
295   }
296
297   if (isAccepted) {
298     if (!myInSolver) {
299       myInSolver = true;
300       myStorage->addConstraint(myBaseConstraint, mySolverConstraint);
301     }
302   } else {
303     if (myInSolver) {
304       myInSolver = false;
305       myStorage->removeConstraint(myBaseConstraint);
306     }
307   }
308 }