Salome HOME
Merge remote-tracking branch 'remotes/origin/master' into CEA_2019
[modules/shaper.git] / src / SketchSolver / PlaneGCSSolver / PlaneGCSSolver_Storage.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 <PlaneGCSSolver_Storage.h>
21 #include <PlaneGCSSolver_Solver.h>
22 #include <PlaneGCSSolver_BooleanWrapper.h>
23 #include <PlaneGCSSolver_ConstraintWrapper.h>
24 #include <PlaneGCSSolver_EdgeWrapper.h>
25 #include <PlaneGCSSolver_PointWrapper.h>
26 #include <PlaneGCSSolver_Tools.h>
27
28 #include <PlaneGCSSolver_AttributeBuilder.h>
29 #include <PlaneGCSSolver_FeatureBuilder.h>
30 #include <PlaneGCSSolver_EntityDestroyer.h>
31
32 #include <GeomAPI_Dir2d.h>
33 #include <GeomAPI_Pnt2d.h>
34 #include <GeomAPI_XY.h>
35 #include <GeomDataAPI_Point2D.h>
36 #include <ModelAPI_AttributeRefAttr.h>
37 #include <SketchPlugin_Projection.h>
38
39 #include <cmath>
40
41
42 static void constraintsToSolver(const ConstraintWrapperPtr& theConstraint,
43                                 const SolverPtr& theSolver)
44 {
45   const std::list<GCSConstraintPtr>& aConstraints =
46       std::dynamic_pointer_cast<PlaneGCSSolver_ConstraintWrapper>(theConstraint)->constraints();
47   theSolver->addConstraint(theConstraint->id(), aConstraints);
48 }
49
50
51 PlaneGCSSolver_Storage::PlaneGCSSolver_Storage(const SolverPtr& theSolver)
52   : SketchSolver_Storage(theSolver),
53     myConstraintLastID(CID_UNKNOWN)
54 {
55 }
56
57 void PlaneGCSSolver_Storage::addConstraint(
58     ConstraintPtr        theConstraint,
59     ConstraintWrapperPtr theSolverConstraint)
60 {
61   SketchSolver_Storage::addConstraint(theConstraint, theSolverConstraint);
62
63   theSolverConstraint->setId(++myConstraintLastID);
64   constraintsToSolver(theSolverConstraint, mySketchSolver);
65 }
66
67 void PlaneGCSSolver_Storage::addMovementConstraint(
68     const ConstraintWrapperPtr& theSolverConstraint)
69 {
70   // before adding movement constraint to solver, re-check its DOF
71   if (mySketchSolver->dof() == 0)
72     mySketchSolver->diagnose();
73
74   theSolverConstraint->setId(CID_MOVEMENT);
75   constraintsToSolver(theSolverConstraint, mySketchSolver);
76 }
77
78
79 EntityWrapperPtr PlaneGCSSolver_Storage::createFeature(
80     const FeaturePtr&             theFeature,
81     PlaneGCSSolver_EntityBuilder* theBuilder)
82 {
83   std::list<AttributePtr> anAttributes = theFeature->data()->attributes(std::string());
84   std::list<AttributePtr>::const_iterator anIt = anAttributes.begin();
85   for (; anIt != anAttributes.end(); ++anIt)
86     createAttribute(*anIt, theBuilder);
87
88   EntityWrapperPtr aResult = theBuilder->createFeature(theFeature);
89   if (aResult)
90     addEntity(theFeature, aResult);
91   return aResult;
92 }
93
94 EntityWrapperPtr PlaneGCSSolver_Storage::createAttribute(
95     const AttributePtr&           theAttribute,
96     PlaneGCSSolver_EntityBuilder* theBuilder)
97 {
98   EntityWrapperPtr aResult = theBuilder->createAttribute(theAttribute);
99   if (aResult)
100     addEntity(theAttribute, aResult);
101   return aResult;
102 }
103
104 /// \brief Update value
105 static bool updateValue(const double& theSource, double& theDest)
106 {
107   static const double aTol = 1000. * tolerance;
108   bool isUpdated = fabs(theSource - theDest) > aTol;
109   if (isUpdated)
110     theDest = theSource;
111   return isUpdated;
112 }
113
114 /// \brief Update coordinates of the point or scalar using its base attribute
115 static bool updateValues(AttributePtr& theAttribute, EntityWrapperPtr& theEntity)
116 {
117   bool isUpdated = false;
118
119   std::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
120       std::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute);
121   if (aPoint2D) {
122     const GCSPointPtr& aGCSPoint =
123         std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theEntity)->point();
124     isUpdated = updateValue(aPoint2D->x(), *(aGCSPoint->x)) || isUpdated;
125     isUpdated = updateValue(aPoint2D->y(), *(aGCSPoint->y)) || isUpdated;
126   } else {
127     AttributeDoublePtr aScalar =
128         std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theAttribute);
129     if (aScalar) {
130       ScalarWrapperPtr aWrapper =
131           std::dynamic_pointer_cast<PlaneGCSSolver_ScalarWrapper>(theEntity);
132       // There is possible angular value, which is converted between degrees and radians.
133       // So, we use its value instead of using direct pointer to value.
134       double aValue = aWrapper->value();
135       isUpdated = updateValue(aScalar->value(), aValue);
136       if (isUpdated)
137         aWrapper->setValue(aValue);
138     } else {
139       AttributeBooleanPtr aBoolean =
140           std::dynamic_pointer_cast<ModelAPI_AttributeBoolean>(theAttribute);
141       if (aBoolean) {
142         BooleanWrapperPtr aWrapper =
143             std::dynamic_pointer_cast<PlaneGCSSolver_BooleanWrapper>(theEntity);
144         isUpdated = aWrapper->value() != aBoolean->value();
145         aWrapper->setValue(aBoolean->value());
146       }
147     }
148   }
149
150   return isUpdated;
151 }
152
153 static bool hasReference(std::shared_ptr<SketchPlugin_Feature> theFeature,
154                          const std::string& theFeatureKind)
155 {
156   const std::set<AttributePtr>& aRefs = theFeature->data()->refsToMe();
157   for (std::set<AttributePtr>::const_iterator aRefIt = aRefs.begin();
158        aRefIt != aRefs.end(); ++aRefIt) {
159      FeaturePtr anOwner = ModelAPI_Feature::feature((*aRefIt)->owner());
160      if (anOwner && anOwner->getKind() == theFeatureKind)
161        return true;
162   }
163   return false;
164 }
165
166 static bool isCopyFeature(std::shared_ptr<SketchPlugin_Feature> theFeature)
167 {
168   return theFeature && theFeature->isCopy();
169 }
170
171 bool PlaneGCSSolver_Storage::update(FeaturePtr theFeature, bool theForce)
172 {
173   bool sendNotify = false;
174   bool isUpdated = false;
175   std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
176       std::dynamic_pointer_cast<SketchPlugin_Feature>(theFeature);
177   EntityWrapperPtr aRelated = entity(theFeature);
178   if (aRelated) // send signal to subscribers
179     sendNotify = true;
180   else { // Feature is not exist, create it
181     bool isCopy = isCopyFeature(aSketchFeature);
182     bool isProjReferred = hasReference(aSketchFeature, SketchPlugin_Projection::ID());
183     // the feature is a copy in "Multi" constraint and does not used in other constraints
184     if (!theForce && (isCopy && !isProjReferred) &&
185         myFeatureMap.find(theFeature) == myFeatureMap.end())
186       return false;
187
188     // external feature processing
189     bool isExternal =
190         (aSketchFeature && (aSketchFeature->isExternal() || isCopy || isProjReferred));
191
192     PlaneGCSSolver_FeatureBuilder aBuilder(isExternal ? 0 : this);
193
194     // Reserve the feature in the map of features
195     // (do not want to add several copies of it while adding attributes)
196     aRelated = createFeature(theFeature, &aBuilder);
197     myFeatureMap[theFeature] = aRelated;
198     createArcConstraints(aRelated);
199     isUpdated = true;
200   }
201
202   std::list<AttributePtr> anAttributes = theFeature->data()->attributes(std::string());
203   std::list<AttributePtr>::iterator anAttrIt = anAttributes.begin();
204   for (; anAttrIt != anAttributes.end(); ++anAttrIt)
205     if ((*anAttrIt)->attributeType() == GeomDataAPI_Point2D::typeId() ||
206         (*anAttrIt)->attributeType() == ModelAPI_AttributeDouble::typeId() ||
207         (*anAttrIt)->attributeType() == ModelAPI_AttributeBoolean::typeId())
208       isUpdated = update(*anAttrIt) || isUpdated;
209
210   // check external attribute is changed
211   bool isExternal = aSketchFeature &&
212                    (aSketchFeature->isExternal() || isCopyFeature(aSketchFeature));
213   if (aRelated && isExternal != aRelated->isExternal()) {
214     if (isExternal)
215       makeExternal(aRelated);
216     else
217       makeNonExternal(aRelated);
218     isUpdated = true;
219   }
220
221   // send notification to listeners due to at least one attribute is changed
222   if (sendNotify && isUpdated)
223     notify(theFeature);
224
225   return isUpdated;
226 }
227
228 bool PlaneGCSSolver_Storage::update(AttributePtr theAttribute, bool theForce)
229 {
230   if (!theAttribute->isInitialized())
231     return false;
232
233   AttributePtr anAttribute = theAttribute;
234   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(anAttribute);
235   if (aRefAttr) {
236     if (aRefAttr->isObject()) {
237       FeaturePtr aFeature;
238       /// TODO: Check resultToFeatureOrAttribute() precisely.
239       resultToFeatureOrAttribute(aRefAttr->object(), aFeature, anAttribute);
240       if (aFeature)
241         return update(aFeature, theForce);
242     } else
243       anAttribute = aRefAttr->attr();
244   }
245
246   EntityWrapperPtr aRelated = entity(anAttribute);
247   FeaturePtr aFeature = ModelAPI_Feature::feature(anAttribute->owner());
248   if (!aRelated) { // Attribute does not exist, create it.
249     // First of all check if the parent feature exists. If not, add it.
250     if (aFeature && myFeatureMap.find(aFeature) == myFeatureMap.end())
251       return update(aFeature, theForce); // theAttribute has been processed while adding feature
252     return aRelated.get() != 0;
253   }
254
255   bool isUpdated = updateValues(anAttribute, aRelated);
256   if (isUpdated) {
257     setNeedToResolve(true);
258     notify(aFeature);
259   }
260   return isUpdated;
261 }
262
263 void PlaneGCSSolver_Storage::makeExternal(const EntityWrapperPtr& theEntity)
264 {
265   if (theEntity->isExternal())
266     return;
267
268   removeArcConstraints(theEntity);
269
270   GCS::SET_pD aParameters = PlaneGCSSolver_Tools::parameters(theEntity);
271   mySketchSolver->removeParameters(aParameters);
272   theEntity->setExternal(true);
273   myNeedToResolve = true;
274 }
275
276 void PlaneGCSSolver_Storage::makeNonExternal(const EntityWrapperPtr& theEntity)
277 {
278   if (!theEntity->isExternal())
279     return;
280
281   GCS::SET_pD aParameters = PlaneGCSSolver_Tools::parameters(theEntity);
282   mySketchSolver->addParameters(aParameters);
283   theEntity->setExternal(false);
284
285   createArcConstraints(theEntity);
286
287   myNeedToResolve = true;
288 }
289
290
291 void PlaneGCSSolver_Storage::createArcConstraints(const EntityWrapperPtr& theArc)
292 {
293   if (!theArc || theArc->type() != ENTITY_ARC || theArc->isExternal())
294     return;
295
296   EdgeWrapperPtr anEdge = std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(theArc);
297   std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(anEdge->entity());
298
299   // Additional constaints to fix arc's extra DoF (if the arc is not external):
300   std::list<GCSConstraintPtr> anArcConstraints;
301   // constrain the start point on the arc
302   anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintCurveValue(
303       anArc->start, anArc->start.x, *anArc, anArc->startAngle)));
304   anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintCurveValue(
305       anArc->start, anArc->start.y, *anArc, anArc->startAngle)));
306   // constrain the end point on the arc
307   anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintCurveValue(
308       anArc->end, anArc->end.x, *anArc, anArc->endAngle)));
309   anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintCurveValue(
310       anArc->end, anArc->end.y, *anArc, anArc->endAngle)));
311
312   ConstraintWrapperPtr aWrapper(
313       new PlaneGCSSolver_ConstraintWrapper(anArcConstraints, CONSTRAINT_UNKNOWN));
314   aWrapper->setId(++myConstraintLastID);
315   constraintsToSolver(aWrapper, mySketchSolver);
316
317   myArcConstraintMap[theArc] = aWrapper;
318 }
319
320 void PlaneGCSSolver_Storage::removeArcConstraints(const EntityWrapperPtr& theArc)
321 {
322   std::map<EntityWrapperPtr, ConstraintWrapperPtr>::iterator
323       aFound = myArcConstraintMap.find(theArc);
324   if (aFound != myArcConstraintMap.end()) {
325     mySketchSolver->removeConstraint(aFound->second->id());
326     myArcConstraintMap.erase(aFound);
327   }
328 }
329
330 void PlaneGCSSolver_Storage::adjustParametrizationOfArcs()
331 {
332   std::map<EntityWrapperPtr, ConstraintWrapperPtr>::iterator anIt = myArcConstraintMap.begin();
333   for (; anIt != myArcConstraintMap.end(); ++anIt) {
334     EdgeWrapperPtr anEdge = std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(anIt->first);
335     std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(anEdge->entity());
336     // tune start angle of the arc to be in [0, 2PI]
337     while (*anArc->startAngle < -PI)
338       *anArc->startAngle += 2.0 * PI;
339     while (*anArc->startAngle >= PI)
340       *anArc->startAngle -= 2.0 * PI;
341     // adjust end angle of the arc
342     if (anEdge->isReversed()) {
343       while (*anArc->endAngle > *anArc->startAngle)
344         *anArc->endAngle -= 2.0 * PI;
345       while (*anArc->endAngle + 2 * PI < *anArc->startAngle)
346         *anArc->endAngle += 2.0 * PI;
347     } else {
348       while (*anArc->endAngle < *anArc->startAngle)
349         *anArc->endAngle += 2.0 * PI;
350       while (*anArc->endAngle > *anArc->startAngle + 2 * PI)
351         *anArc->endAngle -= 2.0 * PI;
352     }
353   }
354
355   // update parameters of Middle point constraint for point on arc
356   std::map<ConstraintPtr, ConstraintWrapperPtr>::iterator aCIt = myConstraintMap.begin();
357   for (; aCIt != myConstraintMap.end(); ++aCIt)
358     if (aCIt->second->type() == CONSTRAINT_MIDDLE_POINT) {
359       notify(aCIt->first);
360     }
361 }
362
363
364 bool PlaneGCSSolver_Storage::removeConstraint(ConstraintPtr theConstraint)
365 {
366   std::map<ConstraintPtr, ConstraintWrapperPtr>::iterator
367       aFound = myConstraintMap.find(theConstraint);
368   if (aFound != myConstraintMap.end()) {
369     ConstraintWrapperPtr aCW = aFound->second;
370     ConstraintID anID = aCW->id();
371
372     // Remove solver's constraints
373     mySketchSolver->removeConstraint(anID);
374
375     // Remove value if exists
376     const ScalarWrapperPtr& aValue = aCW->valueParameter();
377     if (aValue) {
378       GCS::SET_pD aParToRemove;
379       aParToRemove.insert(aValue->scalar());
380       removeParameters(aParToRemove);
381     }
382
383     // Remove constraint
384     myConstraintMap.erase(aFound);
385
386     if (anID != CID_MOVEMENT)
387       myNeedToResolve = true;
388
389     // notify subscibers
390     notify(theConstraint);
391   }
392   return true;
393 }
394
395 void PlaneGCSSolver_Storage::removeInvalidEntities()
396 {
397   PlaneGCSSolver_EntityDestroyer aDestroyer;
398
399   // Remove invalid constraints
400   std::list<ConstraintPtr> anInvalidConstraints;
401   std::map<ConstraintPtr, ConstraintWrapperPtr>::const_iterator
402       aCIter = myConstraintMap.begin();
403   for (; aCIter != myConstraintMap.end(); ++aCIter)
404     if (!aCIter->first->data() || !aCIter->first->data()->isValid())
405       anInvalidConstraints.push_back(aCIter->first);
406   std::list<ConstraintPtr>::const_iterator anInvCIt = anInvalidConstraints.begin();
407   for (; anInvCIt != anInvalidConstraints.end(); ++anInvCIt)
408     removeConstraint(*anInvCIt);
409
410   // Remove invalid features
411   std::list<FeaturePtr> anInvalidFeatures;
412   std::map<FeaturePtr, EntityWrapperPtr>::const_iterator aFIter = myFeatureMap.begin();
413   for (; aFIter != myFeatureMap.end(); aFIter++)
414     if (!aFIter->first->data() || !aFIter->first->data()->isValid()) {
415       anInvalidFeatures.push_back(aFIter->first);
416       if (aFIter->second)
417         aDestroyer.remove(aFIter->second);
418
419       // remove invalid arc
420       removeArcConstraints(aFIter->second);
421     }
422   std::list<FeaturePtr>::const_iterator anInvFIt = anInvalidFeatures.begin();
423   for (; anInvFIt != anInvalidFeatures.end(); ++anInvFIt)
424     removeFeature(*anInvFIt);
425
426   // Remove invalid attributes
427   std::list<AttributePtr> anInvalidAttributes;
428   std::map<AttributePtr, EntityWrapperPtr>::const_iterator anAttrIt = myAttributeMap.begin();
429   for (; anAttrIt != myAttributeMap.end(); ++anAttrIt) {
430     FeaturePtr anOwner = ModelAPI_Feature::feature(anAttrIt->first->owner());
431     if (!anOwner || !anOwner->data() || !anOwner->data()->isValid()) {
432       anInvalidAttributes.push_back(anAttrIt->first);
433       aDestroyer.remove(anAttrIt->second);
434     }
435   }
436   std::list<AttributePtr>::const_iterator anInvAtIt = anInvalidAttributes.begin();
437   for (; anInvAtIt != anInvalidAttributes.end(); ++anInvAtIt)
438     removeAttribute(*anInvAtIt);
439
440   // free memory occupied by parameters
441   removeParameters(aDestroyer.parametersToRemove());
442
443   /// TODO: Think on optimization of checking invalid features and attributes
444 }
445
446
447
448 double* PlaneGCSSolver_Storage::createParameter()
449 {
450   return mySketchSolver->createParameter();
451 }
452
453 void PlaneGCSSolver_Storage::removeParameters(const GCS::SET_pD& theParams)
454 {
455   mySketchSolver->removeParameters(theParams);
456 }
457
458 // indicates attribute containing in the external feature
459 static bool isExternalAttribute(const AttributePtr& theAttribute)
460 {
461   if (!theAttribute)
462     return false;
463   std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
464       std::dynamic_pointer_cast<SketchPlugin_Feature>(theAttribute->owner());
465   return aSketchFeature.get() && aSketchFeature->isExternal();
466 }
467
468 static void addOwnerToSet(const AttributePtr& theAttribute, std::set<FeaturePtr>& theFeatures)
469 {
470   FeaturePtr anOwner = ModelAPI_Feature::feature(theAttribute->owner());
471   if (anOwner)
472     theFeatures.insert(anOwner);
473 }
474
475 void PlaneGCSSolver_Storage::refresh() const
476 {
477   const double aTol = 1000. * tolerance; // tolerance to prevent frequent updates
478
479   std::set<FeaturePtr> anUpdatedFeatures;
480
481   std::map<AttributePtr, EntityWrapperPtr>::const_iterator anIt = myAttributeMap.begin();
482   for (; anIt != myAttributeMap.end(); ++anIt) {
483     if (!anIt->first->isInitialized())
484       continue;
485
486     // the external feature always should keep the up to date values, so,
487     // refresh from the solver is never needed
488     if (isExternalAttribute(anIt->first))
489       continue;
490
491     std::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
492         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anIt->first);
493     if (aPoint2D) {
494       std::shared_ptr<PlaneGCSSolver_PointWrapper> aPointWrapper =
495           std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(anIt->second);
496       GCSPointPtr aGCSPoint = aPointWrapper->point();
497       if (fabs(aPoint2D->x() - (*aGCSPoint->x)) > aTol ||
498           fabs(aPoint2D->y() - (*aGCSPoint->y)) > aTol) {
499         aPoint2D->setValue(*aGCSPoint->x, *aGCSPoint->y);
500         addOwnerToSet(anIt->first, anUpdatedFeatures);
501       }
502       continue;
503     }
504     AttributeDoublePtr aScalar = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(anIt->first);
505     if (aScalar) {
506       ScalarWrapperPtr aScalarWrapper =
507           std::dynamic_pointer_cast<PlaneGCSSolver_ScalarWrapper>(anIt->second);
508       if (fabs(aScalar->value() - aScalarWrapper->value()) > aTol) {
509         aScalar->setValue(aScalarWrapper->value());
510         addOwnerToSet(anIt->first, anUpdatedFeatures);
511       }
512       continue;
513     }
514   }
515
516   // notify listeners about features update
517   std::set<FeaturePtr>::const_iterator aFIt = anUpdatedFeatures.begin();
518   for (; aFIt != anUpdatedFeatures.end(); ++aFIt)
519     notify(*aFIt);
520 }
521
522 PlaneGCSSolver_Solver::SolveStatus PlaneGCSSolver_Storage::checkDegeneratedGeometry() const
523 {
524   std::map<FeaturePtr, EntityWrapperPtr>::const_iterator aFIt = myFeatureMap.begin();
525   for (; aFIt != myFeatureMap.end(); ++aFIt) {
526     EdgeWrapperPtr anEdge = std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(aFIt->second);
527     if (anEdge && anEdge->isDegenerated())
528       return PlaneGCSSolver_Solver::STATUS_DEGENERATED;
529   }
530   return PlaneGCSSolver_Solver::STATUS_OK;
531 }