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