Salome HOME
Update constraints defined by parameters (issue #976)
[modules/shaper.git] / src / SketchSolver / SketchSolver_Group.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 // File:    SketchSolver_Group.cpp
4 // Created: 27 May 2014
5 // Author:  Artem ZHIDKOV
6
7 #include "SketchSolver_Group.h"
8
9 #include <SketchSolver_Builder.h>
10 #include <SketchSolver_Constraint.h>
11 #include <SketchSolver_ConstraintCoincidence.h>
12 #include <SketchSolver_Error.h>
13
14 #include <Events_Error.h>
15 #include <Events_Loop.h>
16 #include <GeomAPI_XY.h>
17 #include <GeomAPI_Dir2d.h>
18 #include <GeomAPI_Pnt2d.h>
19 #include <GeomDataAPI_Dir.h>
20 #include <GeomDataAPI_Point.h>
21 #include <GeomDataAPI_Point2D.h>
22 #include <ModelAPI_AttributeDouble.h>
23 #include <ModelAPI_AttributeString.h>
24 #include <ModelAPI_Document.h>
25 #include <ModelAPI_Events.h>
26 #include <ModelAPI_ResultConstruction.h>
27 #include <ModelAPI_Session.h>
28 #include <ModelAPI_Validator.h>
29
30 #include <SketchPlugin_Constraint.h>
31 #include <SketchPlugin_ConstraintAngle.h>
32 #include <SketchPlugin_ConstraintCoincidence.h>
33 #include <SketchPlugin_ConstraintDistance.h>
34 #include <SketchPlugin_ConstraintEqual.h>
35 #include <SketchPlugin_ConstraintHorizontal.h>
36 #include <SketchPlugin_ConstraintLength.h>
37 #include <SketchPlugin_ConstraintFillet.h>
38 #include <SketchPlugin_ConstraintMirror.h>
39 #include <SketchPlugin_ConstraintParallel.h>
40 #include <SketchPlugin_ConstraintPerpendicular.h>
41 #include <SketchPlugin_ConstraintRadius.h>
42 #include <SketchPlugin_ConstraintRigid.h>
43 #include <SketchPlugin_ConstraintTangent.h>
44 #include <SketchPlugin_ConstraintVertical.h>
45 #include <SketchPlugin_Feature.h>
46 #include <SketchPlugin_MultiRotation.h>
47 #include <SketchPlugin_MultiTranslation.h>
48 #include <SketchPlugin_Sketch.h>
49
50 #include <SketchPlugin_Arc.h>
51 #include <SketchPlugin_Circle.h>
52 #include <SketchPlugin_Line.h>
53 #include <SketchPlugin_Point.h>
54 #include <SketchPlugin_Sketch.h>
55
56 #include <math.h>
57 #include <assert.h>
58
59
60 /// \brief This class is used to give unique index to the groups
61 class GroupIndexer
62 {
63 public:
64   /// \brief Return vacant index
65   static Slvs_hGroup NEW_GROUP() { return ++myGroupIndex; }
66   /// \brief Removes the index
67   static void REMOVE_GROUP(const Slvs_hGroup& theIndex) {
68     if (myGroupIndex == theIndex)
69       myGroupIndex--;
70   }
71
72 private:
73   GroupIndexer() {};
74
75   static Slvs_hGroup myGroupIndex; ///< index of the group
76 };
77
78 Slvs_hGroup GroupIndexer::myGroupIndex = 0;
79
80
81 static void sendMessage(const char* theMessageName)
82 {
83   std::shared_ptr<Events_Message> aMessage = std::shared_ptr<Events_Message>(
84       new Events_Message(Events_Loop::eventByName(theMessageName)));
85   Events_Loop::loop()->send(aMessage);
86 }
87
88
89
90 // ========================================================
91 // =========  SketchSolver_Group  ===============
92 // ========================================================
93
94 SketchSolver_Group::SketchSolver_Group(
95     std::shared_ptr<ModelAPI_CompositeFeature> theWorkplane)
96     : myID(GroupIndexer::NEW_GROUP()),
97       myPrevSolved(true)
98 {
99   // Initialize workplane
100   myWorkplaneID = SLVS_E_UNKNOWN;
101 #ifndef NDEBUG
102   assert(addWorkplane(theWorkplane));
103 #else
104   addWorkplane(theWorkplane);
105 #endif
106 }
107
108 SketchSolver_Group::~SketchSolver_Group()
109 {
110   myConstraints.clear();
111   GroupIndexer::REMOVE_GROUP(myID);
112 }
113
114 // ============================================================================
115 //  Function: isBaseWorkplane
116 //  Class:    SketchSolver_Group
117 //  Purpose:  verify the group is based on the given workplane
118 // ============================================================================
119 bool SketchSolver_Group::isBaseWorkplane(CompositeFeaturePtr theWorkplane) const
120 {
121   return theWorkplane == mySketch;
122 }
123
124 // ============================================================================
125 //  Function: isInteract
126 //  Class:    SketchSolver_Group
127 //  Purpose:  verify are there any entities in the group used by given constraint
128 // ============================================================================
129 bool SketchSolver_Group::isInteract(
130     std::shared_ptr<SketchPlugin_Feature> theFeature) const
131 {
132   // Empty group interacts with everything
133   if (isEmpty()) return true;
134   ConstraintPtr aConstraint = std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
135   if (aConstraint)
136     return myFeatureStorage->isInteract(aConstraint);
137   return myFeatureStorage->isInteract(std::dynamic_pointer_cast<ModelAPI_Feature>(theFeature));
138 }
139
140 // ============================================================================
141 //  Function: getFeatureId
142 //  Class:    SketchSolver_Group
143 //  Purpose:  Find the identifier of the feature, if it already exists in the group
144 // ============================================================================
145 Slvs_hEntity SketchSolver_Group::getFeatureId(FeaturePtr theFeature) const
146 {
147   Slvs_hEntity aResult = SLVS_E_UNKNOWN;
148   if (!myFeatureStorage)
149     return aResult;
150   // Obtain regular constraints interacting with the feature and find its ID
151   std::set<ConstraintPtr> aConstraints = myFeatureStorage->getConstraints(theFeature);
152   if (aConstraints.empty())
153     return aResult;
154   std::set<ConstraintPtr>::iterator aConstrIter = aConstraints.begin();
155   for (; aConstrIter != aConstraints.end(); ++aConstrIter) {
156     ConstraintConstraintMap::const_iterator aCIter = myConstraints.find(*aConstrIter);
157     if (aCIter == myConstraints.end())
158       continue;
159     aResult = aCIter->second->getId(theFeature);
160     if (aResult != SLVS_E_UNKNOWN)
161       return aResult;
162   }
163   // The feature is not found, check it in the temporary constraints
164   std::set<SolverConstraintPtr>::iterator aTmpCIter = myTempConstraints.begin();
165   for (; aTmpCIter != myTempConstraints.end() && aResult == SLVS_E_UNKNOWN; ++aTmpCIter)
166     aResult = (*aTmpCIter)->getId(theFeature);
167   return aResult;
168 }
169
170 // ============================================================================
171 //  Function: getAttributeId
172 //  Class:    SketchSolver_Group
173 //  Purpose:  Find the identifier of the attribute, if it already exists in the group
174 // ============================================================================
175 Slvs_hEntity SketchSolver_Group::getAttributeId(AttributePtr theAttribute) const
176 {
177   Slvs_hEntity aResult = SLVS_E_UNKNOWN;
178   if (!myFeatureStorage)
179     return aResult;
180   // Obtain regular constraints interacting with the attribute and find its ID
181   std::set<ConstraintPtr> aConstraints = myFeatureStorage->getConstraints(theAttribute);
182   std::set<ConstraintPtr>::iterator aConstrIter = aConstraints.begin();
183   for (; aConstrIter != aConstraints.end(); aConstrIter++) {
184     ConstraintConstraintMap::const_iterator aCIter = myConstraints.find(*aConstrIter);
185     if (aCIter == myConstraints.end())
186       continue;
187     aResult = aCIter->second->getId(theAttribute);
188     if (aResult != SLVS_E_UNKNOWN)
189       return aResult;
190   }
191   // The attribute is not found, check it in the temporary constraints
192   std::set<SolverConstraintPtr>::const_iterator aTmpCIter = myTempConstraints.begin();
193   for (; aTmpCIter != myTempConstraints.end() && aResult == SLVS_E_UNKNOWN; ++aTmpCIter)
194     aResult = (*aTmpCIter)->getId(theAttribute);
195   // Last chance to find attribute in parametric constraints
196   std::map<AttributePtr, SolverConstraintPtr>::const_iterator aParIter =
197       myParametricConstraints.find(theAttribute);
198   if (aParIter != myParametricConstraints.end())
199     aResult = aParIter->second->getId(theAttribute);
200   return aResult;
201 }
202
203 // ============================================================================
204 //  Function: changeConstraint
205 //  Class:    SketchSolver_Group
206 //  Purpose:  create/update the constraint in the group
207 // ============================================================================
208 bool SketchSolver_Group::changeConstraint(
209     std::shared_ptr<SketchPlugin_Constraint> theConstraint)
210 {
211   // There is no workplane yet, something wrong
212   if (myWorkplaneID == SLVS_E_UNKNOWN)
213     return false;
214
215   if (!theConstraint || !theConstraint->data())
216     return false;
217
218   if (!checkFeatureValidity(theConstraint))
219     return false;
220
221   bool isNewConstraint = myConstraints.find(theConstraint) == myConstraints.end();
222   if (isNewConstraint) {
223     // Add constraint to the current group
224     SolverConstraintPtr aConstraint =
225         SketchSolver_Builder::getInstance()->createConstraint(theConstraint);
226     if (!aConstraint)
227       return false;
228     aConstraint->setGroup(this);
229     aConstraint->setStorage(myStorage);
230     if (!aConstraint->error().empty()) {
231       if (aConstraint->error() == SketchSolver_Error::NOT_INITIALIZED())
232         return false; // some attribute are not initialized yet, don't show message
233       Events_Error::send(aConstraint->error(), this);
234     }
235
236     // Additional verification of coincidence of several points
237     if (theConstraint->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
238       ConstraintConstraintMap::iterator aCIter = myConstraints.begin();
239       for (; aCIter != myConstraints.end(); aCIter++) {
240         std::shared_ptr<SketchSolver_ConstraintCoincidence> aCoincidence =
241           std::dynamic_pointer_cast<SketchSolver_ConstraintCoincidence>(aCIter->second);
242         if (!aCoincidence)
243           continue;
244         std::shared_ptr<SketchSolver_ConstraintCoincidence> aCoinc2 =
245           std::dynamic_pointer_cast<SketchSolver_ConstraintCoincidence>(aConstraint);
246         if (aCoincidence != aCoinc2 && aCoincidence->isCoincide(aCoinc2)) {
247           aCoinc2->attach(aCoincidence);
248           // update other coincidences
249           ConstraintConstraintMap::iterator anIt = aCIter;
250           for (++anIt; anIt != myConstraints.end(); ++anIt)
251             if (anIt->second == aCIter->second)
252               anIt->second = aCoinc2;
253           aCIter->second = aCoinc2;
254         }
255       }
256     }
257     myConstraints[theConstraint] = aConstraint;
258   }
259   else
260     myConstraints[theConstraint]->update();
261
262   // Fix base features for fillet
263   if (isNewConstraint && theConstraint->getKind() == SketchPlugin_ConstraintFillet::ID()) {
264     std::list<AttributePtr> anAttrList =
265         theConstraint->data()->attributes(ModelAPI_AttributeRefAttr::typeId());
266     std::list<AttributePtr>::iterator anAttrIter = anAttrList.begin();
267     for (; anAttrIter != anAttrList.end(); anAttrIter++) {
268       AttributeRefAttrPtr aRefAttr =
269           std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(*anAttrIter);
270       if (!aRefAttr || !aRefAttr->isObject())
271         continue;
272       FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttr->object());
273       SolverConstraintPtr aConstraint =
274           SketchSolver_Builder::getInstance()->createRigidConstraint(aFeature);
275       if (!aConstraint)
276         continue;
277       aConstraint->setGroup(this);
278       aConstraint->setStorage(myStorage);
279       setTemporary(aConstraint);
280     }
281   }
282   // Fix mirror line
283   if (theConstraint->getKind() == SketchPlugin_ConstraintMirror::ID()) {
284     AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
285         theConstraint->attribute(SketchPlugin_ConstraintMirror::ENTITY_A()));
286     if (aRefAttr && aRefAttr->isObject()) {
287       FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttr->object());
288       if (aFeature) {
289         SolverConstraintPtr aConstraint =
290             SketchSolver_Builder::getInstance()->createRigidConstraint(aFeature);
291         if (aConstraint) {
292           aConstraint->setGroup(this);
293           aConstraint->setStorage(myStorage);
294           setTemporary(aConstraint);
295         }
296       }
297     }
298   }
299
300   if (!myFeatureStorage)
301     myFeatureStorage = FeatureStoragePtr(new SketchSolver_FeatureStorage);
302   myFeatureStorage->changeConstraint(theConstraint);
303
304   // Check the attributes of constraint are given by parametric expression
305   std::list<AttributePtr> anAttributes =
306       theConstraint->data()->attributes(ModelAPI_AttributeRefAttr::typeId());
307   std::list<AttributePtr>::iterator anAttrIt = anAttributes.begin();
308   for (; anAttrIt != anAttributes.end(); ++anAttrIt) {
309     AttributeRefAttrPtr aRefAttr =
310         std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(*anAttrIt);
311     if (!aRefAttr || aRefAttr->isObject())
312       continue;
313     std::shared_ptr<GeomDataAPI_Point2D> aPoint =
314         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aRefAttr->attr());
315     if (!aPoint || (aPoint->textX().empty() && aPoint->textY().empty()))
316       continue;
317
318     std::map<AttributePtr, SolverConstraintPtr>::iterator aFound =
319         myParametricConstraints.find(aRefAttr->attr());
320     if (aFound == myParametricConstraints.end()) {
321       SolverConstraintPtr aConstraint =
322           SketchSolver_Builder::getInstance()->createParametricConstraint(aRefAttr->attr());
323       if (!aConstraint)
324         continue;
325       aConstraint->setGroup(this);
326       aConstraint->setStorage(myStorage);
327       myParametricConstraints[aRefAttr->attr()] = aConstraint;
328     } else
329       aFound->second->update();
330   }
331
332   return true;
333 }
334
335
336 void SketchSolver_Group::updateConstraints()
337 {
338   std::set<SolverConstraintPtr> aPostponed; // postponed constraints Multi-Rotation and Multi-Translation
339
340   ConstraintConstraintMap::iterator anIt = myConstraints.begin();
341   for (; anIt != myConstraints.end(); ++anIt) {
342     if (myChangedConstraints.find(anIt->first) == myChangedConstraints.end())
343       continue;
344     if (anIt->first->getKind() == SketchPlugin_MultiRotation::ID() ||
345         anIt->first->getKind() == SketchPlugin_MultiTranslation::ID())
346       aPostponed.insert(anIt->second);
347     else
348       anIt->second->update();
349   }
350
351   // Update postponed constraints
352   std::set<SolverConstraintPtr>::iterator aSCIter = aPostponed.begin();
353   for (; aSCIter != aPostponed.end(); ++aSCIter)
354     (*aSCIter)->update();
355
356   myChangedConstraints.clear();
357 }
358
359 bool SketchSolver_Group::updateFeature(std::shared_ptr<SketchPlugin_Feature> theFeature)
360 {
361   if (!checkFeatureValidity(theFeature))
362     return false;
363
364   std::set<ConstraintPtr> aConstraints =
365       myFeatureStorage->getConstraints(std::dynamic_pointer_cast<ModelAPI_Feature>(theFeature));
366   if (aConstraints.empty())
367     return false;
368   std::set<ConstraintPtr>::iterator aCIter = aConstraints.begin();
369   for (; aCIter != aConstraints.end(); aCIter++) {
370     ConstraintConstraintMap::iterator aSolConIter = myConstraints.find(*aCIter);
371     if (aSolConIter == myConstraints.end() || !aSolConIter->first->data() ||
372         !aSolConIter->first->data()->isValid())
373       continue;
374     myFeatureStorage->changeFeature(theFeature, aSolConIter->first);
375
376     aSolConIter->second->addFeature(theFeature);
377     myChangedConstraints.insert(aSolConIter->first);
378   }
379
380   // Search attributes of the feature in the set of parametric constraints and update them
381   std::list<AttributePtr> anAttrList =
382       theFeature->data()->attributes(GeomDataAPI_Point2D::typeId());
383   std::list<AttributePtr>::iterator anAttrIt = anAttrList.begin();
384   for (; anAttrIt != anAttrList.end(); ++anAttrIt) {
385     std::map<AttributePtr, SolverConstraintPtr>::iterator aFound =
386         myParametricConstraints.find(*anAttrIt);
387     if (aFound != myParametricConstraints.end())
388       aFound->second->update();
389     else {
390       std::shared_ptr<GeomDataAPI_Point2D> aPoint =
391           std::dynamic_pointer_cast<GeomDataAPI_Point2D>(*anAttrIt);
392       if (aPoint && (!aPoint->textX().empty() || !aPoint->textY().empty())) {
393         // Create new parametric constraint
394         SolverConstraintPtr aConstraint =
395             SketchSolver_Builder::getInstance()->createParametricConstraint(*anAttrIt);
396         if (!aConstraint)
397           continue;
398         aConstraint->setGroup(this);
399         aConstraint->setStorage(myStorage);
400         myParametricConstraints[*anAttrIt] = aConstraint;
401       }
402     }
403   }
404   return true;
405 }
406
407 void SketchSolver_Group::moveFeature(std::shared_ptr<SketchPlugin_Feature> theFeature)
408 {
409   // Firstly, create temporary rigid constraint
410   SolverConstraintPtr aConstraint =
411       SketchSolver_Builder::getInstance()->createMovementConstraint(theFeature);
412   if (!aConstraint)
413     return;
414   aConstraint->setGroup(this);
415   aConstraint->setStorage(myStorage);
416   if (aConstraint->error().empty())
417     setTemporary(aConstraint);
418   // Secondly, update the feature
419   updateFeature(theFeature);
420 }
421
422 // ============================================================================
423 //  Function: fixFeaturesList
424 //  Class:    SketchSolver_Group
425 //  Purpose:  Apply temporary rigid constraints for the list of features
426 // ============================================================================
427 void SketchSolver_Group::fixFeaturesList(AttributeRefListPtr theList)
428 {
429   std::list<ObjectPtr> aList = theList->list();
430   std::list<ObjectPtr>::iterator anIt = aList.begin();
431   std::list<FeaturePtr> aFeatures;
432   // Sort features, at begining there are features used by Equal constraint
433   for (; anIt != aList.end(); anIt++) {
434     if (!(*anIt))
435       continue;
436     FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt);
437     std::set<ConstraintPtr> aConstraints = myFeatureStorage->getConstraints(aFeature);
438     std::set<ConstraintPtr>::iterator aCIter = aConstraints.begin();
439     for (; aCIter != aConstraints.end(); aCIter++)
440       if ((*aCIter)->getKind() == SketchPlugin_ConstraintEqual::ID())
441         break;
442     if (aCIter != aConstraints.end())
443       aFeatures.push_front(aFeature);
444     else
445       aFeatures.push_back(aFeature);
446   }
447
448   std::list<FeaturePtr>::iterator aFeatIter = aFeatures.begin();
449   for (; aFeatIter != aFeatures.end(); aFeatIter++) {
450     SolverConstraintPtr aConstraint =
451         SketchSolver_Builder::getInstance()->createRigidConstraint(*aFeatIter);
452     if (!aConstraint)
453       continue;
454     aConstraint->setGroup(this);
455     aConstraint->setStorage(myStorage);
456     setTemporary(aConstraint);
457   }
458 }
459
460 // ============================================================================
461 //  Function: addWorkplane
462 //  Class:    SketchSolver_Group
463 //  Purpose:  create workplane for the group
464 // ============================================================================
465 bool SketchSolver_Group::addWorkplane(CompositeFeaturePtr theSketch)
466 {
467   if (myWorkplaneID != SLVS_E_UNKNOWN || theSketch->getKind() != SketchPlugin_Sketch::ID())
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_Group
478 //  Purpose:  update parameters of workplane
479 // ============================================================================
480 bool SketchSolver_Group::updateWorkplane()
481 {
482   if (!myStorage) // Create storage if not exists
483     myStorage = StoragePtr(new SketchSolver_Storage);
484   SketchSolver_Builder* aBuilder = SketchSolver_Builder::getInstance();
485
486   std::vector<Slvs_Entity> anEntities;
487   std::vector<Slvs_Param> aParams;
488   if (!aBuilder->createWorkplane(mySketch, anEntities, aParams))
489     return false;
490
491   if (myWorkplaneID == SLVS_E_UNKNOWN) {
492     myWorkplaneID = anEntities.back().h;
493     // Add new workplane elements
494     std::vector<Slvs_Param>::iterator aParIter = aParams.begin();
495     for (; aParIter != aParams.end(); aParIter++) {
496       aParIter->h = SLVS_E_UNKNOWN; // the ID should be generated by storage
497       aParIter->group = myID;
498       aParIter->h = myStorage->addParameter(*aParIter);
499     }
500     std::vector<Slvs_Entity>::iterator anEntIter = anEntities.begin();
501     for (; anEntIter != anEntities.end(); anEntIter++) {
502       anEntIter->h = SLVS_E_UNKNOWN; // the ID should be generated by storage
503       anEntIter->group = myID;
504       anEntIter->wrkpl = myWorkplaneID;
505       for (int i = 0; i < 4; i++)
506         if (anEntIter->param[i] != SLVS_E_UNKNOWN)
507           anEntIter->param[i] = aParams[anEntIter->param[i]-1].h;
508       for (int i = 0; i < 4; i++)
509         if (anEntIter->point[i] != SLVS_E_UNKNOWN)
510           anEntIter->point[i] = anEntities[anEntIter->point[i]-1].h;
511       anEntIter->h = myStorage->addEntity(*anEntIter);
512     }
513   } else {
514     // Update existent workplane
515     const Slvs_Entity& aWP = myStorage->getEntity(myWorkplaneID);
516     const Slvs_Entity& anOrigin = myStorage->getEntity(aWP.point[0]);
517     const Slvs_Entity& aNormal = myStorage->getEntity(aWP.normal);
518     // Get parameters and update them
519     Slvs_hParam aWPParams[7] = {
520         anOrigin.param[0], anOrigin.param[1], anOrigin.param[2],
521         aNormal.param[0], aNormal.param[1], aNormal.param[2], aNormal.param[3]
522       };
523     std::vector<Slvs_Param>::iterator aParIter = aParams.begin();
524     for (int i = 0; aParIter != aParams.end(); aParIter++, i++) {
525       Slvs_Param aParam = myStorage->getParameter(aWPParams[i]);
526       aParam.val = aParIter->val;
527       myStorage->updateParameter(aParam);
528     }
529   }
530   return myWorkplaneID > 0;
531 }
532
533 // ============================================================================
534 //  Function: resolveConstraints
535 //  Class:    SketchSolver_Group
536 //  Purpose:  solve the set of constraints for the current group
537 // ============================================================================
538 bool SketchSolver_Group::resolveConstraints()
539 {
540   if (!myChangedConstraints.empty())
541     updateConstraints();
542
543   bool aResolved = false;
544   if (myStorage->isNeedToResolve() && !isEmpty()) {
545     myConstrSolver.setGroupID(myID);
546     myStorage->initializeSolver(myConstrSolver);
547
548     int aResult = SLVS_RESULT_OKAY;
549     try {
550       if (myStorage->hasDuplicatedConstraint())
551         aResult = SLVS_RESULT_INCONSISTENT;
552       else {
553         // To avoid overconstraint situation, we will remove temporary constraints one-by-one
554         // and try to find the case without overconstraint
555         bool isLastChance = false;
556         int aNbTemp = myStorage->numberTemporary();
557         while (true) {
558           aResult = myConstrSolver.solve();
559           if (aResult == SLVS_RESULT_OKAY || isLastChance)
560             break;
561           if (aNbTemp <= 0) {
562             // try to update parameters and resolve once again
563             ConstraintConstraintMap::iterator aConstrIt = myConstraints.begin();
564             for (; aConstrIt != myConstraints.end(); ++aConstrIt)
565               aConstrIt->second->update();
566             isLastChance = true;
567           } else
568             aNbTemp = myStorage->deleteTemporaryConstraint();
569           myStorage->initializeSolver(myConstrSolver);
570         }
571       }
572     } catch (...) {
573 //      Events_Error::send(SketchSolver_Error::SOLVESPACE_CRASH(), this);
574       getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue(SketchSolver_Error::SOLVESPACE_CRASH());
575       if (myPrevSolved) {
576         // the error message should be changed before sending the message
577         sendMessage(EVENT_SOLVER_FAILED);
578         myPrevSolved = false;
579       }
580       return false;
581     }
582     if (aResult == SLVS_RESULT_OKAY) {  // solution succeeded, store results into correspondent attributes
583       myFeatureStorage->blockEvents(true);
584       // First refresh parametric constraints to satisfy parameters
585       std::map<AttributePtr, SolverConstraintPtr>::iterator aParIter = myParametricConstraints.begin();
586       for (; aParIter != myParametricConstraints.end(); ++aParIter)
587         aParIter->second->refresh();
588       // Update all other constraints
589       ConstraintConstraintMap::iterator aConstrIter = myConstraints.begin();
590       for (; aConstrIter != myConstraints.end(); ++aConstrIter)
591         aConstrIter->second->refresh();
592       myFeatureStorage->blockEvents(false);
593       if (!myPrevSolved) {
594         getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue("");
595         // the error message should be changed before sending the message
596         sendMessage(EVENT_SOLVER_REPAIRED);
597         myPrevSolved = true;
598       }
599     } else if (!myConstraints.empty()) {
600 //      Events_Error::send(SketchSolver_Error::CONSTRAINTS(), this);
601       getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue(SketchSolver_Error::CONSTRAINTS());
602       if (myPrevSolved) {
603         // the error message should be changed before sending the message
604         sendMessage(EVENT_SOLVER_FAILED);
605         myPrevSolved = false;
606       }
607     }
608
609     aResolved = true;
610   }
611   removeTemporaryConstraints();
612   myStorage->setNeedToResolve(false);
613   return aResolved;
614 }
615
616 // ============================================================================
617 //  Function: mergeGroups
618 //  Class:    SketchSolver_Group
619 //  Purpose:  append specified group to the current group
620 // ============================================================================
621 void SketchSolver_Group::mergeGroups(const SketchSolver_Group& theGroup)
622 {
623   // If specified group is empty, no need to merge
624   if (theGroup.isEmpty())
625     return;
626   if (!myFeatureStorage)
627     myFeatureStorage = FeatureStoragePtr(new SketchSolver_FeatureStorage);
628
629   std::set<ObjectPtr> aConstraints;
630   ConstraintConstraintMap::const_iterator aConstrIter = theGroup.myConstraints.begin();
631   for (; aConstrIter != theGroup.myConstraints.end(); aConstrIter++)
632     aConstraints.insert(aConstrIter->first);
633
634   std::list<FeaturePtr> aSortedConstraints = selectApplicableFeatures(aConstraints);
635   std::list<FeaturePtr>::iterator aSCIter = aSortedConstraints.begin();
636   for (; aSCIter != aSortedConstraints.end(); ++aSCIter) {
637     ConstraintPtr aConstr = std::dynamic_pointer_cast<SketchPlugin_Constraint>(*aSCIter);
638     if (!aConstr)
639       continue;
640     changeConstraint(aConstr);
641   }
642 }
643
644 // ============================================================================
645 //  Function: splitGroup
646 //  Class:    SketchSolver_Group
647 //  Purpose:  divide the group into several subgroups
648 // ============================================================================
649 void SketchSolver_Group::splitGroup(std::vector<SketchSolver_Group*>& theCuts)
650 {
651   // Obtain constraints, which should be separated
652   FeatureStoragePtr aNewFeatStorage(new SketchSolver_FeatureStorage);
653   std::vector<ConstraintPtr> anUnusedConstraints;
654   ConstraintConstraintMap::iterator aCIter = myConstraints.begin();
655   for ( ; aCIter != myConstraints.end(); aCIter++) {
656     std::list<ConstraintPtr> aBaseConstraints = aCIter->second->constraints();
657     std::list<ConstraintPtr>::iterator anIter = aBaseConstraints.begin();
658     for (; anIter != aBaseConstraints.end(); anIter++)
659       if (aNewFeatStorage->isInteract(*anIter)) {
660         aNewFeatStorage->changeConstraint(*anIter);
661       } else
662         anUnusedConstraints.push_back(*anIter);
663   }
664
665   // Check the unused constraints once again, because they may become interacted with new storage since adding constraints
666   std::vector<ConstraintPtr>::iterator aUnuseIt = anUnusedConstraints.begin();
667   while (aUnuseIt != anUnusedConstraints.end()) {
668     if (aNewFeatStorage->isInteract(*aUnuseIt)) {
669       size_t aShift = aUnuseIt - anUnusedConstraints.begin();
670       anUnusedConstraints.erase(aUnuseIt);
671       aUnuseIt = anUnusedConstraints.begin() + aShift;
672       continue;
673     }
674     aUnuseIt++;
675   }
676
677   std::vector<SketchSolver_Group*>::iterator aCutsIter;
678   aUnuseIt = anUnusedConstraints.begin();
679   for ( ; aUnuseIt != anUnusedConstraints.end(); aUnuseIt++) {
680     // Remove unused constraints
681     removeConstraint(*aUnuseIt);
682     // Try to append constraint to already existent group
683     for (aCutsIter = theCuts.begin(); aCutsIter != theCuts.end(); aCutsIter++)
684       if ((*aCutsIter)->isInteract(*aUnuseIt)) {
685         (*aCutsIter)->changeConstraint(*aUnuseIt);
686         break;
687       }
688     if (aCutsIter == theCuts.end()) {
689       // Add new group
690       SketchSolver_Group* aGroup = new SketchSolver_Group(mySketch);
691       aGroup->changeConstraint(*aUnuseIt);
692       theCuts.push_back(aGroup);
693     }
694   }
695 }
696
697 // ============================================================================
698 //  Function: isConsistent
699 //  Class:    SketchSolver_Group
700 //  Purpose:  search removed entities and constraints
701 // ============================================================================
702 bool SketchSolver_Group::isConsistent()
703 {
704   if (!myFeatureStorage) // no one constraint is initialized yet
705     return true;
706
707   bool aResult = myFeatureStorage->isConsistent();
708   if (!aResult) {
709     // remove invalid entities
710     std::set<ConstraintPtr> anInvalidConstraints;
711     ConstraintConstraintMap::iterator aCIter = myConstraints.begin();
712     for (; aCIter != myConstraints.end(); ++aCIter) {
713       if (!aCIter->first->data() || !aCIter->first->data()->isValid())
714         anInvalidConstraints.insert(aCIter->first);
715     }
716     std::set<ConstraintPtr>::const_iterator aRemoveIt = anInvalidConstraints.begin();
717     for (; aRemoveIt != anInvalidConstraints.end(); ++aRemoveIt)
718       removeConstraint(*aRemoveIt);
719   }
720   return aResult;
721 }
722
723 // ============================================================================
724 //  Function: removeTemporaryConstraints
725 //  Class:    SketchSolver_Group
726 //  Purpose:  remove all transient SLVS_C_WHERE_DRAGGED constraints after
727 //            resolving the set of constraints
728 // ============================================================================
729 void SketchSolver_Group::removeTemporaryConstraints()
730 {
731   std::set<SolverConstraintPtr>::iterator aTmpIt = myTempConstraints.begin();
732   for (; aTmpIt != myTempConstraints.end(); ++aTmpIt)
733     (*aTmpIt)->remove();
734   myTempConstraints.clear();
735
736   while (myStorage->numberTemporary())
737     myStorage->deleteTemporaryConstraint();
738   // Clean lists of removed entities in the storage
739   std::set<Slvs_hParam> aRemPar;
740   std::set<Slvs_hEntity> aRemEnt;
741   std::set<Slvs_hConstraint> aRemCon;
742   myStorage->getRemoved(aRemPar, aRemEnt, aRemCon);
743   myStorage->setNeedToResolve(false);
744 }
745
746 // ============================================================================
747 //  Function: removeConstraint
748 //  Class:    SketchSolver_Group
749 //  Purpose:  remove constraint and all unused entities
750 // ============================================================================
751 void SketchSolver_Group::removeConstraint(ConstraintPtr theConstraint)
752 {
753   bool isFullyRemoved = true;
754   myFeatureStorage->removeConstraint(theConstraint);
755   ConstraintConstraintMap::iterator aCIter = myConstraints.begin();
756   for (; aCIter != myConstraints.end(); aCIter++)
757     if (aCIter->second->hasConstraint(theConstraint)) {
758       if (!aCIter->second->remove(theConstraint)) // the constraint is not fully removed
759         isFullyRemoved = false;
760       break;
761     }
762   if (aCIter == myConstraints.end())
763     return;
764
765   if (isFullyRemoved)
766     myConstraints.erase(aCIter);
767   else if (aCIter != myConstraints.end() &&
768            aCIter->first->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
769     // Update multicoincidence
770     std::list<ConstraintPtr> aMultiCoinc;
771     SolverConstraintPtr aCoincidence = aCIter->second;
772     while (aCIter != myConstraints.end()) {
773       if (aCIter->second != aCoincidence) {
774         ++aCIter;
775         continue;
776       }
777       if (aCIter->first != theConstraint)
778         aMultiCoinc.push_back(aCIter->first);
779       aCIter->second->remove(aCIter->first);
780       ConstraintConstraintMap::iterator aRemoveIt = aCIter++;
781       myConstraints.erase(aRemoveIt);
782     }
783
784     std::list<ConstraintPtr>::iterator anIt = aMultiCoinc.begin();
785     for (; anIt != aMultiCoinc.end(); ++anIt)
786       changeConstraint(*anIt);
787   }
788 }
789
790 // ============================================================================
791 //  Function: isComplexConstraint
792 //  Class:    SketchSolver_Group
793 //  Purpose:  verifies the constraint is complex, i.e. it needs another constraints to be created before
794 // ============================================================================
795 bool SketchSolver_Group::isComplexConstraint(FeaturePtr theConstraint)
796 {
797   return theConstraint->getKind() == SketchPlugin_ConstraintFillet::ID() ||
798          theConstraint->getKind() == SketchPlugin_ConstraintMirror::ID() ||
799          theConstraint->getKind() == SketchPlugin_ConstraintTangent::ID();
800 }
801
802 // ============================================================================
803 //  Function: setTemporary
804 //  Class:    SketchSolver_Group
805 //  Purpose:  append given constraint to th group of temporary constraints
806 // ============================================================================
807 void SketchSolver_Group::setTemporary(SolverConstraintPtr theConstraint)
808 {
809   theConstraint->makeTemporary();
810   myTempConstraints.insert(theConstraint);
811 }
812
813
814 // ============================================================================
815 //  Function: checkFeatureValidity
816 //  Class:    SketchSolver_Group
817 //  Purpose:  verifies is the feature valid
818 // ============================================================================
819 bool SketchSolver_Group::checkFeatureValidity(FeaturePtr theFeature)
820 {
821   if (!theFeature || !theFeature->data()->isValid())
822     return true;
823
824   SessionPtr aMgr = ModelAPI_Session::get();
825   ModelAPI_ValidatorsFactory* aFactory = aMgr->validators();
826   return aFactory->validate(theFeature);
827 }
828
829
830
831
832
833 // ===========   Auxiliary functions   ========================================
834 static double featureToVal(FeaturePtr theFeature)
835 {
836   if (theFeature->getKind() == SketchPlugin_Sketch::ID())
837     return 0.0; // sketch
838   ConstraintPtr aConstraint = std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
839   if (!aConstraint)
840     return 1.0; // features (arc, circle, line, point)
841
842   const std::string& anID = aConstraint->getKind();
843   if (anID == SketchPlugin_ConstraintCoincidence::ID()) {
844     AttributeRefAttrPtr anAttrA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
845         aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
846     AttributeRefAttrPtr anAttrB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
847         aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
848     if (anAttrA && anAttrB && (anAttrA->isObject() || anAttrB->isObject()))
849       return 2.0; // point-on-line and point-on-circle should go before points coincidence constraint
850     return 2.5;
851   }
852   if (anID == SketchPlugin_ConstraintDistance::ID() ||
853       anID == SketchPlugin_ConstraintLength::ID() ||
854       anID == SketchPlugin_ConstraintRadius::ID())
855     return 3.0;
856   if (anID == SketchPlugin_ConstraintAngle::ID())
857     return 3.5;
858   if (anID == SketchPlugin_ConstraintHorizontal::ID() ||
859       anID == SketchPlugin_ConstraintVertical::ID() ||
860       anID == SketchPlugin_ConstraintParallel::ID() ||
861       anID == SketchPlugin_ConstraintPerpendicular::ID())
862     return 4.0;
863   if (anID == SketchPlugin_ConstraintEqual::ID())
864     return 5.0;
865   if (anID == SketchPlugin_ConstraintTangent::ID() ||
866       anID == SketchPlugin_ConstraintMirror::ID())
867     return 6.0;
868   if (anID == SketchPlugin_ConstraintRigid::ID())
869     return 7.0;
870   if (anID == SketchPlugin_MultiRotation::ID() ||
871       anID == SketchPlugin_MultiTranslation::ID())
872     return 8.0;
873
874   // all other constraints are placed between Equal and Tangent constraints
875   return 5.5;
876 }
877
878 static bool isLess(FeaturePtr theFeature1, FeaturePtr theFeature2)
879 {
880   return featureToVal(theFeature1) < featureToVal(theFeature2);
881 }
882
883 std::list<FeaturePtr> SketchSolver_Group::selectApplicableFeatures(const std::set<ObjectPtr>& theObjects)
884 {
885   std::list<FeaturePtr> aResult;
886   std::list<FeaturePtr>::iterator aResIt;
887
888   std::set<ObjectPtr>::const_iterator anObjIter = theObjects.begin();
889   for (; anObjIter != theObjects.end(); ++anObjIter) {
890     // Operate sketch itself and SketchPlugin features only.
891     // Also, the Fillet need to be skipped, because there are several separated constraints composing it.
892     FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anObjIter);
893     if (!aFeature)
894       continue;
895     std::shared_ptr<SketchPlugin_Feature> aSketchFeature = 
896         std::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
897     if ((aFeature->getKind() != SketchPlugin_Sketch::ID() && !aSketchFeature) ||
898         aFeature->getKind() == SketchPlugin_ConstraintFillet::ID())
899       continue;
900
901     // Find the place where to insert a feature
902     for (aResIt = aResult.begin(); aResIt != aResult.end(); ++aResIt)
903       if (isLess(aFeature, *aResIt))
904         break;
905     aResult.insert(aResIt, aFeature);
906   }
907
908   return aResult;
909 }
910