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