]> SALOME platform Git repositories - modules/shaper.git/blob - src/SketchSolver/SketchSolver_ConstraintGroup.cpp
Salome HOME
Small refactoring of SketchSolver.
[modules/shaper.git] / src / SketchSolver / SketchSolver_ConstraintGroup.cpp
1 // File:    SketchSolver_ConstraintGroup.cpp
2 // Created: 27 May 2014
3 // Author:  Artem ZHIDKOV
4
5 #include "SketchSolver_ConstraintGroup.h"
6
7 #include <SketchSolver_Constraint.h>
8
9 #include <Events_Loop.h>
10 #include <GeomDataAPI_Dir.h>
11 #include <GeomDataAPI_Point.h>
12 #include <GeomDataAPI_Point2D.h>
13 #include <ModelAPI_AttributeDouble.h>
14 #include <ModelAPI_AttributeRefList.h>
15 #include <ModelAPI_Data.h>
16 #include <Model_Events.h>
17
18 #include <SketchPlugin_Constraint.h>
19
20 #include <SketchPlugin_Arc.h>
21 #include <SketchPlugin_Circle.h>
22 #include <SketchPlugin_Line.h>
23 #include <SketchPlugin_Point.h>
24 #include <SketchPlugin_Sketch.h>
25
26 #include <math.h>
27 #include <assert.h>
28
29 /// Tolerance for value of parameters
30 const double tolerance = 1.e-10;
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 // =========  SketchSolver_ConstraintGroup  ===============
46 // ========================================================
47
48 SketchSolver_ConstraintGroup::
49   SketchSolver_ConstraintGroup(boost::shared_ptr<SketchPlugin_Feature> theWorkplane)
50   : myID(++myGroupIndexer),
51     myParamMaxID(0),
52     myEntityMaxID(0),
53     myConstrMaxID(0),
54     myConstraintMap(),
55     myNeedToSolve(false),
56     myConstrSolver()
57 {
58   myParams.clear();
59   myEntities.clear();
60   myConstraints.clear();
61
62   // Initialize workplane
63   myWorkplane.h = SLVS_E_UNKNOWN;
64 #ifndef NDEBUG
65   assert(addWorkplane(theWorkplane));
66 #else
67   addWorkplane(theWorkplane);
68 #endif
69 }
70
71 SketchSolver_ConstraintGroup::~SketchSolver_ConstraintGroup()
72 {
73   myParams.clear();
74   myEntities.clear();
75   myConstraints.clear();
76   myConstraintMap.clear();
77
78   // If the group with maximal identifier is deleted, decrease the indexer
79   if (myID == myGroupIndexer)
80     myGroupIndexer--;
81 }
82
83 // ============================================================================
84 //  Function: isBaseWorkplane
85 //  Class:    SketchSolver_ConstraintGroup
86 //  Purpose:  verify the group is based on the given workplane
87 // ============================================================================
88 bool SketchSolver_ConstraintGroup::isBaseWorkplane(
89                 boost::shared_ptr<SketchPlugin_Feature> theWorkplane) const
90 {
91   return theWorkplane == mySketch;
92 }
93
94 // ============================================================================
95 //  Function: isInteract
96 //  Class:    SketchSolver_ConstraintGroup
97 //  Purpose:  verify are there any entities in the group used by given constraint
98 // ============================================================================
99 bool SketchSolver_ConstraintGroup::isInteract(
100                 boost::shared_ptr<SketchPlugin_Constraint> theConstraint) const
101 {
102   // Check the group is empty
103   if (myWorkplane.h != SLVS_E_UNKNOWN && myConstraints.empty())
104     return true;
105
106   // Go through constraint entities and verify if some of them already in the group
107   for (int i = 0; i < CONSTRAINT_ATTR_SIZE; i++)
108   {
109     boost::shared_ptr<ModelAPI_AttributeRefAttr> aCAttrRef =
110       boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
111         theConstraint->data()->attribute(CONSTRAINT_ATTRIBUTES[i])
112       );
113     if (!aCAttrRef) continue;
114     if (myEntityMap.find(aCAttrRef->attr()) != myEntityMap.end())
115       return true;
116   }
117
118   // Entities did not found
119   return false;
120 }
121
122 // ============================================================================
123 //  Function: changeConstraint
124 //  Class:    SketchSolver_ConstraintGroup
125 //  Purpose:  create/update the constraint in the group
126 // ============================================================================
127 bool SketchSolver_ConstraintGroup::changeConstraint(
128                 boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
129 {
130   // There is no workplane yet, something wrong
131   if (myWorkplane.h == SLVS_E_UNKNOWN)
132     return false;
133
134   // Search this constraint in the current group to update it
135   std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::const_iterator
136     aConstrMapIter = myConstraintMap.find(theConstraint);
137   std::vector<Slvs_Constraint>::iterator aConstrIter;
138   if (aConstrMapIter != myConstraintMap.end())
139   {
140     int aConstrPos = Search(aConstrMapIter->second, myConstraints);
141     aConstrIter = myConstraints.begin() + aConstrPos;
142   }
143
144   // Get constraint type and verify the constraint parameters are correct
145   SketchSolver_Constraint aConstraint(theConstraint);
146   int aConstrType = aConstraint.getType();
147   if (aConstrType == SLVS_C_UNKNOWN ||
148      (aConstrMapIter != myConstraintMap.end() && aConstrIter->type != aConstrType))
149     return false;
150   const std::vector<std::string>& aConstraintAttributes = aConstraint.getAttributes();
151
152   // Create constraint parameters
153   double aDistance = 0.0; // scalar value of the constraint
154   boost::shared_ptr<ModelAPI_AttributeDouble> aDistAttr =
155     boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theConstraint->data()->attribute(CONSTRAINT_ATTR_VALUE));
156   if (aDistAttr)
157   {
158     aDistance = aDistAttr->value();
159     if (aConstrMapIter != myConstraintMap.end() && fabs(aConstrIter->valA - aDistance) > tolerance)
160     {
161       myNeedToSolve = true;
162       aConstrIter->valA = aDistance;
163     }
164   }
165
166   Slvs_hEntity aConstrEnt[CONSTRAINT_ATTR_SIZE]; // parameters of the constraint
167   for (unsigned int indAttr = 0; indAttr < CONSTRAINT_ATTR_SIZE; indAttr++)
168   {
169     aConstrEnt[indAttr] = SLVS_E_UNKNOWN;
170     boost::shared_ptr<ModelAPI_AttributeRefAttr> aConstrAttr =
171       boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
172         theConstraint->data()->attribute(aConstraintAttributes[indAttr])
173       );
174     if (!aConstrAttr) continue;
175     aConstrEnt[indAttr] = changeEntity(aConstrAttr->attr());
176   }
177
178   if (aConstrMapIter == myConstraintMap.end())
179   {
180     // Create SolveSpace constraint structure
181     Slvs_Constraint aConstraint =
182       Slvs_MakeConstraint(++myConstrMaxID, myID, aConstrType, myWorkplane.h,
183                           aDistance, aConstrEnt[0], aConstrEnt[1], aConstrEnt[2], aConstrEnt[3]);
184     myConstraints.push_back(aConstraint);
185     myConstraintMap[theConstraint] = aConstraint.h;
186   }
187   return true;
188 }
189
190 // ============================================================================
191 //  Function: changeEntity
192 //  Class:    SketchSolver_ConstraintGroup
193 //  Purpose:  create/update the element affected by any constraint
194 // ============================================================================
195 Slvs_hEntity SketchSolver_ConstraintGroup::changeEntity(
196                 boost::shared_ptr<ModelAPI_Attribute> theEntity)
197 {
198   // If the entity is already in the group, try to find it
199   std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
200     aEntIter = myEntityMap.find(theEntity);
201   std::vector<Slvs_Param>::const_iterator aParamIter; // looks at first parameter of already existent entity or at the end of vector otherwise
202   if (aEntIter == myEntityMap.end()) // no such entity => should be created
203     aParamIter = myParams.end();
204   else
205   { // the entity already exists
206     int aEntPos = Search(aEntIter->second, myEntities);
207     int aParamPos = Search(myEntities[aEntPos].param[0], myParams);
208     aParamIter = myParams.begin() + aParamPos;
209   }
210
211   // Look over supported types of entities
212
213   // Point in 3D
214   boost::shared_ptr<GeomDataAPI_Point> aPoint =
215     boost::dynamic_pointer_cast<GeomDataAPI_Point>(theEntity);
216   if (aPoint)
217   {
218     Slvs_hParam aX = changeParameter(aPoint->x(), aParamIter);
219     Slvs_hParam aY = changeParameter(aPoint->y(), aParamIter);
220     Slvs_hParam aZ = changeParameter(aPoint->z(), aParamIter);
221
222     if (aEntIter != myEntityMap.end()) // the entity already exists
223       return aEntIter->second;
224
225     // New entity
226     Slvs_Entity aPtEntity = Slvs_MakePoint3d(++myEntityMaxID, myID, aX, aY, aZ);
227     myEntities.push_back(aPtEntity);
228     myEntityMap[theEntity] = aPtEntity.h;
229     return aPtEntity.h;
230   }
231
232   // All entities except 3D points are created on workplane. So, if there is no workplane yet, then error
233   if (myWorkplane.h == SLVS_E_UNKNOWN)
234     return SLVS_E_UNKNOWN;
235
236   // Point in 2D
237   boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
238     boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theEntity);
239   if (aPoint2D)
240   {
241     Slvs_hParam aU = changeParameter(aPoint2D->x(), aParamIter);
242     Slvs_hParam aV = changeParameter(aPoint2D->y(), aParamIter);
243
244     if (aEntIter != myEntityMap.end()) // the entity already exists
245       return aEntIter->second;
246
247     // New entity
248     Slvs_Entity aPt2DEntity = Slvs_MakePoint2d(++myEntityMaxID, myID, myWorkplane.h, aU, aV);
249     myEntities.push_back(aPt2DEntity);
250     myEntityMap[theEntity] = aPt2DEntity.h;
251     return aPt2DEntity.h;
252   }
253
254   // Scalar value (used for the distance entities)
255   boost::shared_ptr<ModelAPI_AttributeDouble> aScalar = 
256     boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theEntity);
257   if (aScalar)
258   {
259     Slvs_hParam aValue = changeParameter(aScalar->value(), aParamIter);
260
261     if (aEntIter != myEntityMap.end()) // the entity already exists
262       return aEntIter->second;
263
264     // New entity
265     Slvs_Entity aDistance = Slvs_MakeDistance(++myEntityMaxID, myID, myWorkplane.h, aValue);
266     myEntities.push_back(aDistance);
267     myEntityMap[theEntity] = aDistance.h;
268     return aDistance.h;
269   }
270
271   // SketchPlugin features
272   boost::shared_ptr<SketchPlugin_Feature> aFeature =
273     boost::dynamic_pointer_cast<SketchPlugin_Feature>(theEntity);
274   if (aFeature)
275   { // Verify the feature by its kind
276     const std::string& aFeatureKind = aFeature->getKind();
277
278     // Line
279     if (aFeatureKind.compare("SketchLine") == 0)
280     {
281       Slvs_hEntity aStart = changeEntity(aFeature->data()->attribute(LINE_ATTR_START));
282       Slvs_hEntity aEnd   = changeEntity(aFeature->data()->attribute(LINE_ATTR_END));
283
284       if (aEntIter != myEntityMap.end()) // the entity already exists
285         return aEntIter->second;
286
287       // New entity
288       Slvs_Entity aLineEntity = Slvs_MakeLineSegment(++myEntityMaxID, myID, myWorkplane.h, aStart, aEnd);
289       myEntities.push_back(aLineEntity);
290       myEntityMap[theEntity] = aLineEntity.h;
291       return aLineEntity.h;
292     }
293     // Circle
294     else if (aFeatureKind.compare("SketchCircle") == 0)
295     {
296       Slvs_hEntity aCenter = changeEntity(aFeature->data()->attribute(CIRCLE_ATTR_CENTER));
297       Slvs_hEntity aRadius = changeEntity(aFeature->data()->attribute(CIRCLE_ATTR_RADIUS));
298
299       if (aEntIter != myEntityMap.end()) // the entity already exists
300         return aEntIter->second;
301
302       // New entity
303       Slvs_Entity aCircleEntity = 
304         Slvs_MakeCircle(++myEntityMaxID, myID, myWorkplane.h, aCenter, myWorkplane.normal, aRadius);
305       myEntities.push_back(aCircleEntity);
306       myEntityMap[theEntity] = aCircleEntity.h;
307       return aCircleEntity.h;
308     }
309     // Arc
310     else if (aFeatureKind.compare("SketchArc") == 0)
311     {
312       Slvs_hEntity aCenter = changeEntity(aFeature->data()->attribute(ARC_ATTR_CENTER));
313       Slvs_hEntity aStart  = changeEntity(aFeature->data()->attribute(ARC_ATTR_START));
314       Slvs_hEntity aEnd    = changeEntity(aFeature->data()->attribute(ARC_ATTR_END));
315
316       if (aEntIter != myEntityMap.end()) // the entity already exists
317         return aEntIter->second;
318
319       Slvs_Entity anArcEntity = Slvs_MakeArcOfCircle(++myEntityMaxID, myID, 
320                                   myWorkplane.h, myWorkplane.normal, aCenter, aStart, aEnd);
321       myEntities.push_back(anArcEntity);
322       myEntityMap[theEntity] = anArcEntity.h;
323       return anArcEntity.h;
324     }
325     // Point (it has low probability to be an attribute of constraint, so it is checked at the end)
326     else if (aFeatureKind.compare("SketchPoint") == 0)
327     {
328       Slvs_hEntity aPoint = changeEntity(aFeature->data()->attribute(POINT_ATTR_COORD));
329
330       if (aEntIter != myEntityMap.end()) // the entity already exists
331         return aEntIter->second;
332
333       // Both the sketch point and its attribute (coordinates) link to the same SolveSpace point identifier
334       myEntityMap[theEntity] = aPoint;
335       return aPoint;
336     }
337   }
338
339   /// \todo Other types of entities
340
341   // Unsupported or wrong entity type
342   return SLVS_E_UNKNOWN;
343 }
344
345 // ============================================================================
346 //  Function: changeNormal
347 //  Class:    SketchSolver_ConstraintGroup
348 //  Purpose:  create/update the normal of workplane
349 // ============================================================================
350 Slvs_hEntity SketchSolver_ConstraintGroup::changeNormal(
351                 boost::shared_ptr<ModelAPI_Attribute> theDirX,
352                 boost::shared_ptr<ModelAPI_Attribute> theDirY,
353                 boost::shared_ptr<ModelAPI_Attribute> theNorm)
354 {
355   boost::shared_ptr<GeomDataAPI_Dir> aDirX =
356     boost::dynamic_pointer_cast<GeomDataAPI_Dir>(theDirX);
357   boost::shared_ptr<GeomDataAPI_Dir> aDirY =
358     boost::dynamic_pointer_cast<GeomDataAPI_Dir>(theDirY);
359   if (!aDirX || !aDirY ||
360      (fabs(aDirX->x()) + fabs(aDirX->y()) + fabs(aDirX->z()) < tolerance) ||
361      (fabs(aDirY->x()) + fabs(aDirY->y()) + fabs(aDirY->z()) < tolerance))
362     return SLVS_E_UNKNOWN;
363
364   // quaternion parameters of normal vector
365   double qw, qx, qy, qz;
366   Slvs_MakeQuaternion(aDirX->x(), aDirX->y(), aDirX->z(),
367                       aDirY->x(), aDirY->y(), aDirY->z(),
368                       &qw, &qx, &qy, &qz);
369   double aNormCoord[4] = {qw, qx, qy, qz};
370
371   // Try to find existent normal
372   std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
373     aEntIter = myEntityMap.find(theNorm);
374   std::vector<Slvs_Param>::const_iterator aParamIter; // looks to the first parameter of already existent entity or to the end of vector otherwise
375   if (aEntIter == myEntityMap.end()) // no such entity => should be created
376     aParamIter = myParams.end();
377   else
378   { // the entity already exists, update it
379     int aEntPos = Search(aEntIter->second, myEntities);
380     int aParamPos = Search(myEntities[aEntPos].param[0], myParams);
381     aParamIter = myParams.begin() + aParamPos;
382   }
383
384   // Change parameters of the normal
385   Slvs_hParam aNormParams[4];
386   for (int i = 0; i < 4; i++)
387     aNormParams[i] = changeParameter(aNormCoord[i], aParamIter);
388
389   if (aEntIter != myEntityMap.end()) // the entity already exists
390     return aEntIter->second;
391
392   // Create a normal
393   Slvs_Entity aNormal = Slvs_MakeNormal3d(++myEntityMaxID, myID,
394                 aNormParams[0], aNormParams[1], aNormParams[2], aNormParams[3]);
395   myEntities.push_back(aNormal);
396   myEntityMap[theNorm] = aNormal.h;
397   return aNormal.h;
398 }
399
400
401 // ============================================================================
402 //  Function: addWorkplane
403 //  Class:    SketchSolver_ConstraintGroup
404 //  Purpose:  create workplane for the group
405 // ============================================================================
406 bool SketchSolver_ConstraintGroup::addWorkplane(
407                 boost::shared_ptr<SketchPlugin_Feature> theSketch)
408 {
409   if (myWorkplane.h || theSketch->getKind().compare("Sketch") != 0)
410     return false; // the workplane already exists or the function parameter is not Sketch
411
412   mySketch = theSketch;
413   updateWorkplane();
414   return true;
415 }
416
417 // ============================================================================
418 //  Function: updateWorkplane
419 //  Class:    SketchSolver_ConstraintGroup
420 //  Purpose:  update parameters of workplane
421 // ============================================================================
422 bool SketchSolver_ConstraintGroup::updateWorkplane()
423 {
424   // Get parameters of workplane
425   boost::shared_ptr<ModelAPI_Attribute> aDirX    = mySketch->data()->attribute(SKETCH_ATTR_DIRX);
426   boost::shared_ptr<ModelAPI_Attribute> aDirY    = mySketch->data()->attribute(SKETCH_ATTR_DIRY);
427   boost::shared_ptr<ModelAPI_Attribute> aNorm    = mySketch->data()->attribute(SKETCH_ATTR_NORM);
428   boost::shared_ptr<ModelAPI_Attribute> anOrigin = mySketch->data()->attribute(SKETCH_ATTR_ORIGIN);
429   // Transform them into SolveSpace format
430   Slvs_hEntity aNormalWP = changeNormal(aDirX, aDirY, aNorm);
431   if (!aNormalWP) return false;
432   Slvs_hEntity anOriginWP = changeEntity(anOrigin);
433   if (!anOriginWP) return false;
434
435   if (!myWorkplane.h)
436   {
437     // Create workplane
438     myWorkplane = Slvs_MakeWorkplane(++myEntityMaxID, myID, anOriginWP, aNormalWP);
439     // Workplane should be added to the list of entities
440     myEntities.push_back(myWorkplane);
441   }
442   return true;
443 }
444
445 // ============================================================================
446 //  Function: changeParameter
447 //  Class:    SketchSolver_ConstraintGroup
448 //  Purpose:  create/update value of parameter
449 // ============================================================================
450 Slvs_hParam SketchSolver_ConstraintGroup::changeParameter(
451                 const double&                            theParam,
452                 std::vector<Slvs_Param>::const_iterator& thePrmIter)
453 {
454   if (thePrmIter != myParams.end())
455   { // Parameter should be updated
456     int aParamPos = thePrmIter - myParams.begin();
457     if (fabs(thePrmIter->val - theParam) > tolerance)
458     {
459       myNeedToSolve = true; // parameter is changed, need to resolve constraints
460       myParams[aParamPos].val = theParam;
461     }
462     thePrmIter++;
463     return myParams[aParamPos].h;
464   }
465
466   // Newly created parameter
467   Slvs_Param aParam = Slvs_MakeParam(++myParamMaxID, myID, theParam);
468   myParams.push_back(aParam);
469   myNeedToSolve = true;
470   // The list of parameters is changed, move iterator to the end of the list to avoid problems
471   thePrmIter = myParams.end();
472   return aParam.h;
473 }
474
475 // ============================================================================
476 //  Function: resolveConstraints
477 //  Class:    SketchSolver_ConstraintGroup
478 //  Purpose:  solve the set of constraints for the current group
479 // ============================================================================
480 void SketchSolver_ConstraintGroup::resolveConstraints()
481 {
482   if (!myNeedToSolve)
483     return;
484
485   myConstrSolver.setGroupID(myID);
486   myConstrSolver.setParameters(myParams);
487   myConstrSolver.setEntities(myEntities);
488   myConstrSolver.setConstraints(myConstraints);
489
490   if (myConstrSolver.solve() == SLVS_RESULT_OKAY)
491   { // solution succeeded, store results into correspondent attributes
492     // Obtain result into the same list of parameters
493     if (!myConstrSolver.getResult(myParams))
494       return;
495
496     std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator
497       anEntIter = myEntityMap.begin();
498     for ( ; anEntIter != myEntityMap.end(); anEntIter++)
499       updateAttribute(anEntIter->first, anEntIter->second);
500   }
501   /// \todo Implement error handling
502
503   removeTemporaryConstraints();
504   myNeedToSolve = false;
505 }
506
507 // ============================================================================
508 //  Function: mergeGroups
509 //  Class:    SketchSolver_ConstraintGroup
510 //  Purpose:  append specified group to the current group
511 // ============================================================================
512 void SketchSolver_ConstraintGroup::mergeGroups(
513                 const SketchSolver_ConstraintGroup& theGroup)
514 {
515   // If specified group is empty, no need to merge
516   if (theGroup.myConstraintMap.empty())
517     return ;
518
519   // NOTE: The possibility, that some elements are placed into both groups, is around 0, 
520   // so the objects should be copied with changing the indexes
521
522   // Maps between old and new indexes of SolveSpace elements:
523   std::map<Slvs_hParam, Slvs_hParam>           aParamMap;
524   std::map<Slvs_hEntity, Slvs_hEntity>         anEntityMap;
525   std::map<Slvs_hConstraint, Slvs_hConstraint> aConstrMap;
526
527   // Go through copying constraints
528   std::vector<Slvs_Constraint>::const_iterator aConstrIter = theGroup.myConstraints.begin();
529   for ( ; aConstrIter != theGroup.myConstraints.end(); aConstrIter++)
530   {
531     Slvs_Constraint aConstraintCopy = *aConstrIter;
532     // Go through constraint entities
533     Slvs_hEntity* anEntities[CONSTRAINT_ATTR_SIZE] = {
534       &(aConstraintCopy.ptA),     &(aConstraintCopy.ptB), 
535       &(aConstraintCopy.entityA), &(aConstraintCopy.entityB)
536     };
537     for (int indEnt = 0; indEnt < CONSTRAINT_ATTR_SIZE; indEnt++)
538     {
539       if (*(anEntities[indEnt]) == 0)
540         continue;
541       if (anEntityMap.find(*(anEntities[indEnt])) != anEntityMap.end())
542       { // entity is already copied
543         *(anEntities[indEnt]) = anEntityMap[*(anEntities[indEnt])];
544         continue;
545       }
546
547       // Copy entity
548       Slvs_Entity anEntityCopy = theGroup.myEntities[Search(*(anEntities[indEnt]), theGroup.myEntities)];
549       // Go through entity parameters
550       const int aNbEntParams = 4; // maximal number of entity parameters
551       for (int indPrm = 0; indPrm < aNbEntParams; indPrm++)
552       {
553         if (anEntityCopy.param[indPrm] == 0)
554           continue;
555         if (aParamMap.find(anEntityCopy.param[indPrm]) != aParamMap.end())
556         {
557           anEntityCopy.param[indPrm] = aParamMap[anEntityCopy.param[indPrm]];
558           continue;
559         }
560
561         Slvs_Param aParamCopy = theGroup.myParams[Search(anEntityCopy.param[indPrm], theGroup.myParams)];
562         aParamMap[aParamCopy.h] = ++myParamMaxID;
563         aParamCopy.h = myParamMaxID;
564         myParams.push_back(aParamCopy);
565       }
566
567       anEntityMap[anEntityCopy.h] = ++myEntityMaxID;
568       anEntityCopy.h = myEntityMaxID;
569       myEntities.push_back(anEntityCopy);
570       *(anEntities[indEnt]) = anEntityCopy.h;
571     }
572
573     aConstraintCopy.h = ++myConstrMaxID;
574     myConstraints.push_back(aConstraintCopy);
575     aConstrMap[aConstrIter->h] = aConstraintCopy.h;
576   }
577
578   // Append maps of SketchPlugin to SolveSpace parameters
579   std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::const_iterator
580     aSPConstrMapIter = theGroup.myConstraintMap.begin();
581   for ( ; aSPConstrMapIter!= theGroup.myConstraintMap.end(); aSPConstrMapIter++)
582   {
583     std::map<Slvs_hConstraint, Slvs_hConstraint>::iterator aFind = aConstrMap.find(aSPConstrMapIter->second);
584     if (aFind != aConstrMap.end())
585       myConstraintMap[aSPConstrMapIter->first] = aFind->second;
586   }
587
588   std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
589     aSPEntMapIter = theGroup.myEntityMap.begin();
590   for ( ; aSPEntMapIter != theGroup.myEntityMap.end(); aSPEntMapIter++) {
591     std::map<Slvs_hEntity, Slvs_hEntity>::iterator aFind = anEntityMap.find(aSPEntMapIter->second);
592     if (aFind != anEntityMap.end())
593       myEntityMap[aSPEntMapIter->first] = aFind->second;
594   }
595
596   // Add temporary constraints
597   std::list<Slvs_hConstraint>::const_iterator aTempConstrIter = theGroup.myTempConstraints.begin();
598   for ( ; aTempConstrIter != theGroup.myTempConstraints.end(); aTempConstrIter++)
599   {
600     std::map<Slvs_hConstraint, Slvs_hConstraint>::iterator aFind = aConstrMap.find(*aTempConstrIter);
601     if (aFind != aConstrMap.end())
602       myTempConstraints.push_back(aFind->second);
603   }
604   myTempConstraints.sort();
605
606   myNeedToSolve = myNeedToSolve || theGroup.myNeedToSolve;
607 }
608
609 // ============================================================================
610 //  Function: updateGroup
611 //  Class:    SketchSolver_ConstraintGroup
612 //  Purpose:  search removed entities and constraints
613 // ============================================================================
614 bool SketchSolver_ConstraintGroup::updateGroup()
615 {
616   // Check for valid sketch
617   if (!mySketch->data()->isValid())
618     return true;
619
620   // Fast check for constraint validity. If all constraints are valid, no need to update the group
621   std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::reverse_iterator
622     aConstrIter = myConstraintMap.rbegin();
623   bool isAllValid = true;
624   for ( ; isAllValid && aConstrIter != myConstraintMap.rend(); aConstrIter++)
625     if (!aConstrIter->first->data()->isValid())
626       isAllValid = false;
627   if (isAllValid)
628     return false;
629
630   // Remove invalid constraints.
631   // There only constraint will be deleted (parameters and entities) will be removed below
632   std::list< boost::shared_ptr<SketchPlugin_Constraint> > aConstrToDelete;
633   std::map<Slvs_hEntity, bool> anEntToDelete; // entities will be removed if no valid constraints use them
634   for (aConstrIter = myConstraintMap.rbegin(); aConstrIter != myConstraintMap.rend(); aConstrIter++)
635   {
636     bool isValid = aConstrIter->first->data()->isValid();
637
638     int aConstrPos = Search(aConstrIter->second, myConstraints);
639     if (aConstrPos < (int)myConstraints.size())
640     {
641       Slvs_hEntity aConstrEnt[] = {
642         myConstraints[aConstrPos].ptA,     myConstraints[aConstrPos].ptB,
643         myConstraints[aConstrPos].entityA, myConstraints[aConstrPos].entityB};
644       for (int i = 0; i < 4; i++)
645         if (aConstrEnt[i] != SLVS_E_UNKNOWN)
646         {
647           if (anEntToDelete.find(aConstrEnt[i]) == anEntToDelete.end())
648             anEntToDelete[aConstrEnt[i]] = !isValid;
649           else if (isValid) // constraint is valid => no need to remove its entities
650             anEntToDelete[aConstrEnt[i]] = false;
651         }
652       if (!isValid)
653       {
654         myConstraints.erase(myConstraints.begin() + aConstrPos);
655         if (aConstrIter->second == myConstrMaxID) // When the constraint with highest ID is removed, decrease indexer
656           myConstrMaxID--;
657         aConstrToDelete.push_front(aConstrIter->first);
658       }
659     }
660   }
661   std::list< boost::shared_ptr<SketchPlugin_Constraint> >::iterator aDelIter;
662   for (aDelIter = aConstrToDelete.begin(); aDelIter != aConstrToDelete.end(); aDelIter++)
663     myConstraintMap.erase(*aDelIter);
664
665   // Remove invalid and unused entities
666   std::map<Slvs_hEntity, bool>::reverse_iterator aEDelIter;
667   for (aEDelIter = anEntToDelete.rbegin(); aEDelIter != anEntToDelete.rend(); aEDelIter++)
668     if (aEDelIter->second)
669     {
670       int anEntPos = Search(aEDelIter->first, myEntities);
671       std::vector<Slvs_Entity>::iterator aEntIter = myEntities.begin() + anEntPos;
672       // Number of parameters for the entity
673       int aNbParams = 0;
674       while (aEntIter->param[aNbParams]) aNbParams++;
675       if (aNbParams == 0) continue;
676       // Decrease parameter indexer if there are deleted parameter with higher IDs
677       if (aEntIter->param[aNbParams-1] == myParamMaxID)
678         myParamMaxID -= aNbParams;
679       // Remove parameters of the entity
680       int aParamPos = Search(aEntIter->param[0], myParams);
681       myParams.erase(myParams.begin() + aParamPos,
682                      myParams.begin() + aParamPos + aNbParams);
683
684       // Remove entity
685       if (aEDelIter->first == myEntityMaxID)
686         myEntityMaxID--;
687       myEntities.erase(myEntities.begin() + anEntPos);
688       // Remove such entity from myEntityMap
689       std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator
690         anEntMapIter = myEntityMap.begin();
691       for ( ; anEntMapIter != myEntityMap.end(); anEntMapIter++)
692         if (anEntMapIter->second == aEDelIter->first)
693           break;
694       if (anEntMapIter != myEntityMap.end())
695         myEntityMap.erase(anEntMapIter);
696     }
697
698   return false;
699 }
700
701 // ============================================================================
702 //  Function: updateAttribute
703 //  Class:    SketchSolver_ConstraintGroup
704 //  Purpose:  update features of sketch after resolving constraints
705 // ============================================================================
706 void SketchSolver_ConstraintGroup::updateAttribute(
707                 boost::shared_ptr<ModelAPI_Attribute> theAttribute,
708                 const Slvs_hEntity&                   theEntityID)
709 {
710   // Search the position of the first parameter of the entity
711   int anEntPos = Search(theEntityID, myEntities);
712   int aFirstParamPos = Search(myEntities[anEntPos].param[0], myParams);
713
714   // Look over supported types of entities
715
716   // Point in 3D
717   boost::shared_ptr<GeomDataAPI_Point> aPoint =
718     boost::dynamic_pointer_cast<GeomDataAPI_Point>(theAttribute);
719   if (aPoint)
720   {
721     aPoint->setValue(myParams[aFirstParamPos].val,
722                      myParams[aFirstParamPos+1].val,
723                      myParams[aFirstParamPos+2].val);
724     return ;
725   }
726
727   // Point in 2D
728   boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
729     boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute);
730   if (aPoint2D)
731   {
732     aPoint2D->setValue(myParams[aFirstParamPos].val,
733                        myParams[aFirstParamPos+1].val);
734     return ;
735   }
736
737   // Scalar value
738   boost::shared_ptr<ModelAPI_AttributeDouble> aScalar = 
739     boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theAttribute);
740   if (aScalar)
741   {
742     aScalar->setValue(myParams[aFirstParamPos].val);
743     return ;
744   }
745
746   /// \todo Support other types of entities
747 }
748
749 // ============================================================================
750 //  Function: updateEntityIfPossible
751 //  Class:    SketchSolver_ConstraintGroup
752 //  Purpose:  search the entity in this group and update it
753 // ============================================================================
754 void 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 guarantee 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 // ============================================================================
786 //  Function: addTemporaryConstraintWhereDragged
787 //  Class:    SketchSolver_ConstraintGroup
788 //  Purpose:  add transient constraint SLVS_C_WHERE_DRAGGED for the entity, 
789 //            which was moved by user
790 // ============================================================================
791 void SketchSolver_ConstraintGroup::addTemporaryConstraintWhereDragged(
792                 boost::shared_ptr<ModelAPI_Attribute> theEntity)
793 {
794   // Find identifier of the entity
795   std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
796     anEntIter = myEntityMap.find(theEntity);
797
798   // Create SLVS_C_WHERE_DRAGGED constraint
799   Slvs_Constraint aWDConstr = Slvs_MakeConstraint(++myConstrMaxID, myID, SLVS_C_WHERE_DRAGGED,
800                                                   myWorkplane.h, 0.0, anEntIter->second, 0, 0, 0);
801   myConstraints.push_back(aWDConstr);
802   myTempConstraints.push_back(aWDConstr.h);
803 }
804
805 // ============================================================================
806 //  Function: removeTemporaryConstraints
807 //  Class:    SketchSolver_ConstraintGroup
808 //  Purpose:  remove all transient SLVS_C_WHERE_DRAGGED constraints after
809 //            resolving the set of constraints
810 // ============================================================================
811 void SketchSolver_ConstraintGroup::removeTemporaryConstraints()
812 {
813   std::list<Slvs_hConstraint>::reverse_iterator aTmpConstrIter;
814   for (aTmpConstrIter = myTempConstraints.rbegin(); aTmpConstrIter != myTempConstraints.rend(); aTmpConstrIter++)
815   {
816     int aConstrPos = Search(*aTmpConstrIter, myConstraints);
817     myConstraints.erase(myConstraints.begin() + aConstrPos);
818
819     // If the removing constraint has higher index, decrease the indexer
820     if (*aTmpConstrIter == myConstrMaxID)
821       myConstrMaxID--;
822   }
823   myTempConstraints.clear();
824 }
825
826
827
828 // ========================================================
829 // =========      Auxiliary functions       ===============
830 // ========================================================
831
832 template <typename T>
833 int Search(const uint32_t& theEntityID, const std::vector<T>& theEntities)
834 {
835   int aResIndex = theEntityID <= theEntities.size() ? theEntityID - 1 : 0;
836   int aVecSize = theEntities.size();
837   while (aResIndex >= 0 && theEntities[aResIndex].h > theEntityID)
838     aResIndex--;
839   while (aResIndex < aVecSize && theEntities[aResIndex].h < theEntityID)
840     aResIndex++;
841   if (aResIndex == -1)
842     aResIndex = aVecSize;
843   return aResIndex;
844 }