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 myTempConstraints.clear();
63 myTempPointWhereDragged.clear();
64 myTempPointWDrgdID = 0;
66 // Initialize workplane
67 myWorkplane.h = SLVS_E_UNKNOWN;
69 assert(addWorkplane(theWorkplane));
71 addWorkplane(theWorkplane);
75 SketchSolver_ConstraintGroup::~SketchSolver_ConstraintGroup()
79 myConstraints.clear();
80 myConstraintMap.clear();
81 myTempConstraints.clear();
82 myTempPointWhereDragged.clear();
84 // If the group with maximal identifier is deleted, decrease the indexer
85 if (myID == myGroupIndexer)
89 // ============================================================================
90 // Function: isBaseWorkplane
91 // Class: SketchSolver_ConstraintGroup
92 // Purpose: verify the group is based on the given workplane
93 // ============================================================================
94 bool SketchSolver_ConstraintGroup::isBaseWorkplane(
95 boost::shared_ptr<SketchPlugin_Feature> theWorkplane) const
97 return theWorkplane == mySketch;
100 // ============================================================================
101 // Function: isInteract
102 // Class: SketchSolver_ConstraintGroup
103 // Purpose: verify are there any entities in the group used by given constraint
104 // ============================================================================
105 bool SketchSolver_ConstraintGroup::isInteract(
106 boost::shared_ptr<SketchPlugin_Constraint> theConstraint) const
108 // Check the group is empty
109 if (myWorkplane.h != SLVS_E_UNKNOWN && myConstraints.empty())
112 // Go through constraint entities and verify if some of them already in the group
113 for (int i = 0; i < CONSTRAINT_ATTR_SIZE; i++)
115 boost::shared_ptr<ModelAPI_AttributeRefAttr> aCAttrRef =
116 boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
117 theConstraint->data()->attribute(CONSTRAINT_ATTRIBUTES[i])
119 if (!aCAttrRef) continue;
120 if (myEntityMap.find(aCAttrRef->attr()) != myEntityMap.end())
124 // Entities did not found
128 // ============================================================================
129 // Function: changeConstraint
130 // Class: SketchSolver_ConstraintGroup
131 // Purpose: create/update the constraint in the group
132 // ============================================================================
133 bool SketchSolver_ConstraintGroup::changeConstraint(
134 boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
136 // There is no workplane yet, something wrong
137 if (myWorkplane.h == SLVS_E_UNKNOWN)
140 // Search this constraint in the current group to update it
141 std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::const_iterator
142 aConstrMapIter = myConstraintMap.find(theConstraint);
143 std::vector<Slvs_Constraint>::iterator aConstrIter;
144 if (aConstrMapIter != myConstraintMap.end())
146 int aConstrPos = Search(aConstrMapIter->second, myConstraints);
147 aConstrIter = myConstraints.begin() + aConstrPos;
150 // Get constraint type and verify the constraint parameters are correct
151 SketchSolver_Constraint aConstraint(theConstraint);
152 int aConstrType = aConstraint.getType();
153 if (aConstrType == SLVS_C_UNKNOWN ||
154 (aConstrMapIter != myConstraintMap.end() && aConstrIter->type != aConstrType))
156 const std::vector<std::string>& aConstraintAttributes = aConstraint.getAttributes();
158 // Create constraint parameters
159 double aDistance = 0.0; // scalar value of the constraint
160 boost::shared_ptr<ModelAPI_AttributeDouble> aDistAttr =
161 boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theConstraint->data()->attribute(CONSTRAINT_ATTR_VALUE));
164 aDistance = aDistAttr->value();
165 if (aConstrMapIter != myConstraintMap.end() && fabs(aConstrIter->valA - aDistance) > tolerance)
167 myNeedToSolve = true;
168 aConstrIter->valA = aDistance;
172 Slvs_hEntity aConstrEnt[CONSTRAINT_ATTR_SIZE]; // parameters of the constraint
173 for (unsigned int indAttr = 0; indAttr < CONSTRAINT_ATTR_SIZE; indAttr++)
175 aConstrEnt[indAttr] = SLVS_E_UNKNOWN;
176 boost::shared_ptr<ModelAPI_AttributeRefAttr> aConstrAttr =
177 boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
178 theConstraint->data()->attribute(aConstraintAttributes[indAttr])
180 if (!aConstrAttr) continue;
181 aConstrEnt[indAttr] = changeEntity(aConstrAttr->attr());
184 if (aConstrMapIter == myConstraintMap.end())
186 // Several points may be coincident, it is not necessary to store all constraints between them.
187 // Try to find sequence of coincident points which connects the points of new constraint
188 if (aConstrType == SLVS_C_POINTS_COINCIDENT)
190 std::vector< std::set<Slvs_hEntity> >::iterator aCoPtIter = myCoincidentPoints.begin();
191 std::vector< std::set<Slvs_hEntity> >::iterator aFirstFound = myCoincidentPoints.end();
192 for ( ; aCoPtIter != myCoincidentPoints.end(); aCoPtIter++)
194 bool isFound[2] = { // indicate which point ID was already in coincidence constraint
195 aCoPtIter->find(aConstrEnt[0]) != aCoPtIter->end(),
196 aCoPtIter->find(aConstrEnt[1]) != aCoPtIter->end(),
198 if (isFound[0] && isFound[1]) // points are already connected by coincidence constraints => no need to additional one
200 if ((isFound[0] && !isFound[1]) || (!isFound[0] && isFound[1]))
202 if (aFirstFound != myCoincidentPoints.end())
203 { // there are two groups of coincident points connected by created constraint => merge them
204 int aFirstFoundShift = aFirstFound - myCoincidentPoints.begin();
205 int aCurrentShift = aCoPtIter - myCoincidentPoints.begin();
206 aFirstFound->insert(aCoPtIter->begin(), aCoPtIter->end());
207 myCoincidentPoints.erase(aCoPtIter);
208 aFirstFound = myCoincidentPoints.begin() + aFirstFoundShift;
209 aCoPtIter = myCoincidentPoints.begin() + aCurrentShift;
213 aCoPtIter->insert(aConstrEnt[isFound[0] ? 1 : 0]);
214 aFirstFound = aCoPtIter;
218 // No points were found, need to create new set
219 if (aFirstFound == myCoincidentPoints.end())
221 std::set<Slvs_hEntity> aNewSet;
222 aNewSet.insert(aConstrEnt[0]);
223 aNewSet.insert(aConstrEnt[1]);
224 myCoincidentPoints.push_back(aNewSet);
228 // Create SolveSpace constraint structure
229 Slvs_Constraint aConstraint =
230 Slvs_MakeConstraint(++myConstrMaxID, myID, aConstrType, myWorkplane.h,
231 aDistance, aConstrEnt[0], aConstrEnt[1], aConstrEnt[2], aConstrEnt[3]);
232 myConstraints.push_back(aConstraint);
233 myConstraintMap[theConstraint] = aConstraint.h;
238 // ============================================================================
239 // Function: changeEntity
240 // Class: SketchSolver_ConstraintGroup
241 // Purpose: create/update the element affected by any constraint
242 // ============================================================================
243 Slvs_hEntity SketchSolver_ConstraintGroup::changeEntity(
244 boost::shared_ptr<ModelAPI_Attribute> theEntity)
246 // If the entity is already in the group, try to find it
247 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
248 aEntIter = myEntityMap.find(theEntity);
249 std::vector<Slvs_Param>::const_iterator aParamIter; // looks at first parameter of already existent entity or at the end of vector otherwise
250 if (aEntIter == myEntityMap.end()) // no such entity => should be created
251 aParamIter = myParams.end();
253 { // the entity already exists
254 int aEntPos = Search(aEntIter->second, myEntities);
255 int aParamPos = Search(myEntities[aEntPos].param[0], myParams);
256 aParamIter = myParams.begin() + aParamPos;
259 // Look over supported types of entities
262 boost::shared_ptr<GeomDataAPI_Point> aPoint =
263 boost::dynamic_pointer_cast<GeomDataAPI_Point>(theEntity);
266 Slvs_hParam aX = changeParameter(aPoint->x(), aParamIter);
267 Slvs_hParam aY = changeParameter(aPoint->y(), aParamIter);
268 Slvs_hParam aZ = changeParameter(aPoint->z(), aParamIter);
270 if (aEntIter != myEntityMap.end()) // the entity already exists
271 return aEntIter->second;
274 Slvs_Entity aPtEntity = Slvs_MakePoint3d(++myEntityMaxID, myID, aX, aY, aZ);
275 myEntities.push_back(aPtEntity);
276 myEntityMap[theEntity] = aPtEntity.h;
280 // All entities except 3D points are created on workplane. So, if there is no workplane yet, then error
281 if (myWorkplane.h == SLVS_E_UNKNOWN)
282 return SLVS_E_UNKNOWN;
285 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
286 boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theEntity);
289 Slvs_hParam aU = changeParameter(aPoint2D->x(), aParamIter);
290 Slvs_hParam aV = changeParameter(aPoint2D->y(), aParamIter);
292 if (aEntIter != myEntityMap.end()) // the entity already exists
293 return aEntIter->second;
296 Slvs_Entity aPt2DEntity = Slvs_MakePoint2d(++myEntityMaxID, myID, myWorkplane.h, aU, aV);
297 myEntities.push_back(aPt2DEntity);
298 myEntityMap[theEntity] = aPt2DEntity.h;
299 return aPt2DEntity.h;
302 // Scalar value (used for the distance entities)
303 boost::shared_ptr<ModelAPI_AttributeDouble> aScalar =
304 boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theEntity);
307 Slvs_hParam aValue = changeParameter(aScalar->value(), aParamIter);
309 if (aEntIter != myEntityMap.end()) // the entity already exists
310 return aEntIter->second;
313 Slvs_Entity aDistance = Slvs_MakeDistance(++myEntityMaxID, myID, myWorkplane.h, aValue);
314 myEntities.push_back(aDistance);
315 myEntityMap[theEntity] = aDistance.h;
319 // SketchPlugin features
320 boost::shared_ptr<SketchPlugin_Feature> aFeature =
321 boost::dynamic_pointer_cast<SketchPlugin_Feature>(theEntity);
323 { // Verify the feature by its kind
324 const std::string& aFeatureKind = aFeature->getKind();
327 if (aFeatureKind.compare("SketchLine") == 0)
329 Slvs_hEntity aStart = changeEntity(aFeature->data()->attribute(LINE_ATTR_START));
330 Slvs_hEntity aEnd = changeEntity(aFeature->data()->attribute(LINE_ATTR_END));
332 if (aEntIter != myEntityMap.end()) // the entity already exists
333 return aEntIter->second;
336 Slvs_Entity aLineEntity = Slvs_MakeLineSegment(++myEntityMaxID, myID, myWorkplane.h, aStart, aEnd);
337 myEntities.push_back(aLineEntity);
338 myEntityMap[theEntity] = aLineEntity.h;
339 return aLineEntity.h;
342 else if (aFeatureKind.compare("SketchCircle") == 0)
344 Slvs_hEntity aCenter = changeEntity(aFeature->data()->attribute(CIRCLE_ATTR_CENTER));
345 Slvs_hEntity aRadius = changeEntity(aFeature->data()->attribute(CIRCLE_ATTR_RADIUS));
347 if (aEntIter != myEntityMap.end()) // the entity already exists
348 return aEntIter->second;
351 Slvs_Entity aCircleEntity =
352 Slvs_MakeCircle(++myEntityMaxID, myID, myWorkplane.h, aCenter, myWorkplane.normal, aRadius);
353 myEntities.push_back(aCircleEntity);
354 myEntityMap[theEntity] = aCircleEntity.h;
355 return aCircleEntity.h;
358 else if (aFeatureKind.compare("SketchArc") == 0)
360 Slvs_hEntity aCenter = changeEntity(aFeature->data()->attribute(ARC_ATTR_CENTER));
361 Slvs_hEntity aStart = changeEntity(aFeature->data()->attribute(ARC_ATTR_START));
362 Slvs_hEntity aEnd = changeEntity(aFeature->data()->attribute(ARC_ATTR_END));
364 if (aEntIter != myEntityMap.end()) // the entity already exists
365 return aEntIter->second;
367 Slvs_Entity anArcEntity = Slvs_MakeArcOfCircle(++myEntityMaxID, myID,
368 myWorkplane.h, myWorkplane.normal, aCenter, aStart, aEnd);
369 myEntities.push_back(anArcEntity);
370 myEntityMap[theEntity] = anArcEntity.h;
371 return anArcEntity.h;
373 // Point (it has low probability to be an attribute of constraint, so it is checked at the end)
374 else if (aFeatureKind.compare("SketchPoint") == 0)
376 Slvs_hEntity aPoint = changeEntity(aFeature->data()->attribute(POINT_ATTR_COORD));
378 if (aEntIter != myEntityMap.end()) // the entity already exists
379 return aEntIter->second;
381 // Both the sketch point and its attribute (coordinates) link to the same SolveSpace point identifier
382 myEntityMap[theEntity] = aPoint;
387 /// \todo Other types of entities
389 // Unsupported or wrong entity type
390 return SLVS_E_UNKNOWN;
393 // ============================================================================
394 // Function: changeNormal
395 // Class: SketchSolver_ConstraintGroup
396 // Purpose: create/update the normal of workplane
397 // ============================================================================
398 Slvs_hEntity SketchSolver_ConstraintGroup::changeNormal(
399 boost::shared_ptr<ModelAPI_Attribute> theDirX,
400 boost::shared_ptr<ModelAPI_Attribute> theDirY,
401 boost::shared_ptr<ModelAPI_Attribute> theNorm)
403 boost::shared_ptr<GeomDataAPI_Dir> aDirX =
404 boost::dynamic_pointer_cast<GeomDataAPI_Dir>(theDirX);
405 boost::shared_ptr<GeomDataAPI_Dir> aDirY =
406 boost::dynamic_pointer_cast<GeomDataAPI_Dir>(theDirY);
407 if (!aDirX || !aDirY ||
408 (fabs(aDirX->x()) + fabs(aDirX->y()) + fabs(aDirX->z()) < tolerance) ||
409 (fabs(aDirY->x()) + fabs(aDirY->y()) + fabs(aDirY->z()) < tolerance))
410 return SLVS_E_UNKNOWN;
412 // quaternion parameters of normal vector
413 double qw, qx, qy, qz;
414 Slvs_MakeQuaternion(aDirX->x(), aDirX->y(), aDirX->z(),
415 aDirY->x(), aDirY->y(), aDirY->z(),
417 double aNormCoord[4] = {qw, qx, qy, qz};
419 // Try to find existent normal
420 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
421 aEntIter = myEntityMap.find(theNorm);
422 std::vector<Slvs_Param>::const_iterator aParamIter; // looks to the first parameter of already existent entity or to the end of vector otherwise
423 if (aEntIter == myEntityMap.end()) // no such entity => should be created
424 aParamIter = myParams.end();
426 { // the entity already exists, update it
427 int aEntPos = Search(aEntIter->second, myEntities);
428 int aParamPos = Search(myEntities[aEntPos].param[0], myParams);
429 aParamIter = myParams.begin() + aParamPos;
432 // Change parameters of the normal
433 Slvs_hParam aNormParams[4];
434 for (int i = 0; i < 4; i++)
435 aNormParams[i] = changeParameter(aNormCoord[i], aParamIter);
437 if (aEntIter != myEntityMap.end()) // the entity already exists
438 return aEntIter->second;
441 Slvs_Entity aNormal = Slvs_MakeNormal3d(++myEntityMaxID, myID,
442 aNormParams[0], aNormParams[1], aNormParams[2], aNormParams[3]);
443 myEntities.push_back(aNormal);
444 myEntityMap[theNorm] = aNormal.h;
449 // ============================================================================
450 // Function: addWorkplane
451 // Class: SketchSolver_ConstraintGroup
452 // Purpose: create workplane for the group
453 // ============================================================================
454 bool SketchSolver_ConstraintGroup::addWorkplane(
455 boost::shared_ptr<SketchPlugin_Feature> theSketch)
457 if (myWorkplane.h || theSketch->getKind().compare("Sketch") != 0)
458 return false; // the workplane already exists or the function parameter is not Sketch
460 mySketch = theSketch;
465 // ============================================================================
466 // Function: updateWorkplane
467 // Class: SketchSolver_ConstraintGroup
468 // Purpose: update parameters of workplane
469 // ============================================================================
470 bool SketchSolver_ConstraintGroup::updateWorkplane()
472 // Get parameters of workplane
473 boost::shared_ptr<ModelAPI_Attribute> aDirX = mySketch->data()->attribute(SKETCH_ATTR_DIRX);
474 boost::shared_ptr<ModelAPI_Attribute> aDirY = mySketch->data()->attribute(SKETCH_ATTR_DIRY);
475 boost::shared_ptr<ModelAPI_Attribute> aNorm = mySketch->data()->attribute(SKETCH_ATTR_NORM);
476 boost::shared_ptr<ModelAPI_Attribute> anOrigin = mySketch->data()->attribute(SKETCH_ATTR_ORIGIN);
477 // Transform them into SolveSpace format
478 Slvs_hEntity aNormalWP = changeNormal(aDirX, aDirY, aNorm);
479 if (!aNormalWP) return false;
480 Slvs_hEntity anOriginWP = changeEntity(anOrigin);
481 if (!anOriginWP) return false;
486 myWorkplane = Slvs_MakeWorkplane(++myEntityMaxID, myID, anOriginWP, aNormalWP);
487 // Workplane should be added to the list of entities
488 myEntities.push_back(myWorkplane);
493 // ============================================================================
494 // Function: changeParameter
495 // Class: SketchSolver_ConstraintGroup
496 // Purpose: create/update value of parameter
497 // ============================================================================
498 Slvs_hParam SketchSolver_ConstraintGroup::changeParameter(
499 const double& theParam,
500 std::vector<Slvs_Param>::const_iterator& thePrmIter)
502 if (thePrmIter != myParams.end())
503 { // Parameter should be updated
504 int aParamPos = thePrmIter - myParams.begin();
505 if (fabs(thePrmIter->val - theParam) > tolerance)
507 myNeedToSolve = true; // parameter is changed, need to resolve constraints
508 myParams[aParamPos].val = theParam;
511 return myParams[aParamPos].h;
514 // Newly created parameter
515 Slvs_Param aParam = Slvs_MakeParam(++myParamMaxID, myID, theParam);
516 myParams.push_back(aParam);
517 myNeedToSolve = true;
518 // The list of parameters is changed, move iterator to the end of the list to avoid problems
519 thePrmIter = myParams.end();
523 // ============================================================================
524 // Function: resolveConstraints
525 // Class: SketchSolver_ConstraintGroup
526 // Purpose: solve the set of constraints for the current group
527 // ============================================================================
528 void SketchSolver_ConstraintGroup::resolveConstraints()
533 myConstrSolver.setGroupID(myID);
534 myConstrSolver.setParameters(myParams);
535 myConstrSolver.setEntities(myEntities);
536 myConstrSolver.setConstraints(myConstraints);
537 myConstrSolver.setDraggedParameters(myTempPointWhereDragged);
539 int aResult = myConstrSolver.solve();
540 if (aResult == SLVS_RESULT_OKAY)
541 { // solution succeeded, store results into correspondent attributes
542 // Obtain result into the same list of parameters
543 if (!myConstrSolver.getResult(myParams))
546 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator
547 anEntIter = myEntityMap.begin();
548 for ( ; anEntIter != myEntityMap.end(); anEntIter++)
549 updateAttribute(anEntIter->first, anEntIter->second);
551 /// \todo Implement error handling
553 removeTemporaryConstraints();
554 myNeedToSolve = false;
557 // ============================================================================
558 // Function: mergeGroups
559 // Class: SketchSolver_ConstraintGroup
560 // Purpose: append specified group to the current group
561 // ============================================================================
562 void SketchSolver_ConstraintGroup::mergeGroups(
563 const SketchSolver_ConstraintGroup& theGroup)
565 // If specified group is empty, no need to merge
566 if (theGroup.myConstraintMap.empty())
569 // Map between old and new indexes of SolveSpace constraints
570 std::map<Slvs_hConstraint, Slvs_hConstraint> aConstrMap;
572 // Add all constraints from theGroup to the current group
573 std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::const_iterator
574 aConstrIter = theGroup.myConstraintMap.begin();
575 for ( ; aConstrIter != theGroup.myConstraintMap.end(); aConstrIter++)
576 if (changeConstraint(aConstrIter->first))
577 aConstrMap[aConstrIter->second] = myConstrMaxID; // the constraint was added => store its ID
579 // Add temporary constraints from theGroup
580 std::list<Slvs_hConstraint>::const_iterator aTempConstrIter = theGroup.myTempConstraints.begin();
581 for ( ; aTempConstrIter != theGroup.myTempConstraints.end(); aTempConstrIter++)
583 std::map<Slvs_hConstraint, Slvs_hConstraint>::iterator aFind = aConstrMap.find(*aTempConstrIter);
584 if (aFind != aConstrMap.end())
585 myTempConstraints.push_back(aFind->second);
588 if (myTempPointWhereDragged.empty())
589 myTempPointWhereDragged = theGroup.myTempPointWhereDragged;
590 else if (!theGroup.myTempPointWhereDragged.empty())
591 { // Need to create additional transient constraint
592 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
593 aFeatureIter = theGroup.myEntityMap.begin();
594 for (; aFeatureIter != theGroup.myEntityMap.end(); aFeatureIter++)
595 if (aFeatureIter->second == myTempPointWDrgdID)
597 addTemporaryConstraintWhereDragged(aFeatureIter->first);
602 myNeedToSolve = myNeedToSolve || theGroup.myNeedToSolve;
605 // ============================================================================
606 // Function: updateGroup
607 // Class: SketchSolver_ConstraintGroup
608 // Purpose: search removed entities and constraints
609 // ============================================================================
610 bool SketchSolver_ConstraintGroup::updateGroup()
612 // Check for valid sketch
613 if (!mySketch->data()->isValid())
616 // Fast check for constraint validity. If all constraints are valid, no need to update the group
617 std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::reverse_iterator
618 aConstrIter = myConstraintMap.rbegin();
619 bool isAllValid = true;
620 for ( ; isAllValid && aConstrIter != myConstraintMap.rend(); aConstrIter++)
621 if (!aConstrIter->first->data()->isValid())
626 // Remove invalid constraints.
627 // There only constraint will be deleted (parameters and entities) will be removed below
628 std::list< boost::shared_ptr<SketchPlugin_Constraint> > aConstrToDelete;
629 std::map<Slvs_hEntity, bool> anEntToDelete; // entities will be removed if no valid constraints use them
630 std::map<Slvs_hEntity, bool> aCoincPtToDelete; // list of entities (points) used in coincidence constaints which will be removed;
631 for (aConstrIter = myConstraintMap.rbegin(); aConstrIter != myConstraintMap.rend(); aConstrIter++)
633 bool isValid = aConstrIter->first->data()->isValid();
635 int aConstrPos = Search(aConstrIter->second, myConstraints);
636 if (aConstrPos < (int)myConstraints.size())
638 Slvs_hEntity aConstrEnt[] = {
639 myConstraints[aConstrPos].ptA, myConstraints[aConstrPos].ptB,
640 myConstraints[aConstrPos].entityA, myConstraints[aConstrPos].entityB};
641 for (int i = 0; i < 4; i++)
642 if (aConstrEnt[i] != SLVS_E_UNKNOWN)
644 if (anEntToDelete.find(aConstrEnt[i]) == anEntToDelete.end())
646 anEntToDelete[aConstrEnt[i]] = !isValid;
647 aCoincPtToDelete[aConstrEnt[i]] = !isValid && (myConstraints[aConstrPos].type == SLVS_C_POINTS_COINCIDENT);
649 else if (isValid) // constraint is valid => no need to remove its entities
651 anEntToDelete[aConstrEnt[i]] = false;
652 aCoincPtToDelete[aConstrEnt[i]] = false;
657 myConstraints.erase(myConstraints.begin() + aConstrPos);
658 if (aConstrIter->second == myConstrMaxID) // When the constraint with highest ID is removed, decrease indexer
660 aConstrToDelete.push_front(aConstrIter->first);
664 std::list< boost::shared_ptr<SketchPlugin_Constraint> >::iterator aDelIter;
665 for (aDelIter = aConstrToDelete.begin(); aDelIter != aConstrToDelete.end(); aDelIter++)
666 myConstraintMap.erase(*aDelIter);
668 // Remove invalid and unused entities
669 std::map<Slvs_hEntity, bool>::reverse_iterator aEDelIter;
670 std::map<Slvs_hEntity, bool>::reverse_iterator aPtDelIter = aCoincPtToDelete.rbegin();
671 for (aEDelIter = anEntToDelete.rbegin(); aEDelIter != anEntToDelete.rend(); aEDelIter++, aPtDelIter++)
673 if (aEDelIter->second) // remove entity
675 int anEntPos = Search(aEDelIter->first, myEntities);
676 std::vector<Slvs_Entity>::iterator aEntIter = myEntities.begin() + anEntPos;
677 // Number of parameters for the entity
679 while (aEntIter->param[aNbParams]) aNbParams++;
680 if (aNbParams == 0) continue;
681 // Decrease parameter indexer if there are deleted parameter with higher IDs
682 if (aEntIter->param[aNbParams-1] == myParamMaxID)
683 myParamMaxID -= aNbParams;
684 // Remove parameters of the entity
685 int aParamPos = Search(aEntIter->param[0], myParams);
686 myParams.erase(myParams.begin() + aParamPos,
687 myParams.begin() + aParamPos + aNbParams);
690 if (aEDelIter->first == myEntityMaxID)
692 myEntities.erase(myEntities.begin() + anEntPos);
693 // Remove such entity from myEntityMap
694 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator
695 anEntMapIter = myEntityMap.begin();
696 for ( ; anEntMapIter != myEntityMap.end(); anEntMapIter++)
697 if (anEntMapIter->second == aEDelIter->first)
699 if (anEntMapIter != myEntityMap.end())
700 myEntityMap.erase(anEntMapIter);
702 if (aPtDelIter->second) // remove link for this entity from list of coincident points
704 std::vector< std::set<Slvs_hEntity> >::iterator aCoPtIter = myCoincidentPoints.begin();
705 for ( ; aCoPtIter != myCoincidentPoints.end(); aCoPtIter++)
706 aCoPtIter->erase(aPtDelIter->first);
713 // ============================================================================
714 // Function: updateAttribute
715 // Class: SketchSolver_ConstraintGroup
716 // Purpose: update features of sketch after resolving constraints
717 // ============================================================================
718 void SketchSolver_ConstraintGroup::updateAttribute(
719 boost::shared_ptr<ModelAPI_Attribute> theAttribute,
720 const Slvs_hEntity& theEntityID)
722 // Search the position of the first parameter of the entity
723 int anEntPos = Search(theEntityID, myEntities);
724 int aFirstParamPos = Search(myEntities[anEntPos].param[0], myParams);
726 // Look over supported types of entities
729 boost::shared_ptr<GeomDataAPI_Point> aPoint =
730 boost::dynamic_pointer_cast<GeomDataAPI_Point>(theAttribute);
733 aPoint->setValue(myParams[aFirstParamPos].val,
734 myParams[aFirstParamPos+1].val,
735 myParams[aFirstParamPos+2].val);
740 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
741 boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute);
744 aPoint2D->setValue(myParams[aFirstParamPos].val,
745 myParams[aFirstParamPos+1].val);
750 boost::shared_ptr<ModelAPI_AttributeDouble> aScalar =
751 boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theAttribute);
754 aScalar->setValue(myParams[aFirstParamPos].val);
758 /// \todo Support other types of entities
761 // ============================================================================
762 // Function: updateEntityIfPossible
763 // Class: SketchSolver_ConstraintGroup
764 // Purpose: search the entity in this group and update it
765 // ============================================================================
766 void SketchSolver_ConstraintGroup::updateEntityIfPossible(
767 boost::shared_ptr<ModelAPI_Attribute> theEntity)
769 if (myEntityMap.find(theEntity) != myEntityMap.end())
771 // If the attribute is a point and it is changed (the group needs to rebuild),
772 // probably user has dragged this point into this position,
773 // so it is necessary to add constraint which will guarantee the point will not change
775 // Store myNeedToSolve flag to verify the entity is really changed
776 bool aNeedToSolveCopy = myNeedToSolve;
777 myNeedToSolve = false;
779 changeEntity(theEntity);
781 if (myNeedToSolve) // the entity is changed
783 // Verify the entity is a point and add temporary constraint of permanency
784 boost::shared_ptr<GeomDataAPI_Point> aPoint =
785 boost::dynamic_pointer_cast<GeomDataAPI_Point>(theEntity);
786 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
787 boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theEntity);
788 if (aPoint || aPoint2D)
789 addTemporaryConstraintWhereDragged(theEntity);
792 // Restore flag of changes
793 myNeedToSolve = myNeedToSolve || aNeedToSolveCopy;
797 // ============================================================================
798 // Function: addTemporaryConstraintWhereDragged
799 // Class: SketchSolver_ConstraintGroup
800 // Purpose: add transient constraint SLVS_C_WHERE_DRAGGED for the entity,
801 // which was moved by user
802 // ============================================================================
803 void SketchSolver_ConstraintGroup::addTemporaryConstraintWhereDragged(
804 boost::shared_ptr<ModelAPI_Attribute> theEntity)
806 // Find identifier of the entity
807 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
808 anEntIter = myEntityMap.find(theEntity);
809 if (anEntIter == myEntityMap.end())
812 // If this is a first dragged point, its parameters should be placed
813 // into Slvs_System::dragged field to avoid system inconsistense
814 if (myTempPointWhereDragged.empty())
816 int anEntPos = Search(anEntIter->second, myEntities);
817 Slvs_hParam* aDraggedParam = myEntities[anEntPos].param;
818 for (int i = 0; i < 4; i++, aDraggedParam++)
819 if (*aDraggedParam != 0)
820 myTempPointWhereDragged.push_back(*aDraggedParam);
821 myTempPointWDrgdID = myEntities[anEntPos].h;
825 // Get identifiers of all dragged points
826 std::set<Slvs_hEntity> aDraggedPntID;
827 aDraggedPntID.insert(myTempPointWDrgdID);
828 std::list<Slvs_hConstraint>::iterator aTmpCoIter = myTempConstraints.begin();
829 for ( ; aTmpCoIter != myTempConstraints.end(); aTmpCoIter++)
831 unsigned int aConstrPos = Search(*aTmpCoIter, myConstraints);
832 if (aConstrPos < myConstraints.size())
833 aDraggedPntID.insert(myConstraints[aConstrPos].ptA);
835 // Find whether there is a point coincident with theEntity, which already has SLVS_C_WHERE_DRAGGED
836 std::vector< std::set<Slvs_hEntity> >::iterator aCoPtIter = myCoincidentPoints.begin();
837 for ( ; aCoPtIter != myCoincidentPoints.end(); aCoPtIter++)
839 if (aCoPtIter->find(anEntIter->second) == aCoPtIter->end())
840 continue; // the entity was not found in current set
842 // Find one of already created SLVS_C_WHERE_DRAGGED constraints in current set of coincident points
843 std::set<Slvs_hEntity>::const_iterator aDrgIter = aDraggedPntID.begin();
844 for ( ; aDrgIter != aDraggedPntID.end(); aDrgIter++)
845 if (aCoPtIter->find(*aDrgIter) != aCoPtIter->end())
846 return ; // the SLVS_C_WHERE_DRAGGED constraint already exists
849 // Create additional SLVS_C_WHERE_DRAGGED constraint if myTempPointWhereDragged field is not empty
850 Slvs_Constraint aWDConstr = Slvs_MakeConstraint(++myConstrMaxID, myID, SLVS_C_WHERE_DRAGGED,
851 myWorkplane.h, 0.0, anEntIter->second, 0, 0, 0);
852 myConstraints.push_back(aWDConstr);
853 myTempConstraints.push_back(aWDConstr.h);
856 // ============================================================================
857 // Function: removeTemporaryConstraints
858 // Class: SketchSolver_ConstraintGroup
859 // Purpose: remove all transient SLVS_C_WHERE_DRAGGED constraints after
860 // resolving the set of constraints
861 // ============================================================================
862 void SketchSolver_ConstraintGroup::removeTemporaryConstraints()
864 std::list<Slvs_hConstraint>::reverse_iterator aTmpConstrIter;
865 for (aTmpConstrIter = myTempConstraints.rbegin(); aTmpConstrIter != myTempConstraints.rend(); aTmpConstrIter++)
867 unsigned int aConstrPos = Search(*aTmpConstrIter, myConstraints);
868 if (aConstrPos >= myConstraints.size())
870 myConstraints.erase(myConstraints.begin() + aConstrPos);
872 // If the removing constraint has higher index, decrease the indexer
873 if (*aTmpConstrIter == myConstrMaxID)
876 myTempConstraints.clear();
878 // Clear basic dragged point
879 myTempPointWhereDragged.clear();
884 // ========================================================
885 // ========= Auxiliary functions ===============
886 // ========================================================
888 template <typename T>
889 int Search(const uint32_t& theEntityID, const std::vector<T>& theEntities)
891 int aResIndex = theEntityID <= theEntities.size() ? theEntityID - 1 : 0;
892 int aVecSize = theEntities.size();
893 while (aResIndex >= 0 && theEntities[aResIndex].h > theEntityID)
895 while (aResIndex < aVecSize && theEntities[aResIndex].h < theEntityID)
898 aResIndex = aVecSize;