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