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