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