1 // File: SketchSolver_ConstraintManager.cpp
2 // Created: 08 May 2014
3 // Author: Artem ZHIDKOV
5 #include "SketchSolver_ConstraintManager.h"
7 #include <Events_Loop.h>
8 #include <GeomDataAPI_Dir.h>
9 #include <GeomDataAPI_Point.h>
10 #include <GeomDataAPI_Point2D.h>
11 #include <ModelAPI_AttributeDouble.h>
12 #include <ModelAPI_AttributeRefList.h>
13 #include <ModelAPI_Data.h>
14 #include <Model_Events.h>
16 #include <SketchPlugin_Constraint.h>
17 #include <SketchPlugin_ConstraintCoincidence.h>
18 #include <SketchPlugin_Line.h>
19 #include <SketchPlugin_Point.h>
20 #include <SketchPlugin_Sketch.h>
27 /// Tolerance for value of parameters
28 const double tolerance = 1.e-10;
30 // Initialization of constraint manager self pointer
31 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::_self = 0;
33 /// Global constraint manager object
34 SketchSolver_ConstraintManager* myManager = SketchSolver_ConstraintManager::Instance();
36 /// This value is used to give unique index to the groups
37 static Slvs_hGroup myGroupIndexer = 0;
39 /** \brief Search the entity/parameter with specified ID in the list of elements
40 * \param[in] theEntityID unique ID of the element
41 * \param[in] theEntities list of elements
42 * \return position of the found element or -1 if the element is not found
45 static int Search(const uint32_t& theEntityID, const std::vector<T>& theEntities);
49 // ========================================================
50 // ========= SketchSolver_ConstraintManager ===============
51 // ========================================================
52 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::Instance()
55 _self = new SketchSolver_ConstraintManager();
59 SketchSolver_ConstraintManager::SketchSolver_ConstraintManager()
63 // Register in event loop
64 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_CREATED));
65 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_UPDATED));
66 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_DELETED));
67 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_MOVED));
70 SketchSolver_ConstraintManager::~SketchSolver_ConstraintManager()
75 void SketchSolver_ConstraintManager::processEvent(const Events_Message* theMessage)
77 if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_CREATED) ||
78 theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_UPDATED) ||
79 theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_MOVED))
81 const Model_FeatureUpdatedMessage* anUpdateMsg = dynamic_cast<const Model_FeatureUpdatedMessage*>(theMessage);
82 std::set< boost::shared_ptr<ModelAPI_Feature> > aFeatures = anUpdateMsg->features();
84 bool isModifiedEvt = theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_MOVED);
87 std::set< boost::shared_ptr<ModelAPI_Feature> >::iterator aFeatIter;
88 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++)
90 // Only sketches and constraints can be added by Create event
91 const std::string& aFeatureKind = (*aFeatIter)->getKind();
92 if (aFeatureKind.compare("Sketch") == 0)
94 boost::shared_ptr<SketchPlugin_Feature> aSketch =
95 boost::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
96 changeWorkplane(aSketch);
99 boost::shared_ptr<SketchPlugin_Constraint> aConstraint =
100 boost::dynamic_pointer_cast<SketchPlugin_Constraint>(*aFeatIter);
102 changeConstraint(aConstraint);
105 // Sketch plugin features can be only updated
106 boost::shared_ptr<SketchPlugin_Feature> aFeature =
107 boost::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
109 updateEntity(aFeature);
114 // Solve the set of constraints
115 resolveConstraints(isModifiedEvt);
117 else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_DELETED))
119 const Model_FeatureDeletedMessage* aDeleteMsg = dynamic_cast<const Model_FeatureDeletedMessage*>(theMessage);
120 const std::set<std::string>& aFeatureGroups = aDeleteMsg->groups();
122 // Find "Sketch" in groups. The constraint groups should be updated when an object removed from Sketch
123 std::set<std::string>::const_iterator aFGrIter;
124 for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
125 if (aFGrIter->compare("Sketch") == 0)
128 if (aFGrIter != aFeatureGroups.end())
130 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter = myGroups.begin();
131 while (aGroupIter != myGroups.end())
133 if ((*aGroupIter)->updateGroup())
134 { // the group should be removed
136 int aShift = aGroupIter - myGroups.begin();
137 myGroups.erase(aGroupIter);
138 aGroupIter = myGroups.begin() + aShift;
146 bool SketchSolver_ConstraintManager::changeWorkplane(boost::shared_ptr<SketchPlugin_Feature> theSketch)
148 bool aResult = true; // changed when a workplane wrongly updated
149 bool isUpdated = false;
150 // Try to update specified workplane in all groups
151 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
152 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
153 if ((*aGroupIter)->isBaseWorkplane(theSketch))
156 if (!(*aGroupIter)->updateWorkplane())
159 // If the workplane is not updated, so this is a new workplane
162 SketchSolver_ConstraintGroup* aNewGroup = new SketchSolver_ConstraintGroup(theSketch);
163 // Verify that the group is created successfully
164 if (!aNewGroup->isBaseWorkplane(theSketch))
169 myGroups.push_back(aNewGroup);
174 bool SketchSolver_ConstraintManager::changeConstraint(
175 boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
177 // Search the groups which this constraint touchs
178 std::vector<Slvs_hGroup> aGroups;
179 findGroups(theConstraint, aGroups);
181 // Process the groups list
182 if (aGroups.size() == 0)
183 { // There are no groups applicable for this constraint => create new one
184 boost::shared_ptr<SketchPlugin_Feature> aWP = findWorkplaneForConstraint(theConstraint);
185 if (!aWP) return false;
186 SketchSolver_ConstraintGroup* aGroup = new SketchSolver_ConstraintGroup(aWP);
187 if (!aGroup->changeConstraint(theConstraint))
192 myGroups.push_back(aGroup);
195 else if (aGroups.size() == 1)
196 { // Only one group => add constraint into it
197 Slvs_hGroup aGroupId = *(aGroups.begin());
198 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
199 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
200 if ((*aGroupIter)->getId() == aGroupId)
201 return (*aGroupIter)->changeConstraint(theConstraint);
203 else if (aGroups.size() > 1)
204 { // Several groups applicable for this constraint => need to merge them
205 /// \todo Implement merging of groups
208 // Something goes wrong
212 void SketchSolver_ConstraintManager::updateEntity(boost::shared_ptr<SketchPlugin_Feature> theFeature)
214 // Create list of attributes depending on type of the feature
215 std::vector<std::string> anAttrList;
216 const std::string& aFeatureKind = theFeature->getKind();
218 if (aFeatureKind.compare("SketchPoint") == 0)
219 anAttrList.push_back(POINT_ATTR_COORD);
221 else if (aFeatureKind.compare("SketchLine") == 0)
223 anAttrList.push_back(LINE_ATTR_START);
224 anAttrList.push_back(LINE_ATTR_END);
226 /// \todo Other types of features should be implemented
228 // Check changing of feature's attributes (go through the groups and search usage of the attributes)
229 std::vector<std::string>::const_iterator anAttrIter;
230 for (anAttrIter = anAttrList.begin(); anAttrIter != anAttrList.end(); anAttrIter++)
232 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
233 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
235 boost::shared_ptr<ModelAPI_Attribute> anAttribute =
236 boost::dynamic_pointer_cast<ModelAPI_Attribute>(theFeature->data()->attribute(*anAttrIter));
237 (*aGroupIter)->updateEntityIfPossible(anAttribute);
243 void SketchSolver_ConstraintManager::findGroups(
244 boost::shared_ptr<SketchPlugin_Constraint> theConstraint,
245 std::vector<Slvs_hGroup>& theGroupIDs) const
247 boost::shared_ptr<SketchPlugin_Feature> aWP = findWorkplaneForConstraint(theConstraint);
249 std::vector<SketchSolver_ConstraintGroup*>::const_iterator aGroupIter;
250 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
251 if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theConstraint))
252 theGroupIDs.push_back((*aGroupIter)->getId());
255 boost::shared_ptr<SketchPlugin_Feature> SketchSolver_ConstraintManager::findWorkplaneForConstraint(
256 boost::shared_ptr<SketchPlugin_Constraint> theConstraint) const
258 // Already verified workplanes
259 std::set< boost::shared_ptr<SketchPlugin_Feature> > aVerified;
261 std::vector<SketchSolver_ConstraintGroup*>::const_iterator aGroupIter;
262 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
264 boost::shared_ptr<SketchPlugin_Feature> aWP = (*aGroupIter)->getWorkplane();
265 if (aVerified.find(aWP) != aVerified.end())
268 boost::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures =
269 boost::dynamic_pointer_cast<ModelAPI_AttributeRefList>(aWP->data()->attribute(SKETCH_ATTR_FEATURES));
270 std::list< boost::shared_ptr<ModelAPI_Feature> > aFeaturesList = aWPFeatures->list();
271 std::list< boost::shared_ptr<ModelAPI_Feature> >::const_iterator anIter;
272 for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
273 if (*anIter == theConstraint)
274 return aWP; // workplane is found
275 aVerified.insert(aWP);
278 return boost::shared_ptr<SketchPlugin_Feature>();
281 void SketchSolver_ConstraintManager::resolveConstraints(const bool needEvent)
283 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
284 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
285 (*aGroupIter)->resolveConstraints();
287 // Features may be updated => send events
289 Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_FEATURE_UPDATED));
294 // ========================================================
295 // ========= SketchSolver_ConstraintGroup ===============
296 // ========================================================
298 SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::
299 SketchSolver_ConstraintGroup(boost::shared_ptr<SketchPlugin_Feature> theWorkplane)
300 : myID(++myGroupIndexer),
305 myNeedToSolve(false),
310 myConstraints.clear();
312 // Initialize workplane
313 myWorkplane.h = SLVS_E_UNKNOWN;
314 assert(addWorkplane(theWorkplane));
317 SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::~SketchSolver_ConstraintGroup()
321 myConstraints.clear();
322 myConstraintMap.clear();
324 // If the group with maximal identifier is deleted, decrease the indexer
325 if (myID == myGroupIndexer)
329 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::isBaseWorkplane(
330 boost::shared_ptr<SketchPlugin_Feature> theWorkplane) const
332 return theWorkplane == mySketch;
335 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::isInteract(
336 boost::shared_ptr<SketchPlugin_Constraint> theConstraint) const
338 // Check the group is empty
339 if (myWorkplane.h != SLVS_E_UNKNOWN && myConstraints.empty())
342 // Go through constraint entities and verify if some of them already in the group
343 for (int i = 0; i < CONSTRAINT_ATTR_SIZE; i++)
345 boost::shared_ptr<ModelAPI_AttributeRefAttr> aCAttrRef =
346 boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
347 theConstraint->data()->attribute(CONSTRAINT_ATTRIBUTES[i])
349 if (!aCAttrRef) continue;
350 if (myEntityMap.find(aCAttrRef->attr()) != myEntityMap.end())
354 // Entities did not found
358 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::changeConstraint(
359 boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
361 // There is no workplane yet, something wrong
362 if (myWorkplane.h == SLVS_E_UNKNOWN)
365 // Search this constraint in the current group to update it
366 std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::const_iterator
367 aConstrMapIter = myConstraintMap.find(theConstraint);
368 std::vector<Slvs_Constraint>::iterator aConstrIter;
369 if (aConstrMapIter != myConstraintMap.end())
371 int aConstrPos = Search(aConstrMapIter->second, myConstraints);
372 aConstrIter = myConstraints.begin() + aConstrPos;
375 // Get constraint type and verify the constraint parameters are correct
376 int aConstrType = getConstraintType(theConstraint);
377 if (aConstrType == SLVS_C_UNKNOWN ||
378 (aConstrMapIter != myConstraintMap.end() && aConstrIter->type != aConstrType))
381 // Create constraint parameters
382 double aDistance = 0.0; // scalar value of the constraint
383 boost::shared_ptr<ModelAPI_AttributeDouble> aDistAttr =
384 boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theConstraint->data()->attribute(CONSTRAINT_ATTR_VALUE));
387 aDistance = aDistAttr->value();
388 if (aConstrMapIter != myConstraintMap.end() && fabs(aConstrIter->valA - aDistance) > tolerance)
390 myNeedToSolve = true;
391 aConstrIter->valA = aDistance;
395 Slvs_hEntity aConstrEnt[CONSTRAINT_ATTR_SIZE]; // parameters of the constraint
396 for (unsigned int indAttr = 0; indAttr < CONSTRAINT_ATTR_SIZE; indAttr++)
398 aConstrEnt[indAttr] = SLVS_E_UNKNOWN;
399 boost::shared_ptr<ModelAPI_AttributeRefAttr> aConstrAttr =
400 boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
401 theConstraint->data()->attribute(CONSTRAINT_ATTRIBUTES[indAttr])
403 if (!aConstrAttr) continue;
404 aConstrEnt[indAttr] = changeEntity(aConstrAttr->attr());
407 if (aConstrMapIter == myConstraintMap.end())
409 // Create SolveSpace constraint structure
410 Slvs_Constraint aConstraint =
411 Slvs_MakeConstraint(++myConstrMaxID, myID, aConstrType, myWorkplane.h,
412 aDistance, aConstrEnt[0], aConstrEnt[1], aConstrEnt[2], aConstrEnt[3]);
413 myConstraints.push_back(aConstraint);
414 myConstraintMap[theConstraint] = aConstraint.h;
419 Slvs_hEntity SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::changeEntity(
420 boost::shared_ptr<ModelAPI_Attribute> theEntity)
422 // If the entity is already in the group, try to find it
423 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
424 aEntIter = myEntityMap.find(theEntity);
425 std::vector<Slvs_Param>::const_iterator aParamIter; // looks at first parameter of already existent entity or at the end of vector otherwise
426 if (aEntIter == myEntityMap.end()) // no such entity => should be created
427 aParamIter = myParams.end();
429 { // the entity already exists
430 int aEntPos = Search(aEntIter->second, myEntities);
431 int aParamPos = Search(myEntities[aEntPos].param[0], myParams);
432 aParamIter = myParams.begin() + aParamPos;
435 // Look over supported types of entities
438 boost::shared_ptr<GeomDataAPI_Point> aPoint =
439 boost::dynamic_pointer_cast<GeomDataAPI_Point>(theEntity);
442 Slvs_hParam aX = changeParameter(aPoint->x(), aParamIter);
443 Slvs_hParam aY = changeParameter(aPoint->y(), aParamIter);
444 Slvs_hParam aZ = changeParameter(aPoint->z(), aParamIter);
446 if (aEntIter != myEntityMap.end()) // the entity already exists
447 return aEntIter->second;
450 Slvs_Entity aPtEntity = Slvs_MakePoint3d(++myEntityMaxID, myID, aX, aY, aZ);
451 myEntities.push_back(aPtEntity);
452 myEntityMap[theEntity] = aPtEntity.h;
457 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
458 boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theEntity);
461 // The 2D points are created on workplane. So, if there is no workplane yet, then error
462 if (myWorkplane.h == SLVS_E_UNKNOWN)
463 return SLVS_E_UNKNOWN;
464 Slvs_hParam aU = changeParameter(aPoint2D->x(), aParamIter);
465 Slvs_hParam aV = changeParameter(aPoint2D->y(), aParamIter);
467 if (aEntIter != myEntityMap.end()) // the entity already exists
468 return aEntIter->second;
471 Slvs_Entity aPt2DEntity = Slvs_MakePoint2d(++myEntityMaxID, myID, myWorkplane.h, aU, aV);
472 myEntities.push_back(aPt2DEntity);
473 myEntityMap[theEntity] = aPt2DEntity.h;
474 return aPt2DEntity.h;
477 /// \todo Other types of entities
479 // Unsupported or wrong entity type
480 return SLVS_E_UNKNOWN;
483 Slvs_hEntity SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::changeNormal(
484 boost::shared_ptr<ModelAPI_Attribute> theDirX,
485 boost::shared_ptr<ModelAPI_Attribute> theDirY,
486 boost::shared_ptr<ModelAPI_Attribute> theNorm)
488 boost::shared_ptr<GeomDataAPI_Dir> aDirX =
489 boost::dynamic_pointer_cast<GeomDataAPI_Dir>(theDirX);
490 boost::shared_ptr<GeomDataAPI_Dir> aDirY =
491 boost::dynamic_pointer_cast<GeomDataAPI_Dir>(theDirY);
492 if (!aDirX || !aDirY ||
493 (fabs(aDirX->x()) + fabs(aDirX->y()) + fabs(aDirX->z()) < tolerance) ||
494 (fabs(aDirY->x()) + fabs(aDirY->y()) + fabs(aDirY->z()) < tolerance))
495 return SLVS_E_UNKNOWN;
497 // quaternion parameters of normal vector
498 double qw, qx, qy, qz;
499 Slvs_MakeQuaternion(aDirX->x(), aDirX->y(), aDirX->z(),
500 aDirY->x(), aDirY->y(), aDirY->z(),
502 double aNormCoord[4] = {qw, qx, qy, qz};
504 // Try to find existent normal
505 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
506 aEntIter = myEntityMap.find(theNorm);
507 std::vector<Slvs_Param>::const_iterator aParamIter; // looks at first parameter of already existent entity or at the end of vector otherwise
508 if (aEntIter == myEntityMap.end()) // no such entity => should be created
509 aParamIter = myParams.end();
511 { // the entity already exists, update it
512 int aEntPos = Search(aEntIter->second, myEntities);
513 int aParamPos = Search(myEntities[aEntPos].param[0], myParams);
514 aParamIter = myParams.begin() + aParamPos;
517 // Change parameters of the normal
518 Slvs_hParam aNormParams[4];
519 for (int i = 0; i < 4; i++)
520 aNormParams[i] = changeParameter(aNormCoord[i], aParamIter);
522 if (aEntIter != myEntityMap.end()) // the entity already exists
523 return aEntIter->second;
526 Slvs_Entity aNormal = Slvs_MakeNormal3d(++myEntityMaxID, myID,
527 aNormParams[0], aNormParams[1], aNormParams[2], aNormParams[3]);
528 myEntities.push_back(aNormal);
529 myEntityMap[theNorm] = aNormal.h;
534 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::addWorkplane(
535 boost::shared_ptr<SketchPlugin_Feature> theSketch)
537 if (myWorkplane.h || theSketch->getKind().compare("Sketch") != 0)
538 return false; // the workplane already exists or the function parameter is not Sketch
540 mySketch = theSketch;
545 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::updateWorkplane()
547 // Get parameters of workplane
548 boost::shared_ptr<ModelAPI_Attribute> aDirX = mySketch->data()->attribute(SKETCH_ATTR_DIRX);
549 boost::shared_ptr<ModelAPI_Attribute> aDirY = mySketch->data()->attribute(SKETCH_ATTR_DIRY);
550 boost::shared_ptr<ModelAPI_Attribute> aNorm = mySketch->data()->attribute(SKETCH_ATTR_NORM);
551 boost::shared_ptr<ModelAPI_Attribute> anOrigin = mySketch->data()->attribute(SKETCH_ATTR_ORIGIN);
552 // Transform them into SolveSpace format
553 Slvs_hEntity aNormalWP = changeNormal(aDirX, aDirY, aNorm);
554 if (!aNormalWP) return false;
555 Slvs_hEntity anOriginWP = changeEntity(anOrigin);
556 if (!anOriginWP) return false;
561 myWorkplane = Slvs_MakeWorkplane(++myEntityMaxID, myID, anOriginWP, aNormalWP);
562 // Workplane should be added to the list of entities
563 myEntities.push_back(myWorkplane);
569 Slvs_hParam SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::changeParameter(
570 const double& theParam,
571 std::vector<Slvs_Param>::const_iterator& thePrmIter)
573 if (thePrmIter != myParams.end())
574 { // Parameter should be updated
575 int aParamPos = thePrmIter - myParams.begin();
576 if (fabs(thePrmIter->val - theParam) > tolerance)
578 myNeedToSolve = true; // parameter is changed, need to resolve constraints
579 myParams[aParamPos].val = theParam;
582 return myParams[aParamPos].h;
585 // Newly created parameter
586 Slvs_Param aParam = Slvs_MakeParam(++myParamMaxID, myID, theParam);
587 myParams.push_back(aParam);
588 myNeedToSolve = true;
589 // The list of parameters is changed, move iterator to the end of the list to avoid problems
590 thePrmIter = myParams.end();
594 int SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::getConstraintType(
595 const boost::shared_ptr<SketchPlugin_Constraint>& theConstraint) const
597 const std::string& aConstraintKind = theConstraint->getKind();
598 // Constraint for coincidence of two points
599 if (aConstraintKind.compare("SketchConstraintCoincidence") == 0)
601 // Verify the constraint has only two attributes and they are points
602 int aPt2d = 0; // bit-mapped field, each bit indicates whether the attribute is 2D point
603 int aPt3d = 0; // bit-mapped field, the same information for 3D points
604 for (unsigned int indAttr = 0; indAttr < CONSTRAINT_ATTR_SIZE; indAttr++)
606 boost::shared_ptr<ModelAPI_AttributeRefAttr> anAttr =
607 boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
608 theConstraint->data()->attribute(CONSTRAINT_ATTRIBUTES[indAttr])
610 if (!anAttr) continue;
611 // Verify the attribute is a 2D point
612 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
613 boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttr->attr());
616 aPt2d |= (1 << indAttr);
619 // Verify the attribute is a 3D point
620 boost::shared_ptr<GeomDataAPI_Point> aPoint3D =
621 boost::dynamic_pointer_cast<GeomDataAPI_Point>(anAttr->attr());
624 aPt3d |= (1 << indAttr);
627 // Attribute neither 2D nor 3D point is not supported by this type of constraint
628 return SLVS_C_UNKNOWN;
630 // The constrained points should be in first and second positions,
631 // so the expected value of aPt2d or aPt3d is 3
632 if ((aPt2d == 3 && aPt3d == 0) || (aPt2d == 0 && aPt3d == 3))
633 return SLVS_C_POINTS_COINCIDENT;
634 // Constraint parameters are wrong
635 return SLVS_C_UNKNOWN;
638 /// \todo Implement other kind of constrtaints
640 return SLVS_C_UNKNOWN;
643 void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::resolveConstraints()
648 myConstrSolver.setGroupID(myID);
649 myConstrSolver.setParameters(myParams);
650 myConstrSolver.setEntities(myEntities);
651 myConstrSolver.setConstraints(myConstraints);
653 if (myConstrSolver.solve() == SLVS_RESULT_OKAY)
654 { // solution succeeded, store results into correspondent attributes
655 // Obtain result into the same list of parameters
656 if (!myConstrSolver.getResult(myParams))
659 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator
660 anEntIter = myEntityMap.begin();
661 for ( ; anEntIter != myEntityMap.end(); anEntIter++)
662 updateAttribute(anEntIter->first, anEntIter->second);
664 /// \todo Implement error handling
666 removeTemporaryConstraints();
667 myNeedToSolve = false;
670 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::updateGroup()
672 // Check for valid sketch
673 if (!mySketch->data()->isValid())
676 // Fast check for constraint validity. If all constraints are valid, no need to update the group
677 std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::reverse_iterator
678 aConstrIter = myConstraintMap.rbegin();
679 bool isAllValid = true;
680 for ( ; isAllValid && aConstrIter != myConstraintMap.rend(); aConstrIter++)
681 if (!aConstrIter->first->data()->isValid())
686 // Remove invalid constraints.
687 // There only constraint will be deleted (parameters and entities) will be removed below
688 std::list< boost::shared_ptr<SketchPlugin_Constraint> > aConstrToDelete;
689 std::map<Slvs_hEntity, bool> anEntToDelete; // entities will be removed if no valid constraints use them
690 for (aConstrIter = myConstraintMap.rbegin(); aConstrIter != myConstraintMap.rend(); aConstrIter++)
692 bool isValid = aConstrIter->first->data()->isValid();
694 int aConstrPos = Search(aConstrIter->second, myConstraints);
695 if (aConstrPos < (int)myConstraints.size())
697 Slvs_hEntity aConstrEnt[] = {
698 myConstraints[aConstrPos].ptA, myConstraints[aConstrPos].ptB,
699 myConstraints[aConstrPos].entityA, myConstraints[aConstrPos].entityB};
700 for (int i = 0; i < 4; i++)
701 if (aConstrEnt[i] != SLVS_E_UNKNOWN)
703 if (anEntToDelete.find(aConstrEnt[i]) == anEntToDelete.end())
704 anEntToDelete[aConstrEnt[i]] = !isValid;
705 else if (isValid) // constraint is valid => no need to remove its entities
706 anEntToDelete[aConstrEnt[i]] = false;
710 myConstraints.erase(myConstraints.begin() + aConstrPos);
711 if (aConstrIter->second == myConstrMaxID) // When the constraint with highest ID is removed, decrease indexer
713 aConstrToDelete.push_front(aConstrIter->first);
717 std::list< boost::shared_ptr<SketchPlugin_Constraint> >::iterator aDelIter;
718 for (aDelIter = aConstrToDelete.begin(); aDelIter != aConstrToDelete.end(); aDelIter++)
719 myConstraintMap.erase(*aDelIter);
721 // Remove invalid and unused entities
722 std::map<Slvs_hEntity, bool>::reverse_iterator aEDelIter;
723 for (aEDelIter = anEntToDelete.rbegin(); aEDelIter != anEntToDelete.rend(); aEDelIter++)
724 if (aEDelIter->second)
726 int anEntPos = Search(aEDelIter->first, myEntities);
727 std::vector<Slvs_Entity>::iterator aEntIter = myEntities.begin() + anEntPos;
728 // Number of parameters for the entity
730 while (aEntIter->param[aNbParams]) aNbParams++;
731 if (aNbParams == 0) continue;
732 // Decrease parameter indexer if there are deleted parameter with higher IDs
733 if (aEntIter->param[aNbParams-1] == myParamMaxID)
734 myParamMaxID -= aNbParams;
735 // Remove parameters of the entity
736 int aParamPos = Search(aEntIter->param[0], myParams);
737 myParams.erase(myParams.begin() + aParamPos,
738 myParams.begin() + aParamPos + aNbParams);
741 if (aEDelIter->first == myEntityMaxID)
743 myEntities.erase(myEntities.begin() + anEntPos);
744 // Remove such entity from myEntityMap
745 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator
746 anEntMapIter = myEntityMap.begin();
747 for ( ; anEntMapIter != myEntityMap.end(); anEntMapIter++)
748 if (anEntMapIter->second == aEDelIter->first)
750 if (anEntMapIter != myEntityMap.end())
751 myEntityMap.erase(anEntMapIter);
757 void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::updateAttribute(
758 boost::shared_ptr<ModelAPI_Attribute> theAttribute,
759 const Slvs_hEntity& theEntityID)
761 // Search the position of the first parameter of the entity
762 int anEntPos = Search(theEntityID, myEntities);
763 int aFirstParamPos = Search(myEntities[anEntPos].param[0], myParams);
765 // Look over supported types of entities
768 boost::shared_ptr<GeomDataAPI_Point> aPoint =
769 boost::dynamic_pointer_cast<GeomDataAPI_Point>(theAttribute);
772 aPoint->setValue(myParams[aFirstParamPos].val,
773 myParams[aFirstParamPos+1].val,
774 myParams[aFirstParamPos+2].val);
779 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
780 boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute);
783 aPoint2D->setValue(myParams[aFirstParamPos].val,
784 myParams[aFirstParamPos+1].val);
788 /// \todo Support other types of entities
791 void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::updateEntityIfPossible(
792 boost::shared_ptr<ModelAPI_Attribute> theEntity)
794 if (myEntityMap.find(theEntity) != myEntityMap.end())
796 // If the attribute is a point and it is changed (the group needs to rebuild),
797 // probably user has dragged this point into this position,
798 // so it is necessary to add constraint which will guarantee the point will not change
800 // Store myNeedToSolve flag to verify the entity is really changed
801 bool aNeedToSolveCopy = myNeedToSolve;
802 myNeedToSolve = false;
804 changeEntity(theEntity);
806 if (myNeedToSolve) // the entity is changed
808 // Verify the entity is a point and add temporary constraint of permanency
809 boost::shared_ptr<GeomDataAPI_Point> aPoint =
810 boost::dynamic_pointer_cast<GeomDataAPI_Point>(theEntity);
811 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
812 boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theEntity);
813 if (aPoint || aPoint2D)
814 addTemporaryConstraintWhereDragged(theEntity);
817 // Restore flag of changes
818 myNeedToSolve = myNeedToSolve || aNeedToSolveCopy;
822 void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::addTemporaryConstraintWhereDragged(
823 boost::shared_ptr<ModelAPI_Attribute> theEntity)
825 // Find identifier of the entity
826 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
827 anEntIter = myEntityMap.find(theEntity);
829 // Create WHERE_DRAGGED constraint
830 Slvs_Constraint aWDConstr = Slvs_MakeConstraint(++myConstrMaxID, myID, SLVS_C_WHERE_DRAGGED,
831 myWorkplane.h, 0.0, anEntIter->second, 0, 0, 0);
832 myConstraints.push_back(aWDConstr);
833 myTempConstraints.push_back(aWDConstr.h);
836 void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::removeTemporaryConstraints()
838 std::list<Slvs_hConstraint>::reverse_iterator aTmpConstrIter;
839 for (aTmpConstrIter = myTempConstraints.rbegin(); aTmpConstrIter != myTempConstraints.rend(); aTmpConstrIter++)
841 int aConstrPos = Search(*aTmpConstrIter, myConstraints);
842 myConstraints.erase(myConstraints.begin() + aConstrPos);
844 // If the removing constraint has higher index, decrease the indexer
845 if (*aTmpConstrIter == myConstrMaxID)
848 myTempConstraints.clear();
853 // ========================================================
854 // ========= Auxiliary functions ===============
855 // ========================================================
857 template <typename T>
858 int Search(const uint32_t& theEntityID, const std::vector<T>& theEntities)
860 int aResIndex = theEntityID <= theEntities.size() ? theEntityID - 1 : 0;
861 int aVecSize = theEntities.size();
862 while (aResIndex >= 0 && theEntities[aResIndex].h > theEntityID)
864 while (aResIndex < aVecSize && theEntities[aResIndex].h < theEntityID)
867 aResIndex = aVecSize;