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 // NOTE: The possibility, that some elements are placed into both groups, is around 0,
570 // so the objects should be copied with changing the indexes
572 // Maps between old and new indexes of SolveSpace elements:
573 std::map<Slvs_hParam, Slvs_hParam> aParamMap;
574 std::map<Slvs_hEntity, Slvs_hEntity> anEntityMap;
575 std::map<Slvs_hConstraint, Slvs_hConstraint> aConstrMap;
577 // Go through copying constraints
578 std::vector<Slvs_Constraint>::const_iterator aConstrIter = theGroup.myConstraints.begin();
579 for ( ; aConstrIter != theGroup.myConstraints.end(); aConstrIter++)
581 Slvs_Constraint aConstraintCopy = *aConstrIter;
582 // Go through constraint entities
583 Slvs_hEntity* anEntities[CONSTRAINT_ATTR_SIZE] = {
584 &(aConstraintCopy.ptA), &(aConstraintCopy.ptB),
585 &(aConstraintCopy.entityA), &(aConstraintCopy.entityB)
587 for (int indEnt = 0; indEnt < CONSTRAINT_ATTR_SIZE; indEnt++)
589 if (*(anEntities[indEnt]) == 0)
591 if (anEntityMap.find(*(anEntities[indEnt])) != anEntityMap.end())
592 { // entity is already copied
593 *(anEntities[indEnt]) = anEntityMap[*(anEntities[indEnt])];
598 Slvs_Entity anEntityCopy = theGroup.myEntities[Search(*(anEntities[indEnt]), theGroup.myEntities)];
599 // Go through entity parameters
600 const int aNbEntParams = 4; // maximal number of entity parameters
601 for (int indPrm = 0; indPrm < aNbEntParams; indPrm++)
603 if (anEntityCopy.param[indPrm] == 0)
605 if (aParamMap.find(anEntityCopy.param[indPrm]) != aParamMap.end())
607 anEntityCopy.param[indPrm] = aParamMap[anEntityCopy.param[indPrm]];
611 Slvs_Param aParamCopy = theGroup.myParams[Search(anEntityCopy.param[indPrm], theGroup.myParams)];
612 aParamMap[aParamCopy.h] = ++myParamMaxID;
613 aParamCopy.h = myParamMaxID;
614 myParams.push_back(aParamCopy);
617 anEntityMap[anEntityCopy.h] = ++myEntityMaxID;
618 anEntityCopy.h = myEntityMaxID;
619 myEntities.push_back(anEntityCopy);
620 *(anEntities[indEnt]) = anEntityCopy.h;
623 aConstraintCopy.h = ++myConstrMaxID;
624 myConstraints.push_back(aConstraintCopy);
625 aConstrMap[aConstrIter->h] = aConstraintCopy.h;
628 // Append maps of SketchPlugin to SolveSpace parameters
629 std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::const_iterator
630 aSPConstrMapIter = theGroup.myConstraintMap.begin();
631 for ( ; aSPConstrMapIter!= theGroup.myConstraintMap.end(); aSPConstrMapIter++)
633 std::map<Slvs_hConstraint, Slvs_hConstraint>::iterator aFind = aConstrMap.find(aSPConstrMapIter->second);
634 if (aFind != aConstrMap.end())
635 myConstraintMap[aSPConstrMapIter->first] = aFind->second;
638 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
639 aSPEntMapIter = theGroup.myEntityMap.begin();
640 for ( ; aSPEntMapIter != theGroup.myEntityMap.end(); aSPEntMapIter++) {
641 std::map<Slvs_hEntity, Slvs_hEntity>::iterator aFind = anEntityMap.find(aSPEntMapIter->second);
642 if (aFind != anEntityMap.end())
643 myEntityMap[aSPEntMapIter->first] = aFind->second;
646 // Add temporary constraints
647 std::list<Slvs_hConstraint>::const_iterator aTempConstrIter = theGroup.myTempConstraints.begin();
648 for ( ; aTempConstrIter != theGroup.myTempConstraints.end(); aTempConstrIter++)
650 std::map<Slvs_hConstraint, Slvs_hConstraint>::iterator aFind = aConstrMap.find(*aTempConstrIter);
651 if (aFind != aConstrMap.end())
652 myTempConstraints.push_back(aFind->second);
654 myTempConstraints.sort();
656 if (myTempPointWhereDragged.empty())
657 myTempPointWhereDragged = theGroup.myTempPointWhereDragged;
658 else if (!theGroup.myTempPointWhereDragged.empty())
659 { // Need to create additional transient constraint
660 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
661 aFeatureIter = theGroup.myEntityMap.begin();
662 for (; aFeatureIter != theGroup.myEntityMap.end(); aFeatureIter++)
663 if (aFeatureIter->second == myTempPointWDrgdID)
665 addTemporaryConstraintWhereDragged(aFeatureIter->first);
670 // Merge the lists of coincidence points. As the groups were separated, there was
671 // no coincidence constraint, so each two points from different groups don't coincide
672 myCoincidentPoints.insert(myCoincidentPoints.end(),
673 theGroup.myCoincidentPoints.begin(),
674 theGroup.myCoincidentPoints.end());
676 myNeedToSolve = myNeedToSolve || theGroup.myNeedToSolve;
679 // ============================================================================
680 // Function: updateGroup
681 // Class: SketchSolver_ConstraintGroup
682 // Purpose: search removed entities and constraints
683 // ============================================================================
684 bool SketchSolver_ConstraintGroup::updateGroup()
686 // Check for valid sketch
687 if (!mySketch->data()->isValid())
690 // Fast check for constraint validity. If all constraints are valid, no need to update the group
691 std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::reverse_iterator
692 aConstrIter = myConstraintMap.rbegin();
693 bool isAllValid = true;
694 for ( ; isAllValid && aConstrIter != myConstraintMap.rend(); aConstrIter++)
695 if (!aConstrIter->first->data()->isValid())
700 // Remove invalid constraints.
701 // There only constraint will be deleted (parameters and entities) will be removed below
702 std::list< boost::shared_ptr<SketchPlugin_Constraint> > aConstrToDelete;
703 std::map<Slvs_hEntity, bool> anEntToDelete; // entities will be removed if no valid constraints use them
704 std::map<Slvs_hEntity, bool> aCoincPtToDelete; // list of entities (points) used in coincidence constaints which will be removed;
705 for (aConstrIter = myConstraintMap.rbegin(); aConstrIter != myConstraintMap.rend(); aConstrIter++)
707 bool isValid = aConstrIter->first->data()->isValid();
709 int aConstrPos = Search(aConstrIter->second, myConstraints);
710 if (aConstrPos < (int)myConstraints.size())
712 Slvs_hEntity aConstrEnt[] = {
713 myConstraints[aConstrPos].ptA, myConstraints[aConstrPos].ptB,
714 myConstraints[aConstrPos].entityA, myConstraints[aConstrPos].entityB};
715 for (int i = 0; i < 4; i++)
716 if (aConstrEnt[i] != SLVS_E_UNKNOWN)
718 if (anEntToDelete.find(aConstrEnt[i]) == anEntToDelete.end())
720 anEntToDelete[aConstrEnt[i]] = !isValid;
721 aCoincPtToDelete[aConstrEnt[i]] = !isValid && (myConstraints[aConstrPos].type == SLVS_C_POINTS_COINCIDENT);
723 else if (isValid) // constraint is valid => no need to remove its entities
725 anEntToDelete[aConstrEnt[i]] = false;
726 aCoincPtToDelete[aConstrEnt[i]] = false;
731 myConstraints.erase(myConstraints.begin() + aConstrPos);
732 if (aConstrIter->second == myConstrMaxID) // When the constraint with highest ID is removed, decrease indexer
734 aConstrToDelete.push_front(aConstrIter->first);
738 std::list< boost::shared_ptr<SketchPlugin_Constraint> >::iterator aDelIter;
739 for (aDelIter = aConstrToDelete.begin(); aDelIter != aConstrToDelete.end(); aDelIter++)
740 myConstraintMap.erase(*aDelIter);
742 // Remove invalid and unused entities
743 std::map<Slvs_hEntity, bool>::reverse_iterator aEDelIter;
744 std::map<Slvs_hEntity, bool>::reverse_iterator aPtDelIter = aCoincPtToDelete.rbegin();
745 for (aEDelIter = anEntToDelete.rbegin(); aEDelIter != anEntToDelete.rend(); aEDelIter++, aPtDelIter++)
747 if (aEDelIter->second) // remove entity
749 int anEntPos = Search(aEDelIter->first, myEntities);
750 std::vector<Slvs_Entity>::iterator aEntIter = myEntities.begin() + anEntPos;
751 // Number of parameters for the entity
753 while (aEntIter->param[aNbParams]) aNbParams++;
754 if (aNbParams == 0) continue;
755 // Decrease parameter indexer if there are deleted parameter with higher IDs
756 if (aEntIter->param[aNbParams-1] == myParamMaxID)
757 myParamMaxID -= aNbParams;
758 // Remove parameters of the entity
759 int aParamPos = Search(aEntIter->param[0], myParams);
760 myParams.erase(myParams.begin() + aParamPos,
761 myParams.begin() + aParamPos + aNbParams);
764 if (aEDelIter->first == myEntityMaxID)
766 myEntities.erase(myEntities.begin() + anEntPos);
767 // Remove such entity from myEntityMap
768 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator
769 anEntMapIter = myEntityMap.begin();
770 for ( ; anEntMapIter != myEntityMap.end(); anEntMapIter++)
771 if (anEntMapIter->second == aEDelIter->first)
773 if (anEntMapIter != myEntityMap.end())
774 myEntityMap.erase(anEntMapIter);
776 if (aPtDelIter->second) // remove link for this entity from list of coincident points
778 std::vector< std::set<Slvs_hEntity> >::iterator aCoPtIter = myCoincidentPoints.begin();
779 for ( ; aCoPtIter != myCoincidentPoints.end(); aCoPtIter++)
780 aCoPtIter->erase(aPtDelIter->first);
787 // ============================================================================
788 // Function: updateAttribute
789 // Class: SketchSolver_ConstraintGroup
790 // Purpose: update features of sketch after resolving constraints
791 // ============================================================================
792 void SketchSolver_ConstraintGroup::updateAttribute(
793 boost::shared_ptr<ModelAPI_Attribute> theAttribute,
794 const Slvs_hEntity& theEntityID)
796 // Search the position of the first parameter of the entity
797 int anEntPos = Search(theEntityID, myEntities);
798 int aFirstParamPos = Search(myEntities[anEntPos].param[0], myParams);
800 // Look over supported types of entities
803 boost::shared_ptr<GeomDataAPI_Point> aPoint =
804 boost::dynamic_pointer_cast<GeomDataAPI_Point>(theAttribute);
807 aPoint->setValue(myParams[aFirstParamPos].val,
808 myParams[aFirstParamPos+1].val,
809 myParams[aFirstParamPos+2].val);
814 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
815 boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute);
818 aPoint2D->setValue(myParams[aFirstParamPos].val,
819 myParams[aFirstParamPos+1].val);
824 boost::shared_ptr<ModelAPI_AttributeDouble> aScalar =
825 boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theAttribute);
828 aScalar->setValue(myParams[aFirstParamPos].val);
832 /// \todo Support other types of entities
835 // ============================================================================
836 // Function: updateEntityIfPossible
837 // Class: SketchSolver_ConstraintGroup
838 // Purpose: search the entity in this group and update it
839 // ============================================================================
840 void SketchSolver_ConstraintGroup::updateEntityIfPossible(
841 boost::shared_ptr<ModelAPI_Attribute> theEntity)
843 if (myEntityMap.find(theEntity) != myEntityMap.end())
845 // If the attribute is a point and it is changed (the group needs to rebuild),
846 // probably user has dragged this point into this position,
847 // so it is necessary to add constraint which will guarantee the point will not change
849 // Store myNeedToSolve flag to verify the entity is really changed
850 bool aNeedToSolveCopy = myNeedToSolve;
851 myNeedToSolve = false;
853 changeEntity(theEntity);
855 if (myNeedToSolve) // the entity is changed
857 // Verify the entity is a point and add temporary constraint of permanency
858 boost::shared_ptr<GeomDataAPI_Point> aPoint =
859 boost::dynamic_pointer_cast<GeomDataAPI_Point>(theEntity);
860 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
861 boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theEntity);
862 if (aPoint || aPoint2D)
863 addTemporaryConstraintWhereDragged(theEntity);
866 // Restore flag of changes
867 myNeedToSolve = myNeedToSolve || aNeedToSolveCopy;
871 // ============================================================================
872 // Function: addTemporaryConstraintWhereDragged
873 // Class: SketchSolver_ConstraintGroup
874 // Purpose: add transient constraint SLVS_C_WHERE_DRAGGED for the entity,
875 // which was moved by user
876 // ============================================================================
877 void SketchSolver_ConstraintGroup::addTemporaryConstraintWhereDragged(
878 boost::shared_ptr<ModelAPI_Attribute> theEntity)
880 // Find identifier of the entity
881 std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
882 anEntIter = myEntityMap.find(theEntity);
883 if (anEntIter == myEntityMap.end())
886 // If this is a first dragged point, its parameters should be placed
887 // into Slvs_System::dragged field to avoid system inconsistense
888 if (myTempPointWhereDragged.empty())
890 int anEntPos = Search(anEntIter->second, myEntities);
891 Slvs_hParam* aDraggedParam = myEntities[anEntPos].param;
892 for (int i = 0; i < 4; i++, aDraggedParam++)
893 if (*aDraggedParam != 0)
894 myTempPointWhereDragged.push_back(*aDraggedParam);
895 myTempPointWDrgdID = myEntities[anEntPos].h;
899 // Get identifiers of all dragged points
900 std::set<Slvs_hEntity> aDraggedPntID;
901 aDraggedPntID.insert(myTempPointWDrgdID);
902 std::list<Slvs_hConstraint>::iterator aTmpCoIter = myTempConstraints.begin();
903 for ( ; aTmpCoIter != myTempConstraints.end(); aTmpCoIter++)
905 unsigned int aConstrPos = Search(*aTmpCoIter, myConstraints);
906 if (aConstrPos < myConstraints.size())
907 aDraggedPntID.insert(myConstraints[aConstrPos].ptA);
909 // Find whether there is a point coincident with theEntity, which already has SLVS_C_WHERE_DRAGGED
910 std::vector< std::set<Slvs_hEntity> >::iterator aCoPtIter = myCoincidentPoints.begin();
911 for ( ; aCoPtIter != myCoincidentPoints.end(); aCoPtIter++)
913 if (aCoPtIter->find(anEntIter->second) == aCoPtIter->end())
914 continue; // the entity was not found in current set
916 // Find one of already created SLVS_C_WHERE_DRAGGED constraints in current set of coincident points
917 std::set<Slvs_hEntity>::const_iterator aDrgIter = aDraggedPntID.begin();
918 for ( ; aDrgIter != aDraggedPntID.end(); aDrgIter++)
919 if (aCoPtIter->find(*aDrgIter) != aCoPtIter->end())
920 return ; // the SLVS_C_WHERE_DRAGGED constraint already exists
923 // Create additional SLVS_C_WHERE_DRAGGED constraint if myTempPointWhereDragged field is not empty
924 Slvs_Constraint aWDConstr = Slvs_MakeConstraint(++myConstrMaxID, myID, SLVS_C_WHERE_DRAGGED,
925 myWorkplane.h, 0.0, anEntIter->second, 0, 0, 0);
926 myConstraints.push_back(aWDConstr);
927 myTempConstraints.push_back(aWDConstr.h);
930 // ============================================================================
931 // Function: removeTemporaryConstraints
932 // Class: SketchSolver_ConstraintGroup
933 // Purpose: remove all transient SLVS_C_WHERE_DRAGGED constraints after
934 // resolving the set of constraints
935 // ============================================================================
936 void SketchSolver_ConstraintGroup::removeTemporaryConstraints()
938 std::list<Slvs_hConstraint>::reverse_iterator aTmpConstrIter;
939 for (aTmpConstrIter = myTempConstraints.rbegin(); aTmpConstrIter != myTempConstraints.rend(); aTmpConstrIter++)
941 unsigned int aConstrPos = Search(*aTmpConstrIter, myConstraints);
942 if (aConstrPos >= myConstraints.size())
944 myConstraints.erase(myConstraints.begin() + aConstrPos);
946 // If the removing constraint has higher index, decrease the indexer
947 if (*aTmpConstrIter == myConstrMaxID)
950 myTempConstraints.clear();
952 // Clear basic dragged point
953 myTempPointWhereDragged.clear();
958 // ========================================================
959 // ========= Auxiliary functions ===============
960 // ========================================================
962 template <typename T>
963 int Search(const uint32_t& theEntityID, const std::vector<T>& theEntities)
965 int aResIndex = theEntityID <= theEntities.size() ? theEntityID - 1 : 0;
966 int aVecSize = theEntities.size();
967 while (aResIndex >= 0 && theEntities[aResIndex].h > theEntityID)
969 while (aResIndex < aVecSize && theEntities[aResIndex].h < theEntityID)
972 aResIndex = aVecSize;