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