Salome HOME
Fix for the issue #2753 : error when dump/load script
[modules/shaper.git] / src / SketchSolver / PlaneGCSSolver / PlaneGCSSolver_Storage.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 <PlaneGCSSolver_Storage.h>
22 #include <PlaneGCSSolver_Solver.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     }
139   }
140
141   return isUpdated;
142 }
143
144 static bool hasReference(std::shared_ptr<SketchPlugin_Feature> theFeature,
145                          const std::string& theFeatureKind)
146 {
147   const std::set<AttributePtr>& aRefs = theFeature->data()->refsToMe();
148   for (std::set<AttributePtr>::const_iterator aRefIt = aRefs.begin();
149        aRefIt != aRefs.end(); ++aRefIt) {
150      FeaturePtr anOwner = ModelAPI_Feature::feature((*aRefIt)->owner());
151      if (anOwner && anOwner->getKind() == theFeatureKind)
152        return true;
153   }
154   return false;
155 }
156
157 static bool isCopyFeature(std::shared_ptr<SketchPlugin_Feature> theFeature)
158 {
159   return theFeature && theFeature->isCopy();
160 }
161
162 bool PlaneGCSSolver_Storage::update(FeaturePtr theFeature, bool theForce)
163 {
164   bool sendNotify = false;
165   bool isUpdated = false;
166   EntityWrapperPtr aRelated = entity(theFeature);
167   if (aRelated) // send signal to subscribers
168     sendNotify = true;
169   else { // Feature is not exist, create it
170     std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
171         std::dynamic_pointer_cast<SketchPlugin_Feature>(theFeature);
172     bool isCopy = isCopyFeature(aSketchFeature);
173     bool isProjReferred = hasReference(aSketchFeature, SketchPlugin_Projection::ID());
174     // the feature is a copy in "Multi" constraint and does not used in other constraints
175     if (!theForce && (isCopy && !isProjReferred) &&
176         myFeatureMap.find(theFeature) == myFeatureMap.end())
177       return false;
178
179     // external feature processing
180     bool isExternal =
181         (aSketchFeature && (aSketchFeature->isExternal() || isCopy || isProjReferred));
182
183     PlaneGCSSolver_FeatureBuilder aBuilder(isExternal ? 0 : this);
184
185     // Reserve the feature in the map of features
186     // (do not want to add several copies of it while adding attributes)
187     aRelated = createFeature(theFeature, &aBuilder);
188     myFeatureMap[theFeature] = aRelated;
189     createArcConstraints(aRelated);
190     isUpdated = true;
191   }
192
193   std::list<AttributePtr> anAttributes = theFeature->data()->attributes(std::string());
194   std::list<AttributePtr>::iterator anAttrIt = anAttributes.begin();
195   for (; anAttrIt != anAttributes.end(); ++anAttrIt)
196     if ((*anAttrIt)->attributeType() == GeomDataAPI_Point2D::typeId() ||
197         (*anAttrIt)->attributeType() == ModelAPI_AttributeDouble::typeId())
198       isUpdated = update(*anAttrIt) || isUpdated;
199
200   // send notification to listeners due to at least one attribute is changed
201   if (sendNotify && isUpdated)
202     notify(theFeature);
203
204   // update arc
205   if (aRelated && aRelated->type() == ENTITY_ARC) {
206     /// TODO: this code should be shared with FeatureBuilder somehow
207
208     std::shared_ptr<PlaneGCSSolver_EdgeWrapper> anEntity =
209         std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(aRelated);
210     std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(anEntity->entity());
211
212     static std::shared_ptr<GeomAPI_Dir2d> OX(new GeomAPI_Dir2d(1.0, 0.0));
213     std::shared_ptr<GeomAPI_Pnt2d> aCenter(
214         new GeomAPI_Pnt2d(*anArc->center.x, *anArc->center.y));
215     std::shared_ptr<GeomAPI_Pnt2d> aStart(
216         new GeomAPI_Pnt2d(*anArc->start.x, *anArc->start.y));
217
218     *anArc->rad = aStart->distance(aCenter);
219
220     std::shared_ptr<GeomAPI_Dir2d> aDir(new GeomAPI_Dir2d(aStart->xy()->decreased(aCenter->xy())));
221     *anArc->startAngle = OX->angle(aDir);
222
223     aDir = std::shared_ptr<GeomAPI_Dir2d>(
224         new GeomAPI_Dir2d((*anArc->end.x) - aCenter->x(), (*anArc->end.y) - aCenter->y()));
225     *anArc->endAngle = OX->angle(aDir);
226   }
227
228   return isUpdated;
229 }
230
231 bool PlaneGCSSolver_Storage::update(AttributePtr theAttribute, bool theForce)
232 {
233   if (!theAttribute->isInitialized())
234     return false;
235
236   AttributePtr anAttribute = theAttribute;
237   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(anAttribute);
238   if (aRefAttr) {
239     if (aRefAttr->isObject()) {
240       FeaturePtr aFeature;
241       /// TODO: Check resultToFeatureOrAttribute() precisely.
242       resultToFeatureOrAttribute(aRefAttr->object(), aFeature, anAttribute);
243       if (aFeature)
244         return update(aFeature, theForce);
245     } else
246       anAttribute = aRefAttr->attr();
247   }
248
249   EntityWrapperPtr aRelated = entity(anAttribute);
250   FeaturePtr aFeature = ModelAPI_Feature::feature(anAttribute->owner());
251   if (!aRelated) { // Attribute does not exist, create it.
252     // First of all check if the parent feature exists. If not, add it.
253     if (aFeature && myFeatureMap.find(aFeature) == myFeatureMap.end())
254       return update(aFeature, theForce); // theAttribute has been processed while adding feature
255     return aRelated.get() != 0;
256   }
257
258   bool isUpdated = updateValues(anAttribute, aRelated);
259   if (isUpdated) {
260     setNeedToResolve(true);
261     notify(aFeature);
262   }
263   return isUpdated;
264 }
265
266 void PlaneGCSSolver_Storage::makeExternal(const EntityWrapperPtr& theEntity)
267 {
268   if (theEntity->isExternal())
269     return;
270
271   removeArcConstraints(theEntity);
272
273   GCS::SET_pD aParameters = PlaneGCSSolver_Tools::parameters(theEntity);
274   mySketchSolver->removeParameters(aParameters);
275   theEntity->setExternal(true);
276   myNeedToResolve = true;
277 }
278
279 void PlaneGCSSolver_Storage::makeNonExternal(const EntityWrapperPtr& theEntity)
280 {
281   if (!theEntity->isExternal())
282     return;
283
284   GCS::SET_pD aParameters = PlaneGCSSolver_Tools::parameters(theEntity);
285   mySketchSolver->addParameters(aParameters);
286   theEntity->setExternal(false);
287
288   createArcConstraints(theEntity);
289
290   myNeedToResolve = true;
291 }
292
293
294 void PlaneGCSSolver_Storage::createArcConstraints(const EntityWrapperPtr& theArc)
295 {
296   if (!theArc || theArc->type() != ENTITY_ARC || theArc->isExternal())
297     return;
298
299   EdgeWrapperPtr anEdge = std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(theArc);
300   std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(anEdge->entity());
301
302   // Additional constaints to fix arc's extra DoF (if the arc is not external):
303   std::list<GCSConstraintPtr> anArcConstraints;
304   // 1. distances from center till start and end points are equal to radius
305   anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintP2PDistance(
306       anArc->center, anArc->start, anArc->rad)));
307   anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintP2PDistance(
308       anArc->center, anArc->end, anArc->rad)));
309   // 2. angles of start and end points should be equal to the arc angles
310   anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintP2PAngle(
311       anArc->center, anArc->start, anArc->startAngle)));
312   anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintP2PAngle(
313       anArc->center, anArc->end, anArc->endAngle)));
314
315   ConstraintWrapperPtr aWrapper(
316       new PlaneGCSSolver_ConstraintWrapper(anArcConstraints, CONSTRAINT_UNKNOWN));
317   aWrapper->setId(++myConstraintLastID);
318   constraintsToSolver(aWrapper, mySketchSolver);
319
320   myArcConstraintMap[theArc] = aWrapper;
321 }
322
323 void PlaneGCSSolver_Storage::removeArcConstraints(const EntityWrapperPtr& theArc)
324 {
325   std::map<EntityWrapperPtr, ConstraintWrapperPtr>::iterator
326       aFound = myArcConstraintMap.find(theArc);
327   if (aFound != myArcConstraintMap.end()) {
328     mySketchSolver->removeConstraint(aFound->second->id());
329     myArcConstraintMap.erase(aFound);
330   }
331 }
332
333
334 bool PlaneGCSSolver_Storage::removeConstraint(ConstraintPtr theConstraint)
335 {
336   std::map<ConstraintPtr, ConstraintWrapperPtr>::iterator
337       aFound = myConstraintMap.find(theConstraint);
338   if (aFound != myConstraintMap.end()) {
339     ConstraintWrapperPtr aCW = aFound->second;
340     ConstraintID anID = aCW->id();
341
342     // Remove solver's constraints
343     mySketchSolver->removeConstraint(anID);
344
345     // Remove value if exists
346     const ScalarWrapperPtr& aValue = aCW->valueParameter();
347     if (aValue) {
348       GCS::SET_pD aParToRemove;
349       aParToRemove.insert(aValue->scalar());
350       removeParameters(aParToRemove);
351     }
352
353     // Remove constraint
354     myConstraintMap.erase(aFound);
355
356     if (anID != CID_MOVEMENT)
357       myNeedToResolve = true;
358
359     // notify subscibers
360     notify(theConstraint);
361   }
362   return true;
363 }
364
365 void PlaneGCSSolver_Storage::removeInvalidEntities()
366 {
367   PlaneGCSSolver_EntityDestroyer aDestroyer;
368
369   // Remove invalid constraints
370   std::list<ConstraintPtr> anInvalidConstraints;
371   std::map<ConstraintPtr, ConstraintWrapperPtr>::const_iterator
372       aCIter = myConstraintMap.begin();
373   for (; aCIter != myConstraintMap.end(); ++aCIter)
374     if (!aCIter->first->data() || !aCIter->first->data()->isValid())
375       anInvalidConstraints.push_back(aCIter->first);
376   std::list<ConstraintPtr>::const_iterator anInvCIt = anInvalidConstraints.begin();
377   for (; anInvCIt != anInvalidConstraints.end(); ++anInvCIt)
378     removeConstraint(*anInvCIt);
379
380   // Remove invalid features
381   std::list<FeaturePtr> anInvalidFeatures;
382   std::map<FeaturePtr, EntityWrapperPtr>::const_iterator aFIter = myFeatureMap.begin();
383   for (; aFIter != myFeatureMap.end(); aFIter++)
384     if (!aFIter->first->data() || !aFIter->first->data()->isValid()) {
385       anInvalidFeatures.push_back(aFIter->first);
386       if (aFIter->second)
387         aDestroyer.remove(aFIter->second);
388
389       // remove invalid arc
390       removeArcConstraints(aFIter->second);
391     }
392   std::list<FeaturePtr>::const_iterator anInvFIt = anInvalidFeatures.begin();
393   for (; anInvFIt != anInvalidFeatures.end(); ++anInvFIt)
394     removeFeature(*anInvFIt);
395
396   // Remove invalid attributes
397   std::list<AttributePtr> anInvalidAttributes;
398   std::map<AttributePtr, EntityWrapperPtr>::const_iterator anAttrIt = myAttributeMap.begin();
399   for (; anAttrIt != myAttributeMap.end(); ++anAttrIt) {
400     FeaturePtr anOwner = ModelAPI_Feature::feature(anAttrIt->first->owner());
401     if (!anOwner || !anOwner->data() || !anOwner->data()->isValid()) {
402       anInvalidAttributes.push_back(anAttrIt->first);
403       aDestroyer.remove(anAttrIt->second);
404     }
405   }
406   std::list<AttributePtr>::const_iterator anInvAtIt = anInvalidAttributes.begin();
407   for (; anInvAtIt != anInvalidAttributes.end(); ++anInvAtIt)
408     removeAttribute(*anInvAtIt);
409
410   // free memory occupied by parameters
411   removeParameters(aDestroyer.parametersToRemove());
412
413   /// TODO: Think on optimization of checking invalid features and attributes
414 }
415
416
417
418 double* PlaneGCSSolver_Storage::createParameter()
419 {
420   return mySketchSolver->createParameter();
421 }
422
423 void PlaneGCSSolver_Storage::removeParameters(const GCS::SET_pD& theParams)
424 {
425   mySketchSolver->removeParameters(theParams);
426 }
427
428 // indicates attribute containing in the external feature
429 static bool isExternalAttribute(const AttributePtr& theAttribute)
430 {
431   if (!theAttribute)
432     return false;
433   std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
434       std::dynamic_pointer_cast<SketchPlugin_Feature>(theAttribute->owner());
435   return aSketchFeature.get() && aSketchFeature->isExternal();
436 }
437
438 static void addOwnerToSet(const AttributePtr& theAttribute, std::set<FeaturePtr>& theFeatures)
439 {
440   FeaturePtr anOwner = ModelAPI_Feature::feature(theAttribute->owner());
441   if (anOwner)
442     theFeatures.insert(anOwner);
443 }
444
445 void PlaneGCSSolver_Storage::refresh() const
446 {
447   const double aTol = 1000. * tolerance; // tolerance to prevent frequent updates
448
449   std::set<FeaturePtr> anUpdatedFeatures;
450
451   std::map<AttributePtr, EntityWrapperPtr>::const_iterator anIt = myAttributeMap.begin();
452   for (; anIt != myAttributeMap.end(); ++anIt) {
453     if (!anIt->first->isInitialized())
454       continue;
455
456     // the external feature always should keep the up to date values, so,
457     // refresh from the solver is never needed
458     if (isExternalAttribute(anIt->first))
459       continue;
460
461     std::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
462         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anIt->first);
463     if (aPoint2D) {
464       std::shared_ptr<PlaneGCSSolver_PointWrapper> aPointWrapper =
465           std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(anIt->second);
466       GCSPointPtr aGCSPoint = aPointWrapper->point();
467       if (fabs(aPoint2D->x() - (*aGCSPoint->x)) > aTol ||
468           fabs(aPoint2D->y() - (*aGCSPoint->y)) > aTol) {
469         aPoint2D->setValue(*aGCSPoint->x, *aGCSPoint->y);
470         addOwnerToSet(anIt->first, anUpdatedFeatures);
471       }
472       continue;
473     }
474     AttributeDoublePtr aScalar = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(anIt->first);
475     if (aScalar) {
476       ScalarWrapperPtr aScalarWrapper =
477           std::dynamic_pointer_cast<PlaneGCSSolver_ScalarWrapper>(anIt->second);
478       if (fabs(aScalar->value() - aScalarWrapper->value()) > aTol) {
479         aScalar->setValue(aScalarWrapper->value());
480         addOwnerToSet(anIt->first, anUpdatedFeatures);
481       }
482       continue;
483     }
484   }
485
486   // notify listeners about features update
487   std::set<FeaturePtr>::const_iterator aFIt = anUpdatedFeatures.begin();
488   for (; aFIt != anUpdatedFeatures.end(); ++aFIt)
489     notify(*aFIt);
490 }
491
492 PlaneGCSSolver_Solver::SolveStatus PlaneGCSSolver_Storage::checkDegeneratedGeometry() const
493 {
494   std::map<FeaturePtr, EntityWrapperPtr>::const_iterator aFIt = myFeatureMap.begin();
495   for (; aFIt != myFeatureMap.end(); ++aFIt) {
496     EdgeWrapperPtr anEdge = std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(aFIt->second);
497     if (anEdge && anEdge->isDegenerated())
498       return PlaneGCSSolver_Solver::STATUS_DEGENERATED;
499   }
500   return PlaneGCSSolver_Solver::STATUS_OK;
501 }