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