]> SALOME platform Git repositories - modules/shaper.git/blob - src/SketchSolver/SketchSolver_ConstraintManager.cpp
Salome HOME
Merge branch 'MessagesGroups' of newgeom:newgeom into MessagesGroups
[modules/shaper.git] / src / SketchSolver / SketchSolver_ConstraintManager.cpp
1 // File:    SketchSolver_ConstraintManager.cpp
2 // Created: 08 May 2014
3 // Author:  Artem ZHIDKOV
4
5 #include "SketchSolver_ConstraintManager.h"
6
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
16 #include <SketchPlugin_Constraint.h>
17 #include <SketchPlugin_ConstraintCoincidence.h>
18 #include <SketchPlugin_Line.h>
19 #include <SketchPlugin_Point.h>
20 #include <SketchPlugin_Sketch.h>
21
22 #include <math.h>
23 #include <assert.h>
24
25 #include <set>
26
27 /// Tolerance for value of parameters
28 const double tolerance = 1.e-10;
29
30 // Initialization of constraint manager self pointer
31 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::_self = 0;
32
33 /// Global constraint manager object
34 SketchSolver_ConstraintManager* myManager = SketchSolver_ConstraintManager::Instance();
35
36 /// This value is used to give unique index to the groups
37 static Slvs_hGroup myGroupIndexer = 0;
38
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
43  */
44 template <typename T>
45 static int Search(const uint32_t& theEntityID, const std::vector<T>& theEntities);
46
47
48
49 // ========================================================
50 // ========= SketchSolver_ConstraintManager ===============
51 // ========================================================
52 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::Instance()
53 {
54   if (!_self)
55     _self = new SketchSolver_ConstraintManager();
56   return _self;
57 }
58
59 SketchSolver_ConstraintManager::SketchSolver_ConstraintManager()
60 {
61   myGroups.clear();
62
63   // Register in event loop
64   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_CREATED));
65   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_UPDATED));
66   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_DELETED));
67   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_MOVED));
68 }
69
70 SketchSolver_ConstraintManager::~SketchSolver_ConstraintManager()
71 {
72   myGroups.clear();
73 }
74
75 void SketchSolver_ConstraintManager::processEvent(const Events_Message* theMessage)
76 {
77   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_CREATED) ||
78       theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_UPDATED) || 
79       theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_MOVED))
80   {
81     const Model_FeatureUpdatedMessage* anUpdateMsg = dynamic_cast<const Model_FeatureUpdatedMessage*>(theMessage);
82     std::set< boost::shared_ptr<ModelAPI_Feature> > aFeatures = anUpdateMsg->features();
83
84     bool isModifiedEvt = theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_MOVED);
85     if (!isModifiedEvt)
86     {
87       std::set< boost::shared_ptr<ModelAPI_Feature> >::iterator aFeatIter;
88       for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++)
89       {
90         // Only sketches and constraints can be added by Create event
91         const std::string& aFeatureKind = (*aFeatIter)->getKind();
92         if (aFeatureKind.compare("Sketch") == 0)
93         {
94           boost::shared_ptr<SketchPlugin_Feature> aSketch =
95             boost::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
96           changeWorkplane(aSketch);
97           return ;
98         }
99         boost::shared_ptr<SketchPlugin_Constraint> aConstraint =
100           boost::dynamic_pointer_cast<SketchPlugin_Constraint>(*aFeatIter);
101         if (aConstraint)
102           changeConstraint(aConstraint);
103         else
104         {
105           // Sketch plugin features can be only updated
106           boost::shared_ptr<SketchPlugin_Feature> aFeature =
107             boost::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
108           if (aFeature)
109             updateEntity(aFeature);
110         }
111       }
112     }
113
114     // Solve the set of constraints
115     resolveConstraints();
116   }
117   else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_DELETED))
118   {
119     const Model_FeatureDeletedMessage* aDeleteMsg = dynamic_cast<const Model_FeatureDeletedMessage*>(theMessage);
120     const std::set<std::string>& aFeatureGroups = aDeleteMsg->groups();
121
122     // Find "Sketch" in groups. The constraint groups should be updated when an object removed from Sketch
123     std::set<std::string>::const_iterator aFGrIter;
124     for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
125       if (aFGrIter->compare("Sketch") == 0)
126         break;
127     
128     if (aFGrIter != aFeatureGroups.end())
129     {
130       std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter = myGroups.begin();
131       while (aGroupIter != myGroups.end())
132       {
133         if ((*aGroupIter)->updateGroup())
134         { // the group should be removed
135           delete *aGroupIter;
136           int aShift = aGroupIter - myGroups.begin();
137           myGroups.erase(aGroupIter);
138           aGroupIter = myGroups.begin() + aShift;
139         }
140         else aGroupIter++;
141       }
142     }
143   }
144 }
145
146 bool SketchSolver_ConstraintManager::changeWorkplane(boost::shared_ptr<SketchPlugin_Feature> theSketch)
147 {
148   bool aResult = true; // changed when a workplane wrongly updated
149   bool isUpdated = false;
150   // Try to update specified workplane in all groups
151   std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
152   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
153     if ((*aGroupIter)->isBaseWorkplane(theSketch))
154     {
155       isUpdated = true;
156       if (!(*aGroupIter)->updateWorkplane())
157         aResult = false;
158     }
159   // If the workplane is not updated, so this is a new workplane
160   if (!isUpdated)
161   {
162     SketchSolver_ConstraintGroup* aNewGroup = new SketchSolver_ConstraintGroup(theSketch);
163     // Verify that the group is created successfully
164     if (!aNewGroup->isBaseWorkplane(theSketch))
165     {
166       delete aNewGroup;
167       return false;
168     }
169     myGroups.push_back(aNewGroup);
170   }
171   return aResult;
172 }
173
174 bool SketchSolver_ConstraintManager::changeConstraint(
175               boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
176 {
177   // Search the groups which this constraint touchs
178   std::vector<Slvs_hGroup> aGroups;
179   findGroups(theConstraint, aGroups);
180
181   // Process the groups list
182   if (aGroups.size() == 0)
183   { // There are no groups applicable for this constraint => create new one
184     boost::shared_ptr<SketchPlugin_Feature> aWP = findWorkplaneForConstraint(theConstraint);
185     if (!aWP) return false;
186     SketchSolver_ConstraintGroup* aGroup = new SketchSolver_ConstraintGroup(aWP);
187     if (!aGroup->changeConstraint(theConstraint))
188     {
189       delete aGroup;
190       return false;
191     }
192     myGroups.push_back(aGroup);
193     return true;
194   }
195   else if (aGroups.size() == 1)
196   { // Only one group => add constraint into it
197     Slvs_hGroup aGroupId = *(aGroups.begin());
198     std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
199     for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
200       if ((*aGroupIter)->getId() == aGroupId)
201         return (*aGroupIter)->changeConstraint(theConstraint);
202   }
203   else if (aGroups.size() > 1)
204   { // Several groups applicable for this constraint => need to merge them
205     /// \todo Implement merging of groups
206   }
207
208   // Something goes wrong
209   return false;
210 }
211
212 void SketchSolver_ConstraintManager::updateEntity(boost::shared_ptr<SketchPlugin_Feature> theFeature)
213 {
214   // Create list of attributes depending on type of the feature
215   std::vector<std::string> anAttrList;
216   const std::string& aFeatureKind = theFeature->getKind();
217   // Point
218   if (aFeatureKind.compare("SketchPoint") == 0)
219     anAttrList.push_back(POINT_ATTR_COORD);
220   // Line
221   else if (aFeatureKind.compare("SketchLine") == 0)
222   {
223     anAttrList.push_back(LINE_ATTR_START);
224     anAttrList.push_back(LINE_ATTR_END);
225   }
226   /// \todo Other types of features should be implemented
227
228   // Check changing of feature's attributes (go through the groups and search usage of the attributes)
229   std::vector<std::string>::const_iterator anAttrIter;
230   for (anAttrIter = anAttrList.begin(); anAttrIter != anAttrList.end(); anAttrIter++)
231   {
232     std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
233     for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
234     {
235       boost::shared_ptr<ModelAPI_Attribute> anAttribute =
236         boost::dynamic_pointer_cast<ModelAPI_Attribute>(theFeature->data()->attribute(*anAttrIter));
237       (*aGroupIter)->updateEntityIfPossible(anAttribute);
238     }
239   }
240 }
241
242
243 void SketchSolver_ConstraintManager::findGroups(
244               boost::shared_ptr<SketchPlugin_Constraint> theConstraint,
245               std::vector<Slvs_hGroup>&                  theGroupIDs) const
246 {
247   boost::shared_ptr<SketchPlugin_Feature> aWP = findWorkplaneForConstraint(theConstraint);
248
249   std::vector<SketchSolver_ConstraintGroup*>::const_iterator aGroupIter;
250   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
251     if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theConstraint))
252       theGroupIDs.push_back((*aGroupIter)->getId());
253 }
254
255 boost::shared_ptr<SketchPlugin_Feature> SketchSolver_ConstraintManager::findWorkplaneForConstraint(
256               boost::shared_ptr<SketchPlugin_Constraint> theConstraint) const
257 {
258   // Already verified workplanes
259   std::set< boost::shared_ptr<SketchPlugin_Feature> > aVerified;
260
261   std::vector<SketchSolver_ConstraintGroup*>::const_iterator aGroupIter;
262   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
263   {
264     boost::shared_ptr<SketchPlugin_Feature> aWP = (*aGroupIter)->getWorkplane();
265     if (aVerified.find(aWP) != aVerified.end())
266       continue;
267
268     boost::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures =
269       boost::dynamic_pointer_cast<ModelAPI_AttributeRefList>(aWP->data()->attribute(SKETCH_ATTR_FEATURES));
270     std::list< boost::shared_ptr<ModelAPI_Feature> > aFeaturesList = aWPFeatures->list();
271     std::list< boost::shared_ptr<ModelAPI_Feature> >::const_iterator anIter;
272     for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
273       if (*anIter == theConstraint)
274         return aWP; // workplane is found
275     aVerified.insert(aWP);
276   }
277
278   return boost::shared_ptr<SketchPlugin_Feature>();
279 }
280
281 void SketchSolver_ConstraintManager::resolveConstraints()
282 {
283   std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
284   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
285     (*aGroupIter)->resolveConstraints();
286
287   // Features may be updated => send events
288   Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_FEATURE_UPDATED));
289 }
290
291
292
293 // ========================================================
294 // =========  SketchSolver_ConstraintGroup  ===============
295 // ========================================================
296
297 SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::
298   SketchSolver_ConstraintGroup(boost::shared_ptr<SketchPlugin_Feature> theWorkplane)
299   : myID(++myGroupIndexer),
300     myParamMaxID(0),
301     myEntityMaxID(0),
302     myConstrMaxID(0),
303     myConstraintMap(),
304     myNeedToSolve(false),
305     myConstrSolver()
306 {
307   myParams.clear();
308   myEntities.clear();
309   myConstraints.clear();
310
311   // Initialize workplane
312   myWorkplane.h = SLVS_E_UNKNOWN;
313   assert(addWorkplane(theWorkplane));
314 }
315
316 SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::~SketchSolver_ConstraintGroup()
317 {
318   myParams.clear();
319   myEntities.clear();
320   myConstraints.clear();
321   myConstraintMap.clear();
322
323   // If the group with maximal identifier is deleted, decrease the indexer
324   if (myID == myGroupIndexer)
325     myGroupIndexer--;
326 }
327
328 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::isBaseWorkplane(
329                 boost::shared_ptr<SketchPlugin_Feature> theWorkplane) const
330 {
331   return theWorkplane == mySketch;
332 }
333
334 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::isInteract(
335                 boost::shared_ptr<SketchPlugin_Constraint> theConstraint) const
336 {
337   // Check the group is empty
338   if (myWorkplane.h != SLVS_E_UNKNOWN && myConstraints.empty())
339     return true;
340
341   // Go through constraint entities and verify if some of them already in the group
342   for (int i = 0; i < CONSTRAINT_ATTR_SIZE; i++)
343   {
344     boost::shared_ptr<ModelAPI_AttributeRefAttr> aCAttrRef =
345       boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
346         theConstraint->data()->attribute(CONSTRAINT_ATTRIBUTES[i])
347       );
348     if (!aCAttrRef) continue;
349     if (myEntityMap.find(aCAttrRef->attr()) != myEntityMap.end())
350       return true;
351   }
352
353   // Entities did not found
354   return false;
355 }
356
357 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::changeConstraint(
358                 boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
359 {
360   // There is no workplane yet, something wrong
361   if (myWorkplane.h == SLVS_E_UNKNOWN)
362     return false;
363
364   // Search this constraint in the current group to update it
365   std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::const_iterator
366     aConstrMapIter = myConstraintMap.find(theConstraint);
367   std::vector<Slvs_Constraint>::iterator aConstrIter;
368   if (aConstrMapIter != myConstraintMap.end())
369   {
370     int aConstrPos = Search(aConstrMapIter->second, myConstraints);
371     aConstrIter = myConstraints.begin() + aConstrPos;
372   }
373
374   // Get constraint type and verify the constraint parameters are correct
375   int aConstrType = getConstraintType(theConstraint);
376   if (aConstrType == SLVS_C_UNKNOWN ||
377      (aConstrMapIter != myConstraintMap.end() && aConstrIter->type != aConstrType))
378     return false;
379
380   // Create constraint parameters
381   double aDistance = 0.0; // scalar value of the constraint
382   boost::shared_ptr<ModelAPI_AttributeDouble> aDistAttr =
383     boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theConstraint->data()->attribute(CONSTRAINT_ATTR_VALUE));
384   if (aDistAttr)
385   {
386     aDistance = aDistAttr->value();
387     if (aConstrMapIter != myConstraintMap.end() && fabs(aConstrIter->valA - aDistance) > tolerance)
388     {
389       myNeedToSolve = true;
390       aConstrIter->valA = aDistance;
391     }
392   }
393
394   Slvs_hEntity aConstrEnt[CONSTRAINT_ATTR_SIZE]; // parameters of the constraint
395   for (unsigned int indAttr = 0; indAttr < CONSTRAINT_ATTR_SIZE; indAttr++)
396   {
397     aConstrEnt[indAttr] = SLVS_E_UNKNOWN;
398     boost::shared_ptr<ModelAPI_AttributeRefAttr> aConstrAttr =
399       boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
400         theConstraint->data()->attribute(CONSTRAINT_ATTRIBUTES[indAttr])
401       );
402     if (!aConstrAttr) continue;
403     aConstrEnt[indAttr] = changeEntity(aConstrAttr->attr());
404   }
405
406   if (aConstrMapIter == myConstraintMap.end())
407   {
408     // Create SolveSpace constraint structure
409     Slvs_Constraint aConstraint =
410       Slvs_MakeConstraint(++myConstrMaxID, myID, aConstrType, myWorkplane.h,
411                           aDistance, aConstrEnt[0], aConstrEnt[1], aConstrEnt[2], aConstrEnt[3]);
412     myConstraints.push_back(aConstraint);
413     myConstraintMap[theConstraint] = aConstraint.h;
414   }
415   return true;
416 }
417
418 Slvs_hEntity SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::changeEntity(
419                 boost::shared_ptr<ModelAPI_Attribute> theEntity)
420 {
421   // If the entity is already in the group, try to find it
422   std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
423     aEntIter = myEntityMap.find(theEntity);
424   std::vector<Slvs_Param>::const_iterator aParamIter; // looks at first parameter of already existent entity or at the end of vector otherwise
425   if (aEntIter == myEntityMap.end()) // no such entity => should be created
426     aParamIter = myParams.end();
427   else
428   { // the entity already exists
429     int aEntPos = Search(aEntIter->second, myEntities);
430     int aParamPos = Search(myEntities[aEntPos].param[0], myParams);
431     aParamIter = myParams.begin() + aParamPos;
432   }
433
434   // Look over supported types of entities
435
436   // Point in 3D
437   boost::shared_ptr<GeomDataAPI_Point> aPoint =
438     boost::dynamic_pointer_cast<GeomDataAPI_Point>(theEntity);
439   if (aPoint)
440   {
441     Slvs_hParam aX = changeParameter(aPoint->x(), aParamIter);
442     Slvs_hParam aY = changeParameter(aPoint->y(), aParamIter);
443     Slvs_hParam aZ = changeParameter(aPoint->z(), aParamIter);
444
445     if (aEntIter != myEntityMap.end()) // the entity already exists
446       return aEntIter->second;
447
448     // New entity
449     Slvs_Entity aPtEntity = Slvs_MakePoint3d(++myEntityMaxID, myID, aX, aY, aZ);
450     myEntities.push_back(aPtEntity);
451     myEntityMap[theEntity] = aPtEntity.h;
452     return aPtEntity.h;
453   }
454
455   // Point in 2D
456   boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
457     boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theEntity);
458   if (aPoint2D)
459   {
460     // The 2D points are created on workplane. So, if there is no workplane yet, then error
461     if (myWorkplane.h == SLVS_E_UNKNOWN)
462       return SLVS_E_UNKNOWN;
463     Slvs_hParam aU = changeParameter(aPoint2D->x(), aParamIter);
464     Slvs_hParam aV = changeParameter(aPoint2D->y(), aParamIter);
465
466     if (aEntIter != myEntityMap.end()) // the entity already exists
467       return aEntIter->second;
468
469     // New entity
470     Slvs_Entity aPt2DEntity = Slvs_MakePoint2d(++myEntityMaxID, myID, myWorkplane.h, aU, aV);
471     myEntities.push_back(aPt2DEntity);
472     myEntityMap[theEntity] = aPt2DEntity.h;
473     return aPt2DEntity.h;
474   }
475
476   /// \todo Other types of entities
477
478   // Unsupported or wrong entity type
479   return SLVS_E_UNKNOWN;
480 }
481
482 Slvs_hEntity SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::changeNormal(
483                 boost::shared_ptr<ModelAPI_Attribute> theDirX,
484                 boost::shared_ptr<ModelAPI_Attribute> theDirY,
485                 boost::shared_ptr<ModelAPI_Attribute> theNorm)
486 {
487   boost::shared_ptr<GeomDataAPI_Dir> aDirX =
488     boost::dynamic_pointer_cast<GeomDataAPI_Dir>(theDirX);
489   boost::shared_ptr<GeomDataAPI_Dir> aDirY =
490     boost::dynamic_pointer_cast<GeomDataAPI_Dir>(theDirY);
491   if (!aDirX || !aDirY ||
492      (fabs(aDirX->x()) + fabs(aDirX->y()) + fabs(aDirX->z()) < tolerance) ||
493      (fabs(aDirY->x()) + fabs(aDirY->y()) + fabs(aDirY->z()) < tolerance))
494     return SLVS_E_UNKNOWN;
495
496   // quaternion parameters of normal vector
497   double qw, qx, qy, qz;
498   Slvs_MakeQuaternion(aDirX->x(), aDirX->y(), aDirX->z(),
499                       aDirY->x(), aDirY->y(), aDirY->z(),
500                       &qw, &qx, &qy, &qz);
501   double aNormCoord[4] = {qw, qx, qy, qz};
502
503   // Try to find existent normal
504   std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
505     aEntIter = myEntityMap.find(theNorm);
506   std::vector<Slvs_Param>::const_iterator aParamIter; // looks at first parameter of already existent entity or at the end of vector otherwise
507   if (aEntIter == myEntityMap.end()) // no such entity => should be created
508     aParamIter = myParams.end();
509   else
510   { // the entity already exists, update it
511     int aEntPos = Search(aEntIter->second, myEntities);
512     int aParamPos = Search(myEntities[aEntPos].param[0], myParams);
513     aParamIter = myParams.begin() + aParamPos;
514   }
515
516   // Change parameters of the normal
517   Slvs_hParam aNormParams[4];
518   for (int i = 0; i < 4; i++)
519     aNormParams[i] = changeParameter(aNormCoord[i], aParamIter);
520
521   if (aEntIter != myEntityMap.end()) // the entity already exists
522     return aEntIter->second;
523
524   // Create a normal
525   Slvs_Entity aNormal = Slvs_MakeNormal3d(++myEntityMaxID, myID,
526                 aNormParams[0], aNormParams[1], aNormParams[2], aNormParams[3]);
527   myEntities.push_back(aNormal);
528   myEntityMap[theNorm] = aNormal.h;
529   return aNormal.h;
530 }
531
532
533 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::addWorkplane(
534                 boost::shared_ptr<SketchPlugin_Feature> theSketch)
535 {
536   if (myWorkplane.h || theSketch->getKind().compare("Sketch") != 0)
537     return false; // the workplane already exists or the function parameter is not Sketch
538
539   mySketch = theSketch;
540   updateWorkplane();
541   return true;
542 }
543
544 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::updateWorkplane()
545 {
546   // Get parameters of workplane
547   boost::shared_ptr<ModelAPI_Attribute> aDirX    = mySketch->data()->attribute(SKETCH_ATTR_DIRX);
548   boost::shared_ptr<ModelAPI_Attribute> aDirY    = mySketch->data()->attribute(SKETCH_ATTR_DIRY);
549   boost::shared_ptr<ModelAPI_Attribute> aNorm    = mySketch->data()->attribute(SKETCH_ATTR_NORM);
550   boost::shared_ptr<ModelAPI_Attribute> anOrigin = mySketch->data()->attribute(SKETCH_ATTR_ORIGIN);
551   // Transform them into SolveSpace format
552   Slvs_hEntity aNormalWP = changeNormal(aDirX, aDirY, aNorm);
553   if (!aNormalWP) return false;
554   Slvs_hEntity anOriginWP = changeEntity(anOrigin);
555   if (!anOriginWP) return false;
556
557   if (!myWorkplane.h)
558   {
559     // Create workplane
560     myWorkplane = Slvs_MakeWorkplane(++myEntityMaxID, myID, anOriginWP, aNormalWP);
561     // Workplane should be added to the list of entities
562     myEntities.push_back(myWorkplane);
563   }
564   return true;
565 }
566
567
568 Slvs_hParam SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::changeParameter(
569                 const double&                            theParam,
570                 std::vector<Slvs_Param>::const_iterator& thePrmIter)
571 {
572   if (thePrmIter != myParams.end())
573   { // Parameter should be updated
574     int aParamPos = thePrmIter - myParams.begin();
575     if (fabs(thePrmIter->val - theParam) > tolerance)
576     {
577       myNeedToSolve = true; // parameter is changed, need to resolve constraints
578       myParams[aParamPos].val = theParam;
579     }
580     thePrmIter++;
581     return myParams[aParamPos].h;
582   }
583
584   // Newly created parameter
585   Slvs_Param aParam = Slvs_MakeParam(++myParamMaxID, myID, theParam);
586   myParams.push_back(aParam);
587   myNeedToSolve = true;
588   // The list of parameters is changed, move iterator to the end of the list to avoid problems
589   thePrmIter = myParams.end();
590   return aParam.h;
591 }
592
593 int SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::getConstraintType(
594                 const boost::shared_ptr<SketchPlugin_Constraint>& theConstraint) const
595 {
596   const std::string& aConstraintKind = theConstraint->getKind();
597   // Constraint for coincidence of two points
598   if (aConstraintKind.compare("SketchConstraintCoincidence") == 0)
599   {
600     // Verify the constraint has only two attributes and they are points
601     int aPt2d = 0; // bit-mapped field, each bit indicates whether the attribute is 2D point
602     int aPt3d = 0; // bit-mapped field, the same information for 3D points
603     for (unsigned int indAttr = 0; indAttr < CONSTRAINT_ATTR_SIZE; indAttr++)
604     {
605       boost::shared_ptr<ModelAPI_AttributeRefAttr> anAttr =
606         boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
607           theConstraint->data()->attribute(CONSTRAINT_ATTRIBUTES[indAttr])
608         );
609       if (!anAttr) continue;
610       // Verify the attribute is a 2D point
611       boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
612         boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttr->attr());
613       if (aPoint2D)
614       {
615         aPt2d |= (1 << indAttr);
616         continue;
617       }
618       // Verify the attribute is a 3D point
619       boost::shared_ptr<GeomDataAPI_Point> aPoint3D =
620         boost::dynamic_pointer_cast<GeomDataAPI_Point>(anAttr->attr());
621       if (aPoint3D)
622       {
623         aPt3d |= (1 << indAttr);
624         continue;
625       }
626       // Attribute neither 2D nor 3D point is not supported by this type of constraint
627       return SLVS_C_UNKNOWN;
628     }
629     // The constrained points should be in first and second positions,
630     // so the expected value of aPt2d or aPt3d is 3
631     if ((aPt2d == 3 && aPt3d == 0) || (aPt2d == 0 && aPt3d == 3))
632       return SLVS_C_POINTS_COINCIDENT;
633     // Constraint parameters are wrong
634     return SLVS_C_UNKNOWN;
635   }
636
637   /// \todo Implement other kind of constrtaints
638
639   return SLVS_C_UNKNOWN;
640 }
641
642 void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::resolveConstraints()
643 {
644   if (!myNeedToSolve)
645     return;
646
647   myConstrSolver.setGroupID(myID);
648   myConstrSolver.setParameters(myParams);
649   myConstrSolver.setEntities(myEntities);
650   myConstrSolver.setConstraints(myConstraints);
651
652   if (myConstrSolver.solve() == SLVS_RESULT_OKAY)
653   { // solution succeeded, store results into correspondent attributes
654     // Obtain result into the same list of parameters
655     if (!myConstrSolver.getResult(myParams))
656       return;
657
658     std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator
659       anEntIter = myEntityMap.begin();
660     for ( ; anEntIter != myEntityMap.end(); anEntIter++)
661       updateAttribute(anEntIter->first, anEntIter->second);
662   }
663   /// \todo Implement error handling
664
665   removeTemporaryConstraints();
666   myNeedToSolve = false;
667 }
668
669 bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::updateGroup()
670 {
671   // Check for valid sketch
672   if (!mySketch->data()->isValid())
673     return true;
674
675   // Fast check for constraint validity. If all constraints are valid, no need to update the group
676   std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::reverse_iterator
677     aConstrIter = myConstraintMap.rbegin();
678   bool isAllValid = true;
679   for ( ; isAllValid && aConstrIter != myConstraintMap.rend(); aConstrIter++)
680     if (!aConstrIter->first->data()->isValid())
681       isAllValid = false;
682   if (isAllValid)
683     return false;
684
685   // Remove invalid constraints.
686   // There only constraint will be deleted (parameters and entities) will be removed below
687   std::list< boost::shared_ptr<SketchPlugin_Constraint> > aConstrToDelete;
688   std::map<Slvs_hEntity, bool> anEntToDelete; // entities will be removed if no valid constraints use them
689   for (aConstrIter = myConstraintMap.rbegin(); aConstrIter != myConstraintMap.rend(); aConstrIter++)
690   {
691     bool isValid = aConstrIter->first->data()->isValid();
692
693     int aConstrPos = Search(aConstrIter->second, myConstraints);
694     if (aConstrPos < (int)myConstraints.size())
695     {
696       Slvs_hEntity aConstrEnt[] = {
697         myConstraints[aConstrPos].ptA,     myConstraints[aConstrPos].ptB,
698         myConstraints[aConstrPos].entityA, myConstraints[aConstrPos].entityB};
699       for (int i = 0; i < 4; i++)
700         if (aConstrEnt[i] != SLVS_E_UNKNOWN)
701         {
702           if (anEntToDelete.find(aConstrEnt[i]) == anEntToDelete.end())
703             anEntToDelete[aConstrEnt[i]] = !isValid;
704           else if (isValid) // constraint is valid => no need to remove its entities
705             anEntToDelete[aConstrEnt[i]] = false;
706         }
707       if (!isValid)
708       {
709         myConstraints.erase(myConstraints.begin() + aConstrPos);
710         if (aConstrIter->second == myConstrMaxID) // When the constraint with highest ID is removed, decrease indexer
711           myConstrMaxID--;
712         aConstrToDelete.push_front(aConstrIter->first);
713       }
714     }
715   }
716   std::list< boost::shared_ptr<SketchPlugin_Constraint> >::iterator aDelIter;
717   for (aDelIter = aConstrToDelete.begin(); aDelIter != aConstrToDelete.end(); aDelIter++)
718     myConstraintMap.erase(*aDelIter);
719
720   // Remove invalid and unused entities
721   std::map<Slvs_hEntity, bool>::reverse_iterator aEDelIter;
722   for (aEDelIter = anEntToDelete.rbegin(); aEDelIter != anEntToDelete.rend(); aEDelIter++)
723     if (aEDelIter->second)
724     {
725       int anEntPos = Search(aEDelIter->first, myEntities);
726       std::vector<Slvs_Entity>::iterator aEntIter = myEntities.begin() + anEntPos;
727       // Number of parameters for the entity
728       int aNbParams = 0;
729       while (aEntIter->param[aNbParams]) aNbParams++;
730       if (aNbParams == 0) continue;
731       // Decrease parameter indexer if there are deleted parameter with higher IDs
732       if (aEntIter->param[aNbParams-1] == myParamMaxID)
733         myParamMaxID -= aNbParams;
734       // Remove parameters of the entity
735       int aParamPos = Search(aEntIter->param[0], myParams);
736       myParams.erase(myParams.begin() + aParamPos,
737                      myParams.begin() + aParamPos + aNbParams);
738
739       // Remove entity
740       if (aEDelIter->first == myEntityMaxID)
741         myEntityMaxID--;
742       myEntities.erase(myEntities.begin() + anEntPos);
743       // Remove such entity from myEntityMap
744       std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator
745         anEntMapIter = myEntityMap.begin();
746       for ( ; anEntMapIter != myEntityMap.end(); anEntMapIter++)
747         if (anEntMapIter->second == aEDelIter->first)
748           break;
749       if (anEntMapIter != myEntityMap.end())
750         myEntityMap.erase(anEntMapIter);
751     }
752
753   return false;
754 }
755
756 void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::updateAttribute(
757                 boost::shared_ptr<ModelAPI_Attribute> theAttribute,
758                 const Slvs_hEntity&                   theEntityID)
759 {
760   // Search the position of the first parameter of the entity
761   int anEntPos = Search(theEntityID, myEntities);
762   int aFirstParamPos = Search(myEntities[anEntPos].param[0], myParams);
763
764   // Look over supported types of entities
765
766   // Point in 3D
767   boost::shared_ptr<GeomDataAPI_Point> aPoint =
768     boost::dynamic_pointer_cast<GeomDataAPI_Point>(theAttribute);
769   if (aPoint)
770   {
771     aPoint->setValue(myParams[aFirstParamPos].val,
772                      myParams[aFirstParamPos+1].val,
773                      myParams[aFirstParamPos+2].val);
774     return ;
775   }
776
777   // Point in 2D
778   boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
779     boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute);
780   if (aPoint2D)
781   {
782     aPoint2D->setValue(myParams[aFirstParamPos].val,
783                        myParams[aFirstParamPos+1].val);
784     return ;
785   }
786
787   /// \todo Support other types of entities
788 }
789
790 void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::updateEntityIfPossible(
791                 boost::shared_ptr<ModelAPI_Attribute> theEntity)
792 {
793   if (myEntityMap.find(theEntity) != myEntityMap.end())
794   {
795     // If the attribute is a point and it is changed (the group needs to rebuild),
796     // probably user has dragged this point into this position,
797     // so it is necessary to add constraint which will guarantee the point will not change
798
799     // Store myNeedToSolve flag to verify the entity is really changed
800     bool aNeedToSolveCopy = myNeedToSolve;
801     myNeedToSolve = false;
802
803     changeEntity(theEntity);
804
805     if (myNeedToSolve) // the entity is changed
806     {
807       // Verify the entity is a point and add temporary constraint of permanency
808       boost::shared_ptr<GeomDataAPI_Point> aPoint =
809         boost::dynamic_pointer_cast<GeomDataAPI_Point>(theEntity);
810       boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
811         boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theEntity);
812       if (aPoint || aPoint2D)
813         addTemporaryConstraintWhereDragged(theEntity);
814     }
815
816     // Restore flag of changes
817     myNeedToSolve = myNeedToSolve || aNeedToSolveCopy;
818   }
819 }
820
821 void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::addTemporaryConstraintWhereDragged(
822                 boost::shared_ptr<ModelAPI_Attribute> theEntity)
823 {
824   // Find identifier of the entity
825   std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
826     anEntIter = myEntityMap.find(theEntity);
827
828   // Create WHERE_DRAGGED constraint
829   Slvs_Constraint aWDConstr = Slvs_MakeConstraint(++myConstrMaxID, myID, SLVS_C_WHERE_DRAGGED,
830                                                   myWorkplane.h, 0.0, anEntIter->second, 0, 0, 0);
831   myConstraints.push_back(aWDConstr);
832   myTempConstraints.push_back(aWDConstr.h);
833 }
834
835 void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::removeTemporaryConstraints()
836 {
837   std::list<Slvs_hConstraint>::reverse_iterator aTmpConstrIter;
838   for (aTmpConstrIter = myTempConstraints.rbegin(); aTmpConstrIter != myTempConstraints.rend(); aTmpConstrIter++)
839   {
840     int aConstrPos = Search(*aTmpConstrIter, myConstraints);
841     myConstraints.erase(myConstraints.begin() + aConstrPos);
842
843     // If the removing constraint has higher index, decrease the indexer
844     if (*aTmpConstrIter == myConstrMaxID)
845       myConstrMaxID--;
846   }
847   myTempConstraints.clear();
848 }
849
850
851
852 // ========================================================
853 // =========      Auxiliary functions       ===============
854 // ========================================================
855
856 template <typename T>
857 int Search(const uint32_t& theEntityID, const std::vector<T>& theEntities)
858 {
859   int aResIndex = theEntityID <= theEntities.size() ? theEntityID - 1 : 0;
860   int aVecSize = theEntities.size();
861   while (aResIndex >= 0 && theEntities[aResIndex].h > theEntityID)
862     aResIndex--;
863   while (aResIndex < aVecSize && theEntities[aResIndex].h < theEntityID)
864     aResIndex++;
865   if (aResIndex == -1)
866     aResIndex = aVecSize;
867   return aResIndex;
868 }