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