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>
19 #include <SketchPlugin_Arc.h>
20 #include <SketchPlugin_Circle.h>
21 #include <SketchPlugin_Line.h>
22 #include <SketchPlugin_Point.h>
23 #include <SketchPlugin_Sketch.h>
28 /// Tolerance for value of parameters
29 const double tolerance = 1.e-10;
31 /// This value is used to give unique index to the groups
32 static Slvs_hGroup myGroupIndexer = 0;
34 /** \brief Search the entity/parameter with specified ID in the list of elements
35 * \param[in] theEntityID unique ID of the element
36 * \param[in] theEntities list of elements
37 * \return position of the found element or -1 if the element is not found
40 static int Search(const uint32_t& theEntityID, const std::vector<T>& theEntities);
43 // ========================================================
44 // ========= SketchSolver_ConstraintGroup ===============
45 // ========================================================
47 SketchSolver_ConstraintGroup::
48 SketchSolver_ConstraintGroup(boost::shared_ptr<SketchPlugin_Feature> theWorkplane)
49 : myID(++myGroupIndexer),
59 myConstraints.clear();
61 myTempConstraints.clear();
62 myTempPointWhereDragged.clear();
63 myTempPointWDrgdID = 0;
65 // Initialize workplane
66 myWorkplane.h = SLVS_E_UNKNOWN;
68 assert(addWorkplane(theWorkplane));
70 addWorkplane(theWorkplane);
74 SketchSolver_ConstraintGroup::~SketchSolver_ConstraintGroup()
78 myConstraints.clear();
79 myConstraintMap.clear();
80 myTempConstraints.clear();
81 myTempPointWhereDragged.clear();
83 // If the group with maximal identifier is deleted, decrease the indexer
84 if (myID == myGroupIndexer)
88 // ============================================================================
89 // Function: isBaseWorkplane
90 // Class: SketchSolver_ConstraintGroup
91 // Purpose: verify the group is based on the given workplane
92 // ============================================================================
93 bool SketchSolver_ConstraintGroup::isBaseWorkplane(
94 boost::shared_ptr<SketchPlugin_Feature> theWorkplane) const
96 return theWorkplane == mySketch;
99 // ============================================================================
100 // Function: isInteract
101 // Class: SketchSolver_ConstraintGroup
102 // Purpose: verify are there any entities in the group used by given constraint
103 // ============================================================================
104 bool SketchSolver_ConstraintGroup::isInteract(
105 boost::shared_ptr<SketchPlugin_Constraint> theConstraint) const
107 // Check the group is empty
108 if (myWorkplane.h != SLVS_E_UNKNOWN && myConstraints.empty())
111 // Go through constraint entities and verify if some of them already in the group
112 for (int i = 0; i < CONSTRAINT_ATTR_SIZE; i++)
114 boost::shared_ptr<ModelAPI_AttributeRefAttr> aCAttrRef =
115 boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
116 theConstraint->data()->attribute(CONSTRAINT_ATTRIBUTES[i])
118 if (!aCAttrRef) continue;
119 if (myEntityMap.find(aCAttrRef->attr()) != myEntityMap.end())
123 // Entities did not found
127 // ============================================================================
128 // Function: changeConstraint
129 // Class: SketchSolver_ConstraintGroup
130 // Purpose: create/update the constraint in the group
131 // ============================================================================
132 bool SketchSolver_ConstraintGroup::changeConstraint(
133 boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
135 // There is no workplane yet, something wrong
136 if (myWorkplane.h == SLVS_E_UNKNOWN)
139 // Search this constraint in the current group to update it
140 std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::const_iterator
141 aConstrMapIter = myConstraintMap.find(theConstraint);
142 std::vector<Slvs_Constraint>::iterator aConstrIter;
143 if (aConstrMapIter != myConstraintMap.end())
145 int aConstrPos = Search(aConstrMapIter->second, myConstraints);
146 aConstrIter = myConstraints.begin() + aConstrPos;
149 // Get constraint type and verify the constraint parameters are correct
150 SketchSolver_Constraint aConstraint(theConstraint);
151 int aConstrType = aConstraint.getType();
152 if (aConstrType == SLVS_C_UNKNOWN ||
153 (aConstrMapIter != myConstraintMap.end() && aConstrIter->type != aConstrType))
155 const std::vector<std::string>& aConstraintAttributes = aConstraint.getAttributes();
157 // Create constraint parameters
158 double aDistance = 0.0; // scalar value of the constraint
159 boost::shared_ptr<ModelAPI_AttributeDouble> aDistAttr =
160 boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theConstraint->data()->attribute(CONSTRAINT_ATTR_VALUE));
163 aDistance = aDistAttr->value();
164 if (aConstrMapIter != myConstraintMap.end() && fabs(aConstrIter->valA - aDistance) > tolerance)
166 myNeedToSolve = true;
167 aConstrIter->valA = aDistance;
171 Slvs_hEntity aConstrEnt[CONSTRAINT_ATTR_SIZE]; // parameters of the constraint
172 for (unsigned int indAttr = 0; indAttr < CONSTRAINT_ATTR_SIZE; indAttr++)
174 aConstrEnt[indAttr] = SLVS_E_UNKNOWN;
175 boost::shared_ptr<ModelAPI_AttributeRefAttr> aConstrAttr =
176 boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
177 theConstraint->data()->attribute(aConstraintAttributes[indAttr])
179 if (!aConstrAttr) continue;
180 aConstrEnt[indAttr] = changeEntity(aConstrAttr->attr());
183 if (aConstrMapIter == myConstraintMap.end())
185 // Several points may be coincident, it is not necessary to store all constraints between them.
186 // Try to find sequence of coincident points which connects the points of new constraint
187 if (aConstrType == SLVS_C_POINTS_COINCIDENT)
189 std::vector< std::set<Slvs_hEntity> >::iterator aCoPtIter = myCoincidentPoints.begin();
190 std::vector< std::set<Slvs_hEntity> >::iterator aFirstFound = myCoincidentPoints.end();
191 for ( ; aCoPtIter != myCoincidentPoints.end(); aCoPtIter++)
193 bool isFound[2] = { // indicate which point ID was already in coincidence constraint
194 aCoPtIter->find(aConstrEnt[0]) != aCoPtIter->end(),
195 aCoPtIter->find(aConstrEnt[1]) != aCoPtIter->end(),
197 if (isFound[0] && isFound[1]) // points are already connected by coincidence constraints => no need to additional one
199 if ((isFound[0] && !isFound[1]) || (!isFound[0] && isFound[1]))
201 if (aFirstFound != myCoincidentPoints.end())
202 { // there are two groups of coincident points connected by created constraint => merge them
203 int aFirstFoundShift = aFirstFound - myCoincidentPoints.begin();
204 int aCurrentShift = aCoPtIter - myCoincidentPoints.begin();
205 aFirstFound->insert(aCoPtIter->begin(), aCoPtIter->end());
206 myCoincidentPoints.erase(aCoPtIter);
207 aFirstFound = myCoincidentPoints.begin() + aFirstFoundShift;
208 aCoPtIter = myCoincidentPoints.begin() + aCurrentShift;
212 aCoPtIter->insert(aConstrEnt[isFound[0] ? 1 : 0]);
213 aFirstFound = aCoPtIter;
217 // No points were found, need to create new set
218 if (aFirstFound == myCoincidentPoints.end())
220 std::set<Slvs_hEntity> aNewSet;
221 aNewSet.insert(aConstrEnt[0]);
222 aNewSet.insert(aConstrEnt[1]);
223 myCoincidentPoints.push_back(aNewSet);
227 // Create SolveSpace constraint structure
228 Slvs_Constraint aConstraint =
229 Slvs_MakeConstraint(++myConstrMaxID, myID, aConstrType, myWorkplane.h,
230 aDistance, aConstrEnt[0], aConstrEnt[1], aConstrEnt[2], aConstrEnt[3]);
231 myConstraints.push_back(aConstraint);
232 myConstraintMap[theConstraint] = aConstraint.h;
237 // ============================================================================
238 // Function: changeEntity
239 // Class: SketchSolver_ConstraintGroup
240 // Purpose: create/update the element affected by any constraint
241 // ============================================================================
242 Slvs_hEntity SketchSolver_ConstraintGroup::changeEntity(
243 boost::shared_ptr<ModelAPI_Attribute> theEntity)
245 // If the entity is already in the group, try to find it
246 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
247 aEntIter = myEntityMap.find(theEntity);
248 std::vector<Slvs_Param>::const_iterator aParamIter; // looks at first parameter of already existent entity or at the end of vector otherwise
249 if (aEntIter == myEntityMap.end()) // no such entity => should be created
250 aParamIter = myParams.end();
252 { // the entity already exists
253 int aEntPos = Search(aEntIter->second, myEntities);
254 int aParamPos = Search(myEntities[aEntPos].param[0], myParams);
255 aParamIter = myParams.begin() + aParamPos;
258 // Look over supported types of entities
261 boost::shared_ptr<GeomDataAPI_Point> aPoint =
262 boost::dynamic_pointer_cast<GeomDataAPI_Point>(theEntity);
265 Slvs_hParam aX = changeParameter(aPoint->x(), aParamIter);
266 Slvs_hParam aY = changeParameter(aPoint->y(), aParamIter);
267 Slvs_hParam aZ = changeParameter(aPoint->z(), aParamIter);
269 if (aEntIter != myEntityMap.end()) // the entity already exists
270 return aEntIter->second;
273 Slvs_Entity aPtEntity = Slvs_MakePoint3d(++myEntityMaxID, myID, aX, aY, aZ);
274 myEntities.push_back(aPtEntity);
275 myEntityMap[theEntity] = aPtEntity.h;
279 // All entities except 3D points are created on workplane. So, if there is no workplane yet, then error
280 if (myWorkplane.h == SLVS_E_UNKNOWN)
281 return SLVS_E_UNKNOWN;
284 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
285 boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theEntity);
288 Slvs_hParam aU = changeParameter(aPoint2D->x(), aParamIter);
289 Slvs_hParam aV = changeParameter(aPoint2D->y(), aParamIter);
291 if (aEntIter != myEntityMap.end()) // the entity already exists
292 return aEntIter->second;
295 Slvs_Entity aPt2DEntity = Slvs_MakePoint2d(++myEntityMaxID, myID, myWorkplane.h, aU, aV);
296 myEntities.push_back(aPt2DEntity);
297 myEntityMap[theEntity] = aPt2DEntity.h;
298 return aPt2DEntity.h;
301 // Scalar value (used for the distance entities)
302 boost::shared_ptr<ModelAPI_AttributeDouble> aScalar =
303 boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theEntity);
306 Slvs_hParam aValue = changeParameter(aScalar->value(), aParamIter);
308 if (aEntIter != myEntityMap.end()) // the entity already exists
309 return aEntIter->second;
312 Slvs_Entity aDistance = Slvs_MakeDistance(++myEntityMaxID, myID, myWorkplane.h, aValue);
313 myEntities.push_back(aDistance);
314 myEntityMap[theEntity] = aDistance.h;
318 // SketchPlugin features
319 boost::shared_ptr<SketchPlugin_Feature> aFeature =
320 boost::dynamic_pointer_cast<SketchPlugin_Feature>(theEntity);
322 { // Verify the feature by its kind
323 const std::string& aFeatureKind = aFeature->getKind();
326 if (aFeatureKind.compare("SketchLine") == 0)
328 Slvs_hEntity aStart = changeEntity(aFeature->data()->attribute(LINE_ATTR_START));
329 Slvs_hEntity aEnd = changeEntity(aFeature->data()->attribute(LINE_ATTR_END));
331 if (aEntIter != myEntityMap.end()) // the entity already exists
332 return aEntIter->second;
335 Slvs_Entity aLineEntity = Slvs_MakeLineSegment(++myEntityMaxID, myID, myWorkplane.h, aStart, aEnd);
336 myEntities.push_back(aLineEntity);
337 myEntityMap[theEntity] = aLineEntity.h;
338 return aLineEntity.h;
341 else if (aFeatureKind.compare("SketchCircle") == 0)
343 Slvs_hEntity aCenter = changeEntity(aFeature->data()->attribute(CIRCLE_ATTR_CENTER));
344 Slvs_hEntity aRadius = changeEntity(aFeature->data()->attribute(CIRCLE_ATTR_RADIUS));
346 if (aEntIter != myEntityMap.end()) // the entity already exists
347 return aEntIter->second;
350 Slvs_Entity aCircleEntity =
351 Slvs_MakeCircle(++myEntityMaxID, myID, myWorkplane.h, aCenter, myWorkplane.normal, aRadius);
352 myEntities.push_back(aCircleEntity);
353 myEntityMap[theEntity] = aCircleEntity.h;
354 return aCircleEntity.h;
357 else if (aFeatureKind.compare("SketchArc") == 0)
359 Slvs_hEntity aCenter = changeEntity(aFeature->data()->attribute(ARC_ATTR_CENTER));
360 Slvs_hEntity aStart = changeEntity(aFeature->data()->attribute(ARC_ATTR_START));
361 Slvs_hEntity aEnd = changeEntity(aFeature->data()->attribute(ARC_ATTR_END));
363 if (aEntIter != myEntityMap.end()) // the entity already exists
364 return aEntIter->second;
366 Slvs_Entity anArcEntity = Slvs_MakeArcOfCircle(++myEntityMaxID, myID,
367 myWorkplane.h, myWorkplane.normal, aCenter, aStart, aEnd);
368 myEntities.push_back(anArcEntity);
369 myEntityMap[theEntity] = anArcEntity.h;
370 return anArcEntity.h;
372 // Point (it has low probability to be an attribute of constraint, so it is checked at the end)
373 else if (aFeatureKind.compare("SketchPoint") == 0)
375 Slvs_hEntity aPoint = changeEntity(aFeature->data()->attribute(POINT_ATTR_COORD));
377 if (aEntIter != myEntityMap.end()) // the entity already exists
378 return aEntIter->second;
380 // Both the sketch point and its attribute (coordinates) link to the same SolveSpace point identifier
381 myEntityMap[theEntity] = aPoint;
386 /// \todo Other types of entities
388 // Unsupported or wrong entity type
389 return SLVS_E_UNKNOWN;
392 // ============================================================================
393 // Function: changeNormal
394 // Class: SketchSolver_ConstraintGroup
395 // Purpose: create/update the normal of workplane
396 // ============================================================================
397 Slvs_hEntity SketchSolver_ConstraintGroup::changeNormal(
398 boost::shared_ptr<ModelAPI_Attribute> theDirX,
399 boost::shared_ptr<ModelAPI_Attribute> theDirY,
400 boost::shared_ptr<ModelAPI_Attribute> theNorm)
402 boost::shared_ptr<GeomDataAPI_Dir> aDirX =
403 boost::dynamic_pointer_cast<GeomDataAPI_Dir>(theDirX);
404 boost::shared_ptr<GeomDataAPI_Dir> aDirY =
405 boost::dynamic_pointer_cast<GeomDataAPI_Dir>(theDirY);
406 if (!aDirX || !aDirY ||
407 (fabs(aDirX->x()) + fabs(aDirX->y()) + fabs(aDirX->z()) < tolerance) ||
408 (fabs(aDirY->x()) + fabs(aDirY->y()) + fabs(aDirY->z()) < tolerance))
409 return SLVS_E_UNKNOWN;
411 // quaternion parameters of normal vector
412 double qw, qx, qy, qz;
413 Slvs_MakeQuaternion(aDirX->x(), aDirX->y(), aDirX->z(),
414 aDirY->x(), aDirY->y(), aDirY->z(),
416 double aNormCoord[4] = {qw, qx, qy, qz};
418 // Try to find existent normal
419 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
420 aEntIter = myEntityMap.find(theNorm);
421 std::vector<Slvs_Param>::const_iterator aParamIter; // looks to the first parameter of already existent entity or to the end of vector otherwise
422 if (aEntIter == myEntityMap.end()) // no such entity => should be created
423 aParamIter = myParams.end();
425 { // the entity already exists, update it
426 int aEntPos = Search(aEntIter->second, myEntities);
427 int aParamPos = Search(myEntities[aEntPos].param[0], myParams);
428 aParamIter = myParams.begin() + aParamPos;
431 // Change parameters of the normal
432 Slvs_hParam aNormParams[4];
433 for (int i = 0; i < 4; i++)
434 aNormParams[i] = changeParameter(aNormCoord[i], aParamIter);
436 if (aEntIter != myEntityMap.end()) // the entity already exists
437 return aEntIter->second;
440 Slvs_Entity aNormal = Slvs_MakeNormal3d(++myEntityMaxID, myID,
441 aNormParams[0], aNormParams[1], aNormParams[2], aNormParams[3]);
442 myEntities.push_back(aNormal);
443 myEntityMap[theNorm] = aNormal.h;
448 // ============================================================================
449 // Function: addWorkplane
450 // Class: SketchSolver_ConstraintGroup
451 // Purpose: create workplane for the group
452 // ============================================================================
453 bool SketchSolver_ConstraintGroup::addWorkplane(
454 boost::shared_ptr<SketchPlugin_Feature> theSketch)
456 if (myWorkplane.h || theSketch->getKind().compare("Sketch") != 0)
457 return false; // the workplane already exists or the function parameter is not Sketch
459 mySketch = theSketch;
464 // ============================================================================
465 // Function: updateWorkplane
466 // Class: SketchSolver_ConstraintGroup
467 // Purpose: update parameters of workplane
468 // ============================================================================
469 bool SketchSolver_ConstraintGroup::updateWorkplane()
471 // Get parameters of workplane
472 boost::shared_ptr<ModelAPI_Attribute> aDirX = mySketch->data()->attribute(SKETCH_ATTR_DIRX);
473 boost::shared_ptr<ModelAPI_Attribute> aDirY = mySketch->data()->attribute(SKETCH_ATTR_DIRY);
474 boost::shared_ptr<ModelAPI_Attribute> aNorm = mySketch->data()->attribute(SKETCH_ATTR_NORM);
475 boost::shared_ptr<ModelAPI_Attribute> anOrigin = mySketch->data()->attribute(SKETCH_ATTR_ORIGIN);
476 // Transform them into SolveSpace format
477 Slvs_hEntity aNormalWP = changeNormal(aDirX, aDirY, aNorm);
478 if (!aNormalWP) return false;
479 Slvs_hEntity anOriginWP = changeEntity(anOrigin);
480 if (!anOriginWP) return false;
485 myWorkplane = Slvs_MakeWorkplane(++myEntityMaxID, myID, anOriginWP, aNormalWP);
486 // Workplane should be added to the list of entities
487 myEntities.push_back(myWorkplane);
492 // ============================================================================
493 // Function: changeParameter
494 // Class: SketchSolver_ConstraintGroup
495 // Purpose: create/update value of parameter
496 // ============================================================================
497 Slvs_hParam SketchSolver_ConstraintGroup::changeParameter(
498 const double& theParam,
499 std::vector<Slvs_Param>::const_iterator& thePrmIter)
501 if (thePrmIter != myParams.end())
502 { // Parameter should be updated
503 int aParamPos = thePrmIter - myParams.begin();
504 if (fabs(thePrmIter->val - theParam) > tolerance)
506 myNeedToSolve = true; // parameter is changed, need to resolve constraints
507 myParams[aParamPos].val = theParam;
510 return myParams[aParamPos].h;
513 // Newly created parameter
514 Slvs_Param aParam = Slvs_MakeParam(++myParamMaxID, myID, theParam);
515 myParams.push_back(aParam);
516 myNeedToSolve = true;
517 // The list of parameters is changed, move iterator to the end of the list to avoid problems
518 thePrmIter = myParams.end();
522 // ============================================================================
523 // Function: resolveConstraints
524 // Class: SketchSolver_ConstraintGroup
525 // Purpose: solve the set of constraints for the current group
526 // ============================================================================
527 void SketchSolver_ConstraintGroup::resolveConstraints()
532 myConstrSolver.setGroupID(myID);
533 myConstrSolver.setParameters(myParams);
534 myConstrSolver.setEntities(myEntities);
535 myConstrSolver.setConstraints(myConstraints);
536 myConstrSolver.setDraggedParameters(myTempPointWhereDragged);
538 int aResult = myConstrSolver.solve();
539 if (aResult == SLVS_RESULT_OKAY)
540 { // solution succeeded, store results into correspondent attributes
541 // Obtain result into the same list of parameters
542 if (!myConstrSolver.getResult(myParams))
545 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator
546 anEntIter = myEntityMap.begin();
547 for ( ; anEntIter != myEntityMap.end(); anEntIter++)
548 updateAttribute(anEntIter->first, anEntIter->second);
550 /// \todo Implement error handling
552 removeTemporaryConstraints();
553 myNeedToSolve = false;
556 // ============================================================================
557 // Function: mergeGroups
558 // Class: SketchSolver_ConstraintGroup
559 // Purpose: append specified group to the current group
560 // ============================================================================
561 void SketchSolver_ConstraintGroup::mergeGroups(
562 const SketchSolver_ConstraintGroup& theGroup)
564 // If specified group is empty, no need to merge
565 if (theGroup.myConstraintMap.empty())
568 // Map between old and new indexes of SolveSpace constraints
569 std::map<Slvs_hConstraint, Slvs_hConstraint> aConstrMap;
571 // Add all constraints from theGroup to the current group
572 std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::const_iterator
573 aConstrIter = theGroup.myConstraintMap.begin();
574 for ( ; aConstrIter != theGroup.myConstraintMap.end(); aConstrIter++)
575 if (changeConstraint(aConstrIter->first))
576 aConstrMap[aConstrIter->second] = myConstrMaxID; // the constraint was added => store its ID
578 // Add temporary constraints from theGroup
579 std::list<Slvs_hConstraint>::const_iterator aTempConstrIter = theGroup.myTempConstraints.begin();
580 for ( ; aTempConstrIter != theGroup.myTempConstraints.end(); aTempConstrIter++)
582 std::map<Slvs_hConstraint, Slvs_hConstraint>::iterator aFind = aConstrMap.find(*aTempConstrIter);
583 if (aFind != aConstrMap.end())
584 myTempConstraints.push_back(aFind->second);
587 if (myTempPointWhereDragged.empty())
588 myTempPointWhereDragged = theGroup.myTempPointWhereDragged;
589 else if (!theGroup.myTempPointWhereDragged.empty())
590 { // Need to create additional transient constraint
591 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
592 aFeatureIter = theGroup.myEntityMap.begin();
593 for (; aFeatureIter != theGroup.myEntityMap.end(); aFeatureIter++)
594 if (aFeatureIter->second == myTempPointWDrgdID)
596 addTemporaryConstraintWhereDragged(aFeatureIter->first);
601 myNeedToSolve = myNeedToSolve || theGroup.myNeedToSolve;
604 // ============================================================================
605 // Function: splitGroup
606 // Class: SketchSolver_ConstraintGroup
607 // Purpose: divide the group into several subgroups
608 // ============================================================================
609 void SketchSolver_ConstraintGroup::splitGroup(std::vector<SketchSolver_ConstraintGroup*>& theCuts)
611 // Divide constraints and entities into several groups
612 std::vector< std::set<Slvs_hEntity> > aGroupsEntities;
613 std::vector< std::set<Slvs_hConstraint> > aGroupsConstr;
614 int aMaxNbEntities = 0; // index of the group with maximal nuber of elements (this group will be left in the current)
615 std::vector<Slvs_Constraint>::const_iterator aConstrIter = myConstraints.begin();
616 for ( ; aConstrIter != myConstraints.end(); aConstrIter++)
618 Slvs_hEntity aConstrEnt[] = {
619 aConstrIter->ptA, aConstrIter->ptB,
620 aConstrIter->entityA, aConstrIter->entityB};
621 // Go through the groupped entities and find even one of entities of current constraint
622 std::vector< std::set<Slvs_hEntity> >::iterator aGrEntIter;
623 for (aGrEntIter = aGroupsEntities.begin(); aGrEntIter != aGroupsEntities.end(); aGrEntIter++)
625 bool isFound = false;
626 for (int i = 0; i < 4 && !isFound; i++)
627 if (aConstrEnt[i] != 0)
628 isFound = (aGrEntIter->find(aConstrEnt[i]) != aGrEntIter->end());
631 for (int i = 0; i < 4; i++)
632 if (aConstrEnt[i] != 0)
633 aGrEntIter->insert(aConstrEnt[i]);
634 aGroupsConstr[aGrEntIter - aGroupsEntities.begin()].insert(aConstrIter->h);
635 if (aGrEntIter->size() > aGroupsEntities[aMaxNbEntities].size())
636 aMaxNbEntities = aGrEntIter - aGroupsEntities.begin();
640 // Add new group is no one is found
641 if (aGrEntIter == aGroupsEntities.end())
643 std::set<Slvs_hEntity> aNewGrEnt;
644 for (int i = 0; i < 4; i++)
645 if (aConstrEnt[i] != 0)
646 aNewGrEnt.insert(aConstrEnt[i]);
647 std::set<Slvs_hConstraint> aNewGrConstr;
648 aNewGrConstr.insert(aConstrIter->h);
650 aGroupsEntities.push_back(aNewGrEnt);
651 aGroupsConstr.push_back(aNewGrConstr);
652 if (aNewGrEnt.size() > aGroupsEntities[aMaxNbEntities].size())
653 aMaxNbEntities = aGroupsEntities.size() - 1;
657 if (aGroupsEntities.size() <= 1)
660 // Remove the group with maximum elements as it will be left in the current group
661 aGroupsEntities.erase(aGroupsEntities.begin() + aMaxNbEntities);
662 aGroupsConstr.erase(aGroupsConstr.begin() + aMaxNbEntities);
664 // Add new groups of constraints and divide current group
665 std::vector<SketchSolver_ConstraintGroup*> aNewGroups;
666 for (int i = aGroupsEntities.size(); i > 0; i--)
668 SketchSolver_ConstraintGroup* aG = new SketchSolver_ConstraintGroup(mySketch);
669 aNewGroups.push_back(aG);
671 std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::const_iterator
672 aConstrMapIter = myConstraintMap.begin();
673 int aConstrMapPos = 0; // position of iterator in the map (used to restore iterator after removing constraint)
674 while (aConstrMapIter != myConstraintMap.end())
676 std::vector< std::set<Slvs_hConstraint> >::const_iterator aGIter = aGroupsConstr.begin();
677 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroup = aNewGroups.begin();
678 for ( ; aGIter != aGroupsConstr.end(); aGIter++, aGroup++)
679 if (aGIter->find(aConstrMapIter->second) != aGIter->end())
681 (*aGroup)->changeConstraint(aConstrMapIter->first);
682 removeConstraint(aConstrMapIter->first);
684 aConstrMapIter = myConstraintMap.begin();
685 for (int i = 0; i < aConstrMapPos; i++)
689 if (aGIter == aGroupsConstr.end())
696 theCuts.insert(theCuts.end(), aNewGroups.begin(), aNewGroups.end());
699 // ============================================================================
700 // Function: updateGroup
701 // Class: SketchSolver_ConstraintGroup
702 // Purpose: search removed entities and constraints
703 // ============================================================================
704 bool SketchSolver_ConstraintGroup::updateGroup()
706 std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::reverse_iterator
707 aConstrIter = myConstraintMap.rbegin();
708 bool isAllValid = true;
709 for ( ; isAllValid && aConstrIter != myConstraintMap.rend(); aConstrIter++)
710 if (!aConstrIter->first->data()->isValid())
712 removeConstraint(aConstrIter->first);
718 // ============================================================================
719 // Function: updateAttribute
720 // Class: SketchSolver_ConstraintGroup
721 // Purpose: update features of sketch after resolving constraints
722 // ============================================================================
723 void SketchSolver_ConstraintGroup::updateAttribute(
724 boost::shared_ptr<ModelAPI_Attribute> theAttribute,
725 const Slvs_hEntity& theEntityID)
727 // Search the position of the first parameter of the entity
728 int anEntPos = Search(theEntityID, myEntities);
729 int aFirstParamPos = Search(myEntities[anEntPos].param[0], myParams);
731 // Look over supported types of entities
734 boost::shared_ptr<GeomDataAPI_Point> aPoint =
735 boost::dynamic_pointer_cast<GeomDataAPI_Point>(theAttribute);
738 aPoint->setValue(myParams[aFirstParamPos].val,
739 myParams[aFirstParamPos+1].val,
740 myParams[aFirstParamPos+2].val);
745 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
746 boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute);
749 aPoint2D->setValue(myParams[aFirstParamPos].val,
750 myParams[aFirstParamPos+1].val);
755 boost::shared_ptr<ModelAPI_AttributeDouble> aScalar =
756 boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theAttribute);
759 aScalar->setValue(myParams[aFirstParamPos].val);
763 /// \todo Support other types of entities
766 // ============================================================================
767 // Function: updateEntityIfPossible
768 // Class: SketchSolver_ConstraintGroup
769 // Purpose: search the entity in this group and update it
770 // ============================================================================
771 void SketchSolver_ConstraintGroup::updateEntityIfPossible(
772 boost::shared_ptr<ModelAPI_Attribute> theEntity)
774 if (myEntityMap.find(theEntity) != myEntityMap.end())
776 // If the attribute is a point and it is changed (the group needs to rebuild),
777 // probably user has dragged this point into this position,
778 // so it is necessary to add constraint which will guarantee the point will not change
780 // Store myNeedToSolve flag to verify the entity is really changed
781 bool aNeedToSolveCopy = myNeedToSolve;
782 myNeedToSolve = false;
784 changeEntity(theEntity);
786 if (myNeedToSolve) // the entity is changed
788 // Verify the entity is a point and add temporary constraint of permanency
789 boost::shared_ptr<GeomDataAPI_Point> aPoint =
790 boost::dynamic_pointer_cast<GeomDataAPI_Point>(theEntity);
791 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
792 boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theEntity);
793 if (aPoint || aPoint2D)
794 addTemporaryConstraintWhereDragged(theEntity);
797 // Restore flag of changes
798 myNeedToSolve = myNeedToSolve || aNeedToSolveCopy;
802 // ============================================================================
803 // Function: addTemporaryConstraintWhereDragged
804 // Class: SketchSolver_ConstraintGroup
805 // Purpose: add transient constraint SLVS_C_WHERE_DRAGGED for the entity,
806 // which was moved by user
807 // ============================================================================
808 void SketchSolver_ConstraintGroup::addTemporaryConstraintWhereDragged(
809 boost::shared_ptr<ModelAPI_Attribute> theEntity)
811 // Find identifier of the entity
812 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
813 anEntIter = myEntityMap.find(theEntity);
814 if (anEntIter == myEntityMap.end())
817 // If this is a first dragged point, its parameters should be placed
818 // into Slvs_System::dragged field to avoid system inconsistense
819 if (myTempPointWhereDragged.empty())
821 int anEntPos = Search(anEntIter->second, myEntities);
822 Slvs_hParam* aDraggedParam = myEntities[anEntPos].param;
823 for (int i = 0; i < 4; i++, aDraggedParam++)
824 if (*aDraggedParam != 0)
825 myTempPointWhereDragged.push_back(*aDraggedParam);
826 myTempPointWDrgdID = myEntities[anEntPos].h;
830 // Get identifiers of all dragged points
831 std::set<Slvs_hEntity> aDraggedPntID;
832 aDraggedPntID.insert(myTempPointWDrgdID);
833 std::list<Slvs_hConstraint>::iterator aTmpCoIter = myTempConstraints.begin();
834 for ( ; aTmpCoIter != myTempConstraints.end(); aTmpCoIter++)
836 unsigned int aConstrPos = Search(*aTmpCoIter, myConstraints);
837 if (aConstrPos < myConstraints.size())
838 aDraggedPntID.insert(myConstraints[aConstrPos].ptA);
840 // Find whether there is a point coincident with theEntity, which already has SLVS_C_WHERE_DRAGGED
841 std::vector< std::set<Slvs_hEntity> >::iterator aCoPtIter = myCoincidentPoints.begin();
842 for ( ; aCoPtIter != myCoincidentPoints.end(); aCoPtIter++)
844 if (aCoPtIter->find(anEntIter->second) == aCoPtIter->end())
845 continue; // the entity was not found in current set
847 // Find one of already created SLVS_C_WHERE_DRAGGED constraints in current set of coincident points
848 std::set<Slvs_hEntity>::const_iterator aDrgIter = aDraggedPntID.begin();
849 for ( ; aDrgIter != aDraggedPntID.end(); aDrgIter++)
850 if (aCoPtIter->find(*aDrgIter) != aCoPtIter->end())
851 return ; // the SLVS_C_WHERE_DRAGGED constraint already exists
854 // Create additional SLVS_C_WHERE_DRAGGED constraint if myTempPointWhereDragged field is not empty
855 Slvs_Constraint aWDConstr = Slvs_MakeConstraint(++myConstrMaxID, myID, SLVS_C_WHERE_DRAGGED,
856 myWorkplane.h, 0.0, anEntIter->second, 0, 0, 0);
857 myConstraints.push_back(aWDConstr);
858 myTempConstraints.push_back(aWDConstr.h);
861 // ============================================================================
862 // Function: removeTemporaryConstraints
863 // Class: SketchSolver_ConstraintGroup
864 // Purpose: remove all transient SLVS_C_WHERE_DRAGGED constraints after
865 // resolving the set of constraints
866 // ============================================================================
867 void SketchSolver_ConstraintGroup::removeTemporaryConstraints()
869 std::list<Slvs_hConstraint>::reverse_iterator aTmpConstrIter;
870 for (aTmpConstrIter = myTempConstraints.rbegin(); aTmpConstrIter != myTempConstraints.rend(); aTmpConstrIter++)
872 unsigned int aConstrPos = Search(*aTmpConstrIter, myConstraints);
873 if (aConstrPos >= myConstraints.size())
875 myConstraints.erase(myConstraints.begin() + aConstrPos);
877 // If the removing constraint has higher index, decrease the indexer
878 if (*aTmpConstrIter == myConstrMaxID)
881 myTempConstraints.clear();
883 // Clear basic dragged point
884 myTempPointWhereDragged.clear();
887 // ============================================================================
888 // Function: removeConstraint
889 // Class: SketchSolver_ConstraintGroup
890 // Purpose: remove constraint and all unused entities
891 // ============================================================================
892 void SketchSolver_ConstraintGroup::removeConstraint(boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
894 std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::iterator
895 anIterToRemove = myConstraintMap.find(theConstraint);
896 if (anIterToRemove == myConstraintMap.end())
899 Slvs_hConstraint aCnstrToRemove = anIterToRemove->second;
900 // Remove constraint from the map
901 myConstraintMap.erase(anIterToRemove);
903 // Find unused entities
904 int aConstrPos = Search(aCnstrToRemove, myConstraints);
905 std::set<Slvs_hEntity> anEntToRemove;
906 Slvs_hEntity aCnstEnt[] = {myConstraints[aConstrPos].ptA, myConstraints[aConstrPos].ptB,
907 myConstraints[aConstrPos].entityA, myConstraints[aConstrPos].entityB};
908 for (int i = 0; i < 4; i++)
909 if (aCnstEnt[i] != 0)
910 anEntToRemove.insert(aCnstEnt[i]);
911 myConstraints.erase(myConstraints.begin() + aConstrPos);
912 if (aCnstrToRemove == myConstrMaxID)
914 std::vector<Slvs_Constraint>::const_iterator aConstrIter = myConstraints.begin();
915 for ( ; aConstrIter != myConstraints.end(); aConstrIter++)
917 Slvs_hEntity aEnts[] = {aConstrIter->ptA, aConstrIter->ptB,
918 aConstrIter->entityA, aConstrIter->entityB};
919 for (int i = 0; i < 4; i++)
920 if (aEnts[i] != 0 && anEntToRemove.find(aEnts[i]) != anEntToRemove.end())
921 anEntToRemove.erase(aEnts[i]);
924 if (anEntToRemove.empty())
927 // Remove unused entities
928 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator
929 anEntMapIter = myEntityMap.begin();
930 while (anEntMapIter != myEntityMap.end())
932 if (anEntToRemove.find(anEntMapIter->second) != anEntToRemove.end())
934 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator
935 aRemovedIter = anEntMapIter;
937 myEntityMap.erase(aRemovedIter);
941 std::set<Slvs_hEntity>::const_reverse_iterator aRemIter = anEntToRemove.rbegin();
942 for ( ; aRemIter != anEntToRemove.rend(); aRemIter++)
944 unsigned int anEntPos = Search(*aRemIter, myEntities);
945 if (anEntPos >= myEntities.size())
947 unsigned int aParamPos = Search(myEntities[anEntPos].param[0], myParams);
948 if (aParamPos >= myParams.size())
951 while (myEntities[anEntPos].param[aNbParams] != 0)
953 if (myEntities[anEntPos].param[aNbParams-1] == myParamMaxID)
954 myParamMaxID -= aNbParams;
955 myParams.erase(myParams.begin() + aParamPos, myParams.begin() + aParamPos + aNbParams);
956 if (*aRemIter == myEntityMaxID)
958 myEntities.erase(myEntities.begin() + anEntPos);
960 // Remove entity's ID from the lists of conincident points
961 std::vector< std::set<Slvs_hEntity> >::iterator aCoPtIter = myCoincidentPoints.begin();
962 for ( ; aCoPtIter != myCoincidentPoints.end(); aCoPtIter++)
963 aCoPtIter->erase(*aRemIter);
969 // ========================================================
970 // ========= Auxiliary functions ===============
971 // ========================================================
973 template <typename T>
974 int Search(const uint32_t& theEntityID, const std::vector<T>& theEntities)
976 int aResIndex = theEntityID <= theEntities.size() ? theEntityID - 1 : 0;
977 int aVecSize = theEntities.size();
978 while (aResIndex >= 0 && theEntities[aResIndex].h > theEntityID)
980 while (aResIndex < aVecSize && theEntities[aResIndex].h < theEntityID)
983 aResIndex = aVecSize;