1 // Copyright (C) 2014-2019 CEA/DEN, EDF R&D
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.
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.
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
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #include <PlaneGCSSolver_Storage.h>
21 #include <PlaneGCSSolver_Solver.h>
22 #include <PlaneGCSSolver_BooleanWrapper.h>
23 #include <PlaneGCSSolver_ConstraintWrapper.h>
24 #include <PlaneGCSSolver_EdgeWrapper.h>
25 #include <PlaneGCSSolver_PointWrapper.h>
26 #include <PlaneGCSSolver_Tools.h>
28 #include <PlaneGCSSolver_AttributeBuilder.h>
29 #include <PlaneGCSSolver_FeatureBuilder.h>
30 #include <PlaneGCSSolver_EntityDestroyer.h>
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>
42 static void constraintsToSolver(const ConstraintWrapperPtr& theConstraint,
43 const SolverPtr& theSolver)
45 const std::list<GCSConstraintPtr>& aConstraints =
46 std::dynamic_pointer_cast<PlaneGCSSolver_ConstraintWrapper>(theConstraint)->constraints();
47 theSolver->addConstraint(theConstraint->id(), aConstraints);
51 PlaneGCSSolver_Storage::PlaneGCSSolver_Storage(const SolverPtr& theSolver)
52 : SketchSolver_Storage(theSolver),
53 myConstraintLastID(CID_UNKNOWN)
57 void PlaneGCSSolver_Storage::addConstraint(
58 ConstraintPtr theConstraint,
59 ConstraintWrapperPtr theSolverConstraint)
61 SketchSolver_Storage::addConstraint(theConstraint, theSolverConstraint);
63 theSolverConstraint->setId(++myConstraintLastID);
64 constraintsToSolver(theSolverConstraint, mySketchSolver);
67 void PlaneGCSSolver_Storage::addMovementConstraint(
68 const ConstraintWrapperPtr& theSolverConstraint)
70 // before adding movement constraint to solver, re-check its DOF
71 if (mySketchSolver->dof() == 0)
72 mySketchSolver->diagnose();
74 theSolverConstraint->setId(CID_MOVEMENT);
75 constraintsToSolver(theSolverConstraint, mySketchSolver);
79 EntityWrapperPtr PlaneGCSSolver_Storage::createFeature(
80 const FeaturePtr& theFeature,
81 PlaneGCSSolver_EntityBuilder* theBuilder)
83 std::list<AttributePtr> anAttributes = theFeature->data()->attributes(std::string());
84 std::list<AttributePtr>::const_iterator anIt = anAttributes.begin();
85 for (; anIt != anAttributes.end(); ++anIt)
86 createAttribute(*anIt, theBuilder);
88 EntityWrapperPtr aResult = theBuilder->createFeature(theFeature);
90 addEntity(theFeature, aResult);
94 EntityWrapperPtr PlaneGCSSolver_Storage::createAttribute(
95 const AttributePtr& theAttribute,
96 PlaneGCSSolver_EntityBuilder* theBuilder)
98 EntityWrapperPtr aResult = theBuilder->createAttribute(theAttribute);
100 addEntity(theAttribute, aResult);
104 /// \brief Update value
105 static bool updateValue(const double& theSource, double& theDest)
107 static const double aTol = 1000. * tolerance;
108 bool isUpdated = fabs(theSource - theDest) > aTol;
114 /// \brief Update coordinates of the point or scalar using its base attribute
115 static bool updateValues(AttributePtr& theAttribute, EntityWrapperPtr& theEntity)
117 bool isUpdated = false;
119 std::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
120 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute);
122 const GCSPointPtr& aGCSPoint =
123 std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theEntity)->point();
124 isUpdated = updateValue(aPoint2D->x(), *(aGCSPoint->x)) || isUpdated;
125 isUpdated = updateValue(aPoint2D->y(), *(aGCSPoint->y)) || isUpdated;
127 AttributeDoublePtr aScalar =
128 std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theAttribute);
130 ScalarWrapperPtr aWrapper =
131 std::dynamic_pointer_cast<PlaneGCSSolver_ScalarWrapper>(theEntity);
132 // There is possible angular value, which is converted between degrees and radians.
133 // So, we use its value instead of using direct pointer to value.
134 double aValue = aWrapper->value();
135 isUpdated = updateValue(aScalar->value(), aValue);
137 aWrapper->setValue(aValue);
139 AttributeBooleanPtr aBoolean =
140 std::dynamic_pointer_cast<ModelAPI_AttributeBoolean>(theAttribute);
142 BooleanWrapperPtr aWrapper =
143 std::dynamic_pointer_cast<PlaneGCSSolver_BooleanWrapper>(theEntity);
144 isUpdated = aWrapper->value() != aBoolean->value();
145 aWrapper->setValue(aBoolean->value());
153 static bool hasReference(std::shared_ptr<SketchPlugin_Feature> theFeature,
154 const std::string& theFeatureKind)
156 const std::set<AttributePtr>& aRefs = theFeature->data()->refsToMe();
157 for (std::set<AttributePtr>::const_iterator aRefIt = aRefs.begin();
158 aRefIt != aRefs.end(); ++aRefIt) {
159 FeaturePtr anOwner = ModelAPI_Feature::feature((*aRefIt)->owner());
160 if (anOwner && anOwner->getKind() == theFeatureKind)
166 static bool isCopyFeature(std::shared_ptr<SketchPlugin_Feature> theFeature)
168 return theFeature && theFeature->isCopy();
171 bool PlaneGCSSolver_Storage::update(FeaturePtr theFeature, bool theForce)
173 bool sendNotify = false;
174 bool isUpdated = false;
175 std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
176 std::dynamic_pointer_cast<SketchPlugin_Feature>(theFeature);
177 EntityWrapperPtr aRelated = entity(theFeature);
178 if (aRelated) // send signal to subscribers
180 else { // Feature is not exist, create it
181 bool isCopy = isCopyFeature(aSketchFeature);
182 bool isProjReferred = hasReference(aSketchFeature, SketchPlugin_Projection::ID());
183 // the feature is a copy in "Multi" constraint and does not used in other constraints
184 if (!theForce && (isCopy && !isProjReferred) &&
185 myFeatureMap.find(theFeature) == myFeatureMap.end())
188 // external feature processing
190 (aSketchFeature && (aSketchFeature->isExternal() || isCopy || isProjReferred));
192 PlaneGCSSolver_FeatureBuilder aBuilder(isExternal ? 0 : this);
194 // Reserve the feature in the map of features
195 // (do not want to add several copies of it while adding attributes)
196 aRelated = createFeature(theFeature, &aBuilder);
197 myFeatureMap[theFeature] = aRelated;
198 createArcConstraints(aRelated);
202 std::list<AttributePtr> anAttributes = theFeature->data()->attributes(std::string());
203 std::list<AttributePtr>::iterator anAttrIt = anAttributes.begin();
204 for (; anAttrIt != anAttributes.end(); ++anAttrIt)
205 if ((*anAttrIt)->attributeType() == GeomDataAPI_Point2D::typeId() ||
206 (*anAttrIt)->attributeType() == ModelAPI_AttributeDouble::typeId() ||
207 (*anAttrIt)->attributeType() == ModelAPI_AttributeBoolean::typeId())
208 isUpdated = update(*anAttrIt) || isUpdated;
210 // check external attribute is changed
211 bool isExternal = aSketchFeature &&
212 (aSketchFeature->isExternal() || isCopyFeature(aSketchFeature));
213 if (aRelated && isExternal != aRelated->isExternal()) {
215 makeExternal(aRelated);
217 makeNonExternal(aRelated);
221 // send notification to listeners due to at least one attribute is changed
222 if (sendNotify && isUpdated)
228 bool PlaneGCSSolver_Storage::update(AttributePtr theAttribute, bool theForce)
230 if (!theAttribute->isInitialized())
233 AttributePtr anAttribute = theAttribute;
234 AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(anAttribute);
236 if (aRefAttr->isObject()) {
238 /// TODO: Check resultToFeatureOrAttribute() precisely.
239 resultToFeatureOrAttribute(aRefAttr->object(), aFeature, anAttribute);
241 return update(aFeature, theForce);
243 anAttribute = aRefAttr->attr();
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;
255 bool isUpdated = updateValues(anAttribute, aRelated);
257 setNeedToResolve(true);
263 void PlaneGCSSolver_Storage::makeExternal(const EntityWrapperPtr& theEntity)
265 if (theEntity->isExternal())
268 removeArcConstraints(theEntity);
270 GCS::SET_pD aParameters = PlaneGCSSolver_Tools::parameters(theEntity);
271 mySketchSolver->removeParameters(aParameters);
272 theEntity->setExternal(true);
273 myNeedToResolve = true;
276 void PlaneGCSSolver_Storage::makeNonExternal(const EntityWrapperPtr& theEntity)
278 if (!theEntity->isExternal())
281 GCS::SET_pD aParameters = PlaneGCSSolver_Tools::parameters(theEntity);
282 mySketchSolver->addParameters(aParameters);
283 theEntity->setExternal(false);
285 createArcConstraints(theEntity);
287 myNeedToResolve = true;
291 void PlaneGCSSolver_Storage::createArcConstraints(const EntityWrapperPtr& theArc)
293 if (!theArc || theArc->type() != ENTITY_ARC || theArc->isExternal())
296 EdgeWrapperPtr anEdge = std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(theArc);
297 std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(anEdge->entity());
299 // Additional constaints to fix arc's extra DoF (if the arc is not external):
300 std::list<GCSConstraintPtr> anArcConstraints;
301 // constrain the start point on the arc
302 anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintCurveValue(
303 anArc->start, anArc->start.x, *anArc, anArc->startAngle)));
304 anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintCurveValue(
305 anArc->start, anArc->start.y, *anArc, anArc->startAngle)));
306 // constrain the end point on the arc
307 anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintCurveValue(
308 anArc->end, anArc->end.x, *anArc, anArc->endAngle)));
309 anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintCurveValue(
310 anArc->end, anArc->end.y, *anArc, anArc->endAngle)));
312 ConstraintWrapperPtr aWrapper(
313 new PlaneGCSSolver_ConstraintWrapper(anArcConstraints, CONSTRAINT_UNKNOWN));
314 aWrapper->setId(++myConstraintLastID);
315 constraintsToSolver(aWrapper, mySketchSolver);
317 myArcConstraintMap[theArc] = aWrapper;
320 void PlaneGCSSolver_Storage::removeArcConstraints(const EntityWrapperPtr& theArc)
322 std::map<EntityWrapperPtr, ConstraintWrapperPtr>::iterator
323 aFound = myArcConstraintMap.find(theArc);
324 if (aFound != myArcConstraintMap.end()) {
325 mySketchSolver->removeConstraint(aFound->second->id());
326 myArcConstraintMap.erase(aFound);
330 void PlaneGCSSolver_Storage::adjustParametrizationOfArcs()
332 std::map<EntityWrapperPtr, ConstraintWrapperPtr>::iterator anIt = myArcConstraintMap.begin();
333 for (; anIt != myArcConstraintMap.end(); ++anIt) {
334 EdgeWrapperPtr anEdge = std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(anIt->first);
335 std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(anEdge->entity());
336 // tune start angle of the arc to be in [0, 2PI]
337 while (*anArc->startAngle < -PI)
338 *anArc->startAngle += 2.0 * PI;
339 while (*anArc->startAngle >= PI)
340 *anArc->startAngle -= 2.0 * PI;
341 // adjust end angle of the arc
342 if (anEdge->isReversed()) {
343 while (*anArc->endAngle > *anArc->startAngle)
344 *anArc->endAngle -= 2.0 * PI;
345 while (*anArc->endAngle + 2 * PI < *anArc->startAngle)
346 *anArc->endAngle += 2.0 * PI;
348 while (*anArc->endAngle < *anArc->startAngle)
349 *anArc->endAngle += 2.0 * PI;
350 while (*anArc->endAngle > *anArc->startAngle + 2 * PI)
351 *anArc->endAngle -= 2.0 * PI;
355 // update parameters of Middle point constraint for point on arc
356 std::map<ConstraintPtr, ConstraintWrapperPtr>::iterator aCIt = myConstraintMap.begin();
357 for (; aCIt != myConstraintMap.end(); ++aCIt)
358 if (aCIt->second->type() == CONSTRAINT_MIDDLE_POINT) {
364 bool PlaneGCSSolver_Storage::removeConstraint(ConstraintPtr theConstraint)
366 std::map<ConstraintPtr, ConstraintWrapperPtr>::iterator
367 aFound = myConstraintMap.find(theConstraint);
368 if (aFound != myConstraintMap.end()) {
369 ConstraintWrapperPtr aCW = aFound->second;
370 ConstraintID anID = aCW->id();
372 // Remove solver's constraints
373 mySketchSolver->removeConstraint(anID);
375 // Remove value if exists
376 const ScalarWrapperPtr& aValue = aCW->valueParameter();
378 GCS::SET_pD aParToRemove;
379 aParToRemove.insert(aValue->scalar());
380 removeParameters(aParToRemove);
384 myConstraintMap.erase(aFound);
386 if (anID != CID_MOVEMENT)
387 myNeedToResolve = true;
390 notify(theConstraint);
395 void PlaneGCSSolver_Storage::removeInvalidEntities()
397 PlaneGCSSolver_EntityDestroyer aDestroyer;
399 // Remove invalid constraints
400 std::list<ConstraintPtr> anInvalidConstraints;
401 std::map<ConstraintPtr, ConstraintWrapperPtr>::const_iterator
402 aCIter = myConstraintMap.begin();
403 for (; aCIter != myConstraintMap.end(); ++aCIter)
404 if (!aCIter->first->data() || !aCIter->first->data()->isValid())
405 anInvalidConstraints.push_back(aCIter->first);
406 std::list<ConstraintPtr>::const_iterator anInvCIt = anInvalidConstraints.begin();
407 for (; anInvCIt != anInvalidConstraints.end(); ++anInvCIt)
408 removeConstraint(*anInvCIt);
410 // Remove invalid features
411 std::list<FeaturePtr> anInvalidFeatures;
412 std::map<FeaturePtr, EntityWrapperPtr>::const_iterator aFIter = myFeatureMap.begin();
413 for (; aFIter != myFeatureMap.end(); aFIter++)
414 if (!aFIter->first->data() || !aFIter->first->data()->isValid()) {
415 anInvalidFeatures.push_back(aFIter->first);
417 aDestroyer.remove(aFIter->second);
419 // remove invalid arc
420 removeArcConstraints(aFIter->second);
422 std::list<FeaturePtr>::const_iterator anInvFIt = anInvalidFeatures.begin();
423 for (; anInvFIt != anInvalidFeatures.end(); ++anInvFIt)
424 removeFeature(*anInvFIt);
426 // Remove invalid attributes
427 std::list<AttributePtr> anInvalidAttributes;
428 std::map<AttributePtr, EntityWrapperPtr>::const_iterator anAttrIt = myAttributeMap.begin();
429 for (; anAttrIt != myAttributeMap.end(); ++anAttrIt) {
430 FeaturePtr anOwner = ModelAPI_Feature::feature(anAttrIt->first->owner());
431 if (!anOwner || !anOwner->data() || !anOwner->data()->isValid()) {
432 anInvalidAttributes.push_back(anAttrIt->first);
433 aDestroyer.remove(anAttrIt->second);
436 std::list<AttributePtr>::const_iterator anInvAtIt = anInvalidAttributes.begin();
437 for (; anInvAtIt != anInvalidAttributes.end(); ++anInvAtIt)
438 removeAttribute(*anInvAtIt);
440 // free memory occupied by parameters
441 removeParameters(aDestroyer.parametersToRemove());
443 /// TODO: Think on optimization of checking invalid features and attributes
448 double* PlaneGCSSolver_Storage::createParameter()
450 return mySketchSolver->createParameter();
453 void PlaneGCSSolver_Storage::removeParameters(const GCS::SET_pD& theParams)
455 mySketchSolver->removeParameters(theParams);
458 // indicates attribute containing in the external feature
459 static bool isExternalAttribute(const AttributePtr& theAttribute)
463 std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
464 std::dynamic_pointer_cast<SketchPlugin_Feature>(theAttribute->owner());
465 return aSketchFeature.get() && aSketchFeature->isExternal();
468 static void addOwnerToSet(const AttributePtr& theAttribute, std::set<FeaturePtr>& theFeatures)
470 FeaturePtr anOwner = ModelAPI_Feature::feature(theAttribute->owner());
472 theFeatures.insert(anOwner);
475 void PlaneGCSSolver_Storage::refresh() const
477 const double aTol = 1000. * tolerance; // tolerance to prevent frequent updates
479 std::set<FeaturePtr> anUpdatedFeatures;
481 std::map<AttributePtr, EntityWrapperPtr>::const_iterator anIt = myAttributeMap.begin();
482 for (; anIt != myAttributeMap.end(); ++anIt) {
483 if (!anIt->first->isInitialized())
486 // the external feature always should keep the up to date values, so,
487 // refresh from the solver is never needed
488 if (isExternalAttribute(anIt->first))
491 std::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
492 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anIt->first);
494 std::shared_ptr<PlaneGCSSolver_PointWrapper> aPointWrapper =
495 std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(anIt->second);
496 GCSPointPtr aGCSPoint = aPointWrapper->point();
497 if (fabs(aPoint2D->x() - (*aGCSPoint->x)) > aTol ||
498 fabs(aPoint2D->y() - (*aGCSPoint->y)) > aTol) {
499 aPoint2D->setValue(*aGCSPoint->x, *aGCSPoint->y);
500 addOwnerToSet(anIt->first, anUpdatedFeatures);
504 AttributeDoublePtr aScalar = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(anIt->first);
506 ScalarWrapperPtr aScalarWrapper =
507 std::dynamic_pointer_cast<PlaneGCSSolver_ScalarWrapper>(anIt->second);
508 if (fabs(aScalar->value() - aScalarWrapper->value()) > aTol) {
509 aScalar->setValue(aScalarWrapper->value());
510 addOwnerToSet(anIt->first, anUpdatedFeatures);
516 // notify listeners about features update
517 std::set<FeaturePtr>::const_iterator aFIt = anUpdatedFeatures.begin();
518 for (; aFIt != anUpdatedFeatures.end(); ++aFIt)
522 PlaneGCSSolver_Solver::SolveStatus PlaneGCSSolver_Storage::checkDegeneratedGeometry() const
524 std::map<FeaturePtr, EntityWrapperPtr>::const_iterator aFIt = myFeatureMap.begin();
525 for (; aFIt != myFeatureMap.end(); ++aFIt) {
526 EdgeWrapperPtr anEdge = std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(aFIt->second);
527 if (anEdge && anEdge->isDegenerated())
528 return PlaneGCSSolver_Solver::STATUS_DEGENERATED;
530 return PlaneGCSSolver_Solver::STATUS_OK;