1 // File: SketchSolver_ConstraintManager.cpp
2 // Created: 08 May 2014
3 // Author: Artem ZHIDKOV
5 #include "SketchSolver_ConstraintManager.h"
7 #include <Events_Loop.h>
8 #include <GeomDataAPI_Dir.h>
9 #include <GeomDataAPI_Point.h>
10 #include <GeomDataAPI_Point2D.h>
11 #include <ModelAPI_AttributeDouble.h>
12 #include <ModelAPI_AttributeRefList.h>
13 #include <ModelAPI_Data.h>
14 #include <Model_Events.h>
15 #include <SketchPlugin_Constraint.h>
16 #include <SketchPlugin_ConstraintCoincidence.h>
17 #include <SketchPlugin_Line.h>
18 #include <SketchPlugin_Sketch.h>
20 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::_self = 0;
22 /// Global constaint manager object
23 SketchSolver_ConstraintManager* myManager = SketchSolver_ConstraintManager::Instance();
25 /// This value is used to give unique index to the groups
26 static Slvs_hGroup myGroupIndexer = 0;
28 /** \brief Makes transformation from ModelAPI_Attribute to the list of parameters' values
29 * \remark Convertion of normal in 3D needs two attributes (coordinate axis of transversal plane)
30 * \param[in,out] theParams list of converted values which appended to incoming list
31 * \param[in] theAttr attribute to be converted
32 * \param[in] theNormExtraAttr additional attribute for conversion of a normal
34 static void ConvertAttributeToParamList(
35 std::vector<double>& theParams,
36 boost::shared_ptr<ModelAPI_Attribute> theAttr,
37 boost::shared_ptr<ModelAPI_Attribute> theNormExtraAttr = boost::shared_ptr<ModelAPI_Attribute>());
39 /** \brief Search the entity/parameter with specified ID in the list of elements
40 * \param[in] theEntityID unique ID of the element
41 * \param[in] theEntities list of elements
42 * \return position of the found element or -1 if the element is not found
45 static int Search(const uint32_t& theEntityID, const std::vector<T>& theEntities);
48 // ========================================================
49 // ========= SketchSolver_ConstraintManager ===============
50 // ========================================================
51 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::Instance()
54 _self = new SketchSolver_ConstraintManager();
58 SketchSolver_ConstraintManager::SketchSolver_ConstraintManager()
62 // Register in event loop
63 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_CREATED));
64 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_UPDATED));
65 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_DELETED));
68 SketchSolver_ConstraintManager::~SketchSolver_ConstraintManager()
73 void SketchSolver_ConstraintManager::processEvent(const Events_Message* theMessage)
75 if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_CREATED))
77 const Model_FeatureUpdatedMessage* aCreateMsg = dynamic_cast<const Model_FeatureUpdatedMessage*>(theMessage);
79 // Only sketches and constraints can be added by Create event
80 boost::shared_ptr<SketchPlugin_Sketch> aSketch =
81 boost::dynamic_pointer_cast<SketchPlugin_Sketch>(aCreateMsg->feature());
84 addWorkplane(aSketch);
87 boost::shared_ptr<SketchPlugin_Constraint> aConstraint =
88 boost::dynamic_pointer_cast<SketchPlugin_Constraint>(aCreateMsg->feature());
91 addConstraint(aConstraint);
95 else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_DELETED))
97 const Model_FeatureDeletedMessage* aDeleteMsg = dynamic_cast<const Model_FeatureDeletedMessage*>(theMessage);
98 /// \todo Implement deleting objects on event
100 else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_UPDATED))
102 const Model_FeatureUpdatedMessage* aUpdateMsg = dynamic_cast<const Model_FeatureUpdatedMessage*>(theMessage);
104 boost::shared_ptr<SketchPlugin_Sketch> aSketch =
105 boost::dynamic_pointer_cast<SketchPlugin_Sketch>(aUpdateMsg->feature());
108 updateWorkplane(aSketch);
112 boost::shared_ptr<SketchPlugin_Constraint> aConstraint =
113 boost::dynamic_pointer_cast<SketchPlugin_Constraint>(aUpdateMsg->feature());
116 // updateConstraint(aConstraint);
120 boost::shared_ptr<SketchPlugin_Feature> aFeature =
121 boost::dynamic_pointer_cast<SketchPlugin_Feature>(aUpdateMsg->feature());
123 // updateEntity(aFeature);
128 bool SketchSolver_ConstraintManager::addWorkplane(boost::shared_ptr<SketchPlugin_Sketch> theSketch)
130 // Check the specified workplane is already used
131 std::vector<SketchSolver_ConstraintGroup>::const_iterator aGroupIter;
132 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
133 if (aGroupIter->isBaseWorkplane(theSketch))
135 // Create new group for workplane
136 SketchSolver_ConstraintGroup aNewGroup(theSketch);
137 // Verify that the group is created successfully
138 if (!aNewGroup.isBaseWorkplane(theSketch))
140 myGroups.push_back(aNewGroup);
144 bool SketchSolver_ConstraintManager::updateWorkplane(boost::shared_ptr<SketchPlugin_Sketch> theSketch)
146 bool aResult = true; // changed when a workplane wrongly updated
147 bool isUpdated = false;
148 // Try to update specified workplane in all groups
149 std::vector<SketchSolver_ConstraintGroup>::iterator aGroupIter;
150 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
151 if (aGroupIter->isBaseWorkplane(theSketch))
154 if (!aGroupIter->updateWorkplane(theSketch))
157 // If the workplane is not updates, so this is a new workplane
159 return addWorkplane(theSketch);
163 bool SketchSolver_ConstraintManager::addConstraint(
164 boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
166 // Search the groups which this constraint touchs
167 std::vector<Slvs_hGroup> aGroups;
168 findGroups(theConstraint, aGroups);
170 // Process the groups list
171 if (aGroups.size() == 0)
172 { // There are no groups applicable for this constraint => create new one
173 boost::shared_ptr<SketchPlugin_Sketch> aWP = findWorkplaneForConstraint(theConstraint);
174 if (!aWP) return false;
175 SketchSolver_ConstraintGroup aGroup(aWP);
176 aGroup.addConstraint(theConstraint);
177 myGroups.push_back(aGroup);
180 else if (aGroups.size() == 1)
181 { // Only one group => add constraint into it
182 Slvs_hGroup aGroupId = *(aGroups.begin());
183 std::vector<SketchSolver_ConstraintGroup>::iterator aGroupIter;
184 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
185 if (aGroupIter->getId() == aGroupId)
186 return aGroupIter->addConstraint(theConstraint);
188 else if (aGroups.size() > 1)
189 { // Several groups applicable for this constraint => need to merge them
190 /// \todo Implement merging of groups
193 // Something goes wrong
198 void SketchSolver_ConstraintManager::findGroups(
199 boost::shared_ptr<SketchPlugin_Constraint> theConstraint,
200 std::vector<Slvs_hGroup>& theGroupIDs) const
202 std::vector<SketchSolver_ConstraintGroup>::const_iterator aGroupIter;
203 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
204 if (aGroupIter->isInteract(theConstraint))
205 theGroupIDs.push_back(aGroupIter->getId());
208 boost::shared_ptr<SketchPlugin_Sketch> SketchSolver_ConstraintManager::findWorkplaneForConstraint(
209 boost::shared_ptr<SketchPlugin_Constraint> theConstraint) const
211 std::vector<SketchSolver_ConstraintGroup>::const_iterator aGroupIter;
212 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
214 boost::shared_ptr<SketchPlugin_Sketch> aWP = aGroupIter->getWorkplane();
215 boost::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures =
216 boost::dynamic_pointer_cast<ModelAPI_AttributeRefList>(aWP->data()->attribute(SKETCH_ATTR_FEATURES));
217 std::list< boost::shared_ptr<ModelAPI_Feature> > aFeaturesList = aWPFeatures->list();
218 std::list< boost::shared_ptr<ModelAPI_Feature> >::const_iterator anIter;
219 for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
220 if (*anIter == theConstraint)
221 return aWP; // workplane is found
224 return boost::shared_ptr<SketchPlugin_Sketch>();
229 // ========================================================
230 // ========= SketchSolver_ConstraintGroup ===============
231 // ========================================================
233 SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::
234 SketchSolver_ConstraintGroup(boost::shared_ptr<SketchPlugin_Sketch> theWorkplane)
235 : myID(++myGroupIndexer),
243 myConstraints.clear();
245 // Nullify all elements of the set of equations
246 myConstrSet.param = 0;
247 myConstrSet.entity = 0;
248 myConstrSet.constraint = 0;
249 myConstrSet.failed = 0;
251 // Initialize workplane
253 addWorkplane(theWorkplane);
256 SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::~SketchSolver_ConstraintGroup()
260 myConstraints.clear();
261 myConstraintMap.clear();
263 if (myConstrSet.param)
264 delete [] myConstrSet.param;
265 if (myConstrSet.entity)
266 delete [] myConstrSet.entity;
267 if (myConstrSet.constraint)
268 delete [] myConstrSet.constraint;
269 if (myConstrSet.failed)
270 delete [] myConstrSet.failed;
273 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::isBaseWorkplane(
274 boost::shared_ptr<SketchPlugin_Sketch> theWorkplane) const
276 return theWorkplane == mySketch;
279 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::isInteract(
280 boost::shared_ptr<SketchPlugin_Constraint> theConstraint) const
282 /// \todo Should be implemented
286 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::addConstraint(
287 boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
289 // There is no workplane yet, something wrong
290 if (myWorkplane.h == 0)
293 // Get constraint type and verify the constraint parameters are correct
294 int aConstrType = getConstraintType(theConstraint);
295 if (aConstrType == SLVS_C_UNKNOWN)
298 // Create constraint parameters
299 double aDistance = 0.0; // scalar value of the constraint
300 boost::shared_ptr<ModelAPI_AttributeDouble> aDistAttr =
301 boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theConstraint->data()->attribute(CONSTRAINT_ATTR_VALUE));
303 aDistance = aDistAttr->value();
305 Slvs_hEntity aConstrEnt[CONSTRAINT_ATTR_SIZE]; // parameters of the constraint
306 for (unsigned int indAttr = 0; indAttr < CONSTRAINT_ATTR_SIZE; indAttr++)
308 boost::shared_ptr<ModelAPI_AttributeRefAttr> aConstrAttr =
309 boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
310 theConstraint->data()->attribute(CONSTRAINT_ATTRIBUTES[indAttr])
312 aConstrEnt[indAttr] = addEntity(aConstrAttr->attr());
315 // Create SolveSpace constraint structure
316 Slvs_Constraint aConstraint =
317 Slvs_MakeConstraint(++myConstrMaxID, myID, aConstrType, myWorkplane.h,
318 aDistance, aConstrEnt[0], aConstrEnt[1], aConstrEnt[2], aConstrEnt[3]);
319 myConstraints.push_back(aConstraint);
320 myConstraintMap[theConstraint] = *(myConstraints.rbegin());
325 Slvs_hEntity SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::addEntity(
326 boost::shared_ptr<ModelAPI_Attribute> theEntity)
328 // Look over supported types of entities
331 boost::shared_ptr<GeomDataAPI_Point> aPoint =
332 boost::dynamic_pointer_cast<GeomDataAPI_Point>(theEntity);
335 Slvs_hParam aX = addParameter(aPoint->x());
336 Slvs_hParam aY = addParameter(aPoint->y());
337 Slvs_hParam aZ = addParameter(aPoint->z());
338 Slvs_Entity aPtEntity = Slvs_MakePoint3d(++myEntityMaxID, myID, aX, aY, aZ);
339 myEntities.push_back(aPtEntity);
344 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
345 boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theEntity);
348 // The 2D points are created on workplane. So, if there is no workplane yet, then error
349 if (myWorkplane.h == 0)
351 Slvs_hParam aU = addParameter(aPoint2D->x());
352 Slvs_hParam aV = addParameter(aPoint2D->y());
353 Slvs_Entity aPt2DEntity = Slvs_MakePoint2d(++myEntityMaxID, myID, myWorkplane.h, aU, aV);
354 myEntities.push_back(aPt2DEntity);
355 return aPt2DEntity.h;
358 /// \todo Other types of entities
360 // Unsupported or wrong entity type
364 Slvs_hEntity SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::addNormal(
365 boost::shared_ptr<ModelAPI_Attribute> theDirX,
366 boost::shared_ptr<ModelAPI_Attribute> theDirY)
368 // Convert axes to the coordinates of normal
369 std::vector<double> aNormCoord;
370 ConvertAttributeToParamList(aNormCoord, theDirX, theDirY);
373 Slvs_hParam aNormParams[4];
374 for (int i = 0; i < 4; i++)
375 aNormParams[i] = addParameter(aNormCoord[i]);
376 Slvs_Entity aNormal = Slvs_MakeNormal3d(++myEntityMaxID, myID,
377 aNormParams[0], aNormParams[1], aNormParams[2], aNormParams[3]);
378 myEntities.push_back(aNormal);
383 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::addWorkplane(
384 boost::shared_ptr<SketchPlugin_Sketch> theSketch)
387 return false; // the workplane already exists
389 // Get parameters of workplane
390 boost::shared_ptr<ModelAPI_Attribute> aDirX = theSketch->data()->attribute(SKETCH_ATTR_DIRX);
391 boost::shared_ptr<ModelAPI_Attribute> aDirY = theSketch->data()->attribute(SKETCH_ATTR_DIRY);
392 boost::shared_ptr<ModelAPI_Attribute> anOrigin = theSketch->data()->attribute(SKETCH_ATTR_ORIGIN);
393 // Transform them into SolveSpace format
394 Slvs_hEntity aNormalWP = addNormal(aDirX, aDirY);
395 if (!aNormalWP) return false;
396 Slvs_hEntity anOriginWP = addEntity(anOrigin);
397 if (!anOriginWP) return false;
399 myWorkplane = Slvs_MakeWorkplane(++myEntityMaxID, myID, anOriginWP, aNormalWP);
400 mySketch = theSketch;
401 // Workplane should be added to the list of entities
402 myEntities.push_back(myWorkplane);
406 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::updateWorkplane(
407 boost::shared_ptr<SketchPlugin_Sketch> theSketch)
409 // Renew Sketch pointer
410 mySketch = theSketch;
412 // Get parameters of workplane
413 boost::shared_ptr<ModelAPI_Attribute> aDirX = theSketch->data()->attribute(SKETCH_ATTR_DIRX);
414 boost::shared_ptr<ModelAPI_Attribute> aDirY = theSketch->data()->attribute(SKETCH_ATTR_DIRY);
415 boost::shared_ptr<ModelAPI_Attribute> anOrig = theSketch->data()->attribute(SKETCH_ATTR_ORIGIN);
416 // Transform them to lists of coordinates
417 std::vector<double> aNormal;
418 ConvertAttributeToParamList(aNormal, aDirX, aDirY);
419 std::vector<double> anOrigin;
420 ConvertAttributeToParamList(anOrigin, anOrig);
422 // Search the normal and the origin in the parameters list and update their values.
423 // Remark: entities are sorted in the vector, so the most probable position
424 // of the entity is equal to identifier the entity
427 int aEntPos = Search(myWorkplane.normal, myEntities);
428 if (aEntPos < 0) return false;
429 // search first parameter of normal
430 int aParamPos = Search(myEntities[aEntPos].param[0], myParams);
431 if (aParamPos < 0) return false;
432 std::vector<Slvs_Param>::iterator aParamIter = myParams.begin() + aParamPos;
433 // change normal parameters
434 std::vector<double>::iterator anIter;
435 for (anIter = aNormal.begin(); anIter != aNormal.end(); anIter++, aParamIter++)
436 aParamIter->val = *anIter;
439 aEntPos = Search(myWorkplane.point[0], myEntities);
440 if (aEntPos < 0) return false;
441 // search first parameter of origin
442 aParamPos = Search(myEntities[aEntPos].param[0], myParams);
443 if (aParamPos < 0) return false;
444 aParamIter = myParams.begin() + aParamPos;
445 // change origin's parameters
446 for (anIter = anOrigin.begin(); anIter != anOrigin.end(); anIter++, aParamIter++)
447 aParamIter->val = *anIter;
453 Slvs_hParam SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::addParameter(double theParam)
455 Slvs_Param aParam = Slvs_MakeParam(++myParamMaxID, myID, theParam);
456 myParams.push_back(aParam);
460 int SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::getConstraintType(
461 const boost::shared_ptr<SketchPlugin_Constraint>& theConstraint) const
463 // Constraint for coincidence of two points
464 boost::shared_ptr<SketchPlugin_ConstraintCoincidence> aPtEquiv =
465 boost::dynamic_pointer_cast<SketchPlugin_ConstraintCoincidence>(theConstraint);
468 // Verify the constraint has only two attributes and they are points
469 int aPt2d = 0; // bit-mapped field, each bit indicates whether the attribute is 2D point
470 int aPt3d = 0; // bit-mapped field, the same information for 3D points
471 for (unsigned int indAttr = 0; indAttr < CONSTRAINT_ATTR_SIZE; indAttr++)
473 boost::shared_ptr<ModelAPI_AttributeRefAttr> anAttr =
474 boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
475 theConstraint->data()->attribute(CONSTRAINT_ATTRIBUTES[indAttr])
477 // Verify the attribute is a 2D point
478 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
479 boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttr->attr());
482 aPt2d |= (1 << indAttr);
485 // Verify the attribute is a 3D point
486 boost::shared_ptr<GeomDataAPI_Point> aPoint3D =
487 boost::dynamic_pointer_cast<GeomDataAPI_Point>(anAttr->attr());
490 aPt3d |= (1 << indAttr);
493 // Attribute neither 2D nor 3D point is not supported by this type of constraint
494 return SLVS_C_UNKNOWN;
496 // The constrained points should be in first and second positions,
497 // so the expected value of aPt2d or aPt3d is 3
498 if ((aPt2d == 3 && aPt3d == 0) || (aPt2d == 0 && aPt3d == 3))
499 return SLVS_C_POINTS_COINCIDENT;
500 // Constraint parameters are wrong
501 return SLVS_C_UNKNOWN;
504 /// \todo Implement other kind of constrtaints
506 return SLVS_C_UNKNOWN;
511 // ========================================================
512 // ========= Auxiliary functions ===============
513 // ========================================================
515 void ConvertAttributeToParamList(
516 std::vector<double>& theParams,
517 boost::shared_ptr<ModelAPI_Attribute> theAttr,
518 boost::shared_ptr<ModelAPI_Attribute> theNormExtraAttr)
521 boost::shared_ptr<GeomDataAPI_Point> aPoint =
522 boost::dynamic_pointer_cast<GeomDataAPI_Point>(theAttr);
525 theParams.push_back(aPoint->x());
526 theParams.push_back(aPoint->y());
527 theParams.push_back(aPoint->z());
532 boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
533 boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttr);
536 theParams.push_back(aPoint2D->x());
537 theParams.push_back(aPoint2D->y());
542 boost::shared_ptr<GeomDataAPI_Dir> aDirX =
543 boost::dynamic_pointer_cast<GeomDataAPI_Dir>(theAttr);
544 boost::shared_ptr<GeomDataAPI_Dir> aDirY =
545 boost::dynamic_pointer_cast<GeomDataAPI_Dir>(theNormExtraAttr);
548 // quaternion parameters of normal vector
549 double qw, qx, qy, qz;
550 Slvs_MakeQuaternion(aDirX->x(), aDirX->y(), aDirX->z(),
551 aDirY->x(), aDirY->y(), aDirY->z(),
553 theParams.push_back(qw);
554 theParams.push_back(qx);
555 theParams.push_back(qy);
556 theParams.push_back(qz);
559 /// \todo Other types of entities
562 template <typename T>
563 int Search(const uint32_t& theEntityID, const std::vector<T>& theEntities)
565 std::vector<T>::const_iterator aEntIter = theEntities.begin() + theEntityID - 1;
566 while (aEntIter != theEntities.end() && aEntIter->h > theEntityID)
568 while (aEntIter != theEntities.end() && aEntIter->h < theEntityID)
570 if (aEntIter == theEntities.end())
572 return aEntIter - theEntities.begin();