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_ConstraintWrapper.h>
23 #include <PlaneGCSSolver_EdgeWrapper.h>
24 #include <PlaneGCSSolver_PointWrapper.h>
25 #include <PlaneGCSSolver_Tools.h>
27 #include <PlaneGCSSolver_AttributeBuilder.h>
28 #include <PlaneGCSSolver_FeatureBuilder.h>
29 #include <PlaneGCSSolver_EntityDestroyer.h>
31 #include <GeomAPI_Dir2d.h>
32 #include <GeomAPI_Pnt2d.h>
33 #include <GeomAPI_XY.h>
34 #include <GeomDataAPI_Point2D.h>
35 #include <ModelAPI_AttributeRefAttr.h>
36 #include <SketchPlugin_Projection.h>
41 static void constraintsToSolver(const ConstraintWrapperPtr& theConstraint,
42 const SolverPtr& theSolver)
44 const std::list<GCSConstraintPtr>& aConstraints =
45 std::dynamic_pointer_cast<PlaneGCSSolver_ConstraintWrapper>(theConstraint)->constraints();
46 theSolver->addConstraint(theConstraint->id(), aConstraints);
50 PlaneGCSSolver_Storage::PlaneGCSSolver_Storage(const SolverPtr& theSolver)
51 : SketchSolver_Storage(theSolver),
52 myConstraintLastID(CID_UNKNOWN)
56 void PlaneGCSSolver_Storage::addConstraint(
57 ConstraintPtr theConstraint,
58 ConstraintWrapperPtr theSolverConstraint)
60 SketchSolver_Storage::addConstraint(theConstraint, theSolverConstraint);
62 theSolverConstraint->setId(++myConstraintLastID);
63 constraintsToSolver(theSolverConstraint, mySketchSolver);
66 void PlaneGCSSolver_Storage::addMovementConstraint(
67 const ConstraintWrapperPtr& theSolverConstraint)
69 // before adding movement constraint to solver, re-check its DOF
70 if (mySketchSolver->dof() == 0)
71 mySketchSolver->diagnose();
73 theSolverConstraint->setId(CID_MOVEMENT);
74 constraintsToSolver(theSolverConstraint, mySketchSolver);
78 EntityWrapperPtr PlaneGCSSolver_Storage::createFeature(
79 const FeaturePtr& theFeature,
80 PlaneGCSSolver_EntityBuilder* theBuilder)
82 std::list<AttributePtr> anAttributes = theFeature->data()->attributes(std::string());
83 std::list<AttributePtr>::const_iterator anIt = anAttributes.begin();
84 for (; anIt != anAttributes.end(); ++anIt)
85 createAttribute(*anIt, theBuilder);
87 EntityWrapperPtr aResult = theBuilder->createFeature(theFeature);
89 addEntity(theFeature, aResult);
93 EntityWrapperPtr PlaneGCSSolver_Storage::createAttribute(
94 const AttributePtr& theAttribute,
95 PlaneGCSSolver_EntityBuilder* theBuilder)
97 EntityWrapperPtr aResult = theBuilder->createAttribute(theAttribute);
99 addEntity(theAttribute, aResult);
103 /// \brief Update value
104 static bool updateValue(const double& theSource, double& theDest)
106 static const double aTol = 1000. * tolerance;
107 bool isUpdated = fabs(theSource - theDest) > aTol;
113 /// \brief Update coordinates of the point or scalar using its base attribute
114 static bool updateValues(AttributePtr& theAttribute, EntityWrapperPtr& theEntity)
116 bool isUpdated = false;
118 std::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
119 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute);
121 const GCSPointPtr& aGCSPoint =
122 std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theEntity)->point();
123 isUpdated = updateValue(aPoint2D->x(), *(aGCSPoint->x)) || isUpdated;
124 isUpdated = updateValue(aPoint2D->y(), *(aGCSPoint->y)) || isUpdated;
126 AttributeDoublePtr aScalar =
127 std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theAttribute);
129 ScalarWrapperPtr aWrapper =
130 std::dynamic_pointer_cast<PlaneGCSSolver_ScalarWrapper>(theEntity);
131 // There is possible angular value, which is converted between degrees and radians.
132 // So, we use its value instead of using direct pointer to value.
133 double aValue = aWrapper->value();
134 isUpdated = updateValue(aScalar->value(), aValue);
136 aWrapper->setValue(aValue);
143 static bool hasReference(std::shared_ptr<SketchPlugin_Feature> theFeature,
144 const std::string& theFeatureKind)
146 const std::set<AttributePtr>& aRefs = theFeature->data()->refsToMe();
147 for (std::set<AttributePtr>::const_iterator aRefIt = aRefs.begin();
148 aRefIt != aRefs.end(); ++aRefIt) {
149 FeaturePtr anOwner = ModelAPI_Feature::feature((*aRefIt)->owner());
150 if (anOwner && anOwner->getKind() == theFeatureKind)
156 static bool isCopyFeature(std::shared_ptr<SketchPlugin_Feature> theFeature)
158 return theFeature && theFeature->isCopy();
161 bool PlaneGCSSolver_Storage::update(FeaturePtr theFeature, bool theForce)
163 bool sendNotify = false;
164 bool isUpdated = false;
165 std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
166 std::dynamic_pointer_cast<SketchPlugin_Feature>(theFeature);
167 EntityWrapperPtr aRelated = entity(theFeature);
168 if (aRelated) // send signal to subscribers
170 else { // Feature is not exist, create it
171 bool isCopy = isCopyFeature(aSketchFeature);
172 bool isProjReferred = hasReference(aSketchFeature, SketchPlugin_Projection::ID());
173 // the feature is a copy in "Multi" constraint and does not used in other constraints
174 if (!theForce && (isCopy && !isProjReferred) &&
175 myFeatureMap.find(theFeature) == myFeatureMap.end())
178 // external feature processing
180 (aSketchFeature && (aSketchFeature->isExternal() || isCopy || isProjReferred));
182 PlaneGCSSolver_FeatureBuilder aBuilder(isExternal ? 0 : this);
184 // Reserve the feature in the map of features
185 // (do not want to add several copies of it while adding attributes)
186 aRelated = createFeature(theFeature, &aBuilder);
187 myFeatureMap[theFeature] = aRelated;
188 createArcConstraints(aRelated);
192 std::list<AttributePtr> anAttributes = theFeature->data()->attributes(std::string());
193 std::list<AttributePtr>::iterator anAttrIt = anAttributes.begin();
194 for (; anAttrIt != anAttributes.end(); ++anAttrIt)
195 if ((*anAttrIt)->attributeType() == GeomDataAPI_Point2D::typeId() ||
196 (*anAttrIt)->attributeType() == ModelAPI_AttributeDouble::typeId())
197 isUpdated = update(*anAttrIt) || isUpdated;
199 // check external attribute is changed
200 bool isExternal = aSketchFeature &&
201 (aSketchFeature->isExternal() || isCopyFeature(aSketchFeature));
202 if (aRelated && isExternal != aRelated->isExternal()) {
204 makeExternal(aRelated);
206 makeNonExternal(aRelated);
210 // send notification to listeners due to at least one attribute is changed
211 if (sendNotify && isUpdated)
215 if (aRelated && aRelated->type() == ENTITY_ARC) {
216 /// TODO: this code should be shared with FeatureBuilder somehow
218 std::shared_ptr<PlaneGCSSolver_EdgeWrapper> anEntity =
219 std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(aRelated);
220 std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(anEntity->entity());
222 static std::shared_ptr<GeomAPI_Dir2d> OX(new GeomAPI_Dir2d(1.0, 0.0));
223 std::shared_ptr<GeomAPI_Pnt2d> aCenter(
224 new GeomAPI_Pnt2d(*anArc->center.x, *anArc->center.y));
225 std::shared_ptr<GeomAPI_Pnt2d> aStart(
226 new GeomAPI_Pnt2d(*anArc->start.x, *anArc->start.y));
228 *anArc->rad = aStart->distance(aCenter);
230 std::shared_ptr<GeomAPI_Dir2d> aDir(new GeomAPI_Dir2d(aStart->xy()->decreased(aCenter->xy())));
231 *anArc->startAngle = OX->angle(aDir);
233 aDir = std::shared_ptr<GeomAPI_Dir2d>(
234 new GeomAPI_Dir2d((*anArc->end.x) - aCenter->x(), (*anArc->end.y) - aCenter->y()));
235 *anArc->endAngle = OX->angle(aDir);
241 bool PlaneGCSSolver_Storage::update(AttributePtr theAttribute, bool theForce)
243 if (!theAttribute->isInitialized())
246 AttributePtr anAttribute = theAttribute;
247 AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(anAttribute);
249 if (aRefAttr->isObject()) {
251 /// TODO: Check resultToFeatureOrAttribute() precisely.
252 resultToFeatureOrAttribute(aRefAttr->object(), aFeature, anAttribute);
254 return update(aFeature, theForce);
256 anAttribute = aRefAttr->attr();
259 EntityWrapperPtr aRelated = entity(anAttribute);
260 FeaturePtr aFeature = ModelAPI_Feature::feature(anAttribute->owner());
261 if (!aRelated) { // Attribute does not exist, create it.
262 // First of all check if the parent feature exists. If not, add it.
263 if (aFeature && myFeatureMap.find(aFeature) == myFeatureMap.end())
264 return update(aFeature, theForce); // theAttribute has been processed while adding feature
265 return aRelated.get() != 0;
268 bool isUpdated = updateValues(anAttribute, aRelated);
270 setNeedToResolve(true);
276 void PlaneGCSSolver_Storage::makeExternal(const EntityWrapperPtr& theEntity)
278 if (theEntity->isExternal())
281 removeArcConstraints(theEntity);
283 GCS::SET_pD aParameters = PlaneGCSSolver_Tools::parameters(theEntity);
284 mySketchSolver->removeParameters(aParameters);
285 theEntity->setExternal(true);
286 myNeedToResolve = true;
289 void PlaneGCSSolver_Storage::makeNonExternal(const EntityWrapperPtr& theEntity)
291 if (!theEntity->isExternal())
294 GCS::SET_pD aParameters = PlaneGCSSolver_Tools::parameters(theEntity);
295 mySketchSolver->addParameters(aParameters);
296 theEntity->setExternal(false);
298 createArcConstraints(theEntity);
300 myNeedToResolve = true;
304 void PlaneGCSSolver_Storage::createArcConstraints(const EntityWrapperPtr& theArc)
306 if (!theArc || theArc->type() != ENTITY_ARC || theArc->isExternal())
309 EdgeWrapperPtr anEdge = std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(theArc);
310 std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(anEdge->entity());
312 // Additional constaints to fix arc's extra DoF (if the arc is not external):
313 std::list<GCSConstraintPtr> anArcConstraints;
314 // 1. distances from center till start and end points are equal to radius
315 anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintP2PDistance(
316 anArc->center, anArc->start, anArc->rad)));
317 anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintP2PDistance(
318 anArc->center, anArc->end, anArc->rad)));
319 // 2. angles of start and end points should be equal to the arc angles
320 anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintP2PAngle(
321 anArc->center, anArc->start, anArc->startAngle)));
322 anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintP2PAngle(
323 anArc->center, anArc->end, anArc->endAngle)));
325 ConstraintWrapperPtr aWrapper(
326 new PlaneGCSSolver_ConstraintWrapper(anArcConstraints, CONSTRAINT_UNKNOWN));
327 aWrapper->setId(++myConstraintLastID);
328 constraintsToSolver(aWrapper, mySketchSolver);
330 myArcConstraintMap[theArc] = aWrapper;
333 void PlaneGCSSolver_Storage::removeArcConstraints(const EntityWrapperPtr& theArc)
335 std::map<EntityWrapperPtr, ConstraintWrapperPtr>::iterator
336 aFound = myArcConstraintMap.find(theArc);
337 if (aFound != myArcConstraintMap.end()) {
338 mySketchSolver->removeConstraint(aFound->second->id());
339 myArcConstraintMap.erase(aFound);
344 bool PlaneGCSSolver_Storage::removeConstraint(ConstraintPtr theConstraint)
346 std::map<ConstraintPtr, ConstraintWrapperPtr>::iterator
347 aFound = myConstraintMap.find(theConstraint);
348 if (aFound != myConstraintMap.end()) {
349 ConstraintWrapperPtr aCW = aFound->second;
350 ConstraintID anID = aCW->id();
352 // Remove solver's constraints
353 mySketchSolver->removeConstraint(anID);
355 // Remove value if exists
356 const ScalarWrapperPtr& aValue = aCW->valueParameter();
358 GCS::SET_pD aParToRemove;
359 aParToRemove.insert(aValue->scalar());
360 removeParameters(aParToRemove);
364 myConstraintMap.erase(aFound);
366 if (anID != CID_MOVEMENT)
367 myNeedToResolve = true;
370 notify(theConstraint);
375 void PlaneGCSSolver_Storage::removeInvalidEntities()
377 PlaneGCSSolver_EntityDestroyer aDestroyer;
379 // Remove invalid constraints
380 std::list<ConstraintPtr> anInvalidConstraints;
381 std::map<ConstraintPtr, ConstraintWrapperPtr>::const_iterator
382 aCIter = myConstraintMap.begin();
383 for (; aCIter != myConstraintMap.end(); ++aCIter)
384 if (!aCIter->first->data() || !aCIter->first->data()->isValid())
385 anInvalidConstraints.push_back(aCIter->first);
386 std::list<ConstraintPtr>::const_iterator anInvCIt = anInvalidConstraints.begin();
387 for (; anInvCIt != anInvalidConstraints.end(); ++anInvCIt)
388 removeConstraint(*anInvCIt);
390 // Remove invalid features
391 std::list<FeaturePtr> anInvalidFeatures;
392 std::map<FeaturePtr, EntityWrapperPtr>::const_iterator aFIter = myFeatureMap.begin();
393 for (; aFIter != myFeatureMap.end(); aFIter++)
394 if (!aFIter->first->data() || !aFIter->first->data()->isValid()) {
395 anInvalidFeatures.push_back(aFIter->first);
397 aDestroyer.remove(aFIter->second);
399 // remove invalid arc
400 removeArcConstraints(aFIter->second);
402 std::list<FeaturePtr>::const_iterator anInvFIt = anInvalidFeatures.begin();
403 for (; anInvFIt != anInvalidFeatures.end(); ++anInvFIt)
404 removeFeature(*anInvFIt);
406 // Remove invalid attributes
407 std::list<AttributePtr> anInvalidAttributes;
408 std::map<AttributePtr, EntityWrapperPtr>::const_iterator anAttrIt = myAttributeMap.begin();
409 for (; anAttrIt != myAttributeMap.end(); ++anAttrIt) {
410 FeaturePtr anOwner = ModelAPI_Feature::feature(anAttrIt->first->owner());
411 if (!anOwner || !anOwner->data() || !anOwner->data()->isValid()) {
412 anInvalidAttributes.push_back(anAttrIt->first);
413 aDestroyer.remove(anAttrIt->second);
416 std::list<AttributePtr>::const_iterator anInvAtIt = anInvalidAttributes.begin();
417 for (; anInvAtIt != anInvalidAttributes.end(); ++anInvAtIt)
418 removeAttribute(*anInvAtIt);
420 // free memory occupied by parameters
421 removeParameters(aDestroyer.parametersToRemove());
423 /// TODO: Think on optimization of checking invalid features and attributes
428 double* PlaneGCSSolver_Storage::createParameter()
430 return mySketchSolver->createParameter();
433 void PlaneGCSSolver_Storage::removeParameters(const GCS::SET_pD& theParams)
435 mySketchSolver->removeParameters(theParams);
438 // indicates attribute containing in the external feature
439 static bool isExternalAttribute(const AttributePtr& theAttribute)
443 std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
444 std::dynamic_pointer_cast<SketchPlugin_Feature>(theAttribute->owner());
445 return aSketchFeature.get() && aSketchFeature->isExternal();
448 static void addOwnerToSet(const AttributePtr& theAttribute, std::set<FeaturePtr>& theFeatures)
450 FeaturePtr anOwner = ModelAPI_Feature::feature(theAttribute->owner());
452 theFeatures.insert(anOwner);
455 void PlaneGCSSolver_Storage::refresh() const
457 const double aTol = 1000. * tolerance; // tolerance to prevent frequent updates
459 std::set<FeaturePtr> anUpdatedFeatures;
461 std::map<AttributePtr, EntityWrapperPtr>::const_iterator anIt = myAttributeMap.begin();
462 for (; anIt != myAttributeMap.end(); ++anIt) {
463 if (!anIt->first->isInitialized())
466 // the external feature always should keep the up to date values, so,
467 // refresh from the solver is never needed
468 if (isExternalAttribute(anIt->first))
471 std::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
472 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anIt->first);
474 std::shared_ptr<PlaneGCSSolver_PointWrapper> aPointWrapper =
475 std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(anIt->second);
476 GCSPointPtr aGCSPoint = aPointWrapper->point();
477 if (fabs(aPoint2D->x() - (*aGCSPoint->x)) > aTol ||
478 fabs(aPoint2D->y() - (*aGCSPoint->y)) > aTol) {
479 aPoint2D->setValue(*aGCSPoint->x, *aGCSPoint->y);
480 addOwnerToSet(anIt->first, anUpdatedFeatures);
484 AttributeDoublePtr aScalar = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(anIt->first);
486 ScalarWrapperPtr aScalarWrapper =
487 std::dynamic_pointer_cast<PlaneGCSSolver_ScalarWrapper>(anIt->second);
488 if (fabs(aScalar->value() - aScalarWrapper->value()) > aTol) {
489 aScalar->setValue(aScalarWrapper->value());
490 addOwnerToSet(anIt->first, anUpdatedFeatures);
496 // notify listeners about features update
497 std::set<FeaturePtr>::const_iterator aFIt = anUpdatedFeatures.begin();
498 for (; aFIt != anUpdatedFeatures.end(); ++aFIt)
502 PlaneGCSSolver_Solver::SolveStatus PlaneGCSSolver_Storage::checkDegeneratedGeometry() const
504 std::map<FeaturePtr, EntityWrapperPtr>::const_iterator aFIt = myFeatureMap.begin();
505 for (; aFIt != myFeatureMap.end(); ++aFIt) {
506 EdgeWrapperPtr anEdge = std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(aFIt->second);
507 if (anEdge && anEdge->isDegenerated())
508 return PlaneGCSSolver_Solver::STATUS_DEGENERATED;
510 return PlaneGCSSolver_Solver::STATUS_OK;