Salome HOME
Merge branch 'Dev_1.1.0' of newgeom:newgeom into Dev_1.1.0
[modules/shaper.git] / src / SketchSolver / SketchSolver_ConstraintGroup.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 // File:    SketchSolver_ConstraintGroup.cpp
4 // Created: 27 May 2014
5 // Author:  Artem ZHIDKOV
6
7 #include "SketchSolver_ConstraintGroup.h"
8
9 #include <SketchSolver_Constraint.h>
10
11 #include <Events_Error.h>
12 #include <Events_Loop.h>
13 #include <GeomAPI_XY.h>
14 #include <GeomAPI_Dir2d.h>
15 #include <GeomAPI_Pnt2d.h>
16 #include <GeomDataAPI_Dir.h>
17 #include <GeomDataAPI_Point.h>
18 #include <GeomDataAPI_Point2D.h>
19 #include <ModelAPI_AttributeDouble.h>
20 #include <ModelAPI_AttributeRefList.h>
21 #include <ModelAPI_Document.h>
22 #include <ModelAPI_Events.h>
23 #include <ModelAPI_ResultConstruction.h>
24
25 #include <SketchPlugin_Constraint.h>
26 #include <SketchPlugin_ConstraintFillet.h>
27 #include <SketchPlugin_ConstraintLength.h>
28 #include <SketchPlugin_ConstraintCoincidence.h>
29 #include <SketchPlugin_ConstraintMirror.h>
30 #include <SketchPlugin_ConstraintRigid.h>
31
32 #include <SketchPlugin_Arc.h>
33 #include <SketchPlugin_Circle.h>
34 #include <SketchPlugin_Line.h>
35 #include <SketchPlugin_Point.h>
36 #include <SketchPlugin_Sketch.h>
37
38 #include <math.h>
39 #include <assert.h>
40
41 /// Tolerance for value of parameters
42 const double tolerance = 1.e-10;
43
44 /**
45  * Collects all sketch solver error' codes
46  * as inline static functions
47  */
48  // TODO: Move this class into a separate file
49 class SketchSolver_Error
50 {
51  public:
52   /// The value parameter for the constraint
53   inline static const std::string& CONSTRAINTS()
54   {
55     static const std::string MY_ERROR_VALUE("Conflicting constraints");
56     return MY_ERROR_VALUE;
57   }
58   /// The entities need to have shared point, but they have not
59   inline static const std::string& NO_COINCIDENT_POINTS()
60   {
61     static const std::string MY_ERROR_VALUE("Objects should have coincident point");
62     return MY_ERROR_VALUE;
63   }
64 };
65
66 /// This value is used to give unique index to the groups
67 static Slvs_hGroup myGroupIndexer = 0;
68
69 /** \brief Search the entity/parameter with specified ID in the list of elements
70  *  \param[in] theEntityID unique ID of the element
71  *  \param[in] theEntities list of elements
72  *  \return position of the found element or -1 if the element is not found
73  */
74 template<typename T>
75 static int Search(const uint32_t& theEntityID, const std::vector<T>& theEntities);
76
77 // ========================================================
78 // =========  SketchSolver_ConstraintGroup  ===============
79 // ========================================================
80
81 SketchSolver_ConstraintGroup::SketchSolver_ConstraintGroup(
82     std::shared_ptr<ModelAPI_CompositeFeature> theWorkplane)
83     : myID(++myGroupIndexer),
84       myParamMaxID(0),
85       myEntityMaxID(0),
86       myConstrMaxID(0),
87       myConstraintMap(),
88       myNeedToSolve(false),
89       myConstrSolver()
90 {
91   myParams.clear();
92   myEntities.clear();
93   myEntOfConstr.clear();
94   myConstraints.clear();
95
96   myTempConstraints.clear();
97   myTempPointWhereDragged.clear();
98   myTempPointWDrgdID = 0;
99
100   // Initialize workplane
101   myWorkplane.h = SLVS_E_UNKNOWN;
102 #ifndef NDEBUG
103   assert(addWorkplane(theWorkplane));
104 #else
105   addWorkplane(theWorkplane);
106 #endif
107 }
108
109 SketchSolver_ConstraintGroup::~SketchSolver_ConstraintGroup()
110 {
111   myParams.clear();
112   myEntities.clear();
113   myEntOfConstr.clear();
114   myConstraints.clear();
115   myConstraintMap.clear();
116   myTempConstraints.clear();
117   myTempPointWhereDragged.clear();
118
119   // If the group with maximal identifier is deleted, decrease the indexer
120   if (myID == myGroupIndexer)
121     myGroupIndexer--;
122 }
123
124 // ============================================================================
125 //  Function: isBaseWorkplane
126 //  Class:    SketchSolver_ConstraintGroup
127 //  Purpose:  verify the group is based on the given workplane
128 // ============================================================================
129 bool SketchSolver_ConstraintGroup::isBaseWorkplane(
130     std::shared_ptr<ModelAPI_CompositeFeature> theWorkplane) const
131 {
132   return theWorkplane == mySketch;
133 }
134
135 // ============================================================================
136 //  Function: isInteract
137 //  Class:    SketchSolver_ConstraintGroup
138 //  Purpose:  verify are there any entities in the group used by given constraint
139 // ============================================================================
140 bool SketchSolver_ConstraintGroup::isInteract(
141     std::shared_ptr<SketchPlugin_Feature> theFeature) const
142 {
143   // Check the group is empty
144   if (isEmpty())
145     return true;
146
147   // Check if the feature is already in the group
148   if (myEntityFeatMap.find(theFeature) != myEntityFeatMap.end())
149     return true;
150   std::shared_ptr<SketchPlugin_Constraint> aConstr =
151       std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
152   if (aConstr && myConstraintMap.find(aConstr) != myConstraintMap.end())
153     return true;
154
155   // Go through the attributes and verify if some of them already in the group
156   std::list<std::shared_ptr<ModelAPI_Attribute>> 
157       anAttrList = theFeature->data()->attributes(std::string());
158   std::list<std::shared_ptr<ModelAPI_Attribute>>::const_iterator
159       anAttrIter = anAttrList.begin();
160   for ( ; anAttrIter != anAttrList.end(); anAttrIter++) {
161     AttributeRefListPtr aCAttrRefList = 
162         std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(*anAttrIter);
163     if (aCAttrRefList) {
164       std::list<ObjectPtr> anObjList = aCAttrRefList->list();
165       std::list<ObjectPtr>::iterator anIt = anObjList.begin();
166       for ( ; anIt != anObjList.end(); anIt++) {
167         FeaturePtr aFeature = std::dynamic_pointer_cast<SketchPlugin_Feature>(*anIt);
168         if (aFeature && myEntityFeatMap.find(aFeature) != myEntityFeatMap.end())
169           return true;
170       }
171       continue;
172     }
173     AttributeRefAttrPtr aCAttrRef =
174         std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(*anAttrIter);
175     if (!aCAttrRef || !aCAttrRef->isObject()) {
176       std::shared_ptr<ModelAPI_Attribute> anAttr = 
177           aCAttrRef ? aCAttrRef->attr() : *anAttrIter;
178       if (myEntityAttrMap.find(anAttr) != myEntityAttrMap.end())
179         return true;
180     } else {
181       ResultConstructionPtr aRC = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(
182           aCAttrRef->object());
183       if (!aRC)
184         continue;
185       std::shared_ptr<ModelAPI_Document> aDoc = aRC->document();
186       FeaturePtr aFeature = aDoc->feature(aRC);
187       if (myEntityFeatMap.find(aFeature) != myEntityFeatMap.end())
188         return true;
189       // search attributes of a feature to be parameters of constraint
190       std::list<std::shared_ptr<ModelAPI_Attribute> > aFeatAttrList =
191           aFeature->data()->attributes(std::string());
192       std::list<std::shared_ptr<ModelAPI_Attribute> >::const_iterator aFAIter = aFeatAttrList
193           .begin();
194       for (; aFAIter != aFeatAttrList.end(); aFAIter++)
195         if (myEntityAttrMap.find(*aFAIter) != myEntityAttrMap.end())
196           return true;
197     }
198   }
199
200   // Entities did not found
201   return false;
202 }
203
204 // ============================================================================
205 //  Function: checkConstraintConsistence
206 //  Class:    SketchSolver_ConstraintGroup
207 //  Purpose:  verifies and changes parameters of the constraint
208 // ============================================================================
209 void SketchSolver_ConstraintGroup::checkConstraintConsistence(Slvs_Constraint& theConstraint)
210 {
211   if (theConstraint.type == SLVS_C_PT_LINE_DISTANCE) {
212     // Get constraint parameters and check the sign of constraint value
213
214     // point coordinates
215     int aPtPos = Search(theConstraint.ptA, myEntities);
216     int aPtParamPos = Search(myEntities[aPtPos].param[0], myParams);
217     std::shared_ptr<GeomAPI_XY> aPoint(
218         new GeomAPI_XY(myParams[aPtParamPos].val, myParams[aPtParamPos + 1].val));
219
220     // line coordinates
221     int aLnPos = Search(theConstraint.entityA, myEntities);
222     aPtPos = Search(myEntities[aLnPos].point[0], myEntities);
223     aPtParamPos = Search(myEntities[aPtPos].param[0], myParams);
224     std::shared_ptr<GeomAPI_XY> aStart(
225         new GeomAPI_XY(-myParams[aPtParamPos].val, -myParams[aPtParamPos + 1].val));
226     aPtPos = Search(myEntities[aLnPos].point[1], myEntities);
227     aPtParamPos = Search(myEntities[aPtPos].param[0], myParams);
228     std::shared_ptr<GeomAPI_XY> aEnd(
229         new GeomAPI_XY(myParams[aPtParamPos].val, myParams[aPtParamPos + 1].val));
230
231     aEnd = aEnd->added(aStart);
232     aPoint = aPoint->added(aStart);
233     if (aPoint->cross(aEnd) * theConstraint.valA < 0.0)
234       theConstraint.valA *= -1.0;
235   }
236 }
237
238 // ============================================================================
239 //  Function: changeConstraint
240 //  Class:    SketchSolver_ConstraintGroup
241 //  Purpose:  create/update the constraint in the group
242 // ============================================================================
243 bool SketchSolver_ConstraintGroup::changeConstraint(
244     std::shared_ptr<SketchPlugin_Constraint> theConstraint)
245 {
246   // There is no workplane yet, something wrong
247   if (myWorkplane.h == SLVS_E_UNKNOWN)
248     return false;
249
250   if (theConstraint) {
251     if (theConstraint->getKind() == SketchPlugin_ConstraintRigid::ID())
252       return changeRigidConstraint(theConstraint);
253     if (theConstraint->getKind() == SketchPlugin_ConstraintMirror::ID())
254       return changeMirrorConstraint(theConstraint);
255     if (theConstraint->getKind() == SketchPlugin_ConstraintFillet::ID())
256       return changeFilletConstraint(theConstraint);
257   }
258
259   // Search this constraint in the current group to update it
260   ConstraintMap::const_iterator aConstrMapIter = myConstraintMap.find(theConstraint);
261   std::vector<Slvs_Constraint>::iterator aConstrIter;
262   if (aConstrMapIter != myConstraintMap.end()) {
263     int aConstrPos = Search(aConstrMapIter->second.front(), myConstraints);
264     aConstrIter = myConstraints.begin() + aConstrPos;
265   }
266
267   // Get constraint type and verify the constraint parameters are correct
268   SketchSolver_Constraint aConstraint(theConstraint);
269   int aConstrType = aConstraint.getType();
270   if (aConstrType == SLVS_C_UNKNOWN
271       || (aConstrMapIter != myConstraintMap.end() && aConstrIter->type != aConstrType))
272     return false;
273   const std::vector<std::string>& aConstraintAttributes = aConstraint.getAttributes();
274
275   // Create constraint parameters
276   double aDistance = 0.0;  // scalar value of the constraint
277   AttributeDoublePtr aDistAttr = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
278       theConstraint->data()->attribute(SketchPlugin_Constraint::VALUE()));
279   if (aDistAttr) {
280     aDistance = aDistAttr->value();
281     // Issue #196: checking the positivity of the distance constraint
282     if (aDistance < tolerance &&
283        (aConstrType == SLVS_C_PT_PT_DISTANCE || aConstrType == SLVS_C_PT_LINE_DISTANCE))
284       return false;
285     // SketchPlugin circle defined by its radius, but SolveSpace uses constraint for diameter
286     if (aConstrType == SLVS_C_DIAMETER)
287       aDistance *= 2.0;
288     if (aConstrMapIter != myConstraintMap.end()
289         && fabs(aConstrIter->valA - aDistance) > tolerance) {
290       myNeedToSolve = true;
291       aConstrIter->valA = aDistance;
292     }
293   }
294
295   size_t aNbTmpConstraints = myTempConstraints.size();
296   Slvs_hEntity aConstrEnt[CONSTRAINT_ATTR_SIZE];  // parameters of the constraint
297   for (unsigned int indAttr = 0; indAttr < CONSTRAINT_ATTR_SIZE; indAttr++) {
298     aConstrEnt[indAttr] = SLVS_E_UNKNOWN;
299     std::shared_ptr<ModelAPI_AttributeRefAttr> aConstrAttr = std::dynamic_pointer_cast<
300         ModelAPI_AttributeRefAttr>(
301         theConstraint->data()->attribute(aConstraintAttributes[indAttr]));
302     if (!aConstrAttr)
303       continue;
304
305     // Convert the object of the attribute to the feature
306     FeaturePtr aFeature;
307     if (aConstrAttr->isObject() && aConstrAttr->object()) {
308       ResultConstructionPtr aRC = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(
309           aConstrAttr->object());
310       if (!aRC)
311         continue;
312       std::shared_ptr<ModelAPI_Document> aDoc = aRC->document();
313       aFeature = aDoc->feature(aRC);
314     }
315
316     // For the length constraint the start and end points of the line should be added to the entities list instead of line
317     if (aConstrType == SLVS_C_PT_PT_DISTANCE
318         && theConstraint->getKind().compare(SketchPlugin_ConstraintLength::ID()) == 0) {
319       Slvs_hEntity aLineEnt = changeEntityFeature(aFeature);
320       int aEntPos = Search(aLineEnt, myEntities);
321       aConstrEnt[indAttr++] = myEntities[aEntPos].point[0];
322       aConstrEnt[indAttr++] = myEntities[aEntPos].point[1];
323       while (indAttr < CONSTRAINT_ATTR_SIZE)
324         aConstrEnt[indAttr++] = 0;
325       break;  // there should be no other entities
326     } else if (aConstrAttr->isObject())
327       aConstrEnt[indAttr] = changeEntityFeature(aFeature);
328     else
329       aConstrEnt[indAttr] = changeEntity(aConstrAttr->attr());
330   }
331
332   if (aConstrMapIter == myConstraintMap.end()) { // Add new constraint
333     // Several points may be coincident, it is not necessary to store all constraints between them.
334     // Try to find sequence of coincident points which connects the points of new constraint
335     if (aConstrType == SLVS_C_POINTS_COINCIDENT) {
336       if (aConstrEnt[0] == aConstrEnt[1])  // no need to add self coincidence
337         return false;
338       if (!addCoincidentPoints(aConstrEnt[0], aConstrEnt[1])) {
339         myExtraCoincidence.insert(theConstraint);  // the constraint is stored for further purposes
340         return false;
341       }
342       if (aNbTmpConstraints < myTempConstraints.size()) {
343         // There was added temporary constraint. Check that there is no coincident points which already rigid.
344
345         // Get list of already fixed points
346         std::set<Slvs_hEntity> anAlreadyFixed;
347         std::vector<Slvs_Constraint>::const_iterator aCIter = myConstraints.begin();
348         for (; aCIter != myConstraints.end(); aCIter++)
349           if (aCIter->type == SLVS_C_WHERE_DRAGGED) {
350             std::list<Slvs_hConstraint>::const_iterator aTmpIt = myTempConstraints.begin();
351             for (; aTmpIt != myTempConstraints.end(); aTmpIt++)
352               if (*aTmpIt == aCIter->h)
353                 break;
354             if (aTmpIt == myTempConstraints.end())
355               anAlreadyFixed.insert(aCIter->ptA);
356           }
357
358         std::set<Slvs_hConstraint> aTmpConstrToDelete;
359         std::list<Slvs_hConstraint>::reverse_iterator aTmpIter = myTempConstraints.rbegin();
360         size_t aCurSize = myTempConstraints.size();
361         for (; aCurSize > aNbTmpConstraints && aTmpIter != myTempConstraints.rend();
362             aTmpIter++, aCurSize--) {
363           int aConstrPos = Search(*aTmpIter, myConstraints);
364           std::vector<std::set<Slvs_hEntity> >::const_iterator
365             aCoincIter = myCoincidentPoints.begin();
366           for (; aCoincIter != myCoincidentPoints.end(); aCoincIter++)
367             if (aCoincIter->find(myConstraints[aConstrPos].ptA) != aCoincIter->end()) {
368               std::set<Slvs_hEntity>::const_iterator anIt;
369               for (anIt = aCoincIter->begin(); anIt != aCoincIter->end(); anIt++)
370                 if (anAlreadyFixed.find(*anIt) != anAlreadyFixed.end()) {
371                   aTmpConstrToDelete.insert(*aTmpIter);
372                   break;
373                 }
374               break;
375             }
376         }
377         if (!aTmpConstrToDelete.empty())
378           removeTemporaryConstraints(aTmpConstrToDelete);
379       }
380     }
381     // For the tangency constraints it is necessary to identify which points of entities are coincident
382     int aSlvsOtherFlag = 0;
383     int aSlvsOther2Flag = 0;
384     if (aConstrType == SLVS_C_ARC_LINE_TANGENT || aConstrType == SLVS_C_CURVE_CURVE_TANGENT) {
385       // Search entities used by constraint
386       int anEnt1Pos = Search(aConstrEnt[2], myEntities);
387       int anEnt2Pos = Search(aConstrEnt[3], myEntities);
388       // Obtain start and end points of entities
389       Slvs_hEntity aPointsToFind[4];
390       aPointsToFind[0] = myEntities[anEnt1Pos].point[1];
391       aPointsToFind[1]= myEntities[anEnt1Pos].point[2];
392       bool hasLine = (myEntities[anEnt2Pos].type == SLVS_E_LINE_SEGMENT);
393       aPointsToFind[2]= myEntities[anEnt2Pos].point[hasLine ? 0 : 1];
394       aPointsToFind[3]= myEntities[anEnt2Pos].point[hasLine ? 1 : 2];
395       // Search coincident points
396       bool isPointFound[4];
397       std::vector<std::set<Slvs_hEntity> >::const_iterator aCPIter = myCoincidentPoints.begin();
398       for ( ; aCPIter != myCoincidentPoints.end(); aCPIter++) {
399         for (int i = 0; i < 4; i++)
400           isPointFound[i] = (aCPIter->find(aPointsToFind[i]) != aCPIter->end());
401         if ((isPointFound[0] || isPointFound[1]) && (isPointFound[2] || isPointFound[3])) {
402           // the arc is tangent by end point
403           if (isPointFound[1]) aSlvsOtherFlag = 1;
404           // the second item is an arc and it is tangent by end point too
405           if (!hasLine && isPointFound[3]) aSlvsOther2Flag = 1;
406           break;
407         }
408       }
409       if (aCPIter == myCoincidentPoints.end()) {
410         // There is no coincident points between tangential objects. Generate error message
411         Events_Error::send(SketchSolver_Error::NO_COINCIDENT_POINTS(), this);
412         return false;
413       }
414     }
415
416     // Create SolveSpace constraint structure
417     Slvs_Constraint aSlvsConstr = Slvs_MakeConstraint(++myConstrMaxID, myID, aConstrType,
418                                                       myWorkplane.h, aDistance, aConstrEnt[0],
419                                                       aConstrEnt[1], aConstrEnt[2], aConstrEnt[3]);
420     if (aSlvsOtherFlag != 0) aSlvsConstr.other = aSlvsOtherFlag;
421     if (aSlvsOther2Flag != 0) aSlvsConstr.other2 = aSlvsOther2Flag;
422     myConstraints.push_back(aSlvsConstr);
423     myConstraintMap[theConstraint] = std::vector<Slvs_hEntity>(1, aSlvsConstr.h);
424     int aConstrPos = Search(aSlvsConstr.h, myConstraints);
425     aConstrIter = myConstraints.begin() + aConstrPos;
426     myNeedToSolve = true;
427   } else { // Attributes of constraint may be changed => update constraint
428     Slvs_hEntity* aCurrentAttr[] = {&aConstrIter->ptA, &aConstrIter->ptB,
429                                    &aConstrIter->entityA, &aConstrIter->entityB,
430                                    &aConstrIter->entityC, &aConstrIter->entityD};
431     for (unsigned int indAttr = 0; indAttr < CONSTRAINT_ATTR_SIZE; indAttr++) {
432       if (*(aCurrentAttr[indAttr]) != aConstrEnt[indAttr])
433       {
434         *(aCurrentAttr[indAttr]) = aConstrEnt[indAttr];
435         myNeedToSolve = true;
436       }
437     }
438   }
439
440   // Update flags of entities to be used by constraints
441   for (unsigned int indAttr = 0; indAttr < CONSTRAINT_ATTR_SIZE; indAttr++)
442     if (aConstrEnt[indAttr] != 0) {
443       int aPos = Search(aConstrEnt[indAttr], myEntities);
444       myEntOfConstr[aPos] = true;
445       // Sub-entities should be used implcitly
446       Slvs_hEntity* aEntPtr = myEntities[aPos].point;
447       while (*aEntPtr != 0) {
448         aPos = Search(*aEntPtr, myEntities);
449         myEntOfConstr[aPos] = true;
450         aEntPtr++;
451       }
452     }
453
454   checkConstraintConsistence(*aConstrIter);
455   return true;
456 }
457
458 // ============================================================================
459 //  Function: changeRigidConstraint
460 //  Class:    SketchSolver_ConstraintGroup
461 //  Purpose:  create/update the "Rigid" constraint in the group
462 // ============================================================================
463 bool SketchSolver_ConstraintGroup::changeRigidConstraint(
464     std::shared_ptr<SketchPlugin_Constraint> theConstraint)
465 {
466   // Search this constraint in the current group to update it
467   ConstraintMap::const_iterator aConstrMapIter = myConstraintMap.find(theConstraint);
468   std::vector<Slvs_Constraint>::iterator aConstrIter;
469   if (aConstrMapIter != myConstraintMap.end()) {
470     int aConstrPos = Search(aConstrMapIter->second.front(), myConstraints);
471     aConstrIter = myConstraints.begin() + aConstrPos;
472   }
473
474   // Get constraint type and verify the constraint parameters are correct
475   SketchSolver_Constraint aConstraint(theConstraint);
476   int aConstrType = aConstraint.getType();
477   if (aConstrType == SLVS_C_UNKNOWN
478       || (aConstrMapIter != myConstraintMap.end() && aConstrIter->type != aConstrType))
479     return false;
480   const std::vector<std::string>& aConstraintAttributes = aConstraint.getAttributes();
481
482   Slvs_hEntity aConstrEnt = SLVS_E_UNKNOWN;
483   std::shared_ptr<ModelAPI_AttributeRefAttr> aConstrAttr = std::dynamic_pointer_cast<
484       ModelAPI_AttributeRefAttr>(
485       theConstraint->data()->attribute(aConstraintAttributes[0]));
486   if (!aConstrAttr)
487     return false;
488
489   // Convert the object of the attribute to the feature
490   FeaturePtr aFeature;
491   if (aConstrAttr->isObject() && aConstrAttr->object()) {
492     ResultConstructionPtr aRC = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(
493         aConstrAttr->object());
494     if (!aRC)
495       return false;
496     std::shared_ptr<ModelAPI_Document> aDoc = aRC->document();
497     aFeature = aDoc->feature(aRC);
498   }
499
500   aConstrEnt = aConstrAttr->isObject() ? changeEntityFeature(aFeature) : changeEntity(aConstrAttr->attr());
501
502   if (aConstrMapIter == myConstraintMap.end()) { // Add new constraint
503     // Check the fixed entity is not a point.
504     std::shared_ptr<ModelAPI_AttributeRefAttr> aConstrAttr = std::dynamic_pointer_cast<
505         ModelAPI_AttributeRefAttr>(theConstraint->data()->attribute(aConstraintAttributes[0]));
506     std::shared_ptr<GeomDataAPI_Point> aPoint =
507         std::dynamic_pointer_cast<GeomDataAPI_Point>(aConstrAttr->attr());
508     std::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
509         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aConstrAttr->attr());
510     std::shared_ptr<SketchPlugin_Point> aSketchPoint = 
511         std::dynamic_pointer_cast<SketchPlugin_Point>(aFeature);
512     if (aPoint || aPoint2D || aSketchPoint) {
513       // Create SolveSpace constraint structure
514       Slvs_Constraint aConstraint = Slvs_MakeConstraint(
515           ++myConstrMaxID, myID, aConstrType, myWorkplane.h, 0.0,
516           aConstrEnt, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN);
517       myConstraints.push_back(aConstraint);
518       myConstraintMap[theConstraint] = std::vector<Slvs_hEntity>(1, aConstraint.h);
519       int aConstrPos = Search(aConstraint.h, myConstraints);
520       aConstrIter = myConstraints.begin() + aConstrPos;
521       myNeedToSolve = true;
522     } else {
523       myConstraintMap[theConstraint] = std::vector<Slvs_hConstraint>();
524
525       // To avoid SolveSpace problems:
526       // * if the circle is rigid, we will fix its center and radius;
527       // * if the arc is rigid, we will fix its start and end points and radius.
528       double aRadius = 0.0;
529       bool isArc = false;
530       bool isCircle = false;
531       if (aFeature) {
532         if (aFeature->getKind() == SketchPlugin_Arc::ID()) {
533           std::shared_ptr<GeomDataAPI_Point2D> aCenter =
534               std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
535               aFeature->data()->attribute(SketchPlugin_Arc::CENTER_ID()));
536           std::shared_ptr<GeomDataAPI_Point2D> aStart =
537               std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
538               aFeature->data()->attribute(SketchPlugin_Arc::START_ID()));
539           aRadius = aStart->pnt()->distance(aCenter->pnt());
540           isArc = true;
541         } else if (aFeature->getKind() == SketchPlugin_Circle::ID()) {
542           aRadius = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
543               aFeature->data()->attribute(SketchPlugin_Circle::RADIUS_ID()))->value();
544           isCircle = true;
545         }
546       }
547
548       // Get list of already fixed points
549       std::set<Slvs_hEntity> anAlreadyFixed;
550       std::vector<Slvs_Constraint>::const_iterator aCIter = myConstraints.begin();
551       for (; aCIter != myConstraints.end(); aCIter++)
552         if (aCIter->type == SLVS_C_WHERE_DRAGGED)
553           anAlreadyFixed.insert(aCIter->ptA);
554
555       // Create constraints to fix the parameters of the entity
556       int aEntPos = Search(aConstrEnt, myEntities);
557       Slvs_hEntity* aPointsPtr = myEntities[aEntPos].point;
558       if (isArc) aPointsPtr++; // avoid to fix center of arc
559       while (*aPointsPtr != 0) {
560         // Avoid to create additional "Rigid" constraints for coincident points
561         bool isCoincAlreadyFixed = false;
562         if (!anAlreadyFixed.empty()) {
563           if (anAlreadyFixed.find(*aPointsPtr) != anAlreadyFixed.end())
564             isCoincAlreadyFixed = true;
565
566           std::vector<std::set<Slvs_hEntity> >::const_iterator aCoincIter =
567               myCoincidentPoints.begin();
568           for (; !isCoincAlreadyFixed && aCoincIter != myCoincidentPoints.end(); aCoincIter++) {
569             if (aCoincIter->find(*aPointsPtr) == aCoincIter->end())
570               continue;
571             std::set<Slvs_hEntity>::const_iterator anIter = anAlreadyFixed.begin();
572             for (; !isCoincAlreadyFixed && anIter != anAlreadyFixed.end(); anIter++)
573               if (aCoincIter->find(*anIter) != aCoincIter->end())
574                 isCoincAlreadyFixed = true;
575           }
576         }
577
578         if (!isCoincAlreadyFixed) {
579           Slvs_Constraint aConstraint = Slvs_MakeConstraint(
580               ++myConstrMaxID, myID, aConstrType, myWorkplane.h, 0.0,
581               *aPointsPtr, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN);
582           myConstraints.push_back(aConstraint);
583           myConstraintMap[theConstraint].push_back(aConstraint.h);
584         }
585         aPointsPtr++;
586       }
587
588       if (isArc || isCircle) { // add radius constraint
589         Slvs_Constraint aConstraint = Slvs_MakeConstraint(
590             ++myConstrMaxID, myID, SLVS_C_DIAMETER, myWorkplane.h, 2.0 * aRadius,
591             SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, aConstrEnt, SLVS_E_UNKNOWN);
592         myConstraints.push_back(aConstraint);
593         myConstraintMap[theConstraint].push_back(aConstraint.h);
594       }
595
596       // The object is already rigid, so there is no constraints added
597       if (myConstraintMap[theConstraint].empty()) {
598         myConstraintMap.erase(theConstraint);
599         myNeedToSolve = false;
600       }
601       else
602         myNeedToSolve = true;
603     }
604   }
605   return true;
606 }
607
608 // ============================================================================
609 //  Function: changeMirrorConstraint
610 //  Class:    SketchSolver_ConstraintGroup
611 //  Purpose:  create/update the "Mirror" constraint in the group
612 // ============================================================================
613 bool SketchSolver_ConstraintGroup::changeMirrorConstraint(
614     std::shared_ptr<SketchPlugin_Constraint> theConstraint)
615 {
616   DataPtr aConstrData = theConstraint->data();
617
618   // Search this constraint in the current group to update it
619   ConstraintMap::const_iterator aConstrMapIter = myConstraintMap.find(theConstraint);
620   std::vector<Slvs_Constraint>::iterator aConstrIter;
621   bool isExists = false;
622   if (aConstrMapIter != myConstraintMap.end()) {
623     int aConstrPos = Search(aConstrMapIter->second.front(), myConstraints);
624     aConstrIter = myConstraints.begin() + aConstrPos;
625     isExists = true;
626   }
627
628   // Get constraint type and verify the constraint parameters are correct
629   SketchSolver_Constraint aConstraint(theConstraint);
630   int aConstrType = aConstraint.getType();
631   if (aConstrType == SLVS_C_UNKNOWN
632       || (aConstrMapIter != myConstraintMap.end() && aConstrIter->type != aConstrType))
633     return false;
634   const std::vector<std::string>& aConstraintAttributes = aConstraint.getAttributes();
635
636   Slvs_hEntity aMirrorLineEnt = SLVS_E_UNKNOWN;
637   AttributeRefAttrPtr aConstrAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
638       aConstrData->attribute(aConstraintAttributes[0]));
639   if (!aConstrAttr)
640     return false;
641
642   // Convert the object of the attribute to the feature
643   FeaturePtr aMirrorLineFeat;
644   if (aConstrAttr->isObject() && aConstrAttr->object()) {
645     ResultConstructionPtr aRC = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(
646         aConstrAttr->object());
647     if (!aRC)
648       return false;
649     std::shared_ptr<ModelAPI_Document> aDoc = aRC->document();
650     aMirrorLineFeat = aDoc->feature(aRC);
651   }
652   aMirrorLineEnt = aConstrAttr->isObject() ?
653       changeEntityFeature(aMirrorLineFeat) : changeEntity(aConstrAttr->attr());
654
655   // Append symmetric constraint for each point of mirroring features
656   AttributeRefListPtr aBaseRefList = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
657     aConstrData->attribute(aConstraintAttributes[1]));
658   AttributeRefListPtr aMirroredRefList = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
659     aConstrData->attribute(aConstraintAttributes[2]));
660   if (!aBaseRefList || !aMirroredRefList)
661     return false;
662
663   std::list<ObjectPtr> aBaseList = aBaseRefList->list();
664   std::list<ObjectPtr> aMirroredList = aMirroredRefList->list();
665   if (aBaseList.empty() || aBaseList.size() != aMirroredList.size())
666     return false;
667
668   std::vector<Slvs_hConstraint> aNewConstraints;
669   // Fill the list of already mirrored points
670   std::vector<Slvs_Constraint> anOldConstraints;
671   std::map<Slvs_hEntity, Slvs_hEntity> aMirroredPoints;
672   if (isExists) {
673     std::vector<Slvs_hConstraint>::const_iterator aCIter = aConstrMapIter->second.begin();
674     for (; aCIter != aConstrMapIter->second.end(); aCIter++) {
675       int anInd = Search(*aCIter, myConstraints);
676       if (myConstraints[anInd].type != aConstrType)
677         continue;
678       aMirroredPoints[myConstraints[anInd].ptA] = myConstraints[anInd].ptB;
679       anOldConstraints.push_back(myConstraints[anInd]);
680     }
681   }
682
683   FeaturePtr aBaseFeature, aMirrorFeature;
684   ResultConstructionPtr aRC;
685   std::list<ObjectPtr>::iterator aBaseIter = aBaseList.begin();
686   std::list<ObjectPtr>::iterator aMirIter = aMirroredList.begin();
687   for ( ; aBaseIter != aBaseList.end(); aBaseIter++, aMirIter++) {
688     aRC = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(*aBaseIter);
689     aBaseFeature = aRC ? aRC->document()->feature(aRC) :
690         std::dynamic_pointer_cast<ModelAPI_Feature>(*aBaseIter);
691     aRC = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(*aMirIter);
692     aMirrorFeature = aRC ? aRC->document()->feature(aRC) :
693         std::dynamic_pointer_cast<ModelAPI_Feature>(*aMirIter);
694
695     if (!aBaseFeature || !aMirrorFeature || 
696         aBaseFeature->getKind() != aMirrorFeature->getKind())
697       return false;
698     Slvs_hEntity aBaseEnt = changeEntityFeature(aBaseFeature);
699     Slvs_hEntity aMirrorEnt = changeEntityFeature(aMirrorFeature);
700     // Make aMirrorEnt parameters to be symmetric with aBaseEnt
701     makeMirrorEntity(aBaseEnt, aMirrorEnt, aMirrorLineEnt);
702
703     if (aBaseFeature->getKind() == SketchPlugin_Point::ID()) {
704       Slvs_hConstraint anID = changeMirrorPoints(aBaseEnt, aMirrorEnt,
705           aMirrorLineEnt, anOldConstraints, aMirroredPoints);
706       aNewConstraints.push_back(anID);
707     } else {
708       int aBasePos = Search(aBaseEnt, myEntities);
709       int aMirrorPos = Search(aMirrorEnt, myEntities);
710       if (aBaseFeature->getKind() == SketchPlugin_Line::ID()) {
711         for (int ind = 0; ind < 2; ind++) {
712           Slvs_hConstraint anID = changeMirrorPoints(myEntities[aBasePos].point[ind],
713               myEntities[aMirrorPos].point[ind], aMirrorLineEnt, anOldConstraints, aMirroredPoints);
714           aNewConstraints.push_back(anID);
715         }
716       } else if (aBaseFeature->getKind() == SketchPlugin_Circle::ID()) {
717         Slvs_hConstraint anID = changeMirrorPoints(myEntities[aBasePos].point[0],
718             myEntities[aMirrorPos].point[0], aMirrorLineEnt, anOldConstraints, aMirroredPoints);
719         aNewConstraints.push_back(anID);
720         // Additional constraint for equal radii
721         Slvs_Constraint anEqRadConstr = Slvs_MakeConstraint(
722             ++myConstrMaxID, myID, SLVS_C_EQUAL_RADIUS, myWorkplane.h, 0.0,
723             SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, aBaseEnt, aMirrorEnt);
724         myConstraints.push_back(anEqRadConstr);
725         myConstraintMap[theConstraint].push_back(anEqRadConstr.h);
726       } else if (aBaseFeature->getKind() == SketchPlugin_Arc::ID()) {
727         // Workaround to avoid problems in SolveSpace.
728         // The symmetry of two arcs will be done using symmetry of three points on these arcs:
729         // start point, end point, and any other point on the arc
730         Slvs_hEntity aBaseArcPoints[3] = {
731             myEntities[aBasePos].point[1],
732             myEntities[aBasePos].point[2],
733             SLVS_E_UNKNOWN};
734         Slvs_hEntity aMirrorArcPoints[3] = { // indices of points of arc, center corresponds center, first point corresponds last point
735             myEntities[aMirrorPos].point[2],
736             myEntities[aMirrorPos].point[1],
737             SLVS_E_UNKNOWN};
738         Slvs_hEntity aBothArcs[2] = {aBaseEnt, aMirrorEnt};
739         Slvs_hEntity aBothMiddlePoints[2];
740         for (int i = 0; i < 2; i++) {
741           double x, y;
742           calculateMiddlePoint(aBothArcs[i], x, y);
743           std::vector<Slvs_Param>::iterator aParamIter = myParams.end();
744           Slvs_hParam u = changeParameter(x, aParamIter);
745           Slvs_hParam v = changeParameter(y, aParamIter);
746           Slvs_Entity aPoint = Slvs_MakePoint2d(++myEntityMaxID, myID, myWorkplane.h, u, v);
747           myEntities.push_back(aPoint);
748           myEntOfConstr.push_back(true);
749           aBothMiddlePoints[i] = aPoint.h;
750           // additional constraint point-on-curve
751           Slvs_Constraint aPonCircConstr = Slvs_MakeConstraint(
752               ++myConstrMaxID, myID, SLVS_C_PT_ON_CIRCLE, myWorkplane.h, 0.0,
753               aPoint.h, SLVS_E_UNKNOWN, aBothArcs[i], SLVS_E_UNKNOWN);
754           myConstraints.push_back(aPonCircConstr);
755           myConstraintMap[theConstraint].push_back(aPonCircConstr.h);
756           aNewConstraints.push_back(aPonCircConstr.h);
757         }
758
759         aBaseArcPoints[2] = aBothMiddlePoints[0];
760         aMirrorArcPoints[2] = aBothMiddlePoints[1];
761         for (int ind = 0; ind < 3; ind++) {
762           Slvs_hConstraint anID = changeMirrorPoints(aBaseArcPoints[ind], aMirrorArcPoints[ind],
763               aMirrorLineEnt, anOldConstraints, aMirroredPoints);
764           aNewConstraints.push_back(anID);
765         }
766       }
767     }
768   }
769
770   // Remove unused constraints
771   std::vector<Slvs_Constraint>::const_iterator anOldCIter = anOldConstraints.begin();
772   for (; anOldCIter != anOldConstraints.end(); anOldCIter++) {
773     int anInd = Search(anOldCIter->h, myConstraints);
774     myConstraints.erase(myConstraints.begin() + anInd);
775   }
776   myConstraintMap[theConstraint] = aNewConstraints;
777
778   if (!isExists) {
779     // Set the mirror line unchanged during constraint recalculation
780     int aMirrorLinePos = Search(aMirrorLineEnt, myEntities);
781     // firstly check the line is not fixed yet
782     bool isFixed[2] = {false, false};
783     std::vector<Slvs_Constraint>::const_iterator aConstrIter = myConstraints.begin();
784     for (; aConstrIter != myConstraints.end() && !(isFixed[0] && isFixed[1]); aConstrIter++)
785       if (aConstrIter->type == SLVS_C_WHERE_DRAGGED) {
786         if (aConstrIter->ptA == myEntities[aMirrorLinePos].point[0])
787           isFixed[0] = true;
788         else if (aConstrIter->ptA == myEntities[aMirrorLinePos].point[1])
789           isFixed[1] = true;
790       }
791     // add new rigid constraints
792     if (!isFixed[0]) {
793       Slvs_Constraint aRigidStart = Slvs_MakeConstraint(
794           ++myConstrMaxID, myID, SLVS_C_WHERE_DRAGGED, myWorkplane.h, 0,
795           myEntities[aMirrorLinePos].point[0], SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN);
796       myConstraints.push_back(aRigidStart);
797       myConstraintMap[theConstraint].push_back(aRigidStart.h);
798     }
799     if (!isFixed[1]) {
800       Slvs_Constraint aRigidEnd = Slvs_MakeConstraint(
801           ++myConstrMaxID, myID, SLVS_C_WHERE_DRAGGED, myWorkplane.h, 0,
802           myEntities[aMirrorLinePos].point[1], SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN);
803       myConstraints.push_back(aRigidEnd);
804       myConstraintMap[theConstraint].push_back(aRigidEnd.h);
805     }
806
807     // Add temporary constraints for initial objects to be unchanged
808     for (aBaseIter = aBaseList.begin(); aBaseIter != aBaseList.end(); aBaseIter++) {
809       aRC = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(*aBaseIter);
810       aBaseFeature = aRC ? aRC->document()->feature(aRC) :
811           std::dynamic_pointer_cast<ModelAPI_Feature>(*aBaseIter);
812       if (!aBaseFeature) continue;
813       std::list<AttributePtr> aPoints = aBaseFeature->data()->attributes(GeomDataAPI_Point2D::typeId());
814       if (aBaseFeature->getKind() != SketchPlugin_Arc::ID()) {
815         std::list<AttributePtr>::iterator anIt = aPoints.begin();
816         for ( ; anIt != aPoints.end(); anIt++) {
817           addTemporaryConstraintWhereDragged(*anIt);
818         }
819       } else {
820         // Arcs are fixed by center and start points only (to avoid solving errors in SolveSpace)
821         AttributePtr aCenterAttr = aBaseFeature->attribute(SketchPlugin_Arc::CENTER_ID());
822         std::map<AttributePtr, Slvs_hEntity>::iterator aFound = myEntityAttrMap.find(aCenterAttr);
823         Slvs_hEntity anArcPoints[3] = {aFound->second, 0, 0};
824         AttributePtr aStartAttr = aBaseFeature->attribute(SketchPlugin_Arc::START_ID());
825         aFound = myEntityAttrMap.find(aStartAttr);
826         anArcPoints[1] = aFound->second;
827         AttributePtr aEndAttr = aBaseFeature->attribute(SketchPlugin_Arc::END_ID());
828         aFound = myEntityAttrMap.find(aEndAttr);
829         anArcPoints[2] = aFound->second;
830
831         bool isFixed[3] = {false, false, false};
832         int aNbFixed = 0; // number of already fixed points on the arc
833         for (int i = 0; i < 3; i++) {
834           std::vector<std::set<Slvs_hEntity> >::iterator aCoPtIter = myCoincidentPoints.begin();
835           for (; aCoPtIter != myCoincidentPoints.end() && !isFixed[i]; aCoPtIter++) {
836             if (aCoPtIter->find(anArcPoints[i]) == aCoPtIter->end())
837               continue;  // the entity was not found in current set
838
839             // Find one of already created SLVS_C_WHERE_DRAGGED constraints in current set of coincident points
840             std::vector<Slvs_Constraint>::iterator aConstrIter = myConstraints.begin();
841             for (; aConstrIter != myConstraints.end(); aConstrIter++)
842               if (aConstrIter->type == SLVS_C_WHERE_DRAGGED && 
843                   aCoPtIter->find(aConstrIter->ptA) != aCoPtIter->end()) {
844                 isFixed[i] = true;
845                 aNbFixed++;
846                 break;  // the SLVS_C_WHERE_DRAGGED constraint already exists
847               }
848           }
849         }
850         if (aNbFixed < 2) { // append constraints
851           if (!isFixed[0])
852             addTemporaryConstraintWhereDragged(aCenterAttr);
853           if (!isFixed[1] && (isFixed[0] || aNbFixed == 0))
854             addTemporaryConstraintWhereDragged(aStartAttr);
855         }
856       }
857     }
858   }
859   return true;
860 }
861
862 // ============================================================================
863 //  Function: changeFilletConstraint
864 //  Class:    SketchSolver_ConstraintGroup
865 //  Purpose:  create/update the "Fillet" constraint in the group
866 // ============================================================================
867 bool SketchSolver_ConstraintGroup::changeFilletConstraint(
868     std::shared_ptr<SketchPlugin_Constraint> theConstraint)
869 {
870   DataPtr aConstrData = theConstraint->data();
871
872   // Search this constraint in the current group to update it
873   ConstraintMap::const_iterator aConstrMapIter = myConstraintMap.find(theConstraint);
874   std::vector<Slvs_Constraint>::iterator aConstrIter;
875   if (aConstrMapIter != myConstraintMap.end()) {
876     int aConstrPos = Search(aConstrMapIter->second.front(), myConstraints);
877     aConstrIter = myConstraints.begin() + aConstrPos;
878   }
879
880   // Get constraint type and verify the constraint parameters are correct
881   SketchSolver_Constraint aConstraint(theConstraint);
882   int aConstrType = aConstraint.getType();
883   if (aConstrType == SLVS_C_UNKNOWN)
884     return false;
885   const std::vector<std::string>& aConstraintAttributes = aConstraint.getAttributes();
886
887   // Obtain hEntity for basic objects of fillet
888   Slvs_hEntity aBaseObject[2];
889   FeaturePtr aBaseFeature[2];
890   for (unsigned int indAttr = 0; indAttr < 2; indAttr++) {
891     AttributeRefAttrPtr aConstrAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
892         aConstrData->attribute(aConstraintAttributes[indAttr]));
893     if (!aConstrAttr)
894       return false;
895     if (aConstrAttr->isObject() && aConstrAttr->object()) {
896       ResultConstructionPtr aRC = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(
897           aConstrAttr->object());
898       if (!aRC)
899         return false;
900       std::shared_ptr<ModelAPI_Document> aDoc = aRC->document();
901       aBaseFeature[indAttr] = aDoc->feature(aRC);
902     }
903     aBaseObject[indAttr] = aConstrAttr->isObject() ?
904         changeEntityFeature(aBaseFeature[indAttr]) : changeEntity(aConstrAttr->attr());
905   }
906   // Check the base entities have a coincident point
907   int aBaseObjInd[2] = {
908       Search(aBaseObject[0], myEntities),
909       Search(aBaseObject[1], myEntities)
910     };
911   int aShift[2] = { // shift for calculating correct start and end points for different types of objects
912       myEntities[aBaseObjInd[0]].type == SLVS_E_ARC_OF_CIRCLE ? 1 : 0,
913       myEntities[aBaseObjInd[1]].type == SLVS_E_ARC_OF_CIRCLE ? 1 : 0,
914     };
915   Slvs_hEntity aFirstObjPoints[2] = { // indices of start and end point of first object
916       myEntities[aBaseObjInd[0]].point[aShift[0]],
917       myEntities[aBaseObjInd[0]].point[1+aShift[0]]
918     };
919   Slvs_hEntity aSecondObjPoints[2] = { // indices of start and end point of second object
920       myEntities[aBaseObjInd[1]].point[aShift[1]],
921       myEntities[aBaseObjInd[1]].point[1+aShift[1]]
922     };
923   bool isCoincidentFound = false;
924   int aBaseCoincInd[2] = {0, 0}; // indices in aFirstObjPoint and aSecondObjPoint identifying coincident points
925   std::vector<std::set<Slvs_hEntity> >::iterator aCPIter = myCoincidentPoints.begin();
926   for ( ; aCPIter != myCoincidentPoints.end() && !isCoincidentFound; aCPIter++)
927     for (int ind1 = 0; ind1 < 2 && !isCoincidentFound; ind1++)
928       for (int ind2 = 0; ind2 < 2 && !isCoincidentFound; ind2++)
929         if (aCPIter->find(aFirstObjPoints[ind1]) != aCPIter->end() &&
930             aCPIter->find(aSecondObjPoints[ind2]) != aCPIter->end()) {
931           aBaseCoincInd[0] = ind1;
932           aBaseCoincInd[1] = ind2;
933           isCoincidentFound = true;
934         }
935   if (!isCoincidentFound) {
936     // There is no coincident points between objects. Generate error message
937     Events_Error::send(SketchSolver_Error::NO_COINCIDENT_POINTS(), this);
938     return false;
939   }
940
941   // Create fillet entities
942   // - first object is placed on the first base 
943   // - second object is on the second base 
944   // - third object is a filleting arc
945   static const int aNbFilletEnt = 3;
946   Slvs_hEntity aFilletEnt[aNbFilletEnt];
947   int aFilletObjInd[aNbFilletEnt];
948   AttributeRefListPtr aFilletRefList = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
949       aConstrData->attribute(aConstraintAttributes[2]));
950   if (!aFilletRefList)
951     return false;
952   std::list<ObjectPtr> aFilletList = aFilletRefList->list();
953   if (aFilletList.size() < aNbFilletEnt)
954     return false;
955   FeaturePtr aFilletFeature;
956   ResultConstructionPtr aRC;
957   std::list<ObjectPtr>::iterator aFilIter = aFilletList.begin();
958   for (int indEnt = 0; aFilIter != aFilletList.end(); aFilIter++, indEnt++) {
959     aRC = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(*aFilIter);
960     aFilletFeature = aRC ? aRC->document()->feature(aRC) :
961         std::dynamic_pointer_cast<ModelAPI_Feature>(*aFilIter);
962     if (!aFilletFeature)
963       return false;
964     aFilletEnt[indEnt] = changeEntityFeature(aFilletFeature);
965     if (aFilletEnt[indEnt] == SLVS_E_UNKNOWN)
966       return false; // not all attributes are initialized yet
967     aFilletObjInd[indEnt] = Search(aFilletEnt[indEnt], myEntities);
968   }
969   // At first time, for correct result, move floating points of fillet on the middle points of base objects
970   if (myConstraintMap.find(theConstraint) == myConstraintMap.end()) {
971     double anArcPoints[6];
972     for (int indEnt = 0; indEnt < aNbFilletEnt - 1; indEnt++) {
973       int anIndShift = myEntities[aFilletObjInd[indEnt]].type == SLVS_E_ARC_OF_CIRCLE ? 1 : 0;
974       int aPointsPos[2] = {
975           Search(myEntities[aFilletObjInd[indEnt]].point[anIndShift], myEntities),
976           Search(myEntities[aFilletObjInd[indEnt]].point[1+anIndShift], myEntities)
977         };
978       int aParamPos[2] = {
979           Search(myEntities[aPointsPos[0]].param[0], myParams),
980           Search(myEntities[aPointsPos[1]].param[0], myParams)
981         };
982       int anIndex = aParamPos[aBaseCoincInd[indEnt]];
983       if (anIndShift == 0) {
984         myParams[anIndex].val =
985             0.5 * (myParams[aParamPos[0]].val + myParams[aParamPos[1]].val);
986         myParams[1 + anIndex].val =
987             0.5 * (myParams[1 + aParamPos[0]].val + myParams[1 + aParamPos[1]].val);
988       } else { // place the changed point on the arc
989         double x = 0, y = 0;
990         calculateMiddlePoint(aFilletEnt[indEnt], x, y);
991         myParams[anIndex].val = x;
992         myParams[1 + anIndex].val = y;
993       }
994       anArcPoints[indEnt*2+2] = myParams[anIndex].val;
995       anArcPoints[indEnt*2+3] = myParams[1 + anIndex].val;
996     }
997     anArcPoints[0] = 0.5 * (anArcPoints[2] + anArcPoints[4]);
998     anArcPoints[1] = 0.5 * (anArcPoints[3] + anArcPoints[5]);
999     for (int indArcPt = 0; indArcPt < 3; indArcPt++) {
1000       int aPtPos = Search(myEntities[aFilletObjInd[2]].point[indArcPt], myEntities);
1001       int aParamPos = Search(myEntities[aPtPos].param[0], myParams);
1002       myParams[aParamPos].val = anArcPoints[indArcPt * 2];
1003       myParams[aParamPos + 1].val = anArcPoints[indArcPt * 2 + 1];
1004     }
1005   }
1006
1007   // Create list of constraints to generate fillet
1008   int aPtInd;
1009   std::vector<Slvs_hConstraint> aConstrList;
1010   bool isExists = myConstraintMap.find(theConstraint) != myConstraintMap.end(); // constraint already exists
1011   std::vector<Slvs_hConstraint>::iterator aCMapIter =
1012     isExists ? myConstraintMap[theConstraint].begin() : aConstrList.begin();
1013   std::vector<Slvs_hConstraint>::iterator aCMapEnd =
1014     isExists ? myConstraintMap[theConstraint].end() : aConstrList.end();
1015   int aCurConstrPos = isExists ? Search(*aCMapIter, myConstraints) : 0;
1016   for (int indEnt = 0; indEnt < aNbFilletEnt - 1; indEnt++) {
1017     // one point of fillet object should be coincident with the point on base, non-coincident with another base object
1018     aPtInd = 1-aBaseCoincInd[indEnt]+aShift[indEnt]; // (1-aBaseCoincInd[indEnt]) = index of non-coincident point, aShift is used to process all types of shapes
1019     Slvs_hEntity aPtBase = myEntities[aBaseObjInd[indEnt]].point[aPtInd];
1020     Slvs_hEntity aPtFillet = myEntities[aFilletObjInd[indEnt]].point[aPtInd];
1021     if (isExists) {
1022       myConstraints[aCurConstrPos].ptA = aPtBase;
1023       myConstraints[aCurConstrPos].ptB = aPtFillet;
1024       aCMapIter++;
1025       aCurConstrPos = Search(*aCMapIter, myConstraints);
1026     } else if (addCoincidentPoints(aPtBase, aPtFillet)) { // the points are not connected by coincidence yet
1027       Slvs_Constraint aCoincConstr = Slvs_MakeConstraint(
1028           ++myConstrMaxID, myID, SLVS_C_POINTS_COINCIDENT, myWorkplane.h,
1029           0, aPtBase, aPtFillet, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN);
1030       myConstraints.push_back(aCoincConstr);
1031       aConstrList.push_back(aCoincConstr.h);
1032     }
1033
1034     // another point of fillet object should be placed on the base object
1035     Slvs_Constraint aPonCurveConstr;
1036     int aTangentType;
1037     if (myEntities[aFilletObjInd[indEnt]].type == SLVS_E_ARC_OF_CIRCLE) {
1038       // centers of arcs should be coincident
1039       aPtBase = myEntities[aBaseObjInd[indEnt]].point[0];
1040       aPtFillet = myEntities[aFilletObjInd[indEnt]].point[0];
1041       if (isExists) {
1042         myConstraints[aCurConstrPos].ptA = aPtBase;
1043         myConstraints[aCurConstrPos].ptB = aPtFillet;
1044         aCMapIter++;
1045         if (aCMapIter != aCMapEnd)
1046           aCurConstrPos = Search(*aCMapIter, myConstraints);
1047       } else if (addCoincidentPoints(aPtBase, aPtFillet)) { // the points are not connected by coincidence yet
1048         aPonCurveConstr = Slvs_MakeConstraint(
1049             ++myConstrMaxID, myID, SLVS_C_POINTS_COINCIDENT, myWorkplane.h,
1050             0, aPtBase, aPtFillet, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN);
1051       }
1052       aPtFillet = myEntities[aFilletObjInd[indEnt]].point[1+aBaseCoincInd[indEnt]]; // !!! will be used below
1053       aTangentType = SLVS_C_CURVE_CURVE_TANGENT;
1054     } else {
1055       aPtInd = aBaseCoincInd[indEnt];
1056       aPtFillet = myEntities[aFilletObjInd[indEnt]].point[aPtInd];
1057       if (isExists) {
1058         myConstraints[aCurConstrPos].ptA = aPtFillet;
1059         aCMapIter++;
1060         if (aCMapIter != aCMapEnd)
1061           aCurConstrPos = Search(*aCMapIter, myConstraints);
1062       } else {
1063         aPonCurveConstr = Slvs_MakeConstraint(
1064             ++myConstrMaxID, myID, SLVS_C_PT_ON_LINE, myWorkplane.h,
1065             0, aPtFillet, SLVS_E_UNKNOWN, aBaseObject[indEnt], SLVS_E_UNKNOWN);
1066       }
1067       aTangentType = SLVS_C_ARC_LINE_TANGENT;
1068     }
1069     if (!isExists) {
1070       myConstraints.push_back(aPonCurveConstr);
1071       aConstrList.push_back(aPonCurveConstr.h);
1072     }
1073   }
1074
1075   if (!isExists)
1076     myConstraintMap[theConstraint] = aConstrList;
1077
1078   // Additional temporary constraints for base objects to be fixed
1079   int aNbArcs = 0;
1080   for (unsigned int indAttr = 0; indAttr < 2; indAttr++) {
1081     if (!aBaseFeature[indAttr]) {
1082       AttributeRefAttrPtr aConstrAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
1083           aConstrData->attribute(aConstraintAttributes[indAttr]));
1084       addTemporaryConstraintWhereDragged(aConstrAttr->attr());
1085       continue;
1086     }
1087     if (aBaseFeature[indAttr]->getKind() == SketchPlugin_Line::ID()) {
1088       addTemporaryConstraintWhereDragged(
1089           aBaseFeature[indAttr]->attribute(SketchPlugin_Line::START_ID()));
1090       addTemporaryConstraintWhereDragged(
1091           aBaseFeature[indAttr]->attribute(SketchPlugin_Line::END_ID()));
1092     } else if (aBaseFeature[indAttr]->getKind() == SketchPlugin_Arc::ID()) {
1093       // Arc should be fixed by its center only (to avoid "conflicting constraints" message)
1094       // If the fillet is made on two arc, the shared point should be fixed too
1095       aNbArcs++;
1096       addTemporaryConstraintWhereDragged(
1097           aBaseFeature[indAttr]->attribute(SketchPlugin_Arc::CENTER_ID()));
1098     }
1099   }
1100   if (aNbArcs == 2) {
1101       addTemporaryConstraintWhereDragged(aBaseCoincInd[0] == 0 ?
1102           aBaseFeature[0]->attribute(SketchPlugin_Arc::START_ID()) : 
1103           aBaseFeature[0]->attribute(SketchPlugin_Arc::END_ID()));
1104   }
1105   return true;
1106 }
1107
1108 // ============================================================================
1109 //  Function: changeEntity
1110 //  Class:    SketchSolver_ConstraintGroup
1111 //  Purpose:  create/update the element affected by any constraint
1112 // ============================================================================
1113 Slvs_hEntity SketchSolver_ConstraintGroup::changeEntity(
1114     std::shared_ptr<ModelAPI_Attribute> theEntity)
1115 {
1116   // If the entity is already in the group, try to find it
1117   std::map<std::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator aEntIter =
1118       myEntityAttrMap.find(theEntity);
1119   int aEntPos;
1120   std::vector<Slvs_Param>::iterator aParamIter;  // looks at first parameter of already existent entity or at the end of vector otherwise
1121   if (aEntIter == myEntityAttrMap.end())  // no such entity => should be created
1122     aParamIter = myParams.end();
1123   else {  // the entity already exists
1124     aEntPos = Search(aEntIter->second, myEntities);
1125     int aParamPos = Search(myEntities[aEntPos].param[0], myParams);
1126     aParamIter = myParams.begin() + aParamPos;
1127   }
1128   const bool isEntExists = (aEntIter != myEntityAttrMap.end());  // defines that the entity already exists
1129   const bool isNeedToSolve = myNeedToSolve;
1130   myNeedToSolve = false;
1131
1132   if (isEntExists) {
1133     // Verify that the entity is not used by "Rigid" constraint.
1134     // If it is used, the object should not move.
1135     std::vector<std::set<Slvs_hEntity> >::iterator aCoincIter = myCoincidentPoints.begin();
1136     for (; aCoincIter != myCoincidentPoints.end(); aCoincIter++)
1137       if (aCoincIter->find(aEntIter->second) != aCoincIter->end())
1138         break;
1139     std::set<Slvs_hEntity> aCoincident;
1140     if (aCoincIter != myCoincidentPoints.end()) {
1141       aCoincident = *aCoincIter;
1142       aCoincident.erase(aEntIter->second);
1143
1144       std::vector<Slvs_Constraint>::const_iterator aConstrIter = myConstraints.begin();
1145       for (; aConstrIter != myConstraints.end(); aConstrIter++)
1146         if (aConstrIter->type == SLVS_C_WHERE_DRAGGED &&
1147             aCoincident.find(aConstrIter->ptA) != aCoincident.end()) {
1148           myNeedToSolve = true;
1149           return aEntIter->second;
1150         }
1151     }
1152   }
1153
1154   // Look over supported types of entities
1155   Slvs_Entity aNewEntity;
1156   aNewEntity.h = SLVS_E_UNKNOWN;
1157
1158   // Point in 3D
1159   std::shared_ptr<GeomDataAPI_Point> aPoint = std::dynamic_pointer_cast<GeomDataAPI_Point>(
1160       theEntity);
1161   if (aPoint) {
1162     Slvs_hParam aX = changeParameter(aPoint->x(), aParamIter);
1163     Slvs_hParam aY = changeParameter(aPoint->y(), aParamIter);
1164     Slvs_hParam aZ = changeParameter(aPoint->z(), aParamIter);
1165     if (!isEntExists) // New entity
1166       aNewEntity = Slvs_MakePoint3d(++myEntityMaxID, myID, aX, aY, aZ);
1167   } else {
1168     // All entities except 3D points are created on workplane. So, if there is no workplane yet, then error
1169     if (myWorkplane.h == SLVS_E_UNKNOWN)
1170       return SLVS_E_UNKNOWN;
1171
1172     // Point in 2D
1173     std::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
1174         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(theEntity);
1175     if (aPoint2D) {
1176       Slvs_hParam aU = changeParameter(aPoint2D->x(), aParamIter);
1177       Slvs_hParam aV = changeParameter(aPoint2D->y(), aParamIter);
1178       if (!isEntExists) // New entity
1179         aNewEntity = Slvs_MakePoint2d(++myEntityMaxID, myID, myWorkplane.h, aU, aV);
1180     } else {
1181       // Scalar value (used for the distance entities)
1182       AttributeDoublePtr aScalar = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theEntity);
1183       if (aScalar) {
1184         Slvs_hParam aValue = changeParameter(aScalar->value(), aParamIter);
1185         if (!isEntExists) // New entity
1186           aNewEntity = Slvs_MakeDistance(++myEntityMaxID, myID, myWorkplane.h, aValue);
1187       }
1188     }
1189   }
1190   /// \todo Other types of entities
1191
1192   Slvs_hEntity aResult = SLVS_E_UNKNOWN; // Unsupported or wrong entity type
1193
1194   if (isEntExists) {
1195     myNeedToSolve = myNeedToSolve || isNeedToSolve;
1196     aResult = aEntIter->second;
1197   } else if (aNewEntity.h != SLVS_E_UNKNOWN) {
1198     myEntities.push_back(aNewEntity);
1199     myEntOfConstr.push_back(false);
1200     myEntityAttrMap[theEntity] = aNewEntity.h;
1201     aResult = aNewEntity.h;
1202   }
1203
1204   // If the attribute was changed by the user, we need to fix it before solving
1205   if (myNeedToSolve && theEntity->isImmutable())
1206     addTemporaryConstraintWhereDragged(theEntity, false);
1207
1208   return aResult;
1209 }
1210
1211 // ============================================================================
1212 //  Function: changeEntity
1213 //  Class:    SketchSolver_ConstraintGroup
1214 //  Purpose:  create/update the element defined by the feature affected by any constraint
1215 // ============================================================================
1216 Slvs_hEntity SketchSolver_ConstraintGroup::changeEntityFeature(FeaturePtr theEntity)
1217 {
1218   if (!theEntity->data()->isValid())
1219     return SLVS_E_UNKNOWN;
1220   // If the entity is already in the group, try to find it
1221   std::map<FeaturePtr, Slvs_hEntity>::const_iterator aEntIter = myEntityFeatMap.find(theEntity);
1222   // defines that the entity already exists
1223   const bool isEntExists = (myEntityFeatMap.find(theEntity) != myEntityFeatMap.end());
1224   
1225   Slvs_Entity aNewEntity;
1226   aNewEntity.h = SLVS_E_UNKNOWN;
1227
1228   // SketchPlugin features
1229   std::shared_ptr<SketchPlugin_Feature> aFeature = std::dynamic_pointer_cast<
1230       SketchPlugin_Feature>(theEntity);
1231   if (aFeature) {  // Verify the feature by its kind
1232     const std::string& aFeatureKind = aFeature->getKind();
1233     AttributePtr anAttribute;
1234
1235     // Line
1236     if (aFeatureKind.compare(SketchPlugin_Line::ID()) == 0) {
1237       anAttribute = aFeature->data()->attribute(SketchPlugin_Line::START_ID());
1238       if (!anAttribute->isInitialized()) return SLVS_E_UNKNOWN;
1239       Slvs_hEntity aStart = changeEntity(anAttribute);
1240
1241       anAttribute = aFeature->data()->attribute(SketchPlugin_Line::END_ID());
1242       if (!anAttribute->isInitialized()) return SLVS_E_UNKNOWN;
1243       Slvs_hEntity aEnd = changeEntity(anAttribute);
1244
1245       if (!isEntExists) // New entity
1246         aNewEntity = Slvs_MakeLineSegment(++myEntityMaxID, myID, myWorkplane.h, aStart, aEnd);
1247     }
1248     // Circle
1249     else if (aFeatureKind.compare(SketchPlugin_Circle::ID()) == 0) {
1250       anAttribute = aFeature->data()->attribute(SketchPlugin_Circle::CENTER_ID());
1251       if (!anAttribute->isInitialized()) return SLVS_E_UNKNOWN;
1252       Slvs_hEntity aCenter = changeEntity(anAttribute);
1253
1254       anAttribute = aFeature->data()->attribute(SketchPlugin_Circle::RADIUS_ID());
1255       if (!anAttribute->isInitialized()) return SLVS_E_UNKNOWN;
1256       Slvs_hEntity aRadius = changeEntity(anAttribute);
1257
1258       if (!isEntExists) // New entity
1259         aNewEntity = Slvs_MakeCircle(++myEntityMaxID, myID, myWorkplane.h, aCenter,
1260                                      myWorkplane.normal, aRadius);
1261     }
1262     // Arc
1263     else if (aFeatureKind.compare(SketchPlugin_Arc::ID()) == 0) {
1264       anAttribute = aFeature->data()->attribute(SketchPlugin_Arc::CENTER_ID());
1265       if (!anAttribute->isInitialized()) return SLVS_E_UNKNOWN;
1266       Slvs_hEntity aCenter = changeEntity(anAttribute);
1267
1268       anAttribute = aFeature->data()->attribute(SketchPlugin_Arc::START_ID());
1269       if (!anAttribute->isInitialized()) return SLVS_E_UNKNOWN;
1270       Slvs_hEntity aStart = changeEntity(anAttribute);
1271
1272       anAttribute = aFeature->data()->attribute(SketchPlugin_Arc::END_ID());
1273       if (!anAttribute->isInitialized()) return SLVS_E_UNKNOWN;
1274       Slvs_hEntity aEnd = changeEntity(anAttribute);
1275
1276       if (!isEntExists)
1277         aNewEntity = Slvs_MakeArcOfCircle(++myEntityMaxID, myID, myWorkplane.h,
1278                                           myWorkplane.normal, aCenter, aStart, aEnd);
1279     }
1280     // Point (it has low probability to be an attribute of constraint, so it is checked at the end)
1281     else if (aFeatureKind.compare(SketchPlugin_Point::ID()) == 0) {
1282       anAttribute = aFeature->data()->attribute(SketchPlugin_Point::COORD_ID());
1283       if (!anAttribute->isInitialized()) return SLVS_E_UNKNOWN;
1284       Slvs_hEntity aPoint = changeEntity(anAttribute);
1285
1286       if (isEntExists)
1287         return aEntIter->second;
1288
1289       // Both the sketch point and its attribute (coordinates) link to the same SolveSpace point identifier
1290       myEntityFeatMap[theEntity] = aPoint;
1291       myNeedToSolve = true;
1292       return aPoint;
1293     }
1294   }
1295   /// \todo Other types of features
1296
1297   if (isEntExists)
1298     return aEntIter->second;
1299
1300   if (aNewEntity.h != SLVS_E_UNKNOWN) {
1301     myEntities.push_back(aNewEntity);
1302     myEntOfConstr.push_back(false);
1303     myEntityFeatMap[theEntity] = aNewEntity.h;
1304     myNeedToSolve = true;
1305     return aNewEntity.h;
1306   }
1307
1308   // Unsupported or wrong entity type
1309   return SLVS_E_UNKNOWN;
1310 }
1311
1312 // ============================================================================
1313 //  Function: changeNormal
1314 //  Class:    SketchSolver_ConstraintGroup
1315 //  Purpose:  create/update the normal of workplane
1316 // ============================================================================
1317 Slvs_hEntity SketchSolver_ConstraintGroup::changeNormal(
1318     std::shared_ptr<ModelAPI_Attribute> theDirX,
1319     std::shared_ptr<ModelAPI_Attribute> theNorm)
1320 {
1321   std::shared_ptr<GeomDataAPI_Dir> aNorm = std::dynamic_pointer_cast<GeomDataAPI_Dir>(theNorm);
1322   std::shared_ptr<GeomDataAPI_Dir> aDirX = std::dynamic_pointer_cast<GeomDataAPI_Dir>(theDirX);
1323   if (!aDirX || (fabs(aDirX->x()) + fabs(aDirX->y()) + fabs(aDirX->z()) < tolerance))
1324     return SLVS_E_UNKNOWN;
1325   // calculate Y direction
1326   std::shared_ptr<GeomAPI_Dir> aDirY(new GeomAPI_Dir(aNorm->dir()->cross(aDirX->dir())));
1327
1328   // quaternion parameters of normal vector
1329   double qw, qx, qy, qz;
1330   Slvs_MakeQuaternion(aDirX->x(), aDirX->y(), aDirX->z(), aDirY->x(), aDirY->y(), aDirY->z(), &qw,
1331                       &qx, &qy, &qz);
1332   double aNormCoord[4] = { qw, qx, qy, qz };
1333
1334   // Try to find existent normal
1335   std::map<std::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator aEntIter =
1336       myEntityAttrMap.find(theNorm);
1337   std::vector<Slvs_Param>::iterator aParamIter;  // looks to the first parameter of already existent entity or to the end of vector otherwise
1338   if (aEntIter == myEntityAttrMap.end())  // no such entity => should be created
1339     aParamIter = myParams.end();
1340   else {  // the entity already exists, update it
1341     int aEntPos = Search(aEntIter->second, myEntities);
1342     int aParamPos = Search(myEntities[aEntPos].param[0], myParams);
1343     aParamIter = myParams.begin() + aParamPos;
1344   }
1345
1346   // Change parameters of the normal
1347   Slvs_hParam aNormParams[4];
1348   for (int i = 0; i < 4; i++)
1349     aNormParams[i] = changeParameter(aNormCoord[i], aParamIter);
1350
1351   if (aEntIter != myEntityAttrMap.end())  // the entity already exists
1352     return aEntIter->second;
1353
1354   // Create a normal
1355   Slvs_Entity aNormal = Slvs_MakeNormal3d(++myEntityMaxID, myID, aNormParams[0], aNormParams[1],
1356                                           aNormParams[2], aNormParams[3]);
1357   myEntities.push_back(aNormal);
1358   myEntOfConstr.push_back(false);
1359   myEntityAttrMap[theNorm] = aNormal.h;
1360   return aNormal.h;
1361 }
1362
1363 // ============================================================================
1364 //  Function: addWorkplane
1365 //  Class:    SketchSolver_ConstraintGroup
1366 //  Purpose:  create workplane for the group
1367 // ============================================================================
1368 bool SketchSolver_ConstraintGroup::addWorkplane(std::shared_ptr<ModelAPI_CompositeFeature> theSketch)
1369 {
1370   if (myWorkplane.h || theSketch->getKind().compare(SketchPlugin_Sketch::ID()) != 0)
1371     return false;  // the workplane already exists or the function parameter is not Sketch
1372
1373   mySketch = theSketch;
1374   updateWorkplane();
1375   return true;
1376 }
1377
1378 // ============================================================================
1379 //  Function: updateWorkplane
1380 //  Class:    SketchSolver_ConstraintGroup
1381 //  Purpose:  update parameters of workplane
1382 // ============================================================================
1383 bool SketchSolver_ConstraintGroup::updateWorkplane()
1384 {
1385   if (!mySketch->data())
1386     return false; // case sketch is deleted
1387   // Get parameters of workplane
1388   std::shared_ptr<ModelAPI_Attribute> aDirX = mySketch->data()->attribute(
1389       SketchPlugin_Sketch::DIRX_ID());
1390   std::shared_ptr<ModelAPI_Attribute> aNorm = mySketch->data()->attribute(
1391       SketchPlugin_Sketch::NORM_ID());
1392   std::shared_ptr<ModelAPI_Attribute> anOrigin = mySketch->data()->attribute(
1393       SketchPlugin_Sketch::ORIGIN_ID());
1394   // Transform them into SolveSpace format
1395   Slvs_hEntity aNormalWP = changeNormal(aDirX, aNorm);
1396   if (!aNormalWP)
1397     return false;
1398   Slvs_hEntity anOriginWP = changeEntity(anOrigin);
1399   if (!anOriginWP)
1400     return false;
1401
1402   if (!myWorkplane.h) {
1403     // Create workplane
1404     myWorkplane = Slvs_MakeWorkplane(++myEntityMaxID, myID, anOriginWP, aNormalWP);
1405     // Workplane should be added to the list of entities
1406     myEntities.push_back(myWorkplane);
1407     myEntOfConstr.push_back(false);
1408   }
1409   return true;
1410 }
1411
1412 // ============================================================================
1413 //  Function: changeParameter
1414 //  Class:    SketchSolver_ConstraintGroup
1415 //  Purpose:  create/update value of parameter
1416 // ============================================================================
1417 Slvs_hParam SketchSolver_ConstraintGroup::changeParameter(
1418     double theParam, std::vector<Slvs_Param>::iterator& thePrmIter)
1419 {
1420   if (thePrmIter != myParams.end()) {  // Parameter should be updated
1421     int aParamPos = thePrmIter - myParams.begin();
1422     if (fabs(thePrmIter->val - theParam) > tolerance) {
1423       myNeedToSolve = true;  // parameter is changed, need to resolve constraints
1424       myParams[aParamPos].val = theParam;
1425     }
1426     thePrmIter++;
1427     return myParams[aParamPos].h;
1428   }
1429
1430   // Newly created parameter
1431   Slvs_Param aParam = Slvs_MakeParam(++myParamMaxID, myID, theParam);
1432   myParams.push_back(aParam);
1433   myNeedToSolve = true;
1434   // The list of parameters is changed, move iterator to the end of the list to avoid problems
1435   thePrmIter = myParams.end();
1436   return aParam.h;
1437 }
1438
1439 // ============================================================================
1440 //  Function: resolveConstraints
1441 //  Class:    SketchSolver_ConstraintGroup
1442 //  Purpose:  solve the set of constraints for the current group
1443 // ============================================================================
1444 bool SketchSolver_ConstraintGroup::resolveConstraints()
1445 {
1446   if (!myNeedToSolve)
1447     return false;
1448
1449   myConstrSolver.setGroupID(myID);
1450   myConstrSolver.setParameters(myParams);
1451   myConstrSolver.setEntities(myEntities);
1452   myConstrSolver.setConstraints(myConstraints);
1453   myConstrSolver.setDraggedParameters(myTempPointWhereDragged);
1454
1455   int aResult = myConstrSolver.solve();
1456   if (aResult == SLVS_RESULT_OKAY) {  // solution succeeded, store results into correspondent attributes
1457                                       // Obtain result into the same list of parameters
1458     if (!myConstrSolver.getResult(myParams))
1459       return true;
1460
1461     // We should go through the attributes map, because only attributes have valued parameters
1462     std::map<std::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator anEntIter =
1463         myEntityAttrMap.begin();
1464     for (; anEntIter != myEntityAttrMap.end(); anEntIter++) {
1465       if (anEntIter->first->owner().get() && anEntIter->first->owner()->data().get())
1466         anEntIter->first->owner()->data()->blockSendAttributeUpdated(true);
1467       if (updateAttribute(anEntIter->first, anEntIter->second))
1468         updateRelatedConstraints(anEntIter->first);
1469     }
1470     updateFilletConstraints();
1471     // unblock all features then
1472     for (anEntIter = myEntityAttrMap.begin(); anEntIter != myEntityAttrMap.end(); anEntIter++) {
1473       if (anEntIter->first->owner().get() && anEntIter->first->owner()->data().get())
1474         anEntIter->first->owner()->data()->blockSendAttributeUpdated(false);
1475     }
1476   } else if (!myConstraints.empty())
1477     Events_Error::send(SketchSolver_Error::CONSTRAINTS(), this);
1478
1479   removeTemporaryConstraints();
1480   myNeedToSolve = false;
1481   return true;
1482 }
1483
1484 // ============================================================================
1485 //  Function: mergeGroups
1486 //  Class:    SketchSolver_ConstraintGroup
1487 //  Purpose:  append specified group to the current group
1488 // ============================================================================
1489 void SketchSolver_ConstraintGroup::mergeGroups(const SketchSolver_ConstraintGroup& theGroup)
1490 {
1491   // If specified group is empty, no need to merge
1492   if (theGroup.myConstraintMap.empty())
1493     return;
1494
1495   // Map between old and new indexes of SolveSpace constraints
1496   std::map<Slvs_hConstraint, Slvs_hConstraint> aConstrMap;
1497
1498   // Add all constraints from theGroup to the current group
1499   ConstraintMap::const_iterator aConstrIter = theGroup.myConstraintMap.begin();
1500   for (; aConstrIter != theGroup.myConstraintMap.end(); aConstrIter++)
1501     if (changeConstraint(aConstrIter->first))
1502       aConstrMap[aConstrIter->second.back()] = myConstrMaxID;  // the constraint was added => store its ID
1503
1504   // Add temporary constraints from theGroup
1505   std::list<Slvs_hConstraint>::const_iterator aTempConstrIter = theGroup.myTempConstraints.begin();
1506   for (; aTempConstrIter != theGroup.myTempConstraints.end(); aTempConstrIter++) {
1507     std::map<Slvs_hConstraint, Slvs_hConstraint>::iterator aFind = aConstrMap.find(
1508         *aTempConstrIter);
1509     if (aFind != aConstrMap.end())
1510       myTempConstraints.push_back(aFind->second);
1511   }
1512
1513   if (myTempPointWhereDragged.empty())
1514     myTempPointWhereDragged = theGroup.myTempPointWhereDragged;
1515   else if (!theGroup.myTempPointWhereDragged.empty()) {  // Need to create additional transient constraint
1516     std::map<std::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator aFeatureIter =
1517         theGroup.myEntityAttrMap.begin();
1518     for (; aFeatureIter != theGroup.myEntityAttrMap.end(); aFeatureIter++)
1519       if (aFeatureIter->second == myTempPointWDrgdID) {
1520         addTemporaryConstraintWhereDragged(aFeatureIter->first);
1521         break;
1522       }
1523   }
1524
1525   myNeedToSolve = myNeedToSolve || theGroup.myNeedToSolve;
1526 }
1527
1528 // ============================================================================
1529 //  Function: splitGroup
1530 //  Class:    SketchSolver_ConstraintGroup
1531 //  Purpose:  divide the group into several subgroups
1532 // ============================================================================
1533 void SketchSolver_ConstraintGroup::splitGroup(std::vector<SketchSolver_ConstraintGroup*>& theCuts)
1534 {
1535   // Divide constraints and entities into several groups
1536   std::vector<std::set<Slvs_hEntity> > aGroupsEntities;
1537   std::vector<std::set<Slvs_hConstraint> > aGroupsConstr;
1538   int aMaxNbEntities = 0;  // index of the group with maximal nuber of elements (this group will be left in the current)
1539   std::vector<Slvs_Constraint>::const_iterator aConstrIter = myConstraints.begin();
1540   for (; aConstrIter != myConstraints.end(); aConstrIter++) {
1541     Slvs_hEntity aConstrEnt[] = { aConstrIter->ptA, aConstrIter->ptB, aConstrIter->entityA,
1542         aConstrIter->entityB };
1543     std::vector<int> anIndexes;
1544     // Go through the groupped entities and find even one of entities of current constraint
1545     std::vector<std::set<Slvs_hEntity> >::iterator aGrEntIter;
1546     for (aGrEntIter = aGroupsEntities.begin(); aGrEntIter != aGroupsEntities.end(); aGrEntIter++) {
1547       bool isFound = false;
1548       for (int i = 0; i < 4 && !isFound; i++)
1549         if (aConstrEnt[i] != 0) {
1550           isFound = (aGrEntIter->find(aConstrEnt[i]) != aGrEntIter->end());
1551           // Also we need to check sub-entities
1552           int aEntPos = Search(aConstrEnt[i], myEntities);
1553           if (aEntPos != myEntities.size()) { // MPV: to fix the crash on close
1554             Slvs_hEntity* aSub = myEntities[aEntPos].point;
1555             for (int j = 0; *aSub != 0 && j < 4 && !isFound; aSub++, j++)
1556               isFound = (aGrEntIter->find(*aSub) != aGrEntIter->end());
1557           }
1558         }
1559       if (isFound)
1560         anIndexes.push_back(aGrEntIter - aGroupsEntities.begin());
1561     }
1562     // Add new group if no one is found
1563     if (anIndexes.empty()) {
1564       std::set<Slvs_hEntity> aNewGrEnt;
1565       for (int i = 0; i < 4; i++)
1566         if (aConstrEnt[i] != 0) {
1567           aNewGrEnt.insert(aConstrEnt[i]);
1568           int aEntPos = Search(aConstrEnt[i], myEntities);
1569           if (aEntPos != myEntities.size()) { // MPV: to fix the crash on close
1570             Slvs_hEntity* aSub = myEntities[aEntPos].point;
1571             for (int j = 0; *aSub != 0 && j < 4; aSub++, j++)
1572               aNewGrEnt.insert(*aSub);
1573           }
1574         }
1575       std::set<Slvs_hConstraint> aNewGrConstr;
1576       aNewGrConstr.insert(aConstrIter->h);
1577
1578       aGroupsEntities.push_back(aNewGrEnt);
1579       aGroupsConstr.push_back(aNewGrConstr);
1580       if (aNewGrEnt.size() > aGroupsEntities[aMaxNbEntities].size())
1581         aMaxNbEntities = aGroupsEntities.size() - 1;
1582     } else {  // Add entities indexes into the found group
1583       aGrEntIter = aGroupsEntities.begin() + anIndexes.front();
1584       for (int i = 0; i < 4; i++)
1585         if (aConstrEnt[i] != 0) {
1586           aGrEntIter->insert(aConstrEnt[i]);
1587           int aEntPos = Search(aConstrEnt[i], myEntities);
1588           if (aEntPos != myEntities.size()) { // MPV: to fix the crash on close
1589             Slvs_hEntity* aSub = myEntities[aEntPos].point;
1590             for (int j = 0; *aSub != 0 && j < 4; aSub++, j++)
1591               aGrEntIter->insert(*aSub);
1592           }
1593         }
1594       aGroupsConstr[anIndexes.front()].insert(aConstrIter->h);
1595       if (aGrEntIter->size() > aGroupsEntities[aMaxNbEntities].size())
1596         aMaxNbEntities = aGrEntIter - aGroupsEntities.begin();
1597       if (anIndexes.size() > 1) {  // There are found several connected groups, merge them
1598         std::vector<std::set<Slvs_hEntity> >::iterator aFirstGroup = aGroupsEntities.begin()
1599             + anIndexes.front();
1600         std::vector<std::set<Slvs_hConstraint> >::iterator aFirstConstr = aGroupsConstr.begin()
1601             + anIndexes.front();
1602         std::vector<int>::iterator anInd = anIndexes.begin();
1603         for (++anInd; anInd != anIndexes.end(); anInd++) {
1604           aFirstGroup->insert(aGroupsEntities[*anInd].begin(), aGroupsEntities[*anInd].end());
1605           aFirstConstr->insert(aGroupsConstr[*anInd].begin(), aGroupsConstr[*anInd].end());
1606         }
1607         if (aFirstGroup->size() > aGroupsEntities[aMaxNbEntities].size())
1608           aMaxNbEntities = anIndexes.front();
1609         // Remove merged groups
1610         for (anInd = anIndexes.end() - 1; anInd != anIndexes.begin(); anInd--) {
1611           aGroupsEntities.erase(aGroupsEntities.begin() + (*anInd));
1612           aGroupsConstr.erase(aGroupsConstr.begin() + (*anInd));
1613         }
1614       }
1615     }
1616   }
1617
1618   if (aGroupsEntities.size() <= 1)
1619     return;
1620
1621   // Remove the group with maximum elements as it will be left in the current group
1622   aGroupsEntities.erase(aGroupsEntities.begin() + aMaxNbEntities);
1623   aGroupsConstr.erase(aGroupsConstr.begin() + aMaxNbEntities);
1624
1625   // Add new groups of constraints and divide current group
1626   std::vector<SketchSolver_ConstraintGroup*> aNewGroups;
1627   for (int i = aGroupsEntities.size(); i > 0; i--) {
1628     SketchSolver_ConstraintGroup* aG = new SketchSolver_ConstraintGroup(mySketch);
1629     aNewGroups.push_back(aG);
1630   }
1631   ConstraintMap::const_iterator aConstrMapIter = myConstraintMap.begin();
1632   int aConstrMapPos = 0;  // position of iterator in the map (used to restore iterator after removing constraint)
1633   while (aConstrMapIter != myConstraintMap.end()) {
1634     std::vector<std::set<Slvs_hConstraint> >::const_iterator aGIter = aGroupsConstr.begin();
1635     std::vector<SketchSolver_ConstraintGroup*>::iterator aGroup = aNewGroups.begin();
1636     for (; aGIter != aGroupsConstr.end(); aGIter++, aGroup++)
1637       if (aGIter->find(aConstrMapIter->second.front()) != aGIter->end()) {
1638         (*aGroup)->changeConstraint(aConstrMapIter->first);
1639         removeConstraint(aConstrMapIter->first);
1640         // restore iterator
1641         aConstrMapIter = myConstraintMap.begin();
1642         for (int i = 0; i < aConstrMapPos; i++)
1643           aConstrMapIter++;
1644         break;
1645       }
1646     if (aGIter == aGroupsConstr.end()) {
1647       aConstrMapIter++;
1648       aConstrMapPos++;
1649     }
1650   }
1651
1652   theCuts.insert(theCuts.end(), aNewGroups.begin(), aNewGroups.end());
1653 }
1654
1655 // ============================================================================
1656 //  Function: updateGroup
1657 //  Class:    SketchSolver_ConstraintGroup
1658 //  Purpose:  search removed entities and constraints
1659 // ============================================================================
1660 bool SketchSolver_ConstraintGroup::updateGroup()
1661 {
1662   ConstraintMap::reverse_iterator aConstrIter = myConstraintMap.rbegin();
1663   bool isAllValid = true;
1664   bool isCCRemoved = false;  // indicates that at least one of coincidence constraints was removed
1665   int aConstrIndex = 0;
1666   while (/*isAllValid && */aConstrIter != myConstraintMap.rend()) {
1667     if (!aConstrIter->first->data() || !aConstrIter->first->data()->isValid()) {
1668       if (aConstrIter->first->getKind().compare(SketchPlugin_ConstraintCoincidence::ID()) == 0)
1669         isCCRemoved = true;
1670       removeConstraint(aConstrIter->first);
1671       isAllValid = false;
1672       // Get back the correct position of iterator after the "remove" operation
1673       aConstrIter = myConstraintMap.rbegin();
1674       for (int i = 0; i < aConstrIndex && aConstrIter != myConstraintMap.rend(); i++)
1675         aConstrIter++;
1676     } else {
1677       aConstrIter++;
1678       aConstrIndex++;
1679     }
1680   }
1681
1682   // Check if some entities are invalid too
1683   std::set<Slvs_hEntity> anEntToRemove;
1684   std::map<std::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator
1685       anAttrIter = myEntityAttrMap.begin();
1686   while (anAttrIter != myEntityAttrMap.end()) {
1687     if (!anAttrIter->first->owner() || !anAttrIter->first->owner()->data() ||
1688         !anAttrIter->first->owner()->data()->isValid()) {
1689       anEntToRemove.insert(anAttrIter->second);
1690       std::map<std::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator
1691           aRemovedIter = anAttrIter;
1692       anAttrIter++;
1693       myEntityAttrMap.erase(aRemovedIter);
1694     } else
1695       anAttrIter++;
1696   }
1697   std::map<FeaturePtr, Slvs_hEntity>::iterator aFeatIter = myEntityFeatMap.begin();
1698   while (aFeatIter != myEntityFeatMap.end()) {
1699     if (!aFeatIter->first || !aFeatIter->first->data() ||
1700         !aFeatIter->first->data()->isValid()) {
1701       anEntToRemove.insert(aFeatIter->second);
1702       std::map<FeaturePtr, Slvs_hEntity>::iterator aRemovedIter = aFeatIter;
1703       aFeatIter++;
1704       myEntityFeatMap.erase(aRemovedIter);
1705     } else
1706       aFeatIter++;
1707   }
1708   removeEntitiesById(anEntToRemove);
1709
1710   // Probably, need to update coincidence constraints
1711   if (isCCRemoved && !myExtraCoincidence.empty()) {
1712     // Make a copy, because the new list of unused constrtaints will be generated
1713     std::set<std::shared_ptr<SketchPlugin_Constraint> > anExtraCopy = myExtraCoincidence;
1714     myExtraCoincidence.clear();
1715
1716     std::set<std::shared_ptr<SketchPlugin_Constraint> >::iterator aCIter = anExtraCopy.begin();
1717     for (; aCIter != anExtraCopy.end(); aCIter++)
1718       if ((*aCIter)->data() && (*aCIter)->data()->isValid())
1719         changeConstraint(*aCIter);
1720   }
1721
1722   return !isAllValid;
1723 }
1724
1725 // ============================================================================
1726 //  Function: updateAttribute
1727 //  Class:    SketchSolver_ConstraintGroup
1728 //  Purpose:  update features of sketch after resolving constraints
1729 // ============================================================================
1730 bool SketchSolver_ConstraintGroup::updateAttribute(
1731     std::shared_ptr<ModelAPI_Attribute> theAttribute, const Slvs_hEntity& theEntityID)
1732 {
1733   // Search the position of the first parameter of the entity
1734   int anEntPos = Search(theEntityID, myEntities);
1735   int aFirstParamPos = Search(myEntities[anEntPos].param[0], myParams);
1736
1737   // Look over supported types of entities
1738
1739   // Point in 3D
1740   std::shared_ptr<GeomDataAPI_Point> aPoint = std::dynamic_pointer_cast<GeomDataAPI_Point>(
1741       theAttribute);
1742   if (aPoint) {
1743     if (fabs(aPoint->x() - myParams[aFirstParamPos].val) > tolerance
1744         || fabs(aPoint->y() - myParams[aFirstParamPos + 1].val) > tolerance
1745         || fabs(aPoint->z() - myParams[aFirstParamPos + 2].val) > tolerance) {
1746       aPoint->setValue(myParams[aFirstParamPos].val, myParams[aFirstParamPos + 1].val,
1747                        myParams[aFirstParamPos + 2].val);
1748       return true;
1749     }
1750     return false;
1751   }
1752
1753   // Point in 2D
1754   std::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
1755       std::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute);
1756   if (aPoint2D) {
1757     if (fabs(aPoint2D->x() - myParams[aFirstParamPos].val) > tolerance
1758         || fabs(aPoint2D->y() - myParams[aFirstParamPos + 1].val) > tolerance) {
1759       aPoint2D->setValue(myParams[aFirstParamPos].val, myParams[aFirstParamPos + 1].val);
1760       return true;
1761     }
1762     return false;
1763   }
1764
1765   // Scalar value
1766   AttributeDoublePtr aScalar = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theAttribute);
1767   if (aScalar) {
1768     if (fabs(aScalar->value() - myParams[aFirstParamPos].val) > tolerance) {
1769       aScalar->setValue(myParams[aFirstParamPos].val);
1770       return true;
1771     }
1772     return false;
1773   }
1774
1775   /// \todo Support other types of entities
1776   return false;
1777 }
1778
1779 // ============================================================================
1780 //  Function: updateEntityIfPossible
1781 //  Class:    SketchSolver_ConstraintGroup
1782 //  Purpose:  search the entity in this group and update it
1783 // ============================================================================
1784 void SketchSolver_ConstraintGroup::updateEntityIfPossible(
1785     std::shared_ptr<ModelAPI_Attribute> theEntity)
1786 {
1787   if (myEntityAttrMap.find(theEntity) != myEntityAttrMap.end()) {
1788     // If the attribute is a point and it is changed (the group needs to rebuild),
1789     // probably user has dragged this point into this position,
1790     // so it is necessary to add constraint which will guarantee the point will not change
1791
1792     // Store myNeedToSolve flag to verify the entity is really changed
1793     bool aNeedToSolveCopy = myNeedToSolve;
1794     myNeedToSolve = false;
1795
1796     changeEntity(theEntity);
1797
1798     if (myNeedToSolve)  // the entity is changed
1799     {
1800       // Verify the entity is a point and add temporary constraint of permanency
1801       std::shared_ptr<GeomDataAPI_Point> aPoint = std::dynamic_pointer_cast<GeomDataAPI_Point>(
1802           theEntity);
1803       std::shared_ptr<GeomDataAPI_Point2D> aPoint2D = std::dynamic_pointer_cast<
1804           GeomDataAPI_Point2D>(theEntity);
1805       if (aPoint || aPoint2D)
1806         addTemporaryConstraintWhereDragged(theEntity);
1807     }
1808
1809     // Restore flag of changes
1810     myNeedToSolve = myNeedToSolve || aNeedToSolveCopy;
1811
1812     if (myNeedToSolve)
1813       updateRelatedConstraints(theEntity);
1814   }
1815 }
1816
1817 // ============================================================================
1818 //  Function: addTemporaryConstraintWhereDragged
1819 //  Class:    SketchSolver_ConstraintGroup
1820 //  Purpose:  add transient constraint SLVS_C_WHERE_DRAGGED for the entity, 
1821 //            which was moved by user
1822 // ============================================================================
1823 void SketchSolver_ConstraintGroup::addTemporaryConstraintWhereDragged(
1824     std::shared_ptr<ModelAPI_Attribute> theEntity,
1825     bool theAllowToFit)
1826 {
1827   // Find identifier of the entity
1828   std::map<std::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator anEntIter =
1829       myEntityAttrMap.find(theEntity);
1830   if (anEntIter == myEntityAttrMap.end())
1831     return;
1832
1833   // Get identifiers of all dragged points
1834   std::set<Slvs_hEntity> aDraggedPntID;
1835   aDraggedPntID.insert(myTempPointWDrgdID);
1836   std::list<Slvs_hConstraint>::const_iterator aTmpCoIter = myTempConstraints.begin();
1837   for (; aTmpCoIter != myTempConstraints.end(); aTmpCoIter++) {
1838     unsigned int aConstrPos = Search(*aTmpCoIter, myConstraints);
1839     if (aConstrPos < myConstraints.size())
1840       aDraggedPntID.insert(myConstraints[aConstrPos].ptA);
1841   }
1842   std::vector<Slvs_Constraint>::const_iterator aConstrIter = myConstraints.begin();
1843   for (; aConstrIter != myConstraints.end(); aConstrIter++)
1844     if (aConstrIter->type == SLVS_C_WHERE_DRAGGED)
1845       aDraggedPntID.insert(aConstrIter->ptA);
1846   // Find whether there is a point coincident with theEntity, which already has SLVS_C_WHERE_DRAGGED
1847   std::vector<std::set<Slvs_hEntity> >::iterator aCoPtIter = myCoincidentPoints.begin();
1848   for (; aCoPtIter != myCoincidentPoints.end(); aCoPtIter++) {
1849     if (aCoPtIter->find(anEntIter->second) == aCoPtIter->end())
1850       continue;  // the entity was not found in current set
1851
1852     // Find one of already created SLVS_C_WHERE_DRAGGED constraints in current set of coincident points
1853     std::set<Slvs_hEntity>::const_iterator aDrgIter = aDraggedPntID.begin();
1854     for (; aDrgIter != aDraggedPntID.end(); aDrgIter++)
1855       if (aCoPtIter->find(*aDrgIter) != aCoPtIter->end())
1856         return;  // the SLVS_C_WHERE_DRAGGED constraint already exists
1857   }
1858   if (aDraggedPntID.find(anEntIter->second) != aDraggedPntID.end())
1859     return;
1860
1861   // If this is a first dragged point, its parameters should be placed 
1862   // into Slvs_System::dragged field to avoid system inconsistense
1863   if (myTempPointWhereDragged.empty() && theAllowToFit) {
1864     int anEntPos = Search(anEntIter->second, myEntities);
1865     Slvs_hParam* aDraggedParam = myEntities[anEntPos].param;
1866     for (int i = 0; i < 4; i++, aDraggedParam++)
1867       if (*aDraggedParam != 0)
1868         myTempPointWhereDragged.push_back(*aDraggedParam);
1869     myTempPointWDrgdID = myEntities[anEntPos].h;
1870     return;
1871   }
1872
1873   // Create additional SLVS_C_WHERE_DRAGGED constraint if myTempPointWhereDragged field is not empty
1874   Slvs_Constraint aWDConstr = Slvs_MakeConstraint(++myConstrMaxID, myID, SLVS_C_WHERE_DRAGGED,
1875                                                   myWorkplane.h, 0.0, anEntIter->second, 0, 0, 0);
1876   myConstraints.push_back(aWDConstr);
1877   myTempConstraints.push_back(aWDConstr.h);
1878 }
1879
1880 // ============================================================================
1881 //  Function: removeTemporaryConstraints
1882 //  Class:    SketchSolver_ConstraintGroup
1883 //  Purpose:  remove all transient SLVS_C_WHERE_DRAGGED constraints after
1884 //            resolving the set of constraints
1885 // ============================================================================
1886 void SketchSolver_ConstraintGroup::removeTemporaryConstraints(
1887     const std::set<Slvs_hConstraint>& theRemoved)
1888 {
1889   std::list<Slvs_hConstraint>::reverse_iterator aTmpConstrIter;
1890   for (aTmpConstrIter = myTempConstraints.rbegin(); aTmpConstrIter != myTempConstraints.rend();
1891       aTmpConstrIter++) {
1892     if (!theRemoved.empty() && theRemoved.find(*aTmpConstrIter) == theRemoved.end())
1893       continue;
1894     unsigned int aConstrPos = Search(*aTmpConstrIter, myConstraints);
1895     if (aConstrPos >= myConstraints.size())
1896       continue;
1897     myConstraints.erase(myConstraints.begin() + aConstrPos);
1898
1899     // If the removing constraint has higher index, decrease the indexer
1900     if (*aTmpConstrIter == myConstrMaxID)
1901       myConstrMaxID--;
1902   }
1903   myTempConstraints.clear();
1904
1905   // Clear basic dragged point
1906   myTempPointWhereDragged.clear();
1907   myTempPointWDrgdID = SLVS_E_UNKNOWN;
1908 }
1909
1910 // ============================================================================
1911 //  Function: removeConstraint
1912 //  Class:    SketchSolver_ConstraintGroup
1913 //  Purpose:  remove constraint and all unused entities
1914 // ============================================================================
1915 void SketchSolver_ConstraintGroup::removeConstraint(
1916     std::shared_ptr<SketchPlugin_Constraint> theConstraint)
1917 {
1918   ConstraintMap::iterator anIterToRemove = myConstraintMap.find(theConstraint);
1919   if (anIterToRemove == myConstraintMap.end())
1920     return;
1921
1922   std::vector<Slvs_hConstraint> aCnstrToRemove = anIterToRemove->second;
1923   // Remove constraint from the map
1924   myConstraintMap.erase(anIterToRemove);
1925
1926   std::set<Slvs_hEntity> anEntToRemove;
1927   
1928   // Find unused entities
1929   std::vector<Slvs_hConstraint>::iterator aCnstrToRemoveIter = aCnstrToRemove.begin();
1930   for (; aCnstrToRemoveIter != aCnstrToRemove.end(); aCnstrToRemoveIter++) {
1931     int aConstrPos = Search(*aCnstrToRemoveIter, myConstraints);
1932     Slvs_hEntity aCnstEnt[] = { myConstraints[aConstrPos].ptA, myConstraints[aConstrPos].ptB,
1933         myConstraints[aConstrPos].entityA, myConstraints[aConstrPos].entityB };
1934     for (int i = 0; i < 4; i++)
1935       if (aCnstEnt[i] != 0)
1936         anEntToRemove.insert(aCnstEnt[i]);
1937     myConstraints.erase(myConstraints.begin() + aConstrPos);
1938     if (*aCnstrToRemoveIter == myConstrMaxID)
1939       myConstrMaxID--;
1940   }
1941
1942   // Find all entities which are based on these unused
1943   std::vector<Slvs_Entity>::const_iterator anEntIter = myEntities.begin();
1944   for ( ; anEntIter != myEntities.end(); anEntIter++)
1945     if (anEntIter->type == SLVS_E_LINE_SEGMENT || anEntIter->type == SLVS_E_CIRCLE ||
1946         anEntIter->type == SLVS_E_ARC_OF_CIRCLE) {
1947       for (int i = 0; i < 4; i++)
1948         if (anEntToRemove.find(anEntIter->point[i]) != anEntToRemove.end()) {
1949           anEntToRemove.insert(anEntIter->h);
1950           for (int j = 0; j < 4; j++)
1951             if (anEntIter->point[j] != 0)
1952               anEntToRemove.insert(anEntIter->point[j]);
1953           break;
1954         }
1955     }
1956
1957   // Find entities used by remaining constraints and remove them from the list to delete
1958   std::vector<Slvs_Constraint>::const_iterator aConstrIter = myConstraints.begin();
1959   for (; aConstrIter != myConstraints.end(); aConstrIter++) {
1960     Slvs_hEntity aEnts[] = { aConstrIter->ptA, aConstrIter->ptB, aConstrIter->entityA, aConstrIter
1961         ->entityB };
1962     for (int i = 0; i < 4; i++)
1963       if (aEnts[i] != 0 && anEntToRemove.find(aEnts[i]) != anEntToRemove.end()) {
1964         anEntToRemove.erase(aEnts[i]);
1965         // remove from the list all points of current entity
1966         int aEntPos = Search(aEnts[i], myEntities);
1967         for (int j = 0; j < 4; j++)
1968           if (myEntities[aEntPos].point[j] != 0)
1969             anEntToRemove.erase(myEntities[aEntPos].point[j]);
1970       }
1971   }
1972
1973   if (anEntToRemove.empty())
1974     return;
1975
1976   // Remove unused entities
1977   std::map<std::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator anEntAttrIter =
1978       myEntityAttrMap.begin();
1979   while (anEntAttrIter != myEntityAttrMap.end()) {
1980     if (anEntToRemove.find(anEntAttrIter->second) != anEntToRemove.end()) {
1981       std::map<std::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator aRemovedIter =
1982           anEntAttrIter;
1983       anEntAttrIter++;
1984       myEntityAttrMap.erase(aRemovedIter);
1985     } else
1986       anEntAttrIter++;
1987   }
1988   std::map<FeaturePtr, Slvs_hEntity>::iterator anEntFeatIter = myEntityFeatMap.begin();
1989   while (anEntFeatIter != myEntityFeatMap.end()) {
1990     if (anEntToRemove.find(anEntFeatIter->second) != anEntToRemove.end()) {
1991       std::map<FeaturePtr, Slvs_hEntity>::iterator aRemovedIter = anEntFeatIter;
1992       anEntFeatIter++;
1993       myEntityFeatMap.erase(aRemovedIter);
1994     } else
1995       anEntFeatIter++;
1996   }
1997
1998   removeEntitiesById(anEntToRemove);
1999
2000   if (myCoincidentPoints.size() == 1 && myCoincidentPoints.front().empty())
2001     myCoincidentPoints.clear();
2002 }
2003
2004 // ============================================================================
2005 //  Function: removeEntitiesById
2006 //  Class:    SketchSolver_ConstraintGroup
2007 //  Purpose:  Removes specified entities and their parameters
2008 // ============================================================================
2009 void SketchSolver_ConstraintGroup::removeEntitiesById(const std::set<Slvs_hEntity>& theEntities)
2010 {
2011   std::set<Slvs_hEntity>::const_reverse_iterator aRemIter = theEntities.rbegin();
2012   for (; aRemIter != theEntities.rend(); aRemIter++) {
2013     unsigned int anEntPos = Search(*aRemIter, myEntities);
2014     if (anEntPos >= myEntities.size())
2015       continue;
2016     if (myEntities[anEntPos].param[0] != 0) {
2017       unsigned int aParamPos = Search(myEntities[anEntPos].param[0], myParams);
2018       if (aParamPos >= myParams.size())
2019         continue;
2020       int aNbParams = 0;
2021       while (myEntities[anEntPos].param[aNbParams] != 0)
2022         aNbParams++;
2023       if (myEntities[anEntPos].param[aNbParams - 1] == myParamMaxID)
2024         myParamMaxID -= aNbParams;
2025       myParams.erase(myParams.begin() + aParamPos, myParams.begin() + aParamPos + aNbParams);
2026       if (*aRemIter == myEntityMaxID)
2027         myEntityMaxID--;
2028     }
2029     myEntities.erase(myEntities.begin() + anEntPos);
2030     myEntOfConstr.erase(myEntOfConstr.begin() + anEntPos);
2031
2032     // Remove entity's ID from the lists of conincident points
2033     std::vector<std::set<Slvs_hEntity> >::iterator aCoPtIter = myCoincidentPoints.begin();
2034     for (; aCoPtIter != myCoincidentPoints.end(); aCoPtIter++)
2035       aCoPtIter->erase(*aRemIter);
2036   }
2037 }
2038
2039 // ============================================================================
2040 //  Function: addCoincidentPoints
2041 //  Class:    SketchSolver_ConstraintGroup
2042 //  Purpose:  add coincident point the appropriate list of such points
2043 // ============================================================================
2044 bool SketchSolver_ConstraintGroup::addCoincidentPoints(const Slvs_hEntity& thePoint1,
2045                                                        const Slvs_hEntity& thePoint2)
2046 {
2047   std::vector<std::set<Slvs_hEntity> >::iterator aCoPtIter = myCoincidentPoints.begin();
2048   std::vector<std::set<Slvs_hEntity> >::iterator aFirstFound = myCoincidentPoints.end();
2049   while (aCoPtIter != myCoincidentPoints.end()) {
2050     bool isFound[2] = {  // indicate which point ID was already in coincidence constraint
2051         aCoPtIter->find(thePoint1) != aCoPtIter->end(), aCoPtIter->find(thePoint2)
2052             != aCoPtIter->end(), };
2053     if (isFound[0] && isFound[1])  // points are already connected by coincidence constraints => no need additional one
2054       return false;
2055     if ((isFound[0] && !isFound[1]) || (!isFound[0] && isFound[1])) {
2056       if (aFirstFound != myCoincidentPoints.end()) {  // there are two groups of coincident points connected by created constraint => merge them
2057         int aFirstFoundShift = aFirstFound - myCoincidentPoints.begin();
2058         int aCurrentShift = aCoPtIter - myCoincidentPoints.begin();
2059         aFirstFound->insert(aCoPtIter->begin(), aCoPtIter->end());
2060         myCoincidentPoints.erase(aCoPtIter);
2061         aFirstFound = myCoincidentPoints.begin() + aFirstFoundShift;
2062         aCoPtIter = myCoincidentPoints.begin() + aCurrentShift;
2063         continue;
2064       } else {
2065         aCoPtIter->insert(isFound[0] ? thePoint2 : thePoint1);
2066         aFirstFound = aCoPtIter;
2067       }
2068     }
2069     aCoPtIter++;
2070   }
2071   // No points were found, need to create new set
2072   if (aFirstFound == myCoincidentPoints.end()) {
2073     std::set<Slvs_hEntity> aNewSet;
2074     aNewSet.insert(thePoint1);
2075     aNewSet.insert(thePoint2);
2076     myCoincidentPoints.push_back(aNewSet);
2077   }
2078
2079   return true;
2080 }
2081
2082 // ============================================================================
2083 //  Function: updateRelatedConstraints
2084 //  Class:    SketchSolver_ConstraintGroup
2085 //  Purpose:  emit the signal to update constraints
2086 // ============================================================================
2087 void SketchSolver_ConstraintGroup::updateRelatedConstraints(
2088     std::shared_ptr<ModelAPI_Attribute> theEntity) const
2089 {
2090   ConstraintMap::const_iterator aConstrIter = myConstraintMap.begin();
2091   for (; aConstrIter != myConstraintMap.end(); aConstrIter++) {
2092     std::list<std::shared_ptr<ModelAPI_Attribute> > anAttributes = aConstrIter->first->data()
2093         ->attributes(std::string());
2094
2095     std::list<std::shared_ptr<ModelAPI_Attribute> >::iterator anAttrIter = anAttributes.begin();
2096     for (; anAttrIter != anAttributes.end(); anAttrIter++) {
2097       bool isUpd = (*anAttrIter == theEntity);
2098       std::shared_ptr<ModelAPI_AttributeRefAttr> aRefAttr = std::dynamic_pointer_cast<
2099           ModelAPI_AttributeRefAttr>(*anAttrIter);
2100       if (aRefAttr && !aRefAttr->isObject() && aRefAttr->attr() == theEntity)
2101         isUpd = true;
2102
2103       if (isUpd) {
2104         static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
2105         ModelAPI_EventCreator::get()->sendUpdated(aConstrIter->first, anEvent);
2106         break;
2107       }
2108     }
2109   }
2110 }
2111
2112 void SketchSolver_ConstraintGroup::updateRelatedConstraintsFeature(
2113     std::shared_ptr<ModelAPI_Feature> theFeature) const
2114 {
2115   ConstraintMap::const_iterator aConstrIter = myConstraintMap.begin();
2116   for (; aConstrIter != myConstraintMap.end(); aConstrIter++) {
2117     std::list<std::shared_ptr<ModelAPI_Attribute> > anAttributes = aConstrIter->first->data()
2118         ->attributes(std::string());
2119
2120     std::list<std::shared_ptr<ModelAPI_Attribute> >::iterator anAttrIter = anAttributes.begin();
2121     for (; anAttrIter != anAttributes.end(); anAttrIter++) {
2122       std::shared_ptr<ModelAPI_AttributeRefAttr> aRefAttr = std::dynamic_pointer_cast<
2123           ModelAPI_AttributeRefAttr>(*anAttrIter);
2124       if (aRefAttr && aRefAttr->isObject() && aRefAttr->object() == theFeature) {
2125         static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
2126         ModelAPI_EventCreator::get()->sendUpdated(aConstrIter->first, anEvent);
2127         break;
2128       }
2129     }
2130   }
2131 }
2132
2133 // ============================================================================
2134 //  Function: updateFilletConstraints
2135 //  Class:    SketchSolver_ConstraintGroup
2136 //  Purpose:  change fillet arc to be less than 180 degree
2137 // ============================================================================
2138 void SketchSolver_ConstraintGroup::updateFilletConstraints()
2139 {
2140   ConstraintMap::const_iterator aConstrIter = myConstraintMap.begin();
2141   for (; aConstrIter != myConstraintMap.end(); aConstrIter++)
2142     if (aConstrIter->first->getKind() == SketchPlugin_ConstraintFillet::ID()) {
2143       AttributeRefListPtr aFilletRefList = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
2144           aConstrIter->first->data()->attribute(SketchPlugin_ConstraintFillet::ENTITY_C()));
2145       if (!aFilletRefList)
2146         return;
2147       ObjectPtr anArcObj = aFilletRefList->object(2);
2148       std::shared_ptr<GeomDataAPI_Point2D> aCenter = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
2149           anArcObj->data()->attribute(SketchPlugin_Arc::CENTER_ID()));
2150       std::shared_ptr<GeomDataAPI_Point2D> aStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
2151           anArcObj->data()->attribute(SketchPlugin_Arc::START_ID()));
2152       std::shared_ptr<GeomDataAPI_Point2D> aEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
2153           anArcObj->data()->attribute(SketchPlugin_Arc::END_ID()));
2154       double aCosA = aStart->x() - aCenter->x();
2155       double aSinA = aStart->y() - aCenter->y();
2156       double aCosB = aEnd->x() - aCenter->x();
2157       double aSinB = aEnd->y() - aCenter->y();
2158       if (aCosA * aSinB - aSinA * aCosB <= 0.0) {
2159         anArcObj->data()->blockSendAttributeUpdated(true);
2160         double x = aStart->x();
2161         double y = aStart->y();
2162         aStart->setValue(aEnd->x(), aEnd->y());
2163         aEnd->setValue(x, y);
2164         // Update constraint data
2165         changeFilletConstraint(aConstrIter->first);
2166         anArcObj->data()->blockSendAttributeUpdated(false);
2167       }
2168     }
2169 }
2170
2171 // ============================================================================
2172 //  Function: makeMirrorEntity
2173 //  Class:    SketchSolver_ConstraintGroup
2174 //  Purpose:  change entities parameters to make them symmetric relating to the mirror line
2175 // ============================================================================
2176 void SketchSolver_ConstraintGroup::makeMirrorEntity(const Slvs_hEntity& theBase,
2177                                                     const Slvs_hEntity& theMirror,
2178                                                     const Slvs_hEntity& theMirrorLine)
2179 {
2180   Slvs_Entity aBase = myEntities[Search(theBase, myEntities)];
2181   Slvs_Entity aMirror = myEntities[Search(theMirror, myEntities)];
2182   int i = 0;
2183   if (aBase.type != SLVS_E_ARC_OF_CIRCLE) {
2184     while (aBase.point[i] != 0 && aMirror.point[i] != 0) {
2185       makeMirrorEntity(aBase.point[i], aMirror.point[i], theMirrorLine);
2186       i++;
2187     }
2188   } else {
2189     // swap mirroring first and last points of an arc
2190     makeMirrorEntity(aBase.point[0], aMirror.point[0], theMirrorLine);
2191     makeMirrorEntity(aBase.point[1], aMirror.point[2], theMirrorLine);
2192     makeMirrorEntity(aBase.point[2], aMirror.point[1], theMirrorLine);
2193   }
2194   if (aBase.param[0] != 0 && aMirror.param[0] != 0) { // this is a point, copy it
2195     Slvs_Entity aMirrorLine = myEntities[Search(theMirrorLine, myEntities)];
2196     std::shared_ptr<GeomAPI_XY> aLinePoints[2];
2197     for (i = 0; i < 2; i++) {
2198       Slvs_Entity aPoint = myEntities[Search(aMirrorLine.point[i], myEntities)];
2199       int aParamPos = Search(aPoint.param[0], myParams);
2200       aLinePoints[i] = std::shared_ptr<GeomAPI_XY>(
2201           new GeomAPI_XY(myParams[aParamPos].val, myParams[1+aParamPos].val));
2202     }
2203     int aBaseParamPos = Search(aBase.param[0], myParams);
2204     int aMirrorParamPos = Search(aMirror.param[0], myParams);
2205     std::shared_ptr<GeomAPI_XY> aPoint = std::shared_ptr<GeomAPI_XY>(
2206         new GeomAPI_XY(myParams[aBaseParamPos].val, myParams[1+aBaseParamPos].val));
2207
2208     // direction of a mirror line
2209     std::shared_ptr<GeomAPI_Dir2d> aDir = std::shared_ptr<GeomAPI_Dir2d>(
2210         new GeomAPI_Dir2d(aLinePoints[1]->added(aLinePoints[0]->multiplied(-1.0))));
2211     // orthogonal direction
2212     aDir = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(aDir->y(), -aDir->x()));
2213
2214     std::shared_ptr<GeomAPI_XY> aVec = std::shared_ptr<GeomAPI_XY>(
2215         new GeomAPI_XY(aPoint->x() - aLinePoints[0]->x(), aPoint->y() - aLinePoints[0]->y()));
2216     double aDist = aVec->dot(aDir->xy());
2217     std::shared_ptr<GeomAPI_XY> aMirrorPoint = aPoint->added(aDir->xy()->multiplied(-2.0 * aDist));
2218
2219     myParams[aMirrorParamPos].val = aMirrorPoint->x();
2220     myParams[1+aMirrorParamPos].val = aMirrorPoint->y();
2221   }
2222 }
2223
2224 // ============================================================================
2225 //  Function: changeMirrorPoints
2226 //  Class:    SketchSolver_ConstraintGroup
2227 //  Purpose:  creates/updates mirror constraint for two points
2228 // ============================================================================
2229 Slvs_hConstraint SketchSolver_ConstraintGroup::changeMirrorPoints(
2230     const Slvs_hEntity& theBasePoint,
2231     const Slvs_hEntity& theMirrorPoint,
2232     const Slvs_hEntity& theMirrorLine,
2233     std::vector<Slvs_Constraint>&  thePrevConstr,
2234     std::map<Slvs_hEntity, Slvs_hEntity>& thePrevMirror)
2235 {
2236   std::map<Slvs_hEntity, Slvs_hEntity>::iterator aMapIter = thePrevMirror.find(theBasePoint);
2237   if (aMapIter != thePrevMirror.end()) {
2238     thePrevMirror.erase(aMapIter);
2239     std::vector<Slvs_Constraint>::iterator anIter = thePrevConstr.begin();
2240     for (; anIter != thePrevConstr.end(); anIter++)
2241       if (anIter->ptA == theBasePoint) {
2242         if (anIter->ptB != theMirrorPoint) {
2243           int aConstrInd = Search(anIter->h, myConstraints);
2244           myConstraints[aConstrInd].ptB = theMirrorPoint;
2245           myConstraints[aConstrInd].entityA = theMirrorLine;
2246         }
2247         Slvs_hConstraint aResult = anIter->h;
2248         thePrevConstr.erase(anIter);
2249         return aResult;
2250       }
2251   }
2252
2253   // Newly created constraint
2254   Slvs_Constraint aConstraint = Slvs_MakeConstraint(
2255       ++myConstrMaxID, myID, SLVS_C_SYMMETRIC_LINE, myWorkplane.h, 0.0,
2256       theBasePoint, theMirrorPoint, theMirrorLine, SLVS_E_UNKNOWN);
2257   myConstraints.push_back(aConstraint);
2258   return aConstraint.h;
2259 }
2260
2261
2262 // ============================================================================
2263 //  Function: calculateMiddlePoint
2264 //  Class:    SketchSolver_ConstraintGroup
2265 //  Purpose:  calculates middle point on line or arc
2266 // ============================================================================
2267 void SketchSolver_ConstraintGroup::calculateMiddlePoint(
2268     const Slvs_hEntity& theEntity,
2269     double& theX,
2270     double& theY) const
2271 {
2272   int anInd = Search(theEntity, myEntities);
2273   if (myEntities[anInd].type == SLVS_E_LINE_SEGMENT) {
2274     int aLineParams[2];
2275     for (int i = 0; i < 2; i++) {
2276       int aPtPos = Search(myEntities[anInd].point[i], myEntities);
2277       aLineParams[i] = Search(myEntities[aPtPos].param[0], myParams);
2278     }
2279     theX = 0.5 * (myParams[aLineParams[0]].val + myParams[aLineParams[1]].val);
2280     theY = 0.5 * (myParams[1 + aLineParams[0]].val + myParams[1 + aLineParams[1]].val);
2281   } else if (myEntities[anInd].type == SLVS_E_ARC_OF_CIRCLE) {
2282     double anArcPoint[3][2];
2283     for (int i = 0; i < 3; i++) {
2284       int aPtPos = Search(myEntities[anInd].point[i], myEntities);
2285       int anArcParam = Search(myEntities[aPtPos].param[0], myParams);
2286       anArcPoint[i][0] = myParams[anArcParam].val;
2287       anArcPoint[i][1] = myParams[1 + anArcParam].val;
2288     }
2289     // project last point of arc on the arc
2290     double x = anArcPoint[1][0] - anArcPoint[0][0];
2291     double y = anArcPoint[1][1] - anArcPoint[0][1];
2292     double aRad = sqrt(x*x + y*y);
2293     x = anArcPoint[2][0] - anArcPoint[0][0];
2294     y = anArcPoint[2][1] - anArcPoint[0][1];
2295     double aNorm = sqrt(x*x + y*y);
2296     if (aNorm >= tolerance) {
2297       anArcPoint[2][0] = anArcPoint[0][0] + x * aRad / aNorm;
2298       anArcPoint[2][1] = anArcPoint[0][1] + y * aRad / aNorm;
2299     }
2300
2301     x = anArcPoint[1][0] + anArcPoint[2][0] - 2.0 * anArcPoint[0][0];
2302     y = anArcPoint[1][1] + anArcPoint[2][1] - 2.0 * anArcPoint[0][1];
2303     aNorm = sqrt(x*x + y*y);
2304     if (aNorm >= tolerance) {
2305       x *= aRad / aNorm;
2306       y *= aRad / aNorm;
2307     } else { // obtain orthogonal direction
2308       x = 0.5 * (anArcPoint[2][1] - anArcPoint[1][1]);
2309       y = -0.5 * (anArcPoint[2][0] - anArcPoint[1][0]);
2310     }
2311     theX = anArcPoint[0][0] + x;
2312     theY = anArcPoint[0][1] + y;
2313   }
2314 }
2315
2316
2317 // ========================================================
2318 // =========      Auxiliary functions       ===============
2319 // ========================================================
2320
2321 template<typename T>
2322 int Search(const uint32_t& theEntityID, const std::vector<T>& theEntities)
2323 {
2324   int aResIndex = theEntityID <= theEntities.size() ? theEntityID - 1 : 0;
2325   int aVecSize = theEntities.size();
2326   while (aResIndex >= 0 && theEntities[aResIndex].h > theEntityID)
2327     aResIndex--;
2328   while (aResIndex < aVecSize && aResIndex >= 0 && theEntities[aResIndex].h < theEntityID)
2329     aResIndex++;
2330   if (aResIndex == -1)
2331     aResIndex = aVecSize;
2332   return aResIndex;
2333 }