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>
25 /// Tolerance for value of parameters
26 const double tolerance = 1.e-10;
28 // Initialization of constraint manager self pointer
29 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::_self = 0;
31 /// Global constraint manager object
32 SketchSolver_ConstraintManager* myManager = SketchSolver_ConstraintManager::Instance();
34 /// This value is used to give unique index to the groups
35 static Slvs_hGroup myGroupIndexer = 0;
37 /** \brief Search the entity/parameter with specified ID in the list of elements
38 * \param[in] theEntityID unique ID of the element
39 * \param[in] theEntities list of elements
40 * \return position of the found element or -1 if the element is not found
43 static int Search(const uint32_t& theEntityID, const std::vector<T>& theEntities);
47 // ========================================================
48 // ========= SketchSolver_ConstraintManager ===============
49 // ========================================================
50 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::Instance()
53 _self = new SketchSolver_ConstraintManager();
57 SketchSolver_ConstraintManager::SketchSolver_ConstraintManager()
61 // Register in event loop
62 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_CREATED));
63 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_UPDATED));
64 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_DELETED));
65 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_MOVED));
68 SketchSolver_ConstraintManager::~SketchSolver_ConstraintManager()
73 void SketchSolver_ConstraintManager::processEvent(const Events_Message* theMessage)
75 if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_CREATED) ||
76 theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_UPDATED) ||
77 theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_MOVED))
79 const Model_FeatureUpdatedMessage* anUpdateMsg = dynamic_cast<const Model_FeatureUpdatedMessage*>(theMessage);
80 std::set< boost::shared_ptr<ModelAPI_Feature> > aFeatures = anUpdateMsg->features();
82 bool isModifiedEvt = theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_MOVED);
85 std::set< boost::shared_ptr<ModelAPI_Feature> >::iterator aFeatIter;
86 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++)
88 // Only sketches and constraints can be added by Create event
89 const std::string& aFeatureKind = (*aFeatIter)->getKind();
90 if (aFeatureKind.compare("Sketch") == 0)
92 boost::shared_ptr<SketchPlugin_Feature> aSketch =
93 boost::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
95 changeWorkplane(aSketch);
98 boost::shared_ptr<SketchPlugin_Constraint> aConstraint =
99 boost::dynamic_pointer_cast<SketchPlugin_Constraint>(*aFeatIter);
101 changeConstraint(aConstraint);
104 // Sketch plugin features can be only updated
105 boost::shared_ptr<SketchPlugin_Feature> aFeature =
106 boost::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
108 updateEntity(aFeature);
113 // Solve the set of constraints
114 resolveConstraints();
116 else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_DELETED))
118 const Model_FeatureDeletedMessage* aDeleteMsg = dynamic_cast<const Model_FeatureDeletedMessage*>(theMessage);
119 const std::set<std::string>& aFeatureGroups = aDeleteMsg->groups();
121 // Find "Sketch" in groups. The constraint groups should be updated when an object removed from Sketch
122 std::set<std::string>::const_iterator aFGrIter;
123 for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
124 if (aFGrIter->compare("Sketch") == 0)
127 if (aFGrIter != aFeatureGroups.end())
129 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter = myGroups.begin();
130 while (aGroupIter != myGroups.end())
132 if ((*aGroupIter)->updateGroup())
133 { // the group should be removed
135 int aShift = aGroupIter - myGroups.begin();
136 myGroups.erase(aGroupIter);
137 aGroupIter = myGroups.begin() + aShift;
145 bool SketchSolver_ConstraintManager::changeWorkplane(boost::shared_ptr<SketchPlugin_Feature> theSketch)
147 bool aResult = true; // changed when a workplane wrongly updated
148 bool isUpdated = false;
149 // Try to update specified workplane in all groups
150 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
151 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
152 if ((*aGroupIter)->isBaseWorkplane(theSketch))
155 if (!(*aGroupIter)->updateWorkplane())
158 // If the workplane is not updated, so this is a new workplane
161 SketchSolver_ConstraintGroup* aNewGroup = new SketchSolver_ConstraintGroup(theSketch);
162 // Verify that the group is created successfully
163 if (!aNewGroup->isBaseWorkplane(theSketch))
168 myGroups.push_back(aNewGroup);
173 bool SketchSolver_ConstraintManager::changeConstraint(
174 boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
176 // Search the groups which this constraint touchs
177 std::set<Slvs_hGroup> aGroups;
178 findGroups(theConstraint, aGroups);
180 // Process the groups list
181 if (aGroups.size() == 0)
182 { // There are no groups applicable for this constraint => create new one
183 boost::shared_ptr<SketchPlugin_Feature> aWP = findWorkplaneForConstraint(theConstraint);
184 if (!aWP) return false;
185 SketchSolver_ConstraintGroup* aGroup = new SketchSolver_ConstraintGroup(aWP);
186 if (!aGroup->changeConstraint(theConstraint))
191 myGroups.push_back(aGroup);
194 else if (aGroups.size() == 1)
195 { // Only one group => add constraint into it
196 Slvs_hGroup aGroupId = *(aGroups.begin());
197 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
198 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
199 if ((*aGroupIter)->getId() == aGroupId)
200 return (*aGroupIter)->changeConstraint(theConstraint);
202 else if (aGroups.size() > 1)
203 { // Several groups applicable for this constraint => need to merge them
204 std::set<Slvs_hGroup>::const_iterator aGroupsIter = aGroups.begin();
206 // Search first group
207 std::vector<SketchSolver_ConstraintGroup*>::iterator aFirstGroupIter;
208 for (aFirstGroupIter = myGroups.begin(); aFirstGroupIter != myGroups.end(); aFirstGroupIter++)
209 if ((*aFirstGroupIter)->getId() == *aGroupsIter)
211 if (aFirstGroupIter == myGroups.end())
214 // Append other groups to the first one
215 std::vector<SketchSolver_ConstraintGroup*>::iterator anOtherGroupIter = aFirstGroupIter + 1;
216 for (aGroupsIter++; aGroupsIter != aGroups.end(); aGroupsIter++)
218 for ( ; anOtherGroupIter != myGroups.end(); anOtherGroupIter++)
219 if ((*anOtherGroupIter)->getId() == *aGroupsIter)
221 if (anOtherGroupIter == myGroups.end())
222 { // Group disappears
223 anOtherGroupIter = aFirstGroupIter + 1;
227 (*aFirstGroupIter)->mergeGroups(**anOtherGroupIter);
228 int aShiftFirst = aFirstGroupIter - myGroups.begin();
229 int aShiftOther = anOtherGroupIter - myGroups.begin();
230 delete *anOtherGroupIter;
231 myGroups.erase(anOtherGroupIter);
232 aFirstGroupIter = myGroups.begin() + aShiftFirst;
233 anOtherGroupIter = myGroups.begin() + aShiftOther;
236 return (*aFirstGroupIter)->changeConstraint(theConstraint);
239 // Something goes wrong
243 void SketchSolver_ConstraintManager::updateEntity(boost::shared_ptr<SketchPlugin_Feature> theFeature)
245 // Create list of attributes depending on type of the feature
246 std::vector<std::string> anAttrList;
247 const std::string& aFeatureKind = theFeature->getKind();
249 if (aFeatureKind.compare("SketchPoint") == 0)
250 anAttrList.push_back(POINT_ATTR_COORD);
252 else if (aFeatureKind.compare("SketchLine") == 0)
254 anAttrList.push_back(LINE_ATTR_START);
255 anAttrList.push_back(LINE_ATTR_END);
257 /// \todo Other types of features should be implemented
259 // Check changing of feature's attributes (go through the groups and search usage of the attributes)
260 std::vector<std::string>::const_iterator anAttrIter;
261 for (anAttrIter = anAttrList.begin(); anAttrIter != anAttrList.end(); anAttrIter++)
263 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
264 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
266 boost::shared_ptr<ModelAPI_Attribute> anAttribute =
267 boost::dynamic_pointer_cast<ModelAPI_Attribute>(theFeature->data()->attribute(*anAttrIter));
268 (*aGroupIter)->updateEntityIfPossible(anAttribute);
274 void SketchSolver_ConstraintManager::findGroups(
275 boost::shared_ptr<SketchPlugin_Constraint> theConstraint,
276 std::set<Slvs_hGroup>& theGroupIDs) const
278 boost::shared_ptr<SketchPlugin_Feature> aWP = findWorkplaneForConstraint(theConstraint);
280 std::vector<SketchSolver_ConstraintGroup*>::const_iterator aGroupIter;
281 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
282 if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theConstraint))
283 theGroupIDs.insert((*aGroupIter)->getId());
286 boost::shared_ptr<SketchPlugin_Feature> SketchSolver_ConstraintManager::findWorkplaneForConstraint(
287 boost::shared_ptr<SketchPlugin_Constraint> theConstraint) const
289 // Already verified workplanes
290 std::set< boost::shared_ptr<SketchPlugin_Feature> > aVerified;
292 std::vector<SketchSolver_ConstraintGroup*>::const_iterator aGroupIter;
293 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
295 boost::shared_ptr<SketchPlugin_Feature> aWP = (*aGroupIter)->getWorkplane();
296 if (aVerified.find(aWP) != aVerified.end())
299 boost::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures =
300 boost::dynamic_pointer_cast<ModelAPI_AttributeRefList>(aWP->data()->attribute(SKETCH_ATTR_FEATURES));
301 std::list< boost::shared_ptr<ModelAPI_Feature> > aFeaturesList = aWPFeatures->list();
302 std::list< boost::shared_ptr<ModelAPI_Feature> >::const_iterator anIter;
303 for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
304 if (*anIter == theConstraint)
305 return aWP; // workplane is found
306 aVerified.insert(aWP);
309 return boost::shared_ptr<SketchPlugin_Feature>();
312 void SketchSolver_ConstraintManager::resolveConstraints()
314 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
315 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
316 (*aGroupIter)->resolveConstraints();
318 // Features may be updated => send events
319 Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_FEATURE_UPDATED));
324 // ========================================================
325 // ========= SketchSolver_ConstraintGroup ===============
326 // ========================================================
328 SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::
329 SketchSolver_ConstraintGroup(boost::shared_ptr<SketchPlugin_Feature> theWorkplane)
330 : myID(++myGroupIndexer),
335 myNeedToSolve(false),
340 myConstraints.clear();
342 // Initialize workplane
343 myWorkplane.h = SLVS_E_UNKNOWN;
345 assert(addWorkplane(theWorkplane));
347 addWorkplane(theWorkplane);
351 SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::~SketchSolver_ConstraintGroup()
355 myConstraints.clear();
356 myConstraintMap.clear();
358 // If the group with maximal identifier is deleted, decrease the indexer
359 if (myID == myGroupIndexer)
363 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::isBaseWorkplane(
364 boost::shared_ptr<SketchPlugin_Feature> theWorkplane) const
366 return theWorkplane == mySketch;
369 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::isInteract(
370 boost::shared_ptr<SketchPlugin_Constraint> theConstraint) const
372 // Check the group is empty
373 if (myWorkplane.h != SLVS_E_UNKNOWN && myConstraints.empty())
376 // Go through constraint entities and verify if some of them already in the group
377 for (int i = 0; i < CONSTRAINT_ATTR_SIZE; i++)
379 boost::shared_ptr<ModelAPI_AttributeRefAttr> aCAttrRef =
380 boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
381 theConstraint->data()->attribute(CONSTRAINT_ATTRIBUTES[i])
383 if (!aCAttrRef) continue;
384 if (myEntityMap.find(aCAttrRef->attr()) != myEntityMap.end())
388 // Entities did not found
392 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::changeConstraint(
393 boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
395 // There is no workplane yet, something wrong
396 if (myWorkplane.h == SLVS_E_UNKNOWN)
399 // Search this constraint in the current group to update it
400 std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::const_iterator
401 aConstrMapIter = myConstraintMap.find(theConstraint);
402 std::vector<Slvs_Constraint>::iterator aConstrIter;
403 if (aConstrMapIter != myConstraintMap.end())
405 int aConstrPos = Search(aConstrMapIter->second, myConstraints);
406 aConstrIter = myConstraints.begin() + aConstrPos;
409 // Get constraint type and verify the constraint parameters are correct
410 int aConstrType = getConstraintType(theConstraint);
411 if (aConstrType == SLVS_C_UNKNOWN ||
412 (aConstrMapIter != myConstraintMap.end() && aConstrIter->type != aConstrType))
415 // Create constraint parameters
416 double aDistance = 0.0; // scalar value of the constraint
417 boost::shared_ptr<ModelAPI_AttributeDouble> aDistAttr =
418 boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theConstraint->data()->attribute(CONSTRAINT_ATTR_VALUE));
421 aDistance = aDistAttr->value();
422 if (aConstrMapIter != myConstraintMap.end() && fabs(aConstrIter->valA - aDistance) > tolerance)
424 myNeedToSolve = true;
425 aConstrIter->valA = aDistance;
429 Slvs_hEntity aConstrEnt[CONSTRAINT_ATTR_SIZE]; // parameters of the constraint
430 for (unsigned int indAttr = 0; indAttr < CONSTRAINT_ATTR_SIZE; indAttr++)
432 aConstrEnt[indAttr] = SLVS_E_UNKNOWN;
433 boost::shared_ptr<ModelAPI_AttributeRefAttr> aConstrAttr =
434 boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
435 theConstraint->data()->attribute(CONSTRAINT_ATTRIBUTES[indAttr])
437 if (!aConstrAttr) continue;
438 aConstrEnt[indAttr] = changeEntity(aConstrAttr->attr());
441 if (aConstrMapIter == myConstraintMap.end())
443 // Create SolveSpace constraint structure
444 Slvs_Constraint aConstraint =
445 Slvs_MakeConstraint(++myConstrMaxID, myID, aConstrType, myWorkplane.h,
446 aDistance, aConstrEnt[0], aConstrEnt[1], aConstrEnt[2], aConstrEnt[3]);
447 myConstraints.push_back(aConstraint);
448 myConstraintMap[theConstraint] = aConstraint.h;
453 Slvs_hEntity SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::changeEntity(
454 boost::shared_ptr<ModelAPI_Attribute> theEntity)
456 // If the entity is already in the group, try to find it
457 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
458 aEntIter = myEntityMap.find(theEntity);
459 std::vector<Slvs_Param>::const_iterator aParamIter; // looks at first parameter of already existent entity or at the end of vector otherwise
460 if (aEntIter == myEntityMap.end()) // no such entity => should be created
461 aParamIter = myParams.end();
463 { // the entity already exists
464 int aEntPos = Search(aEntIter->second, myEntities);
465 int aParamPos = Search(myEntities[aEntPos].param[0], myParams);
466 aParamIter = myParams.begin() + aParamPos;
469 // Look over supported types of entities
472 boost::shared_ptr<GeomDataAPI_Point> aPoint =
473 boost::dynamic_pointer_cast<GeomDataAPI_Point>(theEntity);
476 Slvs_hParam aX = changeParameter(aPoint->x(), aParamIter);
477 Slvs_hParam aY = changeParameter(aPoint->y(), aParamIter);
478 Slvs_hParam aZ = changeParameter(aPoint->z(), aParamIter);
480 if (aEntIter != myEntityMap.end()) // the entity already exists
481 return aEntIter->second;
484 Slvs_Entity aPtEntity = Slvs_MakePoint3d(++myEntityMaxID, myID, aX, aY, aZ);
485 myEntities.push_back(aPtEntity);
486 myEntityMap[theEntity] = aPtEntity.h;
491 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
492 boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theEntity);
495 // The 2D points are created on workplane. So, if there is no workplane yet, then error
496 if (myWorkplane.h == SLVS_E_UNKNOWN)
497 return SLVS_E_UNKNOWN;
498 Slvs_hParam aU = changeParameter(aPoint2D->x(), aParamIter);
499 Slvs_hParam aV = changeParameter(aPoint2D->y(), aParamIter);
501 if (aEntIter != myEntityMap.end()) // the entity already exists
502 return aEntIter->second;
505 Slvs_Entity aPt2DEntity = Slvs_MakePoint2d(++myEntityMaxID, myID, myWorkplane.h, aU, aV);
506 myEntities.push_back(aPt2DEntity);
507 myEntityMap[theEntity] = aPt2DEntity.h;
508 return aPt2DEntity.h;
511 /// \todo Other types of entities
513 // Unsupported or wrong entity type
514 return SLVS_E_UNKNOWN;
517 Slvs_hEntity SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::changeNormal(
518 boost::shared_ptr<ModelAPI_Attribute> theDirX,
519 boost::shared_ptr<ModelAPI_Attribute> theDirY,
520 boost::shared_ptr<ModelAPI_Attribute> theNorm)
522 boost::shared_ptr<GeomDataAPI_Dir> aDirX =
523 boost::dynamic_pointer_cast<GeomDataAPI_Dir>(theDirX);
524 boost::shared_ptr<GeomDataAPI_Dir> aDirY =
525 boost::dynamic_pointer_cast<GeomDataAPI_Dir>(theDirY);
526 if (!aDirX || !aDirY ||
527 (fabs(aDirX->x()) + fabs(aDirX->y()) + fabs(aDirX->z()) < tolerance) ||
528 (fabs(aDirY->x()) + fabs(aDirY->y()) + fabs(aDirY->z()) < tolerance))
529 return SLVS_E_UNKNOWN;
531 // quaternion parameters of normal vector
532 double qw, qx, qy, qz;
533 Slvs_MakeQuaternion(aDirX->x(), aDirX->y(), aDirX->z(),
534 aDirY->x(), aDirY->y(), aDirY->z(),
536 double aNormCoord[4] = {qw, qx, qy, qz};
538 // Try to find existent normal
539 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
540 aEntIter = myEntityMap.find(theNorm);
541 std::vector<Slvs_Param>::const_iterator aParamIter; // looks at first parameter of already existent entity or at the end of vector otherwise
542 if (aEntIter == myEntityMap.end()) // no such entity => should be created
543 aParamIter = myParams.end();
545 { // the entity already exists, update it
546 int aEntPos = Search(aEntIter->second, myEntities);
547 int aParamPos = Search(myEntities[aEntPos].param[0], myParams);
548 aParamIter = myParams.begin() + aParamPos;
551 // Change parameters of the normal
552 Slvs_hParam aNormParams[4];
553 for (int i = 0; i < 4; i++)
554 aNormParams[i] = changeParameter(aNormCoord[i], aParamIter);
556 if (aEntIter != myEntityMap.end()) // the entity already exists
557 return aEntIter->second;
560 Slvs_Entity aNormal = Slvs_MakeNormal3d(++myEntityMaxID, myID,
561 aNormParams[0], aNormParams[1], aNormParams[2], aNormParams[3]);
562 myEntities.push_back(aNormal);
563 myEntityMap[theNorm] = aNormal.h;
568 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::addWorkplane(
569 boost::shared_ptr<SketchPlugin_Feature> theSketch)
571 if (myWorkplane.h || theSketch->getKind().compare("Sketch") != 0)
572 return false; // the workplane already exists or the function parameter is not Sketch
574 mySketch = theSketch;
579 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::updateWorkplane()
581 // Get parameters of workplane
582 boost::shared_ptr<ModelAPI_Attribute> aDirX = mySketch->data()->attribute(SKETCH_ATTR_DIRX);
583 boost::shared_ptr<ModelAPI_Attribute> aDirY = mySketch->data()->attribute(SKETCH_ATTR_DIRY);
584 boost::shared_ptr<ModelAPI_Attribute> aNorm = mySketch->data()->attribute(SKETCH_ATTR_NORM);
585 boost::shared_ptr<ModelAPI_Attribute> anOrigin = mySketch->data()->attribute(SKETCH_ATTR_ORIGIN);
586 // Transform them into SolveSpace format
587 Slvs_hEntity aNormalWP = changeNormal(aDirX, aDirY, aNorm);
588 if (!aNormalWP) return false;
589 Slvs_hEntity anOriginWP = changeEntity(anOrigin);
590 if (!anOriginWP) return false;
595 myWorkplane = Slvs_MakeWorkplane(++myEntityMaxID, myID, anOriginWP, aNormalWP);
596 // Workplane should be added to the list of entities
597 myEntities.push_back(myWorkplane);
603 Slvs_hParam SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::changeParameter(
604 const double& theParam,
605 std::vector<Slvs_Param>::const_iterator& thePrmIter)
607 if (thePrmIter != myParams.end())
608 { // Parameter should be updated
609 int aParamPos = thePrmIter - myParams.begin();
610 if (fabs(thePrmIter->val - theParam) > tolerance)
612 myNeedToSolve = true; // parameter is changed, need to resolve constraints
613 myParams[aParamPos].val = theParam;
616 return myParams[aParamPos].h;
619 // Newly created parameter
620 Slvs_Param aParam = Slvs_MakeParam(++myParamMaxID, myID, theParam);
621 myParams.push_back(aParam);
622 myNeedToSolve = true;
623 // The list of parameters is changed, move iterator to the end of the list to avoid problems
624 thePrmIter = myParams.end();
628 int SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::getConstraintType(
629 const boost::shared_ptr<SketchPlugin_Constraint>& theConstraint) const
631 const std::string& aConstraintKind = theConstraint->getKind();
632 // Constraint for coincidence of two points
633 if (aConstraintKind.compare("SketchConstraintCoincidence") == 0)
635 // Verify the constraint has only two attributes and they are points
636 int aPt2d = 0; // bit-mapped field, each bit indicates whether the attribute is 2D point
637 int aPt3d = 0; // bit-mapped field, the same information for 3D points
638 for (unsigned int indAttr = 0; indAttr < CONSTRAINT_ATTR_SIZE; indAttr++)
640 boost::shared_ptr<ModelAPI_AttributeRefAttr> anAttr =
641 boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
642 theConstraint->data()->attribute(CONSTRAINT_ATTRIBUTES[indAttr])
644 if (!anAttr) continue;
645 // Verify the attribute is a 2D point
646 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
647 boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttr->attr());
650 aPt2d |= (1 << indAttr);
653 // Verify the attribute is a 3D point
654 boost::shared_ptr<GeomDataAPI_Point> aPoint3D =
655 boost::dynamic_pointer_cast<GeomDataAPI_Point>(anAttr->attr());
658 aPt3d |= (1 << indAttr);
661 // Attribute neither 2D nor 3D point is not supported by this type of constraint
662 return SLVS_C_UNKNOWN;
664 // The constrained points should be in first and second positions,
665 // so the expected value of aPt2d or aPt3d is 3
666 if ((aPt2d == 3 && aPt3d == 0) || (aPt2d == 0 && aPt3d == 3))
667 return SLVS_C_POINTS_COINCIDENT;
668 // Constraint parameters are wrong
669 return SLVS_C_UNKNOWN;
672 /// \todo Implement other kind of constrtaints
674 return SLVS_C_UNKNOWN;
677 void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::resolveConstraints()
682 myConstrSolver.setGroupID(myID);
683 myConstrSolver.setParameters(myParams);
684 myConstrSolver.setEntities(myEntities);
685 myConstrSolver.setConstraints(myConstraints);
687 if (myConstrSolver.solve() == SLVS_RESULT_OKAY)
688 { // solution succeeded, store results into correspondent attributes
689 // Obtain result into the same list of parameters
690 if (!myConstrSolver.getResult(myParams))
693 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator
694 anEntIter = myEntityMap.begin();
695 for ( ; anEntIter != myEntityMap.end(); anEntIter++)
696 updateAttribute(anEntIter->first, anEntIter->second);
698 /// \todo Implement error handling
700 removeTemporaryConstraints();
701 myNeedToSolve = false;
704 void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::mergeGroups(
705 const SketchSolver_ConstraintGroup& theGroup)
707 // If specified group is empty, no need to merge
708 if (theGroup.myConstraintMap.size() == 0)
711 // NOTE: The possibility, that some elements are placed into both groups, is around 0,
712 // so the objects should be copied with changing the indexes
714 // Maps between old and new indexes of SolveSpace elements:
715 std::map<Slvs_hParam, Slvs_hParam> aParamMap;
716 std::map<Slvs_hEntity, Slvs_hEntity> anEntityMap;
717 std::map<Slvs_hConstraint, Slvs_hConstraint> aConstrMap;
719 // Go through copying constraints
720 std::vector<Slvs_Constraint>::const_iterator aConstrIter = theGroup.myConstraints.begin();
721 for ( ; aConstrIter != theGroup.myConstraints.end(); aConstrIter++)
723 Slvs_Constraint aConstraintCopy = *aConstrIter;
724 // Go through constraint entities
725 Slvs_hEntity* anEntities[CONSTRAINT_ATTR_SIZE] = {
726 &(aConstraintCopy.ptA), &(aConstraintCopy.ptB),
727 &(aConstraintCopy.entityA), &(aConstraintCopy.entityB)
729 for (int indEnt = 0; indEnt < CONSTRAINT_ATTR_SIZE; indEnt++)
731 if (*(anEntities[indEnt]) == 0)
733 if (anEntityMap.find(*(anEntities[indEnt])) != anEntityMap.end())
734 { // entity is already copied
735 *(anEntities[indEnt]) = anEntityMap[*(anEntities[indEnt])];
740 Slvs_Entity anEntityCopy = theGroup.myEntities[Search(*(anEntities[indEnt]), theGroup.myEntities)];
741 // Go through entity parameters
742 const int aNbEntParams = 4; // maximal number of entity parameters
743 for (int indPrm = 0; indPrm < aNbEntParams; indPrm++)
745 if (anEntityCopy.param[indPrm] == 0)
747 if (aParamMap.find(anEntityCopy.param[indPrm]) != aParamMap.end())
749 anEntityCopy.param[indPrm] = aParamMap[anEntityCopy.param[indPrm]];
753 Slvs_Param aParamCopy = theGroup.myParams[Search(anEntityCopy.param[indPrm], theGroup.myParams)];
754 aParamMap[aParamCopy.h] = ++myParamMaxID;
755 aParamCopy.h = myParamMaxID;
756 myParams.push_back(aParamCopy);
759 anEntityMap[anEntityCopy.h] = ++myEntityMaxID;
760 anEntityCopy.h = myEntityMaxID;
761 myEntities.push_back(anEntityCopy);
762 *(anEntities[indEnt]) = anEntityCopy.h;
765 aConstraintCopy.h = ++myConstrMaxID;
766 myConstraints.push_back(aConstraintCopy);
767 aConstrMap[aConstrIter->h] = aConstraintCopy.h;
770 // Append maps of SketchPlugin to SolveSpace parameters
771 std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::const_iterator
772 aSPConstrMapIter = theGroup.myConstraintMap.begin();
773 for ( ; aSPConstrMapIter!= theGroup.myConstraintMap.end(); aSPConstrMapIter++)
775 std::map<Slvs_hConstraint, Slvs_hConstraint>::iterator aFind = aConstrMap.find(aSPConstrMapIter->second);
776 if (aFind != aConstrMap.end())
777 myConstraintMap[aSPConstrMapIter->first] = aFind->second;
780 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
781 aSPEntMapIter = theGroup.myEntityMap.begin();
782 for ( ; aSPEntMapIter != theGroup.myEntityMap.end(); aSPEntMapIter++) {
783 std::map<Slvs_hEntity, Slvs_hEntity>::iterator aFind = anEntityMap.find(aSPEntMapIter->second);
784 if (aFind != anEntityMap.end())
785 myEntityMap[aSPEntMapIter->first] = aFind->second;
788 // Add temporary constraints
789 std::list<Slvs_hConstraint>::const_iterator aTempConstrIter = theGroup.myTempConstraints.begin();
790 for ( ; aTempConstrIter != theGroup.myTempConstraints.end(); aTempConstrIter++)
792 std::map<Slvs_hConstraint, Slvs_hConstraint>::iterator aFind = aConstrMap.find(*aTempConstrIter);
793 if (aFind != aConstrMap.end())
794 myTempConstraints.push_back(aFind->second);
796 myTempConstraints.sort();
798 myNeedToSolve = myNeedToSolve || theGroup.myNeedToSolve;
801 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::updateGroup()
803 // Check for valid sketch
804 if (!mySketch->data()->isValid())
807 // Fast check for constraint validity. If all constraints are valid, no need to update the group
808 std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::reverse_iterator
809 aConstrIter = myConstraintMap.rbegin();
810 bool isAllValid = true;
811 for ( ; isAllValid && aConstrIter != myConstraintMap.rend(); aConstrIter++)
812 if (!aConstrIter->first->data()->isValid())
817 // Remove invalid constraints.
818 // There only constraint will be deleted (parameters and entities) will be removed below
819 std::list< boost::shared_ptr<SketchPlugin_Constraint> > aConstrToDelete;
820 std::map<Slvs_hEntity, bool> anEntToDelete; // entities will be removed if no valid constraints use them
821 for (aConstrIter = myConstraintMap.rbegin(); aConstrIter != myConstraintMap.rend(); aConstrIter++)
823 bool isValid = aConstrIter->first->data()->isValid();
825 int aConstrPos = Search(aConstrIter->second, myConstraints);
826 if (aConstrPos < (int)myConstraints.size())
828 Slvs_hEntity aConstrEnt[] = {
829 myConstraints[aConstrPos].ptA, myConstraints[aConstrPos].ptB,
830 myConstraints[aConstrPos].entityA, myConstraints[aConstrPos].entityB};
831 for (int i = 0; i < 4; i++)
832 if (aConstrEnt[i] != SLVS_E_UNKNOWN)
834 if (anEntToDelete.find(aConstrEnt[i]) == anEntToDelete.end())
835 anEntToDelete[aConstrEnt[i]] = !isValid;
836 else if (isValid) // constraint is valid => no need to remove its entities
837 anEntToDelete[aConstrEnt[i]] = false;
841 myConstraints.erase(myConstraints.begin() + aConstrPos);
842 if (aConstrIter->second == myConstrMaxID) // When the constraint with highest ID is removed, decrease indexer
844 aConstrToDelete.push_front(aConstrIter->first);
848 std::list< boost::shared_ptr<SketchPlugin_Constraint> >::iterator aDelIter;
849 for (aDelIter = aConstrToDelete.begin(); aDelIter != aConstrToDelete.end(); aDelIter++)
850 myConstraintMap.erase(*aDelIter);
852 // Remove invalid and unused entities
853 std::map<Slvs_hEntity, bool>::reverse_iterator aEDelIter;
854 for (aEDelIter = anEntToDelete.rbegin(); aEDelIter != anEntToDelete.rend(); aEDelIter++)
855 if (aEDelIter->second)
857 int anEntPos = Search(aEDelIter->first, myEntities);
858 std::vector<Slvs_Entity>::iterator aEntIter = myEntities.begin() + anEntPos;
859 // Number of parameters for the entity
861 while (aEntIter->param[aNbParams]) aNbParams++;
862 if (aNbParams == 0) continue;
863 // Decrease parameter indexer if there are deleted parameter with higher IDs
864 if (aEntIter->param[aNbParams-1] == myParamMaxID)
865 myParamMaxID -= aNbParams;
866 // Remove parameters of the entity
867 int aParamPos = Search(aEntIter->param[0], myParams);
868 myParams.erase(myParams.begin() + aParamPos,
869 myParams.begin() + aParamPos + aNbParams);
872 if (aEDelIter->first == myEntityMaxID)
874 myEntities.erase(myEntities.begin() + anEntPos);
875 // Remove such entity from myEntityMap
876 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator
877 anEntMapIter = myEntityMap.begin();
878 for ( ; anEntMapIter != myEntityMap.end(); anEntMapIter++)
879 if (anEntMapIter->second == aEDelIter->first)
881 if (anEntMapIter != myEntityMap.end())
882 myEntityMap.erase(anEntMapIter);
888 void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::updateAttribute(
889 boost::shared_ptr<ModelAPI_Attribute> theAttribute,
890 const Slvs_hEntity& theEntityID)
892 // Search the position of the first parameter of the entity
893 int anEntPos = Search(theEntityID, myEntities);
894 int aFirstParamPos = Search(myEntities[anEntPos].param[0], myParams);
896 // Look over supported types of entities
899 boost::shared_ptr<GeomDataAPI_Point> aPoint =
900 boost::dynamic_pointer_cast<GeomDataAPI_Point>(theAttribute);
903 aPoint->setValue(myParams[aFirstParamPos].val,
904 myParams[aFirstParamPos+1].val,
905 myParams[aFirstParamPos+2].val);
910 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
911 boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute);
914 aPoint2D->setValue(myParams[aFirstParamPos].val,
915 myParams[aFirstParamPos+1].val);
919 /// \todo Support other types of entities
922 void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::updateEntityIfPossible(
923 boost::shared_ptr<ModelAPI_Attribute> theEntity)
925 if (myEntityMap.find(theEntity) != myEntityMap.end())
927 // If the attribute is a point and it is changed (the group needs to rebuild),
928 // probably user has dragged this point into this position,
929 // so it is necessary to add constraint which will guarantee the point will not change
931 // Store myNeedToSolve flag to verify the entity is really changed
932 bool aNeedToSolveCopy = myNeedToSolve;
933 myNeedToSolve = false;
935 changeEntity(theEntity);
937 if (myNeedToSolve) // the entity is changed
939 // Verify the entity is a point and add temporary constraint of permanency
940 boost::shared_ptr<GeomDataAPI_Point> aPoint =
941 boost::dynamic_pointer_cast<GeomDataAPI_Point>(theEntity);
942 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
943 boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theEntity);
944 if (aPoint || aPoint2D)
945 addTemporaryConstraintWhereDragged(theEntity);
948 // Restore flag of changes
949 myNeedToSolve = myNeedToSolve || aNeedToSolveCopy;
953 void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::addTemporaryConstraintWhereDragged(
954 boost::shared_ptr<ModelAPI_Attribute> theEntity)
956 // Find identifier of the entity
957 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
958 anEntIter = myEntityMap.find(theEntity);
960 // Create WHERE_DRAGGED constraint
961 Slvs_Constraint aWDConstr = Slvs_MakeConstraint(++myConstrMaxID, myID, SLVS_C_WHERE_DRAGGED,
962 myWorkplane.h, 0.0, anEntIter->second, 0, 0, 0);
963 myConstraints.push_back(aWDConstr);
964 myTempConstraints.push_back(aWDConstr.h);
967 void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::removeTemporaryConstraints()
969 std::list<Slvs_hConstraint>::reverse_iterator aTmpConstrIter;
970 for (aTmpConstrIter = myTempConstraints.rbegin(); aTmpConstrIter != myTempConstraints.rend(); aTmpConstrIter++)
972 int aConstrPos = Search(*aTmpConstrIter, myConstraints);
973 myConstraints.erase(myConstraints.begin() + aConstrPos);
975 // If the removing constraint has higher index, decrease the indexer
976 if (*aTmpConstrIter == myConstrMaxID)
979 myTempConstraints.clear();
984 // ========================================================
985 // ========= Auxiliary functions ===============
986 // ========================================================
988 template <typename T>
989 int Search(const uint32_t& theEntityID, const std::vector<T>& theEntities)
991 int aResIndex = theEntityID <= theEntities.size() ? theEntityID - 1 : 0;
992 int aVecSize = theEntities.size();
993 while (aResIndex >= 0 && theEntities[aResIndex].h > theEntityID)
995 while (aResIndex < aVecSize && theEntities[aResIndex].h < theEntityID)
998 aResIndex = aVecSize;