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