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