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