]> SALOME platform Git repositories - modules/shaper.git/blob - src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.cpp
Salome HOME
SketchSolver Refactoring: Eliminate SolveSpace as a sketch solver.
[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_Builder.h>
9 #include <PlaneGCSSolver_Solver.h>
10 #include <PlaneGCSSolver_ConstraintWrapper.h>
11 #include <PlaneGCSSolver_EntityWrapper.h>
12 #include <PlaneGCSSolver_PointWrapper.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 <SketchPlugin_Projection.h>
23
24 #include <cmath>
25
26
27 static void constraintsToSolver(const ConstraintWrapperPtr& theConstraint,
28                                 const SolverPtr& theSolver)
29 {
30   std::shared_ptr<PlaneGCSSolver_Solver> aSolver =
31       std::dynamic_pointer_cast<PlaneGCSSolver_Solver>(theSolver);
32   if (!aSolver)
33     return;
34
35   const std::list<GCSConstraintPtr>& aConstraints =
36       std::dynamic_pointer_cast<PlaneGCSSolver_ConstraintWrapper>(theConstraint)->constraints();
37   std::list<GCSConstraintPtr>::const_iterator anIt = aConstraints.begin();
38   for (; anIt != aConstraints.end(); ++anIt)
39     aSolver->addConstraint(*anIt);
40 }
41
42
43 PlaneGCSSolver_Storage::PlaneGCSSolver_Storage(const SolverPtr& theSolver)
44   : SketchSolver_Storage(theSolver),
45     myConstraintLastID(CID_UNKNOWN)
46 {
47 }
48
49 void PlaneGCSSolver_Storage::addConstraint(
50     ConstraintPtr        theConstraint,
51     ConstraintWrapperPtr theSolverConstraint)
52 {
53   SketchSolver_Storage::addConstraint(theConstraint, theSolverConstraint);
54
55   theSolverConstraint->setId(++myConstraintLastID);
56   constraintsToSolver(theSolverConstraint, mySketchSolver);
57 }
58
59 void PlaneGCSSolver_Storage::addTemporaryConstraint(
60     const ConstraintWrapperPtr& theSolverConstraint)
61 {
62   if (myConstraintMap.empty())
63     return; // no need to process temporary constraints if there is no active constraint
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 isCopyInMulti(std::shared_ptr<SketchPlugin_Feature> theFeature)
136 {
137   if (!theFeature)
138     return false;
139
140   bool aResult = theFeature->isCopy();
141   if (aResult) {
142     const std::set<AttributePtr>& aRefs = theFeature->data()->refsToMe();
143     for (std::set<AttributePtr>::const_iterator aRefIt = aRefs.begin();
144          aRefIt != aRefs.end() && aResult; ++aRefIt) {
145       FeaturePtr anOwner = ModelAPI_Feature::feature((*aRefIt)->owner());
146       if (anOwner->getKind() == SketchPlugin_Projection::ID())
147         aResult = false;
148     }
149   }
150   return aResult;
151 }
152
153 bool PlaneGCSSolver_Storage::update(FeaturePtr theFeature, bool theForce)
154 {
155   bool isUpdated = false;
156   EntityWrapperPtr aRelated = entity(theFeature);
157   if (aRelated) // send signal to subscribers
158     notify(theFeature);
159   else { // Feature is not exist, create it
160     std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
161         std::dynamic_pointer_cast<SketchPlugin_Feature>(theFeature);
162     bool isCopy = isCopyInMulti(aSketchFeature);
163     // the feature is a copy in "Multi" constraint and does not used in other constraints
164     if (!theForce && isCopy && myFeatureMap.find(theFeature) == myFeatureMap.end())
165       return false;
166
167     // external feature processing
168     bool isExternal = (aSketchFeature && (aSketchFeature->isExternal() || isCopy));
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   // update arc
198   if (aRelated && aRelated->type() == ENTITY_ARC) {
199     /// TODO: this code should be shared with FeatureBuilder somehow
200
201     std::shared_ptr<PlaneGCSSolver_EntityWrapper> anEntity =
202         std::dynamic_pointer_cast<PlaneGCSSolver_EntityWrapper>(aRelated);
203     std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(anEntity->entity());
204
205     static std::shared_ptr<GeomAPI_Dir2d> OX(new GeomAPI_Dir2d(1.0, 0.0));
206     std::shared_ptr<GeomAPI_Pnt2d> aCenter(
207         new GeomAPI_Pnt2d(*anArc->center.x, *anArc->center.y));
208     std::shared_ptr<GeomAPI_Pnt2d> aStart(
209         new GeomAPI_Pnt2d(*anArc->start.x, *anArc->start.y));
210
211     *anArc->rad = aStart->distance(aCenter);
212
213     std::shared_ptr<GeomAPI_Dir2d> aDir(new GeomAPI_Dir2d(aStart->xy()->decreased(aCenter->xy())));
214     *anArc->startAngle = OX->angle(aDir);
215
216     aDir = std::shared_ptr<GeomAPI_Dir2d>(
217         new GeomAPI_Dir2d((*anArc->end.x) - aCenter->x(), (*anArc->end.y) - aCenter->y()));
218     *anArc->endAngle = OX->angle(aDir);
219   }
220
221   return isUpdated;
222 }
223
224 bool PlaneGCSSolver_Storage::update(AttributePtr theAttribute, bool theForce)
225 {
226   if (!theAttribute->isInitialized())
227     return false;
228
229   AttributePtr anAttribute = theAttribute;
230   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(anAttribute);
231   if (aRefAttr) {
232     if (aRefAttr->isObject()) {
233       FeaturePtr aFeature;
234       /// TODO: Check resultToFeatureOrAttribute() precisely.
235       resultToFeatureOrAttribute(aRefAttr->object(), aFeature, anAttribute);
236       if (aFeature)
237         return update(aFeature, theForce);
238     } else
239       anAttribute = aRefAttr->attr();
240   }
241
242   EntityWrapperPtr aRelated = entity(anAttribute);
243   if (!aRelated) { // Attribute does not exist, create it.
244     // First of all check if the parent feature exists. If not, add it.
245     FeaturePtr aFeature = ModelAPI_Feature::feature(anAttribute->owner());
246     if (aFeature && myFeatureMap.find(aFeature) == myFeatureMap.end())
247       return update(aFeature, theForce); // theAttribute has been processed while adding feature
248
249     PlaneGCSSolver_AttributeBuilder aBuilder(this);
250     aRelated = createAttribute(anAttribute, &aBuilder);
251     return aRelated.get() != 0;
252   }
253
254   bool isUpdated = updateValues(anAttribute, aRelated);
255   if (isUpdated)
256     setNeedToResolve(true);
257   return isUpdated;
258 }
259
260
261
262 bool PlaneGCSSolver_Storage::removeConstraint(ConstraintPtr theConstraint)
263 {
264   std::map<ConstraintPtr, ConstraintWrapperPtr>::iterator
265       aFound = myConstraintMap.find(theConstraint);
266   if (aFound != myConstraintMap.end()) {
267     ConstraintID anID = aFound->second->id();
268     // Remove solver's constraints
269     std::shared_ptr<PlaneGCSSolver_Solver> aSolver =
270         std::dynamic_pointer_cast<PlaneGCSSolver_Solver>(mySketchSolver);
271     aSolver->removeConstraint(anID);
272     // Remove constraint
273     myConstraintMap.erase(aFound);
274
275     // notify subscibers
276     notify(theConstraint);
277   }
278   return true;
279 }
280
281 void PlaneGCSSolver_Storage::removeInvalidEntities()
282 {
283   PlaneGCSSolver_EntityDestroyer aDestroyer;
284
285   // Remove invalid constraints
286   std::list<ConstraintPtr> anInvalidConstraints;
287   std::map<ConstraintPtr, ConstraintWrapperPtr>::const_iterator
288       aCIter = myConstraintMap.begin();
289   for (; aCIter != myConstraintMap.end(); ++aCIter)
290     if (!aCIter->first->data() || !aCIter->first->data()->isValid())
291       anInvalidConstraints.push_back(aCIter->first);
292   std::list<ConstraintPtr>::const_iterator anInvCIt = anInvalidConstraints.begin();
293   for (; anInvCIt != anInvalidConstraints.end(); ++anInvCIt)
294     removeConstraint(*anInvCIt);
295
296   // Remove invalid features
297   std::list<FeaturePtr> anInvalidFeatures;
298   std::map<FeaturePtr, EntityWrapperPtr>::const_iterator aFIter = myFeatureMap.begin();
299   for (; aFIter != myFeatureMap.end(); aFIter++)
300     if (!aFIter->first->data() || !aFIter->first->data()->isValid()) {
301       anInvalidFeatures.push_back(aFIter->first);
302       aDestroyer.remove(aFIter->second);
303
304       // remove invalid arc
305       std::map<EntityWrapperPtr, ConstraintWrapperPtr>::iterator
306           aFound = myArcConstraintMap.find(aFIter->second);
307       if (aFound != myArcConstraintMap.end()) {
308         std::dynamic_pointer_cast<PlaneGCSSolver_Solver>(
309             mySketchSolver)->removeConstraint(aFound->second->id());
310         myArcConstraintMap.erase(aFound);
311       }
312     }
313   std::list<FeaturePtr>::const_iterator anInvFIt = anInvalidFeatures.begin();
314   for (; anInvFIt != anInvalidFeatures.end(); ++anInvFIt)
315     removeFeature(*anInvFIt);
316
317   // Remove invalid attributes
318   std::list<AttributePtr> anInvalidAttributes;
319   std::map<AttributePtr, EntityWrapperPtr>::const_iterator anAttrIt = myAttributeMap.begin();
320   for (; anAttrIt != myAttributeMap.end(); ++anAttrIt) {
321     FeaturePtr anOwner = ModelAPI_Feature::feature(anAttrIt->first->owner());
322     if (!anOwner || !anOwner->data() || !anOwner->data()->isValid()) {
323       anInvalidAttributes.push_back(anAttrIt->first);
324       aDestroyer.remove(anAttrIt->second);
325     }
326   }
327   std::list<AttributePtr>::const_iterator anInvAtIt = anInvalidAttributes.begin();
328   for (; anInvAtIt != anInvalidAttributes.end(); ++anInvAtIt)
329     removeAttribute(*anInvAtIt);
330
331   // free memory occupied by parameters
332   removeParameters(aDestroyer.parametersToRemove());
333
334   /// TODO: Think on optimization of checking invalid features and attributes
335 }
336
337
338
339 double* PlaneGCSSolver_Storage::createParameter()
340 {
341   double* aResult = new double(0);
342   myParameters.push_back(aResult);
343   return aResult;
344 }
345
346 void PlaneGCSSolver_Storage::removeParameters(const GCS::SET_pD& theParams)
347 {
348   for (int i = (int)myParameters.size() - 1; i >= 0; --i)
349     if (theParams.find(myParameters[i]) != theParams.end())
350       myParameters.erase(myParameters.begin() + i);
351 }
352
353
354 bool PlaneGCSSolver_Storage::isRedundant(
355     GCSConstraintPtr theCheckedConstraint,
356     ConstraintWrapperPtr theParentConstraint,
357     std::list<std::set<double*> >& theCoincidentPoints) const
358 {
359   if (theParentConstraint->type() == CONSTRAINT_SYMMETRIC) {
360     if (theCheckedConstraint->getTypeId() == GCS::Perpendicular) {
361       BuilderPtr aBuilder = PlaneGCSSolver_Builder::getInstance();
362       // check the initial point is placed on the mirror line
363       std::list<EntityWrapperPtr> aSubs = theParentConstraint->entities();
364       std::shared_ptr<GeomAPI_Pnt2d> aPoint = aBuilder->point(aSubs.front());
365       std::shared_ptr<GeomAPI_Lin2d> aLine = aBuilder->line(aSubs.back());
366       return aLine->distance(aPoint) < tolerance;
367     }
368   }
369   else if (theParentConstraint->type() == CONSTRAINT_PT_PT_COINCIDENT) {
370     // Verify that the coincidence between points is already added
371     GCS::VEC_pD aParams = theCheckedConstraint->params();
372
373     std::list<std::set<double*> >::iterator aCoincIt, aFound1, aFound2;
374     aFound1 = aFound2 = theCoincidentPoints.end();
375     for (aCoincIt = theCoincidentPoints.begin();
376          aCoincIt != theCoincidentPoints.end(); ++aCoincIt) {
377       if (aFound1 == theCoincidentPoints.end() && aCoincIt->find(aParams[0]) != aCoincIt->end())
378         aFound1 = aCoincIt;
379       if (aFound2 == theCoincidentPoints.end() && aCoincIt->find(aParams[1]) != aCoincIt->end())
380         aFound2 = aCoincIt;
381       if (aFound1 != theCoincidentPoints.end() && aFound2 != theCoincidentPoints.end())
382         break;
383     }
384     if (aCoincIt != theCoincidentPoints.end()) { // both point are found
385       if (aFound1 == aFound2)
386         return true;
387       // merge two groups of coincidence
388       aFound1->insert(aFound2->begin(), aFound2->end());
389       theCoincidentPoints.erase(aFound2);
390     } else {
391       if (aFound1 != theCoincidentPoints.end())
392         aFound1->insert(aParams[1]);
393       else if (aFound2 != theCoincidentPoints.end())
394         aFound2->insert(aParams[0]);
395       else {
396         std::set<double*> aNewCoincidence;
397         aNewCoincidence.insert(aParams[0]);
398         aNewCoincidence.insert(aParams[1]);
399         theCoincidentPoints.push_back(aNewCoincidence);
400       }
401     }
402   }
403
404   return false;
405 }
406
407 void PlaneGCSSolver_Storage::initializeSolver()
408 {
409   std::shared_ptr<PlaneGCSSolver_Solver> aSolver =
410       std::dynamic_pointer_cast<PlaneGCSSolver_Solver>(mySketchSolver);
411   if (aSolver)
412     aSolver->setParameters(myParameters);
413 }
414
415 // indicates attribute containing in the external feature
416 bool isExternalAttribute(const AttributePtr& theAttribute)
417 {
418   if (!theAttribute)
419     return false;
420   std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
421       std::dynamic_pointer_cast<SketchPlugin_Feature>(theAttribute->owner());
422   return aSketchFeature.get() && aSketchFeature->isExternal();
423 }
424
425 void PlaneGCSSolver_Storage::refresh() const
426 {
427   const double aTol = 1000. * tolerance; // tolerance to prevent frequent updates
428
429   std::map<AttributePtr, EntityWrapperPtr>::const_iterator anIt = myAttributeMap.begin();
430   for (; anIt != myAttributeMap.end(); ++anIt) {
431     // the external feature always should keep the up to date values, so,
432     // refresh from the solver is never needed
433     if (isExternalAttribute(anIt->first))
434       continue;
435
436     std::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
437         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anIt->first);
438     if (aPoint2D) {
439       std::shared_ptr<PlaneGCSSolver_PointWrapper> aPointWrapper =
440           std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(anIt->second);
441       GCSPointPtr aGCSPoint = aPointWrapper->point();
442       if (fabs(aPoint2D->x() - (*aGCSPoint->x)) > aTol ||
443           fabs(aPoint2D->y() - (*aGCSPoint->y)) > aTol)
444         aPoint2D->setValue(*aGCSPoint->x, *aGCSPoint->y);
445       continue;
446     }
447     AttributeDoublePtr aScalar = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(anIt->first);
448     if (aScalar) {
449       ScalarWrapperPtr aScalarWrapper =
450           std::dynamic_pointer_cast<PlaneGCSSolver_ScalarWrapper>(anIt->second);
451       if (fabs(aScalar->value() - aScalarWrapper->value()) > aTol)
452         aScalar->setValue(aScalarWrapper->value());
453       continue;
454     }
455   }
456 }