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 <Model_Events.h>
17 #include <SketchPlugin_Constraint.h>
18 #include <SketchPlugin_ConstraintLength.h>
19 #include <SketchPlugin_ConstraintCoincidence.h>
21 #include <SketchPlugin_Arc.h>
22 #include <SketchPlugin_Circle.h>
23 #include <SketchPlugin_Line.h>
24 #include <SketchPlugin_Point.h>
25 #include <SketchPlugin_Sketch.h>
30 /// Tolerance for value of parameters
31 const double tolerance = 1.e-10;
33 /// This value is used to give unique index to the groups
34 static Slvs_hGroup myGroupIndexer = 0;
36 /** \brief Search the entity/parameter with specified ID in the list of elements
37 * \param[in] theEntityID unique ID of the element
38 * \param[in] theEntities list of elements
39 * \return position of the found element or -1 if the element is not found
42 static int Search(const uint32_t& theEntityID, const std::vector<T>& theEntities);
45 // ========================================================
46 // ========= SketchSolver_ConstraintGroup ===============
47 // ========================================================
49 SketchSolver_ConstraintGroup::
50 SketchSolver_ConstraintGroup(boost::shared_ptr<SketchPlugin_Feature> theWorkplane)
51 : myID(++myGroupIndexer),
61 myConstraints.clear();
63 myTempConstraints.clear();
64 myTempPointWhereDragged.clear();
65 myTempPointWDrgdID = 0;
67 // Initialize workplane
68 myWorkplane.h = SLVS_E_UNKNOWN;
70 assert(addWorkplane(theWorkplane));
72 addWorkplane(theWorkplane);
76 SketchSolver_ConstraintGroup::~SketchSolver_ConstraintGroup()
80 myConstraints.clear();
81 myConstraintMap.clear();
82 myTempConstraints.clear();
83 myTempPointWhereDragged.clear();
85 // If the group with maximal identifier is deleted, decrease the indexer
86 if (myID == myGroupIndexer)
90 // ============================================================================
91 // Function: isBaseWorkplane
92 // Class: SketchSolver_ConstraintGroup
93 // Purpose: verify the group is based on the given workplane
94 // ============================================================================
95 bool SketchSolver_ConstraintGroup::isBaseWorkplane(
96 boost::shared_ptr<SketchPlugin_Feature> theWorkplane) const
98 return theWorkplane == mySketch;
101 // ============================================================================
102 // Function: isInteract
103 // Class: SketchSolver_ConstraintGroup
104 // Purpose: verify are there any entities in the group used by given constraint
105 // ============================================================================
106 bool SketchSolver_ConstraintGroup::isInteract(
107 boost::shared_ptr<SketchPlugin_Constraint> theConstraint) const
109 // Check the group is empty
110 if (myWorkplane.h != SLVS_E_UNKNOWN && myConstraints.empty())
113 // Go through constraint entities and verify if some of them already in the group
114 for (int i = 0; i < CONSTRAINT_ATTR_SIZE; i++)
116 boost::shared_ptr<ModelAPI_AttributeRefAttr> aCAttrRef =
117 boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
118 theConstraint->data()->attribute(CONSTRAINT_ATTRIBUTES[i])
120 if (!aCAttrRef) continue;
121 if (!aCAttrRef->isFeature() &&
122 myEntityAttrMap.find(aCAttrRef->attr()) != myEntityAttrMap.end())
124 if (aCAttrRef->isFeature() &&
125 myEntityFeatMap.find(aCAttrRef->feature()) != myEntityFeatMap.end())
129 // Entities did not found
133 // ============================================================================
134 // Function: changeConstraint
135 // Class: SketchSolver_ConstraintGroup
136 // Purpose: create/update the constraint in the group
137 // ============================================================================
138 bool SketchSolver_ConstraintGroup::changeConstraint(
139 boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
141 // There is no workplane yet, something wrong
142 if (myWorkplane.h == SLVS_E_UNKNOWN)
145 // Search this constraint in the current group to update it
146 std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::const_iterator
147 aConstrMapIter = myConstraintMap.find(theConstraint);
148 std::vector<Slvs_Constraint>::iterator aConstrIter;
149 if (aConstrMapIter != myConstraintMap.end())
151 int aConstrPos = Search(aConstrMapIter->second, myConstraints);
152 aConstrIter = myConstraints.begin() + aConstrPos;
155 // Get constraint type and verify the constraint parameters are correct
156 SketchSolver_Constraint aConstraint(theConstraint);
157 int aConstrType = aConstraint.getType();
158 if (aConstrType == SLVS_C_UNKNOWN ||
159 (aConstrMapIter != myConstraintMap.end() && aConstrIter->type != aConstrType))
161 const std::vector<std::string>& aConstraintAttributes = aConstraint.getAttributes();
163 // Create constraint parameters
164 double aDistance = 0.0; // scalar value of the constraint
165 AttributeDoublePtr aDistAttr =
166 boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theConstraint->data()->attribute(CONSTRAINT_ATTR_VALUE));
169 aDistance = aDistAttr->value();
170 // SketchPlugin circle defined by its radius, but SolveSpace uses constraint for diameter
171 if (aConstrType == SLVS_C_DIAMETER)
173 if (aConstrMapIter != myConstraintMap.end() && fabs(aConstrIter->valA - aDistance) > tolerance)
175 myNeedToSolve = true;
176 aConstrIter->valA = aDistance;
180 Slvs_hEntity aConstrEnt[CONSTRAINT_ATTR_SIZE]; // parameters of the constraint
181 for (unsigned int indAttr = 0; indAttr < CONSTRAINT_ATTR_SIZE; indAttr++)
183 aConstrEnt[indAttr] = SLVS_E_UNKNOWN;
184 boost::shared_ptr<ModelAPI_AttributeRefAttr> aConstrAttr =
185 boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
186 theConstraint->data()->attribute(aConstraintAttributes[indAttr])
188 if (!aConstrAttr) continue;
190 // For the length constraint the start and end points of the line should be added to the entities list instead of line
191 if (aConstrType == SLVS_C_PT_PT_DISTANCE && theConstraint->getKind().compare(SKETCH_CONSTRAINT_LENGTH_KIND) == 0)
193 boost::shared_ptr<ModelAPI_Data> aData = aConstrAttr->feature()->data();
194 aConstrEnt[indAttr] = changeEntity(aData->attribute(LINE_ATTR_START));
195 aConstrEnt[indAttr+1] = changeEntity(aData->attribute(LINE_ATTR_END));
196 myEntityFeatMap[aConstrAttr->feature()] = 0; // measured object is added into the map of objects to avoid problems with interaction betwee constraint and group
197 break; // there should be no other entities
199 else if (aConstrAttr->isFeature())
200 aConstrEnt[indAttr] = changeEntity(aConstrAttr->feature());
202 aConstrEnt[indAttr] = changeEntity(aConstrAttr->attr());
205 if (aConstrMapIter == myConstraintMap.end())
207 // Several points may be coincident, it is not necessary to store all constraints between them.
208 // Try to find sequence of coincident points which connects the points of new constraint
209 if (aConstrType == SLVS_C_POINTS_COINCIDENT &&
210 !addCoincidentPoints(aConstrEnt[0], aConstrEnt[1]))
212 myExtraCoincidence.insert(theConstraint); // the constraint is stored for further purposes
216 // Create SolveSpace constraint structure
217 Slvs_Constraint aConstraint =
218 Slvs_MakeConstraint(++myConstrMaxID, myID, aConstrType, myWorkplane.h,
219 aDistance, aConstrEnt[0], aConstrEnt[1], aConstrEnt[2], aConstrEnt[3]);
220 myConstraints.push_back(aConstraint);
221 myConstraintMap[theConstraint] = aConstraint.h;
226 // ============================================================================
227 // Function: changeEntity
228 // Class: SketchSolver_ConstraintGroup
229 // Purpose: create/update the element affected by any constraint
230 // ============================================================================
231 Slvs_hEntity SketchSolver_ConstraintGroup::changeEntity(
232 boost::shared_ptr<ModelAPI_Attribute> theEntity)
234 // If the entity is already in the group, try to find it
235 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
236 aEntIter = myEntityAttrMap.find(theEntity);
237 std::vector<Slvs_Param>::const_iterator aParamIter; // looks at first parameter of already existent entity or at the end of vector otherwise
238 if (aEntIter == myEntityAttrMap.end()) // no such entity => should be created
239 aParamIter = myParams.end();
241 { // the entity already exists
242 int aEntPos = Search(aEntIter->second, myEntities);
243 int aParamPos = Search(myEntities[aEntPos].param[0], myParams);
244 aParamIter = myParams.begin() + aParamPos;
246 const bool isEntExists = (aEntIter != myEntityAttrMap.end()); // defines that the entity already exists
248 // Look over supported types of entities
251 boost::shared_ptr<GeomDataAPI_Point> aPoint =
252 boost::dynamic_pointer_cast<GeomDataAPI_Point>(theEntity);
255 Slvs_hParam aX = changeParameter(aPoint->x(), aParamIter);
256 Slvs_hParam aY = changeParameter(aPoint->y(), aParamIter);
257 Slvs_hParam aZ = changeParameter(aPoint->z(), aParamIter);
260 return aEntIter->second;
263 Slvs_Entity aPtEntity = Slvs_MakePoint3d(++myEntityMaxID, myID, aX, aY, aZ);
264 myEntities.push_back(aPtEntity);
265 myEntityAttrMap[theEntity] = aPtEntity.h;
269 // All entities except 3D points are created on workplane. So, if there is no workplane yet, then error
270 if (myWorkplane.h == SLVS_E_UNKNOWN)
271 return SLVS_E_UNKNOWN;
274 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
275 boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theEntity);
278 Slvs_hParam aU = changeParameter(aPoint2D->x(), aParamIter);
279 Slvs_hParam aV = changeParameter(aPoint2D->y(), aParamIter);
282 return aEntIter->second;
285 Slvs_Entity aPt2DEntity = Slvs_MakePoint2d(++myEntityMaxID, myID, myWorkplane.h, aU, aV);
286 myEntities.push_back(aPt2DEntity);
287 myEntityAttrMap[theEntity] = aPt2DEntity.h;
288 return aPt2DEntity.h;
291 // Scalar value (used for the distance entities)
292 AttributeDoublePtr aScalar =
293 boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theEntity);
296 Slvs_hParam aValue = changeParameter(aScalar->value(), aParamIter);
299 return aEntIter->second;
302 Slvs_Entity aDistance = Slvs_MakeDistance(++myEntityMaxID, myID, myWorkplane.h, aValue);
303 myEntities.push_back(aDistance);
304 myEntityAttrMap[theEntity] = aDistance.h;
308 /// \todo Other types of entities
310 // Unsupported or wrong entity type
311 return SLVS_E_UNKNOWN;
315 // ============================================================================
316 // Function: changeEntity
317 // Class: SketchSolver_ConstraintGroup
318 // Purpose: create/update the element defined by the feature affected by any constraint
319 // ============================================================================
320 Slvs_hEntity SketchSolver_ConstraintGroup::changeEntity(
321 FeaturePtr theEntity)
323 // If the entity is already in the group, try to find it
324 std::map<FeaturePtr, Slvs_hEntity>::const_iterator
325 aEntIter = myEntityFeatMap.find(theEntity);
326 // defines that the entity already exists
327 const bool isEntExists = (myEntityFeatMap.find(theEntity) != myEntityFeatMap.end());
329 // SketchPlugin features
330 boost::shared_ptr<SketchPlugin_Feature> aFeature =
331 boost::dynamic_pointer_cast<SketchPlugin_Feature>(theEntity);
333 { // Verify the feature by its kind
334 const std::string& aFeatureKind = aFeature->getKind();
337 if (aFeatureKind.compare(SKETCH_LINE_KIND) == 0)
339 Slvs_hEntity aStart = changeEntity(aFeature->data()->attribute(LINE_ATTR_START));
340 Slvs_hEntity aEnd = changeEntity(aFeature->data()->attribute(LINE_ATTR_END));
343 return aEntIter->second;
346 Slvs_Entity aLineEntity = Slvs_MakeLineSegment(++myEntityMaxID, myID, myWorkplane.h, aStart, aEnd);
347 myEntities.push_back(aLineEntity);
348 myEntityFeatMap[theEntity] = aLineEntity.h;
349 return aLineEntity.h;
352 else if (aFeatureKind.compare(SKETCH_CIRCLE_KIND) == 0)
354 Slvs_hEntity aCenter = changeEntity(aFeature->data()->attribute(CIRCLE_ATTR_CENTER));
355 Slvs_hEntity aRadius = changeEntity(aFeature->data()->attribute(CIRCLE_ATTR_RADIUS));
358 return aEntIter->second;
361 Slvs_Entity aCircleEntity =
362 Slvs_MakeCircle(++myEntityMaxID, myID, myWorkplane.h, aCenter, myWorkplane.normal, aRadius);
363 myEntities.push_back(aCircleEntity);
364 myEntityFeatMap[theEntity] = aCircleEntity.h;
365 return aCircleEntity.h;
368 else if (aFeatureKind.compare(SKETCH_ARC_KIND) == 0)
370 Slvs_hEntity aCenter = changeEntity(aFeature->data()->attribute(ARC_ATTR_CENTER));
371 Slvs_hEntity aStart = changeEntity(aFeature->data()->attribute(ARC_ATTR_START));
372 Slvs_hEntity aEnd = changeEntity(aFeature->data()->attribute(ARC_ATTR_END));
375 return aEntIter->second;
377 Slvs_Entity anArcEntity = Slvs_MakeArcOfCircle(++myEntityMaxID, myID,
378 myWorkplane.h, myWorkplane.normal, aCenter, aStart, aEnd);
379 myEntities.push_back(anArcEntity);
380 myEntityFeatMap[theEntity] = anArcEntity.h;
381 return anArcEntity.h;
383 // Point (it has low probability to be an attribute of constraint, so it is checked at the end)
384 else if (aFeatureKind.compare(SKETCH_POINT_KIND) == 0)
386 Slvs_hEntity aPoint = changeEntity(aFeature->data()->attribute(POINT_ATTR_COORD));
389 return aEntIter->second;
391 // Both the sketch point and its attribute (coordinates) link to the same SolveSpace point identifier
392 myEntityFeatMap[theEntity] = aPoint;
397 /// \todo Other types of features
399 // Unsupported or wrong entity type
400 return SLVS_E_UNKNOWN;
403 // ============================================================================
404 // Function: changeNormal
405 // Class: SketchSolver_ConstraintGroup
406 // Purpose: create/update the normal of workplane
407 // ============================================================================
408 Slvs_hEntity SketchSolver_ConstraintGroup::changeNormal(
409 boost::shared_ptr<ModelAPI_Attribute> theDirX,
410 boost::shared_ptr<ModelAPI_Attribute> theDirY,
411 boost::shared_ptr<ModelAPI_Attribute> theNorm)
413 boost::shared_ptr<GeomDataAPI_Dir> aDirX =
414 boost::dynamic_pointer_cast<GeomDataAPI_Dir>(theDirX);
415 boost::shared_ptr<GeomDataAPI_Dir> aDirY =
416 boost::dynamic_pointer_cast<GeomDataAPI_Dir>(theDirY);
417 if (!aDirX || !aDirY ||
418 (fabs(aDirX->x()) + fabs(aDirX->y()) + fabs(aDirX->z()) < tolerance) ||
419 (fabs(aDirY->x()) + fabs(aDirY->y()) + fabs(aDirY->z()) < tolerance))
420 return SLVS_E_UNKNOWN;
422 // quaternion parameters of normal vector
423 double qw, qx, qy, qz;
424 Slvs_MakeQuaternion(aDirX->x(), aDirX->y(), aDirX->z(),
425 aDirY->x(), aDirY->y(), aDirY->z(),
427 double aNormCoord[4] = {qw, qx, qy, qz};
429 // Try to find existent normal
430 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
431 aEntIter = myEntityAttrMap.find(theNorm);
432 std::vector<Slvs_Param>::const_iterator aParamIter; // looks to the first parameter of already existent entity or to the end of vector otherwise
433 if (aEntIter == myEntityAttrMap.end()) // no such entity => should be created
434 aParamIter = myParams.end();
436 { // the entity already exists, update it
437 int aEntPos = Search(aEntIter->second, myEntities);
438 int aParamPos = Search(myEntities[aEntPos].param[0], myParams);
439 aParamIter = myParams.begin() + aParamPos;
442 // Change parameters of the normal
443 Slvs_hParam aNormParams[4];
444 for (int i = 0; i < 4; i++)
445 aNormParams[i] = changeParameter(aNormCoord[i], aParamIter);
447 if (aEntIter != myEntityAttrMap.end()) // the entity already exists
448 return aEntIter->second;
451 Slvs_Entity aNormal = Slvs_MakeNormal3d(++myEntityMaxID, myID,
452 aNormParams[0], aNormParams[1], aNormParams[2], aNormParams[3]);
453 myEntities.push_back(aNormal);
454 myEntityAttrMap[theNorm] = aNormal.h;
459 // ============================================================================
460 // Function: addWorkplane
461 // Class: SketchSolver_ConstraintGroup
462 // Purpose: create workplane for the group
463 // ============================================================================
464 bool SketchSolver_ConstraintGroup::addWorkplane(
465 boost::shared_ptr<SketchPlugin_Feature> theSketch)
467 if (myWorkplane.h || theSketch->getKind().compare(SKETCH_KIND) != 0)
468 return false; // the workplane already exists or the function parameter is not Sketch
470 mySketch = theSketch;
475 // ============================================================================
476 // Function: updateWorkplane
477 // Class: SketchSolver_ConstraintGroup
478 // Purpose: update parameters of workplane
479 // ============================================================================
480 bool SketchSolver_ConstraintGroup::updateWorkplane()
482 // Get parameters of workplane
483 boost::shared_ptr<ModelAPI_Attribute> aDirX = mySketch->data()->attribute(SKETCH_ATTR_DIRX);
484 boost::shared_ptr<ModelAPI_Attribute> aDirY = mySketch->data()->attribute(SKETCH_ATTR_DIRY);
485 boost::shared_ptr<ModelAPI_Attribute> aNorm = mySketch->data()->attribute(SKETCH_ATTR_NORM);
486 boost::shared_ptr<ModelAPI_Attribute> anOrigin = mySketch->data()->attribute(SKETCH_ATTR_ORIGIN);
487 // Transform them into SolveSpace format
488 Slvs_hEntity aNormalWP = changeNormal(aDirX, aDirY, aNorm);
489 if (!aNormalWP) return false;
490 Slvs_hEntity anOriginWP = changeEntity(anOrigin);
491 if (!anOriginWP) return false;
496 myWorkplane = Slvs_MakeWorkplane(++myEntityMaxID, myID, anOriginWP, aNormalWP);
497 // Workplane should be added to the list of entities
498 myEntities.push_back(myWorkplane);
503 // ============================================================================
504 // Function: changeParameter
505 // Class: SketchSolver_ConstraintGroup
506 // Purpose: create/update value of parameter
507 // ============================================================================
508 Slvs_hParam SketchSolver_ConstraintGroup::changeParameter(
509 const double& theParam,
510 std::vector<Slvs_Param>::const_iterator& thePrmIter)
512 if (thePrmIter != myParams.end())
513 { // Parameter should be updated
514 int aParamPos = thePrmIter - myParams.begin();
515 if (fabs(thePrmIter->val - theParam) > tolerance)
517 myNeedToSolve = true; // parameter is changed, need to resolve constraints
518 myParams[aParamPos].val = theParam;
521 return myParams[aParamPos].h;
524 // Newly created parameter
525 Slvs_Param aParam = Slvs_MakeParam(++myParamMaxID, myID, theParam);
526 myParams.push_back(aParam);
527 myNeedToSolve = true;
528 // The list of parameters is changed, move iterator to the end of the list to avoid problems
529 thePrmIter = myParams.end();
533 // ============================================================================
534 // Function: resolveConstraints
535 // Class: SketchSolver_ConstraintGroup
536 // Purpose: solve the set of constraints for the current group
537 // ============================================================================
538 void SketchSolver_ConstraintGroup::resolveConstraints()
543 myConstrSolver.setGroupID(myID);
544 myConstrSolver.setParameters(myParams);
545 myConstrSolver.setEntities(myEntities);
546 myConstrSolver.setConstraints(myConstraints);
547 myConstrSolver.setDraggedParameters(myTempPointWhereDragged);
549 int aResult = myConstrSolver.solve();
550 if (aResult == SLVS_RESULT_OKAY)
551 { // solution succeeded, store results into correspondent attributes
552 // Obtain result into the same list of parameters
553 if (!myConstrSolver.getResult(myParams))
556 // We should go through the attributes map, because only attributes have valued parameters
557 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator
558 anEntIter = myEntityAttrMap.begin();
559 for ( ; anEntIter != myEntityAttrMap.end(); anEntIter++)
560 updateAttribute(anEntIter->first, anEntIter->second);
562 /// \todo Implement error handling
564 removeTemporaryConstraints();
565 myNeedToSolve = false;
568 // ============================================================================
569 // Function: mergeGroups
570 // Class: SketchSolver_ConstraintGroup
571 // Purpose: append specified group to the current group
572 // ============================================================================
573 void SketchSolver_ConstraintGroup::mergeGroups(
574 const SketchSolver_ConstraintGroup& theGroup)
576 // If specified group is empty, no need to merge
577 if (theGroup.myConstraintMap.empty())
580 // Map between old and new indexes of SolveSpace constraints
581 std::map<Slvs_hConstraint, Slvs_hConstraint> aConstrMap;
583 // Add all constraints from theGroup to the current group
584 std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::const_iterator
585 aConstrIter = theGroup.myConstraintMap.begin();
586 for ( ; aConstrIter != theGroup.myConstraintMap.end(); aConstrIter++)
587 if (changeConstraint(aConstrIter->first))
588 aConstrMap[aConstrIter->second] = myConstrMaxID; // the constraint was added => store its ID
590 // Add temporary constraints from theGroup
591 std::list<Slvs_hConstraint>::const_iterator aTempConstrIter = theGroup.myTempConstraints.begin();
592 for ( ; aTempConstrIter != theGroup.myTempConstraints.end(); aTempConstrIter++)
594 std::map<Slvs_hConstraint, Slvs_hConstraint>::iterator aFind = aConstrMap.find(*aTempConstrIter);
595 if (aFind != aConstrMap.end())
596 myTempConstraints.push_back(aFind->second);
599 if (myTempPointWhereDragged.empty())
600 myTempPointWhereDragged = theGroup.myTempPointWhereDragged;
601 else if (!theGroup.myTempPointWhereDragged.empty())
602 { // Need to create additional transient constraint
603 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
604 aFeatureIter = theGroup.myEntityAttrMap.begin();
605 for (; aFeatureIter != theGroup.myEntityAttrMap.end(); aFeatureIter++)
606 if (aFeatureIter->second == myTempPointWDrgdID)
608 addTemporaryConstraintWhereDragged(aFeatureIter->first);
613 myNeedToSolve = myNeedToSolve || theGroup.myNeedToSolve;
616 // ============================================================================
617 // Function: splitGroup
618 // Class: SketchSolver_ConstraintGroup
619 // Purpose: divide the group into several subgroups
620 // ============================================================================
621 void SketchSolver_ConstraintGroup::splitGroup(std::vector<SketchSolver_ConstraintGroup*>& theCuts)
623 // Divide constraints and entities into several groups
624 std::vector< std::set<Slvs_hEntity> > aGroupsEntities;
625 std::vector< std::set<Slvs_hConstraint> > aGroupsConstr;
626 int aMaxNbEntities = 0; // index of the group with maximal nuber of elements (this group will be left in the current)
627 std::vector<Slvs_Constraint>::const_iterator aConstrIter = myConstraints.begin();
628 for ( ; aConstrIter != myConstraints.end(); aConstrIter++)
630 Slvs_hEntity aConstrEnt[] = {
631 aConstrIter->ptA, aConstrIter->ptB,
632 aConstrIter->entityA, aConstrIter->entityB};
633 std::vector<int> anIndexes;
634 // Go through the groupped entities and find even one of entities of current constraint
635 std::vector< std::set<Slvs_hEntity> >::iterator aGrEntIter;
636 for (aGrEntIter = aGroupsEntities.begin(); aGrEntIter != aGroupsEntities.end(); aGrEntIter++)
638 bool isFound = false;
639 for (int i = 0; i < 4 && !isFound; i++)
640 if (aConstrEnt[i] != 0)
641 isFound = (aGrEntIter->find(aConstrEnt[i]) != aGrEntIter->end());
643 anIndexes.push_back(aGrEntIter - aGroupsEntities.begin());
645 // Add new group if no one is found
646 if (anIndexes.empty())
648 std::set<Slvs_hEntity> aNewGrEnt;
649 for (int i = 0; i < 4; i++)
650 if (aConstrEnt[i] != 0)
651 aNewGrEnt.insert(aConstrEnt[i]);
652 std::set<Slvs_hConstraint> aNewGrConstr;
653 aNewGrConstr.insert(aConstrIter->h);
655 aGroupsEntities.push_back(aNewGrEnt);
656 aGroupsConstr.push_back(aNewGrConstr);
657 if (aNewGrEnt.size() > aGroupsEntities[aMaxNbEntities].size())
658 aMaxNbEntities = aGroupsEntities.size() - 1;
660 else if (anIndexes.size() == 1)
661 { // Add entities indexes into the found group
662 aGrEntIter = aGroupsEntities.begin() + anIndexes.front();
663 for (int i = 0; i < 4; i++)
664 if (aConstrEnt[i] != 0)
665 aGrEntIter->insert(aConstrEnt[i]);
666 aGroupsConstr[anIndexes.front()].insert(aConstrIter->h);
667 if (aGrEntIter->size() > aGroupsEntities[aMaxNbEntities].size())
668 aMaxNbEntities = aGrEntIter - aGroupsEntities.begin();
671 { // There are found several connected groups, merge them
672 std::vector< std::set<Slvs_hEntity> >::iterator aFirstGroup =
673 aGroupsEntities.begin() + anIndexes.front();
674 std::vector< std::set<Slvs_hConstraint> >::iterator aFirstConstr =
675 aGroupsConstr.begin() + anIndexes.front();
676 std::vector<int>::iterator anInd = anIndexes.begin();
677 for (++anInd; anInd != anIndexes.end(); anInd++)
679 aFirstGroup->insert(aGroupsEntities[*anInd].begin(), aGroupsEntities[*anInd].end());
680 aFirstConstr->insert(aGroupsConstr[*anInd].begin(), aGroupsConstr[*anInd].end());
682 if (aFirstGroup->size() > aGroupsEntities[aMaxNbEntities].size())
683 aMaxNbEntities = anIndexes.front();
684 // Remove merged groups
685 for (anInd = anIndexes.end() - 1; anInd != anIndexes.begin(); anInd--)
687 aGroupsEntities.erase(aGroupsEntities.begin() + (*anInd));
688 aGroupsConstr.erase(aGroupsConstr.begin() + (*anInd));
693 if (aGroupsEntities.size() <= 1)
696 // Remove the group with maximum elements as it will be left in the current group
697 aGroupsEntities.erase(aGroupsEntities.begin() + aMaxNbEntities);
698 aGroupsConstr.erase(aGroupsConstr.begin() + aMaxNbEntities);
700 // Add new groups of constraints and divide current group
701 std::vector<SketchSolver_ConstraintGroup*> aNewGroups;
702 for (int i = aGroupsEntities.size(); i > 0; i--)
704 SketchSolver_ConstraintGroup* aG = new SketchSolver_ConstraintGroup(mySketch);
705 aNewGroups.push_back(aG);
707 std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::const_iterator
708 aConstrMapIter = myConstraintMap.begin();
709 int aConstrMapPos = 0; // position of iterator in the map (used to restore iterator after removing constraint)
710 while (aConstrMapIter != myConstraintMap.end())
712 std::vector< std::set<Slvs_hConstraint> >::const_iterator aGIter = aGroupsConstr.begin();
713 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroup = aNewGroups.begin();
714 for ( ; aGIter != aGroupsConstr.end(); aGIter++, aGroup++)
715 if (aGIter->find(aConstrMapIter->second) != aGIter->end())
717 (*aGroup)->changeConstraint(aConstrMapIter->first);
718 removeConstraint(aConstrMapIter->first);
720 aConstrMapIter = myConstraintMap.begin();
721 for (int i = 0; i < aConstrMapPos; i++)
725 if (aGIter == aGroupsConstr.end())
732 theCuts.insert(theCuts.end(), aNewGroups.begin(), aNewGroups.end());
735 // ============================================================================
736 // Function: updateGroup
737 // Class: SketchSolver_ConstraintGroup
738 // Purpose: search removed entities and constraints
739 // ============================================================================
740 bool SketchSolver_ConstraintGroup::updateGroup()
742 std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::reverse_iterator
743 aConstrIter = myConstraintMap.rbegin();
744 bool isAllValid = true;
745 bool isCCRemoved = false; // indicates that at least one of coincidence constraints was removed
746 while (isAllValid && aConstrIter != myConstraintMap.rend())
748 if (!aConstrIter->first->data()->isValid())
750 if (aConstrIter->first->getKind().compare(SKETCH_CONSTRAINT_COINCIDENCE_KIND) == 0)
752 std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::reverse_iterator
753 aCopyIter = aConstrIter++;
754 removeConstraint(aCopyIter->first);
760 // Probably, need to update coincidence constraints
761 if (isCCRemoved && !myExtraCoincidence.empty())
763 // Make a copy, because the new list of unused constrtaints will be generated
764 std::set< boost::shared_ptr<SketchPlugin_Constraint> > anExtraCopy = myExtraCoincidence;
765 myExtraCoincidence.clear();
767 std::set< boost::shared_ptr<SketchPlugin_Constraint> >::iterator
768 aCIter = anExtraCopy.begin();
769 for ( ; aCIter != anExtraCopy.end(); aCIter++)
770 if ((*aCIter)->data()->isValid())
771 changeConstraint(*aCIter);
777 // ============================================================================
778 // Function: updateAttribute
779 // Class: SketchSolver_ConstraintGroup
780 // Purpose: update features of sketch after resolving constraints
781 // ============================================================================
782 void SketchSolver_ConstraintGroup::updateAttribute(
783 boost::shared_ptr<ModelAPI_Attribute> theAttribute,
784 const Slvs_hEntity& theEntityID)
786 // Search the position of the first parameter of the entity
787 int anEntPos = Search(theEntityID, myEntities);
788 int aFirstParamPos = Search(myEntities[anEntPos].param[0], myParams);
790 // Look over supported types of entities
793 boost::shared_ptr<GeomDataAPI_Point> aPoint =
794 boost::dynamic_pointer_cast<GeomDataAPI_Point>(theAttribute);
797 aPoint->setValue(myParams[aFirstParamPos].val,
798 myParams[aFirstParamPos+1].val,
799 myParams[aFirstParamPos+2].val);
804 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
805 boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute);
808 aPoint2D->setValue(myParams[aFirstParamPos].val,
809 myParams[aFirstParamPos+1].val);
814 AttributeDoublePtr aScalar =
815 boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theAttribute);
818 aScalar->setValue(myParams[aFirstParamPos].val);
822 /// \todo Support other types of entities
825 // ============================================================================
826 // Function: updateEntityIfPossible
827 // Class: SketchSolver_ConstraintGroup
828 // Purpose: search the entity in this group and update it
829 // ============================================================================
830 void SketchSolver_ConstraintGroup::updateEntityIfPossible(
831 boost::shared_ptr<ModelAPI_Attribute> theEntity)
833 if (myEntityAttrMap.find(theEntity) != myEntityAttrMap.end())
835 // If the attribute is a point and it is changed (the group needs to rebuild),
836 // probably user has dragged this point into this position,
837 // so it is necessary to add constraint which will guarantee the point will not change
839 // Store myNeedToSolve flag to verify the entity is really changed
840 bool aNeedToSolveCopy = myNeedToSolve;
841 myNeedToSolve = false;
843 changeEntity(theEntity);
845 if (myNeedToSolve) // the entity is changed
847 // Verify the entity is a point and add temporary constraint of permanency
848 boost::shared_ptr<GeomDataAPI_Point> aPoint =
849 boost::dynamic_pointer_cast<GeomDataAPI_Point>(theEntity);
850 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
851 boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theEntity);
852 if (aPoint || aPoint2D)
853 addTemporaryConstraintWhereDragged(theEntity);
856 // Restore flag of changes
857 myNeedToSolve = myNeedToSolve || aNeedToSolveCopy;
861 // ============================================================================
862 // Function: addTemporaryConstraintWhereDragged
863 // Class: SketchSolver_ConstraintGroup
864 // Purpose: add transient constraint SLVS_C_WHERE_DRAGGED for the entity,
865 // which was moved by user
866 // ============================================================================
867 void SketchSolver_ConstraintGroup::addTemporaryConstraintWhereDragged(
868 boost::shared_ptr<ModelAPI_Attribute> theEntity)
870 // Find identifier of the entity
871 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
872 anEntIter = myEntityAttrMap.find(theEntity);
873 if (anEntIter == myEntityAttrMap.end())
876 // If this is a first dragged point, its parameters should be placed
877 // into Slvs_System::dragged field to avoid system inconsistense
878 if (myTempPointWhereDragged.empty())
880 int anEntPos = Search(anEntIter->second, myEntities);
881 Slvs_hParam* aDraggedParam = myEntities[anEntPos].param;
882 for (int i = 0; i < 4; i++, aDraggedParam++)
883 if (*aDraggedParam != 0)
884 myTempPointWhereDragged.push_back(*aDraggedParam);
885 myTempPointWDrgdID = myEntities[anEntPos].h;
889 // Get identifiers of all dragged points
890 std::set<Slvs_hEntity> aDraggedPntID;
891 aDraggedPntID.insert(myTempPointWDrgdID);
892 std::list<Slvs_hConstraint>::iterator aTmpCoIter = myTempConstraints.begin();
893 for ( ; aTmpCoIter != myTempConstraints.end(); aTmpCoIter++)
895 unsigned int aConstrPos = Search(*aTmpCoIter, myConstraints);
896 if (aConstrPos < myConstraints.size())
897 aDraggedPntID.insert(myConstraints[aConstrPos].ptA);
899 // Find whether there is a point coincident with theEntity, which already has SLVS_C_WHERE_DRAGGED
900 std::vector< std::set<Slvs_hEntity> >::iterator aCoPtIter = myCoincidentPoints.begin();
901 for ( ; aCoPtIter != myCoincidentPoints.end(); aCoPtIter++)
903 if (aCoPtIter->find(anEntIter->second) == aCoPtIter->end())
904 continue; // the entity was not found in current set
906 // Find one of already created SLVS_C_WHERE_DRAGGED constraints in current set of coincident points
907 std::set<Slvs_hEntity>::const_iterator aDrgIter = aDraggedPntID.begin();
908 for ( ; aDrgIter != aDraggedPntID.end(); aDrgIter++)
909 if (aCoPtIter->find(*aDrgIter) != aCoPtIter->end())
910 return ; // the SLVS_C_WHERE_DRAGGED constraint already exists
913 // Create additional SLVS_C_WHERE_DRAGGED constraint if myTempPointWhereDragged field is not empty
914 Slvs_Constraint aWDConstr = Slvs_MakeConstraint(++myConstrMaxID, myID, SLVS_C_WHERE_DRAGGED,
915 myWorkplane.h, 0.0, anEntIter->second, 0, 0, 0);
916 myConstraints.push_back(aWDConstr);
917 myTempConstraints.push_back(aWDConstr.h);
920 // ============================================================================
921 // Function: removeTemporaryConstraints
922 // Class: SketchSolver_ConstraintGroup
923 // Purpose: remove all transient SLVS_C_WHERE_DRAGGED constraints after
924 // resolving the set of constraints
925 // ============================================================================
926 void SketchSolver_ConstraintGroup::removeTemporaryConstraints()
928 std::list<Slvs_hConstraint>::reverse_iterator aTmpConstrIter;
929 for (aTmpConstrIter = myTempConstraints.rbegin(); aTmpConstrIter != myTempConstraints.rend(); aTmpConstrIter++)
931 unsigned int aConstrPos = Search(*aTmpConstrIter, myConstraints);
932 if (aConstrPos >= myConstraints.size())
934 myConstraints.erase(myConstraints.begin() + aConstrPos);
936 // If the removing constraint has higher index, decrease the indexer
937 if (*aTmpConstrIter == myConstrMaxID)
940 myTempConstraints.clear();
942 // Clear basic dragged point
943 myTempPointWhereDragged.clear();
946 // ============================================================================
947 // Function: removeConstraint
948 // Class: SketchSolver_ConstraintGroup
949 // Purpose: remove constraint and all unused entities
950 // ============================================================================
951 void SketchSolver_ConstraintGroup::removeConstraint(boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
953 std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::iterator
954 anIterToRemove = myConstraintMap.find(theConstraint);
955 if (anIterToRemove == myConstraintMap.end())
958 Slvs_hConstraint aCnstrToRemove = anIterToRemove->second;
959 // Remove constraint from the map
960 myConstraintMap.erase(anIterToRemove);
962 // Find unused entities
963 int aConstrPos = Search(aCnstrToRemove, myConstraints);
964 std::set<Slvs_hEntity> anEntToRemove;
965 Slvs_hEntity aCnstEnt[] = {myConstraints[aConstrPos].ptA, myConstraints[aConstrPos].ptB,
966 myConstraints[aConstrPos].entityA, myConstraints[aConstrPos].entityB};
967 for (int i = 0; i < 4; i++)
968 if (aCnstEnt[i] != 0)
969 anEntToRemove.insert(aCnstEnt[i]);
970 myConstraints.erase(myConstraints.begin() + aConstrPos);
971 if (aCnstrToRemove == myConstrMaxID)
973 std::vector<Slvs_Constraint>::const_iterator aConstrIter = myConstraints.begin();
974 for ( ; aConstrIter != myConstraints.end(); aConstrIter++)
976 Slvs_hEntity aEnts[] = {aConstrIter->ptA, aConstrIter->ptB,
977 aConstrIter->entityA, aConstrIter->entityB};
978 for (int i = 0; i < 4; i++)
979 if (aEnts[i] != 0 && anEntToRemove.find(aEnts[i]) != anEntToRemove.end())
980 anEntToRemove.erase(aEnts[i]);
983 if (anEntToRemove.empty())
986 // Remove unused entities
987 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator
988 anEntAttrIter = myEntityAttrMap.begin();
989 while (anEntAttrIter != myEntityAttrMap.end())
991 if (anEntToRemove.find(anEntAttrIter->second) != anEntToRemove.end())
993 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator
994 aRemovedIter = anEntAttrIter;
996 myEntityAttrMap.erase(aRemovedIter);
998 else anEntAttrIter++;
1000 std::map<FeaturePtr, Slvs_hEntity>::iterator
1001 anEntFeatIter = myEntityFeatMap.begin();
1002 while (anEntFeatIter != myEntityFeatMap.end())
1004 if (anEntToRemove.find(anEntFeatIter->second) != anEntToRemove.end())
1006 std::map<FeaturePtr, Slvs_hEntity>::iterator
1007 aRemovedIter = anEntFeatIter;
1009 myEntityFeatMap.erase(aRemovedIter);
1011 else anEntFeatIter++;
1013 std::set<Slvs_hEntity>::const_reverse_iterator aRemIter = anEntToRemove.rbegin();
1014 for ( ; aRemIter != anEntToRemove.rend(); aRemIter++)
1016 unsigned int anEntPos = Search(*aRemIter, myEntities);
1017 if (anEntPos >= myEntities.size())
1019 unsigned int aParamPos = Search(myEntities[anEntPos].param[0], myParams);
1020 if (aParamPos >= myParams.size())
1023 while (myEntities[anEntPos].param[aNbParams] != 0)
1025 if (myEntities[anEntPos].param[aNbParams-1] == myParamMaxID)
1026 myParamMaxID -= aNbParams;
1027 myParams.erase(myParams.begin() + aParamPos, myParams.begin() + aParamPos + aNbParams);
1028 if (*aRemIter == myEntityMaxID)
1030 myEntities.erase(myEntities.begin() + anEntPos);
1032 // Remove entity's ID from the lists of conincident points
1033 std::vector< std::set<Slvs_hEntity> >::iterator aCoPtIter = myCoincidentPoints.begin();
1034 for ( ; aCoPtIter != myCoincidentPoints.end(); aCoPtIter++)
1035 aCoPtIter->erase(*aRemIter);
1037 if (myCoincidentPoints.size() == 1 && myCoincidentPoints.front().empty())
1038 myCoincidentPoints.clear();
1042 // ============================================================================
1043 // Function: addCoincidentPoints
1044 // Class: SketchSolver_ConstraintGroup
1045 // Purpose: add coincident point the appropriate list of such points
1046 // ============================================================================
1047 bool SketchSolver_ConstraintGroup::addCoincidentPoints(
1048 const Slvs_hEntity& thePoint1, const Slvs_hEntity& thePoint2)
1050 std::vector< std::set<Slvs_hEntity> >::iterator aCoPtIter = myCoincidentPoints.begin();
1051 std::vector< std::set<Slvs_hEntity> >::iterator aFirstFound = myCoincidentPoints.end();
1052 while (aCoPtIter != myCoincidentPoints.end())
1054 bool isFound[2] = { // indicate which point ID was already in coincidence constraint
1055 aCoPtIter->find(thePoint1) != aCoPtIter->end(),
1056 aCoPtIter->find(thePoint2) != aCoPtIter->end(),
1058 if (isFound[0] && isFound[1]) // points are already connected by coincidence constraints => no need additional one
1060 if ((isFound[0] && !isFound[1]) || (!isFound[0] && isFound[1]))
1062 if (aFirstFound != myCoincidentPoints.end())
1063 { // there are two groups of coincident points connected by created constraint => merge them
1064 int aFirstFoundShift = aFirstFound - myCoincidentPoints.begin();
1065 int aCurrentShift = aCoPtIter - myCoincidentPoints.begin();
1066 aFirstFound->insert(aCoPtIter->begin(), aCoPtIter->end());
1067 myCoincidentPoints.erase(aCoPtIter);
1068 aFirstFound = myCoincidentPoints.begin() + aFirstFoundShift;
1069 aCoPtIter = myCoincidentPoints.begin() + aCurrentShift;
1074 aCoPtIter->insert(isFound[0] ? thePoint2 : thePoint1);
1075 aFirstFound = aCoPtIter;
1080 // No points were found, need to create new set
1081 if (aFirstFound == myCoincidentPoints.end())
1083 std::set<Slvs_hEntity> aNewSet;
1084 aNewSet.insert(thePoint1);
1085 aNewSet.insert(thePoint2);
1086 myCoincidentPoints.push_back(aNewSet);
1095 // ========================================================
1096 // ========= Auxiliary functions ===============
1097 // ========================================================
1099 template <typename T>
1100 int Search(const uint32_t& theEntityID, const std::vector<T>& theEntities)
1102 int aResIndex = theEntityID <= theEntities.size() ? theEntityID - 1 : 0;
1103 int aVecSize = theEntities.size();
1104 while (aResIndex >= 0 && theEntities[aResIndex].h > theEntityID)
1106 while (aResIndex < aVecSize && aResIndex >= 0 && theEntities[aResIndex].h < theEntityID)
1108 if (aResIndex == -1)
1109 aResIndex = aVecSize;