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