1 // File: SketchSolver_ConstraintGroup.cpp
2 // Created: 27 May 2014
3 // Author: Artem ZHIDKOV
5 #include "SketchSolver_ConstraintGroup.h"
7 #include <SketchSolver_Constraint.h>
9 #include <Events_Loop.h>
10 #include <GeomDataAPI_Dir.h>
11 #include <GeomDataAPI_Point.h>
12 #include <GeomDataAPI_Point2D.h>
13 #include <ModelAPI_AttributeDouble.h>
14 #include <ModelAPI_AttributeRefList.h>
15 #include <ModelAPI_Data.h>
16 #include <Model_Events.h>
18 #include <SketchPlugin_Constraint.h>
20 #include <SketchPlugin_Arc.h>
21 #include <SketchPlugin_Circle.h>
22 #include <SketchPlugin_Line.h>
23 #include <SketchPlugin_Point.h>
24 #include <SketchPlugin_Sketch.h>
29 /// Tolerance for value of parameters
30 const double tolerance = 1.e-10;
32 /// This value is used to give unique index to the groups
33 static Slvs_hGroup myGroupIndexer = 0;
35 /** \brief Search the entity/parameter with specified ID in the list of elements
36 * \param[in] theEntityID unique ID of the element
37 * \param[in] theEntities list of elements
38 * \return position of the found element or -1 if the element is not found
41 static int Search(const uint32_t& theEntityID, const std::vector<T>& theEntities);
44 // ========================================================
45 // ========= SketchSolver_ConstraintGroup ===============
46 // ========================================================
48 SketchSolver_ConstraintGroup::
49 SketchSolver_ConstraintGroup(boost::shared_ptr<SketchPlugin_Feature> theWorkplane)
50 : myID(++myGroupIndexer),
60 myConstraints.clear();
62 // Initialize workplane
63 myWorkplane.h = SLVS_E_UNKNOWN;
65 assert(addWorkplane(theWorkplane));
67 addWorkplane(theWorkplane);
71 SketchSolver_ConstraintGroup::~SketchSolver_ConstraintGroup()
75 myConstraints.clear();
76 myConstraintMap.clear();
78 // If the group with maximal identifier is deleted, decrease the indexer
79 if (myID == myGroupIndexer)
83 // ============================================================================
84 // Function: isBaseWorkplane
85 // Class: SketchSolver_ConstraintGroup
86 // Purpose: verify the group is based on the given workplane
87 // ============================================================================
88 bool SketchSolver_ConstraintGroup::isBaseWorkplane(
89 boost::shared_ptr<SketchPlugin_Feature> theWorkplane) const
91 return theWorkplane == mySketch;
94 // ============================================================================
95 // Function: isInteract
96 // Class: SketchSolver_ConstraintGroup
97 // Purpose: verify are there any entities in the group used by given constraint
98 // ============================================================================
99 bool SketchSolver_ConstraintGroup::isInteract(
100 boost::shared_ptr<SketchPlugin_Constraint> theConstraint) const
102 // Check the group is empty
103 if (myWorkplane.h != SLVS_E_UNKNOWN && myConstraints.empty())
106 // Go through constraint entities and verify if some of them already in the group
107 for (int i = 0; i < CONSTRAINT_ATTR_SIZE; i++)
109 boost::shared_ptr<ModelAPI_AttributeRefAttr> aCAttrRef =
110 boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
111 theConstraint->data()->attribute(CONSTRAINT_ATTRIBUTES[i])
113 if (!aCAttrRef) continue;
114 if (myEntityMap.find(aCAttrRef->attr()) != myEntityMap.end())
118 // Entities did not found
122 // ============================================================================
123 // Function: changeConstraint
124 // Class: SketchSolver_ConstraintGroup
125 // Purpose: create/update the constraint in the group
126 // ============================================================================
127 bool SketchSolver_ConstraintGroup::changeConstraint(
128 boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
130 // There is no workplane yet, something wrong
131 if (myWorkplane.h == SLVS_E_UNKNOWN)
134 // Search this constraint in the current group to update it
135 std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::const_iterator
136 aConstrMapIter = myConstraintMap.find(theConstraint);
137 std::vector<Slvs_Constraint>::iterator aConstrIter;
138 if (aConstrMapIter != myConstraintMap.end())
140 int aConstrPos = Search(aConstrMapIter->second, myConstraints);
141 aConstrIter = myConstraints.begin() + aConstrPos;
144 // Get constraint type and verify the constraint parameters are correct
145 SketchSolver_Constraint aConstraint(theConstraint);
146 int aConstrType = aConstraint.getType();
147 if (aConstrType == SLVS_C_UNKNOWN ||
148 (aConstrMapIter != myConstraintMap.end() && aConstrIter->type != aConstrType))
150 const std::vector<std::string>& aConstraintAttributes = aConstraint.getAttributes();
152 // Create constraint parameters
153 double aDistance = 0.0; // scalar value of the constraint
154 boost::shared_ptr<ModelAPI_AttributeDouble> aDistAttr =
155 boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theConstraint->data()->attribute(CONSTRAINT_ATTR_VALUE));
158 aDistance = aDistAttr->value();
159 if (aConstrMapIter != myConstraintMap.end() && fabs(aConstrIter->valA - aDistance) > tolerance)
161 myNeedToSolve = true;
162 aConstrIter->valA = aDistance;
166 Slvs_hEntity aConstrEnt[CONSTRAINT_ATTR_SIZE]; // parameters of the constraint
167 for (unsigned int indAttr = 0; indAttr < CONSTRAINT_ATTR_SIZE; indAttr++)
169 aConstrEnt[indAttr] = SLVS_E_UNKNOWN;
170 boost::shared_ptr<ModelAPI_AttributeRefAttr> aConstrAttr =
171 boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
172 theConstraint->data()->attribute(aConstraintAttributes[indAttr])
174 if (!aConstrAttr) continue;
175 aConstrEnt[indAttr] = changeEntity(aConstrAttr->attr());
178 if (aConstrMapIter == myConstraintMap.end())
180 // Create SolveSpace constraint structure
181 Slvs_Constraint aConstraint =
182 Slvs_MakeConstraint(++myConstrMaxID, myID, aConstrType, myWorkplane.h,
183 aDistance, aConstrEnt[0], aConstrEnt[1], aConstrEnt[2], aConstrEnt[3]);
184 myConstraints.push_back(aConstraint);
185 myConstraintMap[theConstraint] = aConstraint.h;
190 // ============================================================================
191 // Function: changeEntity
192 // Class: SketchSolver_ConstraintGroup
193 // Purpose: create/update the element affected by any constraint
194 // ============================================================================
195 Slvs_hEntity SketchSolver_ConstraintGroup::changeEntity(
196 boost::shared_ptr<ModelAPI_Attribute> theEntity)
198 // If the entity is already in the group, try to find it
199 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
200 aEntIter = myEntityMap.find(theEntity);
201 std::vector<Slvs_Param>::const_iterator aParamIter; // looks at first parameter of already existent entity or at the end of vector otherwise
202 if (aEntIter == myEntityMap.end()) // no such entity => should be created
203 aParamIter = myParams.end();
205 { // the entity already exists
206 int aEntPos = Search(aEntIter->second, myEntities);
207 int aParamPos = Search(myEntities[aEntPos].param[0], myParams);
208 aParamIter = myParams.begin() + aParamPos;
211 // Look over supported types of entities
214 boost::shared_ptr<GeomDataAPI_Point> aPoint =
215 boost::dynamic_pointer_cast<GeomDataAPI_Point>(theEntity);
218 Slvs_hParam aX = changeParameter(aPoint->x(), aParamIter);
219 Slvs_hParam aY = changeParameter(aPoint->y(), aParamIter);
220 Slvs_hParam aZ = changeParameter(aPoint->z(), aParamIter);
222 if (aEntIter != myEntityMap.end()) // the entity already exists
223 return aEntIter->second;
226 Slvs_Entity aPtEntity = Slvs_MakePoint3d(++myEntityMaxID, myID, aX, aY, aZ);
227 myEntities.push_back(aPtEntity);
228 myEntityMap[theEntity] = aPtEntity.h;
232 // All entities except 3D points are created on workplane. So, if there is no workplane yet, then error
233 if (myWorkplane.h == SLVS_E_UNKNOWN)
234 return SLVS_E_UNKNOWN;
237 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
238 boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theEntity);
241 Slvs_hParam aU = changeParameter(aPoint2D->x(), aParamIter);
242 Slvs_hParam aV = changeParameter(aPoint2D->y(), aParamIter);
244 if (aEntIter != myEntityMap.end()) // the entity already exists
245 return aEntIter->second;
248 Slvs_Entity aPt2DEntity = Slvs_MakePoint2d(++myEntityMaxID, myID, myWorkplane.h, aU, aV);
249 myEntities.push_back(aPt2DEntity);
250 myEntityMap[theEntity] = aPt2DEntity.h;
251 return aPt2DEntity.h;
254 // Scalar value (used for the distance entities)
255 boost::shared_ptr<ModelAPI_AttributeDouble> aScalar =
256 boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theEntity);
259 Slvs_hParam aValue = changeParameter(aScalar->value(), aParamIter);
261 if (aEntIter != myEntityMap.end()) // the entity already exists
262 return aEntIter->second;
265 Slvs_Entity aDistance = Slvs_MakeDistance(++myEntityMaxID, myID, myWorkplane.h, aValue);
266 myEntities.push_back(aDistance);
267 myEntityMap[theEntity] = aDistance.h;
271 // SketchPlugin features
272 boost::shared_ptr<SketchPlugin_Feature> aFeature =
273 boost::dynamic_pointer_cast<SketchPlugin_Feature>(theEntity);
275 { // Verify the feature by its kind
276 const std::string& aFeatureKind = aFeature->getKind();
279 if (aFeatureKind.compare("SketchLine") == 0)
281 Slvs_hEntity aStart = changeEntity(aFeature->data()->attribute(LINE_ATTR_START));
282 Slvs_hEntity aEnd = changeEntity(aFeature->data()->attribute(LINE_ATTR_END));
284 if (aEntIter != myEntityMap.end()) // the entity already exists
285 return aEntIter->second;
288 Slvs_Entity aLineEntity = Slvs_MakeLineSegment(++myEntityMaxID, myID, myWorkplane.h, aStart, aEnd);
289 myEntities.push_back(aLineEntity);
290 myEntityMap[theEntity] = aLineEntity.h;
291 return aLineEntity.h;
294 else if (aFeatureKind.compare("SketchCircle") == 0)
296 Slvs_hEntity aCenter = changeEntity(aFeature->data()->attribute(CIRCLE_ATTR_CENTER));
297 Slvs_hEntity aRadius = changeEntity(aFeature->data()->attribute(CIRCLE_ATTR_RADIUS));
299 if (aEntIter != myEntityMap.end()) // the entity already exists
300 return aEntIter->second;
303 Slvs_Entity aCircleEntity =
304 Slvs_MakeCircle(++myEntityMaxID, myID, myWorkplane.h, aCenter, myWorkplane.normal, aRadius);
305 myEntities.push_back(aCircleEntity);
306 myEntityMap[theEntity] = aCircleEntity.h;
307 return aCircleEntity.h;
310 else if (aFeatureKind.compare("SketchArc") == 0)
312 Slvs_hEntity aCenter = changeEntity(aFeature->data()->attribute(ARC_ATTR_CENTER));
313 Slvs_hEntity aStart = changeEntity(aFeature->data()->attribute(ARC_ATTR_START));
314 Slvs_hEntity aEnd = changeEntity(aFeature->data()->attribute(ARC_ATTR_END));
316 if (aEntIter != myEntityMap.end()) // the entity already exists
317 return aEntIter->second;
319 Slvs_Entity anArcEntity = Slvs_MakeArcOfCircle(++myEntityMaxID, myID,
320 myWorkplane.h, myWorkplane.normal, aCenter, aStart, aEnd);
321 myEntities.push_back(anArcEntity);
322 myEntityMap[theEntity] = anArcEntity.h;
323 return anArcEntity.h;
325 // Point (it has low probability to be an attribute of constraint, so it is checked at the end)
326 else if (aFeatureKind.compare("SketchPoint") == 0)
328 Slvs_hEntity aPoint = changeEntity(aFeature->data()->attribute(POINT_ATTR_COORD));
330 if (aEntIter != myEntityMap.end()) // the entity already exists
331 return aEntIter->second;
333 // Both the sketch point and its attribute (coordinates) link to the same SolveSpace point identifier
334 myEntityMap[theEntity] = aPoint;
339 /// \todo Other types of entities
341 // Unsupported or wrong entity type
342 return SLVS_E_UNKNOWN;
345 // ============================================================================
346 // Function: changeNormal
347 // Class: SketchSolver_ConstraintGroup
348 // Purpose: create/update the normal of workplane
349 // ============================================================================
350 Slvs_hEntity SketchSolver_ConstraintGroup::changeNormal(
351 boost::shared_ptr<ModelAPI_Attribute> theDirX,
352 boost::shared_ptr<ModelAPI_Attribute> theDirY,
353 boost::shared_ptr<ModelAPI_Attribute> theNorm)
355 boost::shared_ptr<GeomDataAPI_Dir> aDirX =
356 boost::dynamic_pointer_cast<GeomDataAPI_Dir>(theDirX);
357 boost::shared_ptr<GeomDataAPI_Dir> aDirY =
358 boost::dynamic_pointer_cast<GeomDataAPI_Dir>(theDirY);
359 if (!aDirX || !aDirY ||
360 (fabs(aDirX->x()) + fabs(aDirX->y()) + fabs(aDirX->z()) < tolerance) ||
361 (fabs(aDirY->x()) + fabs(aDirY->y()) + fabs(aDirY->z()) < tolerance))
362 return SLVS_E_UNKNOWN;
364 // quaternion parameters of normal vector
365 double qw, qx, qy, qz;
366 Slvs_MakeQuaternion(aDirX->x(), aDirX->y(), aDirX->z(),
367 aDirY->x(), aDirY->y(), aDirY->z(),
369 double aNormCoord[4] = {qw, qx, qy, qz};
371 // Try to find existent normal
372 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
373 aEntIter = myEntityMap.find(theNorm);
374 std::vector<Slvs_Param>::const_iterator aParamIter; // looks to the first parameter of already existent entity or to the end of vector otherwise
375 if (aEntIter == myEntityMap.end()) // no such entity => should be created
376 aParamIter = myParams.end();
378 { // the entity already exists, update it
379 int aEntPos = Search(aEntIter->second, myEntities);
380 int aParamPos = Search(myEntities[aEntPos].param[0], myParams);
381 aParamIter = myParams.begin() + aParamPos;
384 // Change parameters of the normal
385 Slvs_hParam aNormParams[4];
386 for (int i = 0; i < 4; i++)
387 aNormParams[i] = changeParameter(aNormCoord[i], aParamIter);
389 if (aEntIter != myEntityMap.end()) // the entity already exists
390 return aEntIter->second;
393 Slvs_Entity aNormal = Slvs_MakeNormal3d(++myEntityMaxID, myID,
394 aNormParams[0], aNormParams[1], aNormParams[2], aNormParams[3]);
395 myEntities.push_back(aNormal);
396 myEntityMap[theNorm] = aNormal.h;
401 // ============================================================================
402 // Function: addWorkplane
403 // Class: SketchSolver_ConstraintGroup
404 // Purpose: create workplane for the group
405 // ============================================================================
406 bool SketchSolver_ConstraintGroup::addWorkplane(
407 boost::shared_ptr<SketchPlugin_Feature> theSketch)
409 if (myWorkplane.h || theSketch->getKind().compare("Sketch") != 0)
410 return false; // the workplane already exists or the function parameter is not Sketch
412 mySketch = theSketch;
417 // ============================================================================
418 // Function: updateWorkplane
419 // Class: SketchSolver_ConstraintGroup
420 // Purpose: update parameters of workplane
421 // ============================================================================
422 bool SketchSolver_ConstraintGroup::updateWorkplane()
424 // Get parameters of workplane
425 boost::shared_ptr<ModelAPI_Attribute> aDirX = mySketch->data()->attribute(SKETCH_ATTR_DIRX);
426 boost::shared_ptr<ModelAPI_Attribute> aDirY = mySketch->data()->attribute(SKETCH_ATTR_DIRY);
427 boost::shared_ptr<ModelAPI_Attribute> aNorm = mySketch->data()->attribute(SKETCH_ATTR_NORM);
428 boost::shared_ptr<ModelAPI_Attribute> anOrigin = mySketch->data()->attribute(SKETCH_ATTR_ORIGIN);
429 // Transform them into SolveSpace format
430 Slvs_hEntity aNormalWP = changeNormal(aDirX, aDirY, aNorm);
431 if (!aNormalWP) return false;
432 Slvs_hEntity anOriginWP = changeEntity(anOrigin);
433 if (!anOriginWP) return false;
438 myWorkplane = Slvs_MakeWorkplane(++myEntityMaxID, myID, anOriginWP, aNormalWP);
439 // Workplane should be added to the list of entities
440 myEntities.push_back(myWorkplane);
445 // ============================================================================
446 // Function: changeParameter
447 // Class: SketchSolver_ConstraintGroup
448 // Purpose: create/update value of parameter
449 // ============================================================================
450 Slvs_hParam SketchSolver_ConstraintGroup::changeParameter(
451 const double& theParam,
452 std::vector<Slvs_Param>::const_iterator& thePrmIter)
454 if (thePrmIter != myParams.end())
455 { // Parameter should be updated
456 int aParamPos = thePrmIter - myParams.begin();
457 if (fabs(thePrmIter->val - theParam) > tolerance)
459 myNeedToSolve = true; // parameter is changed, need to resolve constraints
460 myParams[aParamPos].val = theParam;
463 return myParams[aParamPos].h;
466 // Newly created parameter
467 Slvs_Param aParam = Slvs_MakeParam(++myParamMaxID, myID, theParam);
468 myParams.push_back(aParam);
469 myNeedToSolve = true;
470 // The list of parameters is changed, move iterator to the end of the list to avoid problems
471 thePrmIter = myParams.end();
475 // ============================================================================
476 // Function: resolveConstraints
477 // Class: SketchSolver_ConstraintGroup
478 // Purpose: solve the set of constraints for the current group
479 // ============================================================================
480 void SketchSolver_ConstraintGroup::resolveConstraints()
485 myConstrSolver.setGroupID(myID);
486 myConstrSolver.setParameters(myParams);
487 myConstrSolver.setEntities(myEntities);
488 myConstrSolver.setConstraints(myConstraints);
490 if (myConstrSolver.solve() == SLVS_RESULT_OKAY)
491 { // solution succeeded, store results into correspondent attributes
492 // Obtain result into the same list of parameters
493 if (!myConstrSolver.getResult(myParams))
496 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator
497 anEntIter = myEntityMap.begin();
498 for ( ; anEntIter != myEntityMap.end(); anEntIter++)
499 updateAttribute(anEntIter->first, anEntIter->second);
501 /// \todo Implement error handling
503 removeTemporaryConstraints();
504 myNeedToSolve = false;
507 // ============================================================================
508 // Function: mergeGroups
509 // Class: SketchSolver_ConstraintGroup
510 // Purpose: append specified group to the current group
511 // ============================================================================
512 void SketchSolver_ConstraintGroup::mergeGroups(
513 const SketchSolver_ConstraintGroup& theGroup)
515 // If specified group is empty, no need to merge
516 if (theGroup.myConstraintMap.empty())
519 // NOTE: The possibility, that some elements are placed into both groups, is around 0,
520 // so the objects should be copied with changing the indexes
522 // Maps between old and new indexes of SolveSpace elements:
523 std::map<Slvs_hParam, Slvs_hParam> aParamMap;
524 std::map<Slvs_hEntity, Slvs_hEntity> anEntityMap;
525 std::map<Slvs_hConstraint, Slvs_hConstraint> aConstrMap;
527 // Go through copying constraints
528 std::vector<Slvs_Constraint>::const_iterator aConstrIter = theGroup.myConstraints.begin();
529 for ( ; aConstrIter != theGroup.myConstraints.end(); aConstrIter++)
531 Slvs_Constraint aConstraintCopy = *aConstrIter;
532 // Go through constraint entities
533 Slvs_hEntity* anEntities[CONSTRAINT_ATTR_SIZE] = {
534 &(aConstraintCopy.ptA), &(aConstraintCopy.ptB),
535 &(aConstraintCopy.entityA), &(aConstraintCopy.entityB)
537 for (int indEnt = 0; indEnt < CONSTRAINT_ATTR_SIZE; indEnt++)
539 if (*(anEntities[indEnt]) == 0)
541 if (anEntityMap.find(*(anEntities[indEnt])) != anEntityMap.end())
542 { // entity is already copied
543 *(anEntities[indEnt]) = anEntityMap[*(anEntities[indEnt])];
548 Slvs_Entity anEntityCopy = theGroup.myEntities[Search(*(anEntities[indEnt]), theGroup.myEntities)];
549 // Go through entity parameters
550 const int aNbEntParams = 4; // maximal number of entity parameters
551 for (int indPrm = 0; indPrm < aNbEntParams; indPrm++)
553 if (anEntityCopy.param[indPrm] == 0)
555 if (aParamMap.find(anEntityCopy.param[indPrm]) != aParamMap.end())
557 anEntityCopy.param[indPrm] = aParamMap[anEntityCopy.param[indPrm]];
561 Slvs_Param aParamCopy = theGroup.myParams[Search(anEntityCopy.param[indPrm], theGroup.myParams)];
562 aParamMap[aParamCopy.h] = ++myParamMaxID;
563 aParamCopy.h = myParamMaxID;
564 myParams.push_back(aParamCopy);
567 anEntityMap[anEntityCopy.h] = ++myEntityMaxID;
568 anEntityCopy.h = myEntityMaxID;
569 myEntities.push_back(anEntityCopy);
570 *(anEntities[indEnt]) = anEntityCopy.h;
573 aConstraintCopy.h = ++myConstrMaxID;
574 myConstraints.push_back(aConstraintCopy);
575 aConstrMap[aConstrIter->h] = aConstraintCopy.h;
578 // Append maps of SketchPlugin to SolveSpace parameters
579 std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::const_iterator
580 aSPConstrMapIter = theGroup.myConstraintMap.begin();
581 for ( ; aSPConstrMapIter!= theGroup.myConstraintMap.end(); aSPConstrMapIter++)
583 std::map<Slvs_hConstraint, Slvs_hConstraint>::iterator aFind = aConstrMap.find(aSPConstrMapIter->second);
584 if (aFind != aConstrMap.end())
585 myConstraintMap[aSPConstrMapIter->first] = aFind->second;
588 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
589 aSPEntMapIter = theGroup.myEntityMap.begin();
590 for ( ; aSPEntMapIter != theGroup.myEntityMap.end(); aSPEntMapIter++) {
591 std::map<Slvs_hEntity, Slvs_hEntity>::iterator aFind = anEntityMap.find(aSPEntMapIter->second);
592 if (aFind != anEntityMap.end())
593 myEntityMap[aSPEntMapIter->first] = aFind->second;
596 // Add temporary constraints
597 std::list<Slvs_hConstraint>::const_iterator aTempConstrIter = theGroup.myTempConstraints.begin();
598 for ( ; aTempConstrIter != theGroup.myTempConstraints.end(); aTempConstrIter++)
600 std::map<Slvs_hConstraint, Slvs_hConstraint>::iterator aFind = aConstrMap.find(*aTempConstrIter);
601 if (aFind != aConstrMap.end())
602 myTempConstraints.push_back(aFind->second);
604 myTempConstraints.sort();
606 myNeedToSolve = myNeedToSolve || theGroup.myNeedToSolve;
609 // ============================================================================
610 // Function: updateGroup
611 // Class: SketchSolver_ConstraintGroup
612 // Purpose: search removed entities and constraints
613 // ============================================================================
614 bool SketchSolver_ConstraintGroup::updateGroup()
616 // Check for valid sketch
617 if (!mySketch->data()->isValid())
620 // Fast check for constraint validity. If all constraints are valid, no need to update the group
621 std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::reverse_iterator
622 aConstrIter = myConstraintMap.rbegin();
623 bool isAllValid = true;
624 for ( ; isAllValid && aConstrIter != myConstraintMap.rend(); aConstrIter++)
625 if (!aConstrIter->first->data()->isValid())
630 // Remove invalid constraints.
631 // There only constraint will be deleted (parameters and entities) will be removed below
632 std::list< boost::shared_ptr<SketchPlugin_Constraint> > aConstrToDelete;
633 std::map<Slvs_hEntity, bool> anEntToDelete; // entities will be removed if no valid constraints use them
634 for (aConstrIter = myConstraintMap.rbegin(); aConstrIter != myConstraintMap.rend(); aConstrIter++)
636 bool isValid = aConstrIter->first->data()->isValid();
638 int aConstrPos = Search(aConstrIter->second, myConstraints);
639 if (aConstrPos < (int)myConstraints.size())
641 Slvs_hEntity aConstrEnt[] = {
642 myConstraints[aConstrPos].ptA, myConstraints[aConstrPos].ptB,
643 myConstraints[aConstrPos].entityA, myConstraints[aConstrPos].entityB};
644 for (int i = 0; i < 4; i++)
645 if (aConstrEnt[i] != SLVS_E_UNKNOWN)
647 if (anEntToDelete.find(aConstrEnt[i]) == anEntToDelete.end())
648 anEntToDelete[aConstrEnt[i]] = !isValid;
649 else if (isValid) // constraint is valid => no need to remove its entities
650 anEntToDelete[aConstrEnt[i]] = false;
654 myConstraints.erase(myConstraints.begin() + aConstrPos);
655 if (aConstrIter->second == myConstrMaxID) // When the constraint with highest ID is removed, decrease indexer
657 aConstrToDelete.push_front(aConstrIter->first);
661 std::list< boost::shared_ptr<SketchPlugin_Constraint> >::iterator aDelIter;
662 for (aDelIter = aConstrToDelete.begin(); aDelIter != aConstrToDelete.end(); aDelIter++)
663 myConstraintMap.erase(*aDelIter);
665 // Remove invalid and unused entities
666 std::map<Slvs_hEntity, bool>::reverse_iterator aEDelIter;
667 for (aEDelIter = anEntToDelete.rbegin(); aEDelIter != anEntToDelete.rend(); aEDelIter++)
668 if (aEDelIter->second)
670 int anEntPos = Search(aEDelIter->first, myEntities);
671 std::vector<Slvs_Entity>::iterator aEntIter = myEntities.begin() + anEntPos;
672 // Number of parameters for the entity
674 while (aEntIter->param[aNbParams]) aNbParams++;
675 if (aNbParams == 0) continue;
676 // Decrease parameter indexer if there are deleted parameter with higher IDs
677 if (aEntIter->param[aNbParams-1] == myParamMaxID)
678 myParamMaxID -= aNbParams;
679 // Remove parameters of the entity
680 int aParamPos = Search(aEntIter->param[0], myParams);
681 myParams.erase(myParams.begin() + aParamPos,
682 myParams.begin() + aParamPos + aNbParams);
685 if (aEDelIter->first == myEntityMaxID)
687 myEntities.erase(myEntities.begin() + anEntPos);
688 // Remove such entity from myEntityMap
689 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator
690 anEntMapIter = myEntityMap.begin();
691 for ( ; anEntMapIter != myEntityMap.end(); anEntMapIter++)
692 if (anEntMapIter->second == aEDelIter->first)
694 if (anEntMapIter != myEntityMap.end())
695 myEntityMap.erase(anEntMapIter);
701 // ============================================================================
702 // Function: updateAttribute
703 // Class: SketchSolver_ConstraintGroup
704 // Purpose: update features of sketch after resolving constraints
705 // ============================================================================
706 void SketchSolver_ConstraintGroup::updateAttribute(
707 boost::shared_ptr<ModelAPI_Attribute> theAttribute,
708 const Slvs_hEntity& theEntityID)
710 // Search the position of the first parameter of the entity
711 int anEntPos = Search(theEntityID, myEntities);
712 int aFirstParamPos = Search(myEntities[anEntPos].param[0], myParams);
714 // Look over supported types of entities
717 boost::shared_ptr<GeomDataAPI_Point> aPoint =
718 boost::dynamic_pointer_cast<GeomDataAPI_Point>(theAttribute);
721 aPoint->setValue(myParams[aFirstParamPos].val,
722 myParams[aFirstParamPos+1].val,
723 myParams[aFirstParamPos+2].val);
728 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
729 boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute);
732 aPoint2D->setValue(myParams[aFirstParamPos].val,
733 myParams[aFirstParamPos+1].val);
738 boost::shared_ptr<ModelAPI_AttributeDouble> aScalar =
739 boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theAttribute);
742 aScalar->setValue(myParams[aFirstParamPos].val);
746 /// \todo Support other types of entities
749 // ============================================================================
750 // Function: updateEntityIfPossible
751 // Class: SketchSolver_ConstraintGroup
752 // Purpose: search the entity in this group and update it
753 // ============================================================================
754 void SketchSolver_ConstraintGroup::updateEntityIfPossible(
755 boost::shared_ptr<ModelAPI_Attribute> theEntity)
757 if (myEntityMap.find(theEntity) != myEntityMap.end())
759 // If the attribute is a point and it is changed (the group needs to rebuild),
760 // probably user has dragged this point into this position,
761 // so it is necessary to add constraint which will guarantee the point will not change
763 // Store myNeedToSolve flag to verify the entity is really changed
764 bool aNeedToSolveCopy = myNeedToSolve;
765 myNeedToSolve = false;
767 changeEntity(theEntity);
769 if (myNeedToSolve) // the entity is changed
771 // Verify the entity is a point and add temporary constraint of permanency
772 boost::shared_ptr<GeomDataAPI_Point> aPoint =
773 boost::dynamic_pointer_cast<GeomDataAPI_Point>(theEntity);
774 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
775 boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theEntity);
776 if (aPoint || aPoint2D)
777 addTemporaryConstraintWhereDragged(theEntity);
780 // Restore flag of changes
781 myNeedToSolve = myNeedToSolve || aNeedToSolveCopy;
785 // ============================================================================
786 // Function: addTemporaryConstraintWhereDragged
787 // Class: SketchSolver_ConstraintGroup
788 // Purpose: add transient constraint SLVS_C_WHERE_DRAGGED for the entity,
789 // which was moved by user
790 // ============================================================================
791 void SketchSolver_ConstraintGroup::addTemporaryConstraintWhereDragged(
792 boost::shared_ptr<ModelAPI_Attribute> theEntity)
794 // Find identifier of the entity
795 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
796 anEntIter = myEntityMap.find(theEntity);
798 // Create SLVS_C_WHERE_DRAGGED constraint
799 Slvs_Constraint aWDConstr = Slvs_MakeConstraint(++myConstrMaxID, myID, SLVS_C_WHERE_DRAGGED,
800 myWorkplane.h, 0.0, anEntIter->second, 0, 0, 0);
801 myConstraints.push_back(aWDConstr);
802 myTempConstraints.push_back(aWDConstr.h);
805 // ============================================================================
806 // Function: removeTemporaryConstraints
807 // Class: SketchSolver_ConstraintGroup
808 // Purpose: remove all transient SLVS_C_WHERE_DRAGGED constraints after
809 // resolving the set of constraints
810 // ============================================================================
811 void SketchSolver_ConstraintGroup::removeTemporaryConstraints()
813 std::list<Slvs_hConstraint>::reverse_iterator aTmpConstrIter;
814 for (aTmpConstrIter = myTempConstraints.rbegin(); aTmpConstrIter != myTempConstraints.rend(); aTmpConstrIter++)
816 int aConstrPos = Search(*aTmpConstrIter, myConstraints);
817 myConstraints.erase(myConstraints.begin() + aConstrPos);
819 // If the removing constraint has higher index, decrease the indexer
820 if (*aTmpConstrIter == myConstrMaxID)
823 myTempConstraints.clear();
828 // ========================================================
829 // ========= Auxiliary functions ===============
830 // ========================================================
832 template <typename T>
833 int Search(const uint32_t& theEntityID, const std::vector<T>& theEntities)
835 int aResIndex = theEntityID <= theEntities.size() ? theEntityID - 1 : 0;
836 int aVecSize = theEntities.size();
837 while (aResIndex >= 0 && theEntities[aResIndex].h > theEntityID)
839 while (aResIndex < aVecSize && theEntities[aResIndex].h < theEntityID)
842 aResIndex = aVecSize;