Salome HOME
It replaces direct names of constraints to the specific kind.
[modules/shaper.git] / src / SketchSolver / SketchSolver_ConstraintGroup.cpp
1 // File:    SketchSolver_ConstraintGroup.cpp
2 // Created: 27 May 2014
3 // Author:  Artem ZHIDKOV
4
5 #include "SketchSolver_ConstraintGroup.h"
6
7 #include <SketchSolver_Constraint.h>
8
9 #include <Events_Loop.h>
10 #include <GeomDataAPI_Dir.h>
11 #include <GeomDataAPI_Point.h>
12 #include <GeomDataAPI_Point2D.h>
13 #include <ModelAPI_AttributeDouble.h>
14 #include <ModelAPI_AttributeRefList.h>
15 #include <Model_Events.h>
16
17 #include <SketchPlugin_Constraint.h>
18 #include <SketchPlugin_ConstraintLength.h>
19 #include <SketchPlugin_ConstraintCoincidence.h>
20
21 #include <SketchPlugin_Arc.h>
22 #include <SketchPlugin_Circle.h>
23 #include <SketchPlugin_Line.h>
24 #include <SketchPlugin_Point.h>
25 #include <SketchPlugin_Sketch.h>
26
27 #include <math.h>
28 #include <assert.h>
29
30 /// Tolerance for value of parameters
31 const double tolerance = 1.e-10;
32
33 /// This value is used to give unique index to the groups
34 static Slvs_hGroup myGroupIndexer = 0;
35
36 /** \brief Search the entity/parameter with specified ID in the list of elements
37  *  \param[in] theEntityID unique ID of the element
38  *  \param[in] theEntities list of elements
39  *  \return position of the found element or -1 if the element is not found
40  */
41 template <typename T>
42 static int Search(const uint32_t& theEntityID, const std::vector<T>& theEntities);
43
44
45 // ========================================================
46 // =========  SketchSolver_ConstraintGroup  ===============
47 // ========================================================
48
49 SketchSolver_ConstraintGroup::
50   SketchSolver_ConstraintGroup(boost::shared_ptr<SketchPlugin_Feature> theWorkplane)
51   : myID(++myGroupIndexer),
52     myParamMaxID(0),
53     myEntityMaxID(0),
54     myConstrMaxID(0),
55     myConstraintMap(),
56     myNeedToSolve(false),
57     myConstrSolver()
58 {
59   myParams.clear();
60   myEntities.clear();
61   myConstraints.clear();
62   
63   myTempConstraints.clear();
64   myTempPointWhereDragged.clear();
65   myTempPointWDrgdID = 0;
66
67   // Initialize workplane
68   myWorkplane.h = SLVS_E_UNKNOWN;
69 #ifndef NDEBUG
70   assert(addWorkplane(theWorkplane));
71 #else
72   addWorkplane(theWorkplane);
73 #endif
74 }
75
76 SketchSolver_ConstraintGroup::~SketchSolver_ConstraintGroup()
77 {
78   myParams.clear();
79   myEntities.clear();
80   myConstraints.clear();
81   myConstraintMap.clear();
82   myTempConstraints.clear();
83   myTempPointWhereDragged.clear();
84
85   // If the group with maximal identifier is deleted, decrease the indexer
86   if (myID == myGroupIndexer)
87     myGroupIndexer--;
88 }
89
90 // ============================================================================
91 //  Function: isBaseWorkplane
92 //  Class:    SketchSolver_ConstraintGroup
93 //  Purpose:  verify the group is based on the given workplane
94 // ============================================================================
95 bool SketchSolver_ConstraintGroup::isBaseWorkplane(
96                 boost::shared_ptr<SketchPlugin_Feature> theWorkplane) const
97 {
98   return theWorkplane == mySketch;
99 }
100
101 // ============================================================================
102 //  Function: isInteract
103 //  Class:    SketchSolver_ConstraintGroup
104 //  Purpose:  verify are there any entities in the group used by given constraint
105 // ============================================================================
106 bool SketchSolver_ConstraintGroup::isInteract(
107                 boost::shared_ptr<SketchPlugin_Constraint> theConstraint) const
108 {
109   // Check the group is empty
110   if (myWorkplane.h != SLVS_E_UNKNOWN && myConstraints.empty())
111     return true;
112
113   // Go through constraint entities and verify if some of them already in the group
114   for (int i = 0; i < CONSTRAINT_ATTR_SIZE; i++)
115   {
116     boost::shared_ptr<ModelAPI_AttributeRefAttr> aCAttrRef =
117       boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
118         theConstraint->data()->attribute(CONSTRAINT_ATTRIBUTES[i])
119       );
120     if (!aCAttrRef) continue;
121     if (!aCAttrRef->isFeature() && 
122         myEntityAttrMap.find(aCAttrRef->attr()) != myEntityAttrMap.end())
123       return true;
124     if (aCAttrRef->isFeature() && 
125         myEntityFeatMap.find(aCAttrRef->feature()) != myEntityFeatMap.end())
126       return true;
127   }
128
129   // Entities did not found
130   return false;
131 }
132
133 // ============================================================================
134 //  Function: changeConstraint
135 //  Class:    SketchSolver_ConstraintGroup
136 //  Purpose:  create/update the constraint in the group
137 // ============================================================================
138 bool SketchSolver_ConstraintGroup::changeConstraint(
139                 boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
140 {
141   // There is no workplane yet, something wrong
142   if (myWorkplane.h == SLVS_E_UNKNOWN)
143     return false;
144
145   // Search this constraint in the current group to update it
146   std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::const_iterator
147     aConstrMapIter = myConstraintMap.find(theConstraint);
148   std::vector<Slvs_Constraint>::iterator aConstrIter;
149   if (aConstrMapIter != myConstraintMap.end())
150   {
151     int aConstrPos = Search(aConstrMapIter->second, myConstraints);
152     aConstrIter = myConstraints.begin() + aConstrPos;
153   }
154
155   // Get constraint type and verify the constraint parameters are correct
156   SketchSolver_Constraint aConstraint(theConstraint);
157   int aConstrType = aConstraint.getType();
158   if (aConstrType == SLVS_C_UNKNOWN ||
159      (aConstrMapIter != myConstraintMap.end() && aConstrIter->type != aConstrType))
160     return false;
161   const std::vector<std::string>& aConstraintAttributes = aConstraint.getAttributes();
162
163   // Create constraint parameters
164   double aDistance = 0.0; // scalar value of the constraint
165   boost::shared_ptr<ModelAPI_AttributeDouble> aDistAttr =
166     boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theConstraint->data()->attribute(CONSTRAINT_ATTR_VALUE));
167   if (aDistAttr)
168   {
169     aDistance = aDistAttr->value();
170     if (aConstrMapIter != myConstraintMap.end() && fabs(aConstrIter->valA - aDistance) > tolerance)
171     {
172       myNeedToSolve = true;
173       aConstrIter->valA = aDistance;
174     }
175     // SketchPlugin circle defined by its radius, but SolveSpace uses constraint for diameter
176     if (aConstrType == SLVS_C_DIAMETER)
177       aDistance *= 2.0;
178   }
179
180   Slvs_hEntity aConstrEnt[CONSTRAINT_ATTR_SIZE]; // parameters of the constraint
181   for (unsigned int indAttr = 0; indAttr < CONSTRAINT_ATTR_SIZE; indAttr++)
182   {
183     aConstrEnt[indAttr] = SLVS_E_UNKNOWN;
184     boost::shared_ptr<ModelAPI_AttributeRefAttr> aConstrAttr =
185       boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
186         theConstraint->data()->attribute(aConstraintAttributes[indAttr])
187       );
188     if (!aConstrAttr) continue;
189
190     // For the length constraint the start and end points of the line should be added to the entities list instead of line
191     if (aConstrType == SLVS_C_PT_PT_DISTANCE && theConstraint->getKind().compare(SKETCH_CONSTRAINT_LENGTH_KIND) == 0)
192     {
193       boost::shared_ptr<ModelAPI_Data> aData = aConstrAttr->feature()->data();
194       aConstrEnt[indAttr]   = changeEntity(aData->attribute(LINE_ATTR_START));
195       aConstrEnt[indAttr+1] = changeEntity(aData->attribute(LINE_ATTR_END));
196       break; // there should be no other entities
197     }
198     else if (aConstrAttr->isFeature())
199       aConstrEnt[indAttr] = changeEntity(aConstrAttr->feature());
200     else
201       aConstrEnt[indAttr] = changeEntity(aConstrAttr->attr());
202   }
203
204   if (aConstrMapIter == myConstraintMap.end())
205   {
206     // Several points may be coincident, it is not necessary to store all constraints between them.
207     // Try to find sequence of coincident points which connects the points of new constraint
208     if (aConstrType == SLVS_C_POINTS_COINCIDENT &&
209         !addCoincidentPoints(aConstrEnt[0], aConstrEnt[1]))
210     {
211       myExtraCoincidence.insert(theConstraint); // the constraint is stored for further purposes
212       return false;
213     }
214
215     // Create SolveSpace constraint structure
216     Slvs_Constraint aConstraint =
217       Slvs_MakeConstraint(++myConstrMaxID, myID, aConstrType, myWorkplane.h,
218                           aDistance, aConstrEnt[0], aConstrEnt[1], aConstrEnt[2], aConstrEnt[3]);
219     myConstraints.push_back(aConstraint);
220     myConstraintMap[theConstraint] = aConstraint.h;
221   }
222   return true;
223 }
224
225 // ============================================================================
226 //  Function: changeEntity
227 //  Class:    SketchSolver_ConstraintGroup
228 //  Purpose:  create/update the element affected by any constraint
229 // ============================================================================
230 Slvs_hEntity SketchSolver_ConstraintGroup::changeEntity(
231                 boost::shared_ptr<ModelAPI_Attribute> theEntity)
232 {
233   // If the entity is already in the group, try to find it
234   std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
235     aEntIter = myEntityAttrMap.find(theEntity);
236   std::vector<Slvs_Param>::const_iterator aParamIter; // looks at first parameter of already existent entity or at the end of vector otherwise
237   if (aEntIter == myEntityAttrMap.end()) // no such entity => should be created
238     aParamIter = myParams.end();
239   else
240   { // the entity already exists
241     int aEntPos = Search(aEntIter->second, myEntities);
242     int aParamPos = Search(myEntities[aEntPos].param[0], myParams);
243     aParamIter = myParams.begin() + aParamPos;
244   }
245   const bool isEntExists = (aEntIter != myEntityAttrMap.end()); // defines that the entity already exists
246
247   // Look over supported types of entities
248
249   // Point in 3D
250   boost::shared_ptr<GeomDataAPI_Point> aPoint =
251     boost::dynamic_pointer_cast<GeomDataAPI_Point>(theEntity);
252   if (aPoint)
253   {
254     Slvs_hParam aX = changeParameter(aPoint->x(), aParamIter);
255     Slvs_hParam aY = changeParameter(aPoint->y(), aParamIter);
256     Slvs_hParam aZ = changeParameter(aPoint->z(), aParamIter);
257
258     if (isEntExists)
259       return aEntIter->second;
260
261     // New entity
262     Slvs_Entity aPtEntity = Slvs_MakePoint3d(++myEntityMaxID, myID, aX, aY, aZ);
263     myEntities.push_back(aPtEntity);
264     myEntityAttrMap[theEntity] = aPtEntity.h;
265     return aPtEntity.h;
266   }
267
268   // All entities except 3D points are created on workplane. So, if there is no workplane yet, then error
269   if (myWorkplane.h == SLVS_E_UNKNOWN)
270     return SLVS_E_UNKNOWN;
271
272   // Point in 2D
273   boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
274     boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theEntity);
275   if (aPoint2D)
276   {
277     Slvs_hParam aU = changeParameter(aPoint2D->x(), aParamIter);
278     Slvs_hParam aV = changeParameter(aPoint2D->y(), aParamIter);
279
280     if (isEntExists)
281       return aEntIter->second;
282
283     // New entity
284     Slvs_Entity aPt2DEntity = Slvs_MakePoint2d(++myEntityMaxID, myID, myWorkplane.h, aU, aV);
285     myEntities.push_back(aPt2DEntity);
286     myEntityAttrMap[theEntity] = aPt2DEntity.h;
287     return aPt2DEntity.h;
288   }
289
290   // Scalar value (used for the distance entities)
291   boost::shared_ptr<ModelAPI_AttributeDouble> aScalar = 
292     boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theEntity);
293   if (aScalar)
294   {
295     Slvs_hParam aValue = changeParameter(aScalar->value(), aParamIter);
296
297     if (isEntExists)
298       return aEntIter->second;
299
300     // New entity
301     Slvs_Entity aDistance = Slvs_MakeDistance(++myEntityMaxID, myID, myWorkplane.h, aValue);
302     myEntities.push_back(aDistance);
303     myEntityAttrMap[theEntity] = aDistance.h;
304     return aDistance.h;
305   }
306
307   /// \todo Other types of entities
308
309   // Unsupported or wrong entity type
310   return SLVS_E_UNKNOWN;
311 }
312
313
314 // ============================================================================
315 //  Function: changeEntity
316 //  Class:    SketchSolver_ConstraintGroup
317 //  Purpose:  create/update the element defined by the feature affected by any constraint
318 // ============================================================================
319 Slvs_hEntity SketchSolver_ConstraintGroup::changeEntity(
320                 boost::shared_ptr<ModelAPI_Feature> theEntity)
321 {
322   // If the entity is already in the group, try to find it
323   std::map<boost::shared_ptr<ModelAPI_Feature>, Slvs_hEntity>::const_iterator
324     aEntIter = myEntityFeatMap.find(theEntity);
325   // defines that the entity already exists
326   const bool isEntExists = (myEntityFeatMap.find(theEntity) != myEntityFeatMap.end());
327
328   // SketchPlugin features
329   boost::shared_ptr<SketchPlugin_Feature> aFeature;
330     boost::dynamic_pointer_cast<SketchPlugin_Feature>(theEntity);
331   if (aFeature)
332   { // Verify the feature by its kind
333     const std::string& aFeatureKind = aFeature->getKind();
334
335     // Line
336     if (aFeatureKind.compare(SKETCH_LINE_KIND) == 0)
337     {
338       Slvs_hEntity aStart = changeEntity(aFeature->data()->attribute(LINE_ATTR_START));
339       Slvs_hEntity aEnd   = changeEntity(aFeature->data()->attribute(LINE_ATTR_END));
340
341       if (isEntExists)
342         return aEntIter->second;
343
344       // New entity
345       Slvs_Entity aLineEntity = Slvs_MakeLineSegment(++myEntityMaxID, myID, myWorkplane.h, aStart, aEnd);
346       myEntities.push_back(aLineEntity);
347       myEntityFeatMap[theEntity] = aLineEntity.h;
348       return aLineEntity.h;
349     }
350     // Circle
351     else if (aFeatureKind.compare(SKETCH_CIRCLE_KIND) == 0)
352     {
353       Slvs_hEntity aCenter = changeEntity(aFeature->data()->attribute(CIRCLE_ATTR_CENTER));
354       Slvs_hEntity aRadius = changeEntity(aFeature->data()->attribute(CIRCLE_ATTR_RADIUS));
355
356       if (isEntExists)
357         return aEntIter->second;
358
359       // New entity
360       Slvs_Entity aCircleEntity = 
361         Slvs_MakeCircle(++myEntityMaxID, myID, myWorkplane.h, aCenter, myWorkplane.normal, aRadius);
362       myEntities.push_back(aCircleEntity);
363       myEntityFeatMap[theEntity] = aCircleEntity.h;
364       return aCircleEntity.h;
365     }
366     // Arc
367     else if (aFeatureKind.compare(SKETCH_ARC_KIND) == 0)
368     {
369       Slvs_hEntity aCenter = changeEntity(aFeature->data()->attribute(ARC_ATTR_CENTER));
370       Slvs_hEntity aStart  = changeEntity(aFeature->data()->attribute(ARC_ATTR_START));
371       Slvs_hEntity aEnd    = changeEntity(aFeature->data()->attribute(ARC_ATTR_END));
372
373       if (isEntExists)
374         return aEntIter->second;
375
376       Slvs_Entity anArcEntity = Slvs_MakeArcOfCircle(++myEntityMaxID, myID, 
377                                   myWorkplane.h, myWorkplane.normal, aCenter, aStart, aEnd);
378       myEntities.push_back(anArcEntity);
379       myEntityFeatMap[theEntity] = anArcEntity.h;
380       return anArcEntity.h;
381     }
382     // Point (it has low probability to be an attribute of constraint, so it is checked at the end)
383     else if (aFeatureKind.compare(SKETCH_POINT_KIND) == 0)
384     {
385       Slvs_hEntity aPoint = changeEntity(aFeature->data()->attribute(POINT_ATTR_COORD));
386
387       if (isEntExists)
388         return aEntIter->second;
389
390       // Both the sketch point and its attribute (coordinates) link to the same SolveSpace point identifier
391       myEntityFeatMap[theEntity] = aPoint;
392       return aPoint;
393     }
394   }
395
396   /// \todo Other types of features
397
398   // Unsupported or wrong entity type
399   return SLVS_E_UNKNOWN;
400 }
401
402 // ============================================================================
403 //  Function: changeNormal
404 //  Class:    SketchSolver_ConstraintGroup
405 //  Purpose:  create/update the normal of workplane
406 // ============================================================================
407 Slvs_hEntity SketchSolver_ConstraintGroup::changeNormal(
408                 boost::shared_ptr<ModelAPI_Attribute> theDirX,
409                 boost::shared_ptr<ModelAPI_Attribute> theDirY,
410                 boost::shared_ptr<ModelAPI_Attribute> theNorm)
411 {
412   boost::shared_ptr<GeomDataAPI_Dir> aDirX =
413     boost::dynamic_pointer_cast<GeomDataAPI_Dir>(theDirX);
414   boost::shared_ptr<GeomDataAPI_Dir> aDirY =
415     boost::dynamic_pointer_cast<GeomDataAPI_Dir>(theDirY);
416   if (!aDirX || !aDirY ||
417      (fabs(aDirX->x()) + fabs(aDirX->y()) + fabs(aDirX->z()) < tolerance) ||
418      (fabs(aDirY->x()) + fabs(aDirY->y()) + fabs(aDirY->z()) < tolerance))
419     return SLVS_E_UNKNOWN;
420
421   // quaternion parameters of normal vector
422   double qw, qx, qy, qz;
423   Slvs_MakeQuaternion(aDirX->x(), aDirX->y(), aDirX->z(),
424                       aDirY->x(), aDirY->y(), aDirY->z(),
425                       &qw, &qx, &qy, &qz);
426   double aNormCoord[4] = {qw, qx, qy, qz};
427
428   // Try to find existent normal
429   std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
430     aEntIter = myEntityAttrMap.find(theNorm);
431   std::vector<Slvs_Param>::const_iterator aParamIter; // looks to the first parameter of already existent entity or to the end of vector otherwise
432   if (aEntIter == myEntityAttrMap.end()) // no such entity => should be created
433     aParamIter = myParams.end();
434   else
435   { // the entity already exists, update it
436     int aEntPos = Search(aEntIter->second, myEntities);
437     int aParamPos = Search(myEntities[aEntPos].param[0], myParams);
438     aParamIter = myParams.begin() + aParamPos;
439   }
440
441   // Change parameters of the normal
442   Slvs_hParam aNormParams[4];
443   for (int i = 0; i < 4; i++)
444     aNormParams[i] = changeParameter(aNormCoord[i], aParamIter);
445
446   if (aEntIter != myEntityAttrMap.end()) // the entity already exists
447     return aEntIter->second;
448
449   // Create a normal
450   Slvs_Entity aNormal = Slvs_MakeNormal3d(++myEntityMaxID, myID,
451                 aNormParams[0], aNormParams[1], aNormParams[2], aNormParams[3]);
452   myEntities.push_back(aNormal);
453   myEntityAttrMap[theNorm] = aNormal.h;
454   return aNormal.h;
455 }
456
457
458 // ============================================================================
459 //  Function: addWorkplane
460 //  Class:    SketchSolver_ConstraintGroup
461 //  Purpose:  create workplane for the group
462 // ============================================================================
463 bool SketchSolver_ConstraintGroup::addWorkplane(
464                 boost::shared_ptr<SketchPlugin_Feature> theSketch)
465 {
466   if (myWorkplane.h || theSketch->getKind().compare(SKETCH_KIND) != 0)
467     return false; // the workplane already exists or the function parameter is not Sketch
468
469   mySketch = theSketch;
470   updateWorkplane();
471   return true;
472 }
473
474 // ============================================================================
475 //  Function: updateWorkplane
476 //  Class:    SketchSolver_ConstraintGroup
477 //  Purpose:  update parameters of workplane
478 // ============================================================================
479 bool SketchSolver_ConstraintGroup::updateWorkplane()
480 {
481   // Get parameters of workplane
482   boost::shared_ptr<ModelAPI_Attribute> aDirX    = mySketch->data()->attribute(SKETCH_ATTR_DIRX);
483   boost::shared_ptr<ModelAPI_Attribute> aDirY    = mySketch->data()->attribute(SKETCH_ATTR_DIRY);
484   boost::shared_ptr<ModelAPI_Attribute> aNorm    = mySketch->data()->attribute(SKETCH_ATTR_NORM);
485   boost::shared_ptr<ModelAPI_Attribute> anOrigin = mySketch->data()->attribute(SKETCH_ATTR_ORIGIN);
486   // Transform them into SolveSpace format
487   Slvs_hEntity aNormalWP = changeNormal(aDirX, aDirY, aNorm);
488   if (!aNormalWP) return false;
489   Slvs_hEntity anOriginWP = changeEntity(anOrigin);
490   if (!anOriginWP) return false;
491
492   if (!myWorkplane.h)
493   {
494     // Create workplane
495     myWorkplane = Slvs_MakeWorkplane(++myEntityMaxID, myID, anOriginWP, aNormalWP);
496     // Workplane should be added to the list of entities
497     myEntities.push_back(myWorkplane);
498   }
499   return true;
500 }
501
502 // ============================================================================
503 //  Function: changeParameter
504 //  Class:    SketchSolver_ConstraintGroup
505 //  Purpose:  create/update value of parameter
506 // ============================================================================
507 Slvs_hParam SketchSolver_ConstraintGroup::changeParameter(
508                 const double&                            theParam,
509                 std::vector<Slvs_Param>::const_iterator& thePrmIter)
510 {
511   if (thePrmIter != myParams.end())
512   { // Parameter should be updated
513     int aParamPos = thePrmIter - myParams.begin();
514     if (fabs(thePrmIter->val - theParam) > tolerance)
515     {
516       myNeedToSolve = true; // parameter is changed, need to resolve constraints
517       myParams[aParamPos].val = theParam;
518     }
519     thePrmIter++;
520     return myParams[aParamPos].h;
521   }
522
523   // Newly created parameter
524   Slvs_Param aParam = Slvs_MakeParam(++myParamMaxID, myID, theParam);
525   myParams.push_back(aParam);
526   myNeedToSolve = true;
527   // The list of parameters is changed, move iterator to the end of the list to avoid problems
528   thePrmIter = myParams.end();
529   return aParam.h;
530 }
531
532 // ============================================================================
533 //  Function: resolveConstraints
534 //  Class:    SketchSolver_ConstraintGroup
535 //  Purpose:  solve the set of constraints for the current group
536 // ============================================================================
537 void SketchSolver_ConstraintGroup::resolveConstraints()
538 {
539   if (!myNeedToSolve)
540     return;
541
542   myConstrSolver.setGroupID(myID);
543   myConstrSolver.setParameters(myParams);
544   myConstrSolver.setEntities(myEntities);
545   myConstrSolver.setConstraints(myConstraints);
546   myConstrSolver.setDraggedParameters(myTempPointWhereDragged);
547
548   int aResult = myConstrSolver.solve();
549   if (aResult == SLVS_RESULT_OKAY)
550   { // solution succeeded, store results into correspondent attributes
551     // Obtain result into the same list of parameters
552     if (!myConstrSolver.getResult(myParams))
553       return;
554
555     // We should go through the attributes map, because only attributes have valued parameters
556     std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator
557       anEntIter = myEntityAttrMap.begin();
558     for ( ; anEntIter != myEntityAttrMap.end(); anEntIter++)
559       updateAttribute(anEntIter->first, anEntIter->second);
560   }
561   /// \todo Implement error handling
562
563   removeTemporaryConstraints();
564   myNeedToSolve = false;
565 }
566
567 // ============================================================================
568 //  Function: mergeGroups
569 //  Class:    SketchSolver_ConstraintGroup
570 //  Purpose:  append specified group to the current group
571 // ============================================================================
572 void SketchSolver_ConstraintGroup::mergeGroups(
573                 const SketchSolver_ConstraintGroup& theGroup)
574 {
575   // If specified group is empty, no need to merge
576   if (theGroup.myConstraintMap.empty())
577     return ;
578
579   // Map between old and new indexes of SolveSpace constraints
580   std::map<Slvs_hConstraint, Slvs_hConstraint> aConstrMap;
581
582   // Add all constraints from theGroup to the current group
583   std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::const_iterator
584     aConstrIter = theGroup.myConstraintMap.begin();
585   for ( ; aConstrIter != theGroup.myConstraintMap.end(); aConstrIter++)
586     if (changeConstraint(aConstrIter->first))
587       aConstrMap[aConstrIter->second] = myConstrMaxID; // the constraint was added => store its ID
588
589   // Add temporary constraints from theGroup
590   std::list<Slvs_hConstraint>::const_iterator aTempConstrIter = theGroup.myTempConstraints.begin();
591   for ( ; aTempConstrIter != theGroup.myTempConstraints.end(); aTempConstrIter++)
592   {
593     std::map<Slvs_hConstraint, Slvs_hConstraint>::iterator aFind = aConstrMap.find(*aTempConstrIter);
594     if (aFind != aConstrMap.end())
595       myTempConstraints.push_back(aFind->second);
596   }
597
598   if (myTempPointWhereDragged.empty())
599     myTempPointWhereDragged = theGroup.myTempPointWhereDragged;
600   else if (!theGroup.myTempPointWhereDragged.empty())
601   { // Need to create additional transient constraint
602     std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
603       aFeatureIter = theGroup.myEntityAttrMap.begin();
604     for (; aFeatureIter != theGroup.myEntityAttrMap.end(); aFeatureIter++)
605       if (aFeatureIter->second == myTempPointWDrgdID)
606       {
607         addTemporaryConstraintWhereDragged(aFeatureIter->first);
608         break;
609       }
610   }
611
612   myNeedToSolve = myNeedToSolve || theGroup.myNeedToSolve;
613 }
614
615 // ============================================================================
616 //  Function: splitGroup
617 //  Class:    SketchSolver_ConstraintGroup
618 //  Purpose:  divide the group into several subgroups
619 // ============================================================================
620 void SketchSolver_ConstraintGroup::splitGroup(std::vector<SketchSolver_ConstraintGroup*>& theCuts)
621 {
622   // Divide constraints and entities into several groups
623   std::vector< std::set<Slvs_hEntity> >     aGroupsEntities;
624   std::vector< std::set<Slvs_hConstraint> > aGroupsConstr;
625   int aMaxNbEntities = 0; // index of the group with maximal nuber of elements (this group will be left in the current)
626   std::vector<Slvs_Constraint>::const_iterator aConstrIter = myConstraints.begin();
627   for ( ; aConstrIter != myConstraints.end(); aConstrIter++)
628   {
629     Slvs_hEntity aConstrEnt[] = {
630       aConstrIter->ptA,     aConstrIter->ptB,
631       aConstrIter->entityA, aConstrIter->entityB};
632     std::vector<int> anIndexes;
633     // Go through the groupped entities and find even one of entities of current constraint
634     std::vector< std::set<Slvs_hEntity> >::iterator aGrEntIter;
635     for (aGrEntIter = aGroupsEntities.begin(); aGrEntIter != aGroupsEntities.end(); aGrEntIter++)
636     {
637       bool isFound = false;
638       for (int i = 0; i < 4 && !isFound; i++)
639         if (aConstrEnt[i] != 0)
640           isFound = (aGrEntIter->find(aConstrEnt[i]) != aGrEntIter->end());
641       if (isFound)
642         anIndexes.push_back(aGrEntIter - aGroupsEntities.begin());
643     }
644     // Add new group if no one is found
645     if (anIndexes.empty())
646     {
647       std::set<Slvs_hEntity> aNewGrEnt;
648       for (int i = 0; i < 4; i++)
649         if (aConstrEnt[i] != 0)
650           aNewGrEnt.insert(aConstrEnt[i]);
651       std::set<Slvs_hConstraint> aNewGrConstr;
652       aNewGrConstr.insert(aConstrIter->h);
653
654       aGroupsEntities.push_back(aNewGrEnt);
655       aGroupsConstr.push_back(aNewGrConstr);
656       if (aNewGrEnt.size() > aGroupsEntities[aMaxNbEntities].size())
657         aMaxNbEntities = aGroupsEntities.size() - 1;
658     }
659     else if (anIndexes.size() == 1)
660     { // Add entities indexes into the found group
661       aGrEntIter = aGroupsEntities.begin() + anIndexes.front();
662       for (int i = 0; i < 4; i++)
663         if (aConstrEnt[i] != 0)
664           aGrEntIter->insert(aConstrEnt[i]);
665       aGroupsConstr[anIndexes.front()].insert(aConstrIter->h);
666       if (aGrEntIter->size() > aGroupsEntities[aMaxNbEntities].size())
667         aMaxNbEntities = aGrEntIter - aGroupsEntities.begin();
668     }
669     else 
670     { // There are found several connected groups, merge them
671       std::vector< std::set<Slvs_hEntity> >::iterator aFirstGroup = 
672         aGroupsEntities.begin() + anIndexes.front();
673       std::vector< std::set<Slvs_hConstraint> >::iterator aFirstConstr = 
674         aGroupsConstr.begin() + anIndexes.front();
675       std::vector<int>::iterator anInd = anIndexes.begin();
676       for (++anInd; anInd != anIndexes.end(); anInd++)
677       {
678         aFirstGroup->insert(aGroupsEntities[*anInd].begin(), aGroupsEntities[*anInd].end());
679         aFirstConstr->insert(aGroupsConstr[*anInd].begin(), aGroupsConstr[*anInd].end());
680       }
681       if (aFirstGroup->size() > aGroupsEntities[aMaxNbEntities].size())
682         aMaxNbEntities = anIndexes.front();
683       // Remove merged groups
684       for (anInd = anIndexes.end() - 1; anInd != anIndexes.begin(); anInd--)
685       {
686         aGroupsEntities.erase(aGroupsEntities.begin() + (*anInd));
687         aGroupsConstr.erase(aGroupsConstr.begin() + (*anInd));
688       }
689     }
690   }
691
692   if (aGroupsEntities.size() <= 1)
693     return ;
694
695   // Remove the group with maximum elements as it will be left in the current group
696   aGroupsEntities.erase(aGroupsEntities.begin() + aMaxNbEntities);
697   aGroupsConstr.erase(aGroupsConstr.begin() + aMaxNbEntities);
698
699   // Add new groups of constraints and divide current group
700   std::vector<SketchSolver_ConstraintGroup*> aNewGroups;
701   for (int i = aGroupsEntities.size(); i > 0; i--)
702   {
703     SketchSolver_ConstraintGroup* aG = new SketchSolver_ConstraintGroup(mySketch);
704     aNewGroups.push_back(aG);
705   }
706   std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::const_iterator
707     aConstrMapIter = myConstraintMap.begin();
708   int aConstrMapPos = 0; // position of iterator in the map (used to restore iterator after removing constraint)
709   while (aConstrMapIter != myConstraintMap.end())
710   {
711     std::vector< std::set<Slvs_hConstraint> >::const_iterator aGIter = aGroupsConstr.begin();
712     std::vector<SketchSolver_ConstraintGroup*>::iterator aGroup = aNewGroups.begin();
713     for ( ; aGIter != aGroupsConstr.end(); aGIter++, aGroup++)
714       if (aGIter->find(aConstrMapIter->second) != aGIter->end())
715       {
716         (*aGroup)->changeConstraint(aConstrMapIter->first);
717         removeConstraint(aConstrMapIter->first);
718         // restore iterator
719         aConstrMapIter = myConstraintMap.begin();
720         for (int i = 0; i < aConstrMapPos; i++)
721           aConstrMapIter++;
722         break;
723       }
724     if (aGIter == aGroupsConstr.end())
725     {
726       aConstrMapIter++;
727       aConstrMapPos++;
728     }
729   }
730
731   theCuts.insert(theCuts.end(), aNewGroups.begin(), aNewGroups.end());
732 }
733
734 // ============================================================================
735 //  Function: updateGroup
736 //  Class:    SketchSolver_ConstraintGroup
737 //  Purpose:  search removed entities and constraints
738 // ============================================================================
739 bool SketchSolver_ConstraintGroup::updateGroup()
740 {
741   std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::reverse_iterator
742     aConstrIter = myConstraintMap.rbegin();
743   bool isAllValid = true;
744   bool isCCRemoved = false; // indicates that at least one of coincidence constraints was removed
745   while (isAllValid && aConstrIter != myConstraintMap.rend())
746   {
747     if (!aConstrIter->first->data()->isValid())
748     {
749       if (aConstrIter->first->getKind().compare(SKETCH_CONSTRAINT_COINCIDENCE_KIND) == 0)
750         isCCRemoved = true;
751       std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::reverse_iterator
752         aCopyIter = aConstrIter++;
753       removeConstraint(aCopyIter->first);
754       isAllValid = false;
755     }
756     else aConstrIter++;
757   }
758
759   // Probably, need to update coincidence constraints
760   if (isCCRemoved && !myExtraCoincidence.empty())
761   {
762     // Make a copy, because the new list of unused constrtaints will be generated
763     std::set< boost::shared_ptr<SketchPlugin_Constraint> > anExtraCopy = myExtraCoincidence;
764     myExtraCoincidence.clear();
765
766     std::set< boost::shared_ptr<SketchPlugin_Constraint> >::iterator
767       aCIter = anExtraCopy.begin();
768     for ( ; aCIter != anExtraCopy.end(); aCIter++)
769       if ((*aCIter)->data()->isValid())
770         changeConstraint(*aCIter);
771   }
772
773   return !isAllValid;
774 }
775
776 // ============================================================================
777 //  Function: updateAttribute
778 //  Class:    SketchSolver_ConstraintGroup
779 //  Purpose:  update features of sketch after resolving constraints
780 // ============================================================================
781 void SketchSolver_ConstraintGroup::updateAttribute(
782                 boost::shared_ptr<ModelAPI_Attribute> theAttribute,
783                 const Slvs_hEntity&                   theEntityID)
784 {
785   // Search the position of the first parameter of the entity
786   int anEntPos = Search(theEntityID, myEntities);
787   int aFirstParamPos = Search(myEntities[anEntPos].param[0], myParams);
788
789   // Look over supported types of entities
790
791   // Point in 3D
792   boost::shared_ptr<GeomDataAPI_Point> aPoint =
793     boost::dynamic_pointer_cast<GeomDataAPI_Point>(theAttribute);
794   if (aPoint)
795   {
796     aPoint->setValue(myParams[aFirstParamPos].val,
797                      myParams[aFirstParamPos+1].val,
798                      myParams[aFirstParamPos+2].val);
799     return ;
800   }
801
802   // Point in 2D
803   boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
804     boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute);
805   if (aPoint2D)
806   {
807     aPoint2D->setValue(myParams[aFirstParamPos].val,
808                        myParams[aFirstParamPos+1].val);
809     return ;
810   }
811
812   // Scalar value
813   boost::shared_ptr<ModelAPI_AttributeDouble> aScalar = 
814     boost::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theAttribute);
815   if (aScalar)
816   {
817     aScalar->setValue(myParams[aFirstParamPos].val);
818     return ;
819   }
820
821   /// \todo Support other types of entities
822 }
823
824 // ============================================================================
825 //  Function: updateEntityIfPossible
826 //  Class:    SketchSolver_ConstraintGroup
827 //  Purpose:  search the entity in this group and update it
828 // ============================================================================
829 void SketchSolver_ConstraintGroup::updateEntityIfPossible(
830                 boost::shared_ptr<ModelAPI_Attribute> theEntity)
831 {
832   if (myEntityAttrMap.find(theEntity) != myEntityAttrMap.end())
833   {
834     // If the attribute is a point and it is changed (the group needs to rebuild),
835     // probably user has dragged this point into this position,
836     // so it is necessary to add constraint which will guarantee the point will not change
837
838     // Store myNeedToSolve flag to verify the entity is really changed
839     bool aNeedToSolveCopy = myNeedToSolve;
840     myNeedToSolve = false;
841
842     changeEntity(theEntity);
843
844     if (myNeedToSolve) // the entity is changed
845     {
846       // Verify the entity is a point and add temporary constraint of permanency
847       boost::shared_ptr<GeomDataAPI_Point> aPoint =
848         boost::dynamic_pointer_cast<GeomDataAPI_Point>(theEntity);
849       boost::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
850         boost::dynamic_pointer_cast<GeomDataAPI_Point2D>(theEntity);
851       if (aPoint || aPoint2D)
852         addTemporaryConstraintWhereDragged(theEntity);
853     }
854
855     // Restore flag of changes
856     myNeedToSolve = myNeedToSolve || aNeedToSolveCopy;
857   }
858 }
859
860 // ============================================================================
861 //  Function: addTemporaryConstraintWhereDragged
862 //  Class:    SketchSolver_ConstraintGroup
863 //  Purpose:  add transient constraint SLVS_C_WHERE_DRAGGED for the entity, 
864 //            which was moved by user
865 // ============================================================================
866 void SketchSolver_ConstraintGroup::addTemporaryConstraintWhereDragged(
867                 boost::shared_ptr<ModelAPI_Attribute> theEntity)
868 {
869   // Find identifier of the entity
870   std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::const_iterator
871     anEntIter = myEntityAttrMap.find(theEntity);
872   if (anEntIter == myEntityAttrMap.end())
873     return ;
874
875   // If this is a first dragged point, its parameters should be placed 
876   // into Slvs_System::dragged field to avoid system inconsistense
877   if (myTempPointWhereDragged.empty())
878   {
879     int anEntPos = Search(anEntIter->second, myEntities);
880     Slvs_hParam* aDraggedParam = myEntities[anEntPos].param;
881     for (int i = 0; i < 4; i++, aDraggedParam++)
882       if (*aDraggedParam != 0)
883         myTempPointWhereDragged.push_back(*aDraggedParam);
884     myTempPointWDrgdID = myEntities[anEntPos].h;
885     return ;
886   }
887
888   // Get identifiers of all dragged points
889   std::set<Slvs_hEntity> aDraggedPntID;
890   aDraggedPntID.insert(myTempPointWDrgdID);
891   std::list<Slvs_hConstraint>::iterator aTmpCoIter = myTempConstraints.begin();
892   for ( ; aTmpCoIter != myTempConstraints.end(); aTmpCoIter++)
893   {
894     unsigned int aConstrPos = Search(*aTmpCoIter, myConstraints);
895     if (aConstrPos < myConstraints.size())
896       aDraggedPntID.insert(myConstraints[aConstrPos].ptA);
897   }
898   // Find whether there is a point coincident with theEntity, which already has SLVS_C_WHERE_DRAGGED
899   std::vector< std::set<Slvs_hEntity> >::iterator aCoPtIter = myCoincidentPoints.begin();
900   for ( ; aCoPtIter != myCoincidentPoints.end(); aCoPtIter++)
901   {
902     if (aCoPtIter->find(anEntIter->second) == aCoPtIter->end())
903       continue; // the entity was not found in current set
904
905     // Find one of already created SLVS_C_WHERE_DRAGGED constraints in current set of coincident points
906     std::set<Slvs_hEntity>::const_iterator aDrgIter = aDraggedPntID.begin();
907     for ( ; aDrgIter != aDraggedPntID.end(); aDrgIter++)
908       if (aCoPtIter->find(*aDrgIter) != aCoPtIter->end())
909         return ; // the SLVS_C_WHERE_DRAGGED constraint already exists
910   }
911
912   // Create additional SLVS_C_WHERE_DRAGGED constraint if myTempPointWhereDragged field is not empty
913   Slvs_Constraint aWDConstr = Slvs_MakeConstraint(++myConstrMaxID, myID, SLVS_C_WHERE_DRAGGED,
914                                                   myWorkplane.h, 0.0, anEntIter->second, 0, 0, 0);
915   myConstraints.push_back(aWDConstr);
916   myTempConstraints.push_back(aWDConstr.h);
917 }
918
919 // ============================================================================
920 //  Function: removeTemporaryConstraints
921 //  Class:    SketchSolver_ConstraintGroup
922 //  Purpose:  remove all transient SLVS_C_WHERE_DRAGGED constraints after
923 //            resolving the set of constraints
924 // ============================================================================
925 void SketchSolver_ConstraintGroup::removeTemporaryConstraints()
926 {
927   std::list<Slvs_hConstraint>::reverse_iterator aTmpConstrIter;
928   for (aTmpConstrIter = myTempConstraints.rbegin(); aTmpConstrIter != myTempConstraints.rend(); aTmpConstrIter++)
929   {
930     unsigned int aConstrPos = Search(*aTmpConstrIter, myConstraints);
931     if (aConstrPos >= myConstraints.size())
932       continue;
933     myConstraints.erase(myConstraints.begin() + aConstrPos);
934
935     // If the removing constraint has higher index, decrease the indexer
936     if (*aTmpConstrIter == myConstrMaxID)
937       myConstrMaxID--;
938   }
939   myTempConstraints.clear();
940
941   // Clear basic dragged point
942   myTempPointWhereDragged.clear();
943 }
944
945 // ============================================================================
946 //  Function: removeConstraint
947 //  Class:    SketchSolver_ConstraintGroup
948 //  Purpose:  remove constraint and all unused entities
949 // ============================================================================
950 void SketchSolver_ConstraintGroup::removeConstraint(boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
951 {
952   std::map<boost::shared_ptr<SketchPlugin_Constraint>, Slvs_hConstraint>::iterator
953     anIterToRemove = myConstraintMap.find(theConstraint);
954   if (anIterToRemove == myConstraintMap.end())
955      return ;
956
957   Slvs_hConstraint aCnstrToRemove = anIterToRemove->second;
958   // Remove constraint from the map
959   myConstraintMap.erase(anIterToRemove);
960
961   // Find unused entities
962   int aConstrPos = Search(aCnstrToRemove, myConstraints);
963   std::set<Slvs_hEntity> anEntToRemove;
964   Slvs_hEntity aCnstEnt[] = {myConstraints[aConstrPos].ptA,     myConstraints[aConstrPos].ptB, 
965                              myConstraints[aConstrPos].entityA, myConstraints[aConstrPos].entityB};
966   for (int i = 0; i < 4; i++)
967     if (aCnstEnt[i] != 0)
968       anEntToRemove.insert(aCnstEnt[i]);
969   myConstraints.erase(myConstraints.begin() + aConstrPos);
970   if (aCnstrToRemove == myConstrMaxID)
971     myConstrMaxID--;
972   std::vector<Slvs_Constraint>::const_iterator aConstrIter = myConstraints.begin();
973   for ( ; aConstrIter != myConstraints.end(); aConstrIter++)
974   {
975     Slvs_hEntity aEnts[] = {aConstrIter->ptA,     aConstrIter->ptB, 
976                             aConstrIter->entityA, aConstrIter->entityB};
977     for (int i = 0; i < 4; i++)
978       if (aEnts[i] != 0 && anEntToRemove.find(aEnts[i]) != anEntToRemove.end())
979         anEntToRemove.erase(aEnts[i]);
980   }
981
982   if (anEntToRemove.empty())
983     return ;
984
985   // Remove unused entities
986   std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator
987     anEntAttrIter = myEntityAttrMap.begin();
988   while (anEntAttrIter != myEntityAttrMap.end())
989   {
990     if (anEntToRemove.find(anEntAttrIter->second) != anEntToRemove.end())
991     {
992       std::map<boost::shared_ptr<ModelAPI_Attribute>, Slvs_hEntity>::iterator
993         aRemovedIter = anEntAttrIter;
994       anEntAttrIter++;
995       myEntityAttrMap.erase(aRemovedIter);
996     }
997     else anEntAttrIter++;
998   }
999   std::map<boost::shared_ptr<ModelAPI_Feature>, Slvs_hEntity>::iterator
1000     anEntFeatIter = myEntityFeatMap.begin();
1001   while (anEntFeatIter != myEntityFeatMap.end())
1002   {
1003     if (anEntToRemove.find(anEntFeatIter->second) != anEntToRemove.end())
1004     {
1005       std::map<boost::shared_ptr<ModelAPI_Feature>, Slvs_hEntity>::iterator
1006         aRemovedIter = anEntFeatIter;
1007       anEntFeatIter++;
1008       myEntityFeatMap.erase(aRemovedIter);
1009     }
1010     else anEntFeatIter++;
1011   }
1012   std::set<Slvs_hEntity>::const_reverse_iterator aRemIter = anEntToRemove.rbegin();
1013   for ( ; aRemIter != anEntToRemove.rend(); aRemIter++)
1014   {
1015     unsigned int anEntPos = Search(*aRemIter, myEntities);
1016     if (anEntPos >= myEntities.size())
1017       continue;
1018     unsigned int aParamPos = Search(myEntities[anEntPos].param[0], myParams);
1019     if (aParamPos >= myParams.size())
1020       continue;
1021     int aNbParams = 0;
1022     while (myEntities[anEntPos].param[aNbParams] != 0) 
1023       aNbParams++;
1024     if (myEntities[anEntPos].param[aNbParams-1] == myParamMaxID)
1025       myParamMaxID -= aNbParams;
1026     myParams.erase(myParams.begin() + aParamPos, myParams.begin() + aParamPos + aNbParams);
1027     if (*aRemIter == myEntityMaxID)
1028       myEntityMaxID--;
1029     myEntities.erase(myEntities.begin() + anEntPos);
1030
1031     // Remove entity's ID from the lists of conincident points
1032     std::vector< std::set<Slvs_hEntity> >::iterator aCoPtIter = myCoincidentPoints.begin();
1033     for ( ; aCoPtIter != myCoincidentPoints.end(); aCoPtIter++)
1034       aCoPtIter->erase(*aRemIter);
1035   }
1036   if (myCoincidentPoints.size() == 1 && myCoincidentPoints.front().empty())
1037     myCoincidentPoints.clear();
1038 }
1039
1040
1041 // ============================================================================
1042 //  Function: addCoincidentPoints
1043 //  Class:    SketchSolver_ConstraintGroup
1044 //  Purpose:  add coincident point the appropriate list of such points
1045 // ============================================================================
1046 bool SketchSolver_ConstraintGroup::addCoincidentPoints(
1047                 const Slvs_hEntity& thePoint1, const Slvs_hEntity& thePoint2)
1048 {
1049   std::vector< std::set<Slvs_hEntity> >::iterator aCoPtIter = myCoincidentPoints.begin();
1050   std::vector< std::set<Slvs_hEntity> >::iterator aFirstFound = myCoincidentPoints.end();
1051   while (aCoPtIter != myCoincidentPoints.end())
1052   {
1053     bool isFound[2] = { // indicate which point ID was already in coincidence constraint
1054       aCoPtIter->find(thePoint1) != aCoPtIter->end(),
1055       aCoPtIter->find(thePoint2) != aCoPtIter->end(),
1056     };
1057     if (isFound[0] && isFound[1]) // points are already connected by coincidence constraints => no need additional one
1058       return false;
1059     if ((isFound[0] && !isFound[1]) || (!isFound[0] && isFound[1]))
1060     {
1061       if (aFirstFound != myCoincidentPoints.end())
1062       { // there are two groups of coincident points connected by created constraint => merge them
1063         int aFirstFoundShift = aFirstFound - myCoincidentPoints.begin();
1064         int aCurrentShift = aCoPtIter - myCoincidentPoints.begin();
1065         aFirstFound->insert(aCoPtIter->begin(), aCoPtIter->end());
1066         myCoincidentPoints.erase(aCoPtIter);
1067         aFirstFound = myCoincidentPoints.begin() + aFirstFoundShift;
1068         aCoPtIter = myCoincidentPoints.begin() + aCurrentShift;
1069         continue;
1070       }
1071       else
1072       {
1073         aCoPtIter->insert(isFound[0] ? thePoint2 : thePoint1);
1074         aFirstFound = aCoPtIter;
1075       }
1076     }
1077     aCoPtIter++;
1078   }
1079   // No points were found, need to create new set
1080   if (aFirstFound == myCoincidentPoints.end())
1081   {
1082     std::set<Slvs_hEntity> aNewSet;
1083     aNewSet.insert(thePoint1);
1084     aNewSet.insert(thePoint2);
1085     myCoincidentPoints.push_back(aNewSet);
1086   }
1087
1088   return true;
1089 }
1090
1091
1092
1093
1094 // ========================================================
1095 // =========      Auxiliary functions       ===============
1096 // ========================================================
1097
1098 template <typename T>
1099 int Search(const uint32_t& theEntityID, const std::vector<T>& theEntities)
1100 {
1101   int aResIndex = theEntityID <= theEntities.size() ? theEntityID - 1 : 0;
1102   int aVecSize = theEntities.size();
1103   while (aResIndex >= 0 && theEntities[aResIndex].h > theEntityID)
1104     aResIndex--;
1105   while (aResIndex < aVecSize && theEntities[aResIndex].h < theEntityID)
1106     aResIndex++;
1107   if (aResIndex == -1)
1108     aResIndex = aVecSize;
1109   return aResIndex;
1110 }