Salome HOME
Degrees of freedom for a sketch (issue #796)
[modules/shaper.git] / src / SketchSolver / SketchSolver_Manager.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 // File:    SketchSolver_Manager.cpp
4 // Created: 08 May 2014
5 // Author:  Artem ZHIDKOV
6
7 #include "SketchSolver_Manager.h"
8 #include "SketchSolver_Error.h"
9
10 #include <Events_Loop.h>
11 #include <ModelAPI_AttributeDouble.h>
12 #include <ModelAPI_AttributeRefList.h>
13 #include <ModelAPI_Data.h>
14 #include <ModelAPI_Events.h>
15 #include <ModelAPI_Object.h>
16 #include <ModelAPI_ResultConstruction.h>
17 #include <ModelAPI_Attribute.h>
18 #include <ModelAPI_AttributeInteger.h>
19 #include <ModelAPI_AttributeString.h>
20
21 #include <SketchPlugin_Constraint.h>
22 #include <SketchPlugin_ConstraintAngle.h>
23 #include <SketchPlugin_ConstraintCoincidence.h>
24 #include <SketchPlugin_ConstraintCollinear.h>
25 #include <SketchPlugin_ConstraintDistance.h>
26 #include <SketchPlugin_ConstraintEqual.h>
27 #include <SketchPlugin_ConstraintHorizontal.h>
28 #include <SketchPlugin_ConstraintLength.h>
29 #include <SketchPlugin_ConstraintMiddle.h>
30 #include <SketchPlugin_ConstraintMirror.h>
31 #include <SketchPlugin_ConstraintParallel.h>
32 #include <SketchPlugin_ConstraintPerpendicular.h>
33 #include <SketchPlugin_ConstraintRadius.h>
34 #include <SketchPlugin_ConstraintRigid.h>
35 #include <SketchPlugin_ConstraintTangent.h>
36 #include <SketchPlugin_ConstraintVertical.h>
37 #include <SketchPlugin_MultiRotation.h>
38 #include <SketchPlugin_MultiTranslation.h>
39
40 #include <SketchPlugin_Arc.h>
41 #include <SketchPlugin_Circle.h>
42 #include <SketchPlugin_Line.h>
43 #include <SketchPlugin_Point.h>
44 #include <SketchPlugin_Sketch.h>
45 #include <SketchPlugin_Feature.h>
46
47 #include <assert.h>
48 #include <list>
49 #include <set>
50 #include <memory>
51
52 static const Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
53
54 // Initialization of constraint manager self pointer
55 SketchSolver_Manager* SketchSolver_Manager::mySelf = 0;
56
57 /// Global constraint manager object
58 SketchSolver_Manager* myManager = SketchSolver_Manager::instance();
59
60
61 // ========================================================
62 // ========= SketchSolver_Manager ===============
63 // ========================================================
64 SketchSolver_Manager* SketchSolver_Manager::instance()
65 {
66   if (!mySelf)
67     mySelf = new SketchSolver_Manager();
68   return mySelf;
69 }
70
71 SketchSolver_Manager::SketchSolver_Manager()
72 {
73   myGroups.clear();
74   myIsComputed = false;
75
76   // Register in event loop
77   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
78   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
79   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
80   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_MOVED));
81
82   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_SOLVER_FAILED));
83   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_SOLVER_REPAIRED));
84 }
85
86 SketchSolver_Manager::~SketchSolver_Manager()
87 {
88   myGroups.clear();
89 }
90
91 void SketchSolver_Manager::setBuilder(BuilderPtr theBuilder)
92 {
93   myBuilder = theBuilder;
94 }
95
96 BuilderPtr SketchSolver_Manager::builder()
97 {
98   return myBuilder;
99 }
100
101 // ============================================================================
102 //  Function: processEvent
103 //  Purpose:  listen the event loop and process the message
104 // ============================================================================
105 void SketchSolver_Manager::processEvent(
106   const std::shared_ptr<Events_Message>& theMessage)
107 {
108   checkConflictingConstraints(theMessage);
109   if (myIsComputed)
110     return;
111   myIsComputed = true;
112   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)
113       || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED)
114       || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED)) {
115     std::shared_ptr<ModelAPI_ObjectUpdatedMessage> anUpdateMsg =
116         std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
117     std::set<ObjectPtr> aFeatures = anUpdateMsg->objects();
118
119     bool isUpdateFlushed = stopSendUpdate();
120     // Shows the message has at least one feature applicable for solver
121     bool hasProperFeature = false;
122
123     bool isMovedEvt = theMessage->eventID()
124           == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED);
125     if (isMovedEvt) {
126       std::set<ObjectPtr>::iterator aFeatIter;
127       for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
128         std::shared_ptr<SketchPlugin_Feature> aSFeature = 
129             std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
130         if (aSFeature) {
131           moveEntity(aSFeature);
132           hasProperFeature = true;
133         }
134       }
135     } else {
136       std::list<FeaturePtr> aSketchFeatures = SketchSolver_Group::selectApplicableFeatures(aFeatures);
137       std::list<FeaturePtr>::iterator aFeatIter = aSketchFeatures.begin();
138       for (; aFeatIter != aSketchFeatures.end(); ++aFeatIter) {
139         if ((*aFeatIter)->getKind() == SketchPlugin_Sketch::ID()) {
140           std::shared_ptr<ModelAPI_CompositeFeature> aSketch = 
141               std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aFeatIter);
142           hasProperFeature = changeWorkplane(aSketch) || hasProperFeature;
143           continue;
144         }
145         std::shared_ptr<SketchPlugin_Feature> aFeature = 
146             std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
147         if (!aFeature)
148           continue;
149         hasProperFeature = changeFeature(aFeature) || hasProperFeature;
150       }
151     }
152
153     bool needToUpdate = false;
154     // Solve the set of constraints
155     if (hasProperFeature)
156       needToUpdate = resolveConstraints();
157
158     // Features may be updated => now send events, but for all changed at once
159     if (isUpdateFlushed)
160       allowSendUpdate();
161     // send update for movement in any case
162     if (needToUpdate || isMovedEvt)
163       Events_Loop::loop()->flush(anUpdateEvent);
164
165   } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
166     std::shared_ptr<ModelAPI_ObjectDeletedMessage> aDeleteMsg =
167       std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
168     const std::set<std::string>& aFeatureGroups = aDeleteMsg->groups();
169
170     // Find SketchPlugin_Sketch::ID() in groups. The constraint groups should be updated when an object removed from Sketch
171     std::set<std::string>::const_iterator aFGrIter;
172     for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
173       if (aFGrIter->compare(ModelAPI_ResultConstruction::group()) == 0 ||
174         aFGrIter->compare(ModelAPI_Feature::group()) == 0)
175         break;
176
177     if (aFGrIter != aFeatureGroups.end()) {
178       std::list<SketchSolver_Group*> aGroupsToResolve;
179       std::list<SketchSolver_Group*>::iterator aGroupIter = myGroups.begin();
180       std::list<SketchSolver_Group*> aSeparatedGroups;
181       while (aGroupIter != myGroups.end()) {
182         if (!(*aGroupIter)->isWorkplaneValid()) {  // the group should be removed
183           delete *aGroupIter;
184           std::list<SketchSolver_Group*>::iterator aRemoveIt = aGroupIter++;
185           myGroups.erase(aRemoveIt);
186           continue;
187         }
188         if (!(*aGroupIter)->isConsistent()) {  // some constraints were removed, try to split the group
189           (*aGroupIter)->splitGroup(aSeparatedGroups);
190           //if (!(*aGroupIter)->getWorkplane()->string(
191           //    SketchPlugin_Sketch::SOLVER_ERROR())->value().empty())
192             aGroupsToResolve.push_back(*aGroupIter);
193         }
194         aGroupIter++;
195       }
196       if (aSeparatedGroups.size() > 0) {
197         myGroups.insert(myGroups.end(), aSeparatedGroups.begin(), aSeparatedGroups.end());
198         aGroupsToResolve.insert(aGroupsToResolve.end(),
199             aSeparatedGroups.begin(), aSeparatedGroups.end());
200       }
201
202       if (!aGroupsToResolve.empty())
203         resolveConstraints(aGroupsToResolve);
204     }
205   }
206   degreesOfFreedom();
207   myIsComputed = false;
208 }
209
210 void SketchSolver_Manager::checkConflictingConstraints(const std::shared_ptr<Events_Message>& theMessage)
211 {
212   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_SOLVER_REPAIRED)) {
213     std::shared_ptr<ModelAPI_SolverFailedMessage> aMessage =
214         std::dynamic_pointer_cast<ModelAPI_SolverFailedMessage>(theMessage);
215     std::set<ObjectPtr> aSentObjs = aMessage->objects();
216     if (!aSentObjs.empty()) {
217       // Obtain sketch where the constraints are placed.
218       // It is enough to check only one constraint.
219       CompositeFeaturePtr aSketch;
220       FeaturePtr aConstraint = ModelAPI_Feature::feature(*aSentObjs.begin());
221       std::list<SketchSolver_Group*>::const_iterator aGrIt = myGroups.begin();
222       for (; aGrIt != myGroups.end(); ++aGrIt)
223         if ((*aGrIt)->isInteract(aConstraint)) {
224           aSketch = (*aGrIt)->getWorkplane();
225           break;
226         }
227
228       // Search failed groups built on the same sketch
229       if (aSketch) {
230         for (aGrIt = myGroups.begin(); aGrIt != myGroups.end(); ++aGrIt) {
231           SketchSolver_Group* aGroup = *aGrIt;
232           if (aGroup->isBaseWorkplane(aSketch) && aGroup->isFailed() &&
233               !aGroup->isInteract(aConstraint)) {
234             // reset error message on the sketch
235             aGroup->getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue(
236                 SketchSolver_Error::CONSTRAINTS());
237             break;
238           }
239         }
240       }
241     }
242   }
243 }
244
245 // ============================================================================
246 //  Function: changeWorkplane
247 //  Purpose:  update workplane by given parameters of the sketch
248 // ============================================================================
249 bool SketchSolver_Manager::changeWorkplane(CompositeFeaturePtr theSketch)
250 {
251   bool aResult = true;  // changed when a workplane wrongly updated
252   bool isUpdated = false;
253   // Try to update specified workplane in all groups
254   std::list<SketchSolver_Group*>::iterator aGroupIter;
255   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
256     if ((*aGroupIter)->isBaseWorkplane(theSketch)) {
257       isUpdated = true;
258       aResult = false;
259     }
260   // If the workplane is not updated, so this is a new workplane
261   if (!isUpdated) {
262     SketchSolver_Group* aNewGroup = new SketchSolver_Group(theSketch);
263     // Verify that the group is created successfully
264     if (!aNewGroup->isBaseWorkplane(theSketch) || !aNewGroup->isWorkplaneValid()) {
265       delete aNewGroup;
266       return false;
267     }
268     myGroups.push_back(aNewGroup);
269   }
270   return aResult;
271 }
272
273 // ============================================================================
274 //  Function: changeConstraintOrEntity
275 //  Purpose:  create/update the constraint or the feature and place it into appropriate group
276 // ============================================================================
277 bool SketchSolver_Manager::changeFeature(std::shared_ptr<SketchPlugin_Feature> theFeature)
278 {
279   // Search the groups which this feature touches
280   std::set<GroupID> aGroups;
281   findGroups(theFeature, aGroups);
282
283   std::shared_ptr<SketchPlugin_Constraint> aConstraint = 
284       std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
285
286   // Process the groups list
287   if (aGroups.size() == 0) {
288     // There are no groups applicable for this constraint => create new one
289     // The group will be created only for constraints, not for features
290     if (!aConstraint) return false;
291     std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(aConstraint);
292     if (!aWP)
293       return false;
294     SketchSolver_Group* aGroup = new SketchSolver_Group(aWP);
295     if (!aGroup->changeConstraint(aConstraint)) {
296       delete aGroup;
297       return false;
298     }
299     myGroups.push_back(aGroup);
300     return true;
301   } else if (aGroups.size() == 1) {  // Only one group => add feature into it
302     GroupID aGroupId = *(aGroups.begin());
303     std::list<SketchSolver_Group*>::iterator aGroupIter;
304     for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
305       if ((*aGroupIter)->getId() == aGroupId) {
306         // If the group is empty, the feature is not added (the constraint only)
307         if (!aConstraint && !(*aGroupIter)->isEmpty())
308           return (*aGroupIter)->updateFeature(theFeature);
309         return (*aGroupIter)->changeConstraint(aConstraint);
310       }
311   } else if (aGroups.size() > 1) {  // Several groups applicable for this feature => need to merge them
312     std::set<GroupID>::const_iterator aGroupsIter = aGroups.begin();
313
314     // Search first group
315     std::list<SketchSolver_Group*>::iterator aFirstGroupIter;
316     for (aFirstGroupIter = myGroups.begin(); aFirstGroupIter != myGroups.end(); aFirstGroupIter++)
317       if ((*aFirstGroupIter)->getId() == *aGroupsIter)
318         break;
319     if (aFirstGroupIter == myGroups.end())
320       return false;
321
322     // Append other groups to the first one
323     std::list<SketchSolver_Group*>::iterator anOtherGroupIter = aFirstGroupIter;
324     ++anOtherGroupIter;
325     for (aGroupsIter++; aGroupsIter != aGroups.end(); aGroupsIter++) {
326       for (; anOtherGroupIter != myGroups.end(); anOtherGroupIter++)
327         if ((*anOtherGroupIter)->getId() == *aGroupsIter)
328           break;
329       if (anOtherGroupIter == myGroups.end()) {  // Group disappears
330         anOtherGroupIter = aFirstGroupIter;
331         ++anOtherGroupIter;
332         continue;
333       }
334
335       (*aFirstGroupIter)->mergeGroups(**anOtherGroupIter);
336       std::list<SketchSolver_Group*>::iterator aRemoveIt = anOtherGroupIter++;
337       delete *aRemoveIt;
338       myGroups.erase(aRemoveIt);
339     }
340
341     if (aConstraint)
342       (*aFirstGroupIter)->changeConstraint(aConstraint);
343     else
344       (*aFirstGroupIter)->updateFeature(theFeature);
345     // groups are merged => need to resolve them
346     return true;
347   }
348
349   // Something goes wrong
350   return false;
351 }
352
353 // ============================================================================
354 //  Function: moveEntity
355 //  Purpose:  update element moved on the sketch, which is used by constraints
356 // ============================================================================
357 void SketchSolver_Manager::moveEntity(std::shared_ptr<SketchPlugin_Feature> theFeature)
358 {
359   bool isMoved = false;
360   std::list<SketchSolver_Group*>::iterator aGroupIt = myGroups.begin();
361   for (; aGroupIt != myGroups.end(); aGroupIt++)
362     if (!(*aGroupIt)->isEmpty() && (*aGroupIt)->isInteract(theFeature)) {
363       (*aGroupIt)->moveFeature(theFeature);
364       isMoved = true;
365     }
366
367   if (!isMoved && theFeature->getKind() == SketchPlugin_Arc::ID()) {
368     // Workaround to move arc.
369     // If the arc has not been constrained, we will push it into empty group and apply movement.
370     for (aGroupIt = myGroups.begin(); aGroupIt != myGroups.end(); aGroupIt++)
371       if ((*aGroupIt)->isEmpty())
372         (*aGroupIt)->moveFeature(theFeature);
373   }
374 }
375
376 // ============================================================================
377 //  Function: findGroups
378 //  Purpose:  search groups of entities interacting with given feature
379 // ============================================================================
380 void SketchSolver_Manager::findGroups(
381     std::shared_ptr<SketchPlugin_Feature> theFeature,
382     std::set<GroupID>& theGroupIDs) const
383 {
384   std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(theFeature);
385
386   SketchSolver_Group* anEmptyGroup = 0;  // appropriate empty group for specified constraint
387   std::list<SketchSolver_Group*>::const_iterator aGroupIter;
388   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
389     if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theFeature)) {
390       if (!(*aGroupIter)->isEmpty())
391         theGroupIDs.insert((*aGroupIter)->getId());
392       else if (!anEmptyGroup)
393         anEmptyGroup = *aGroupIter;
394     }
395
396   // When only empty group is found, use it
397   if (anEmptyGroup && theGroupIDs.empty())
398     theGroupIDs.insert(anEmptyGroup->getId());
399 }
400
401 // ============================================================================
402 //  Function: findWorkplane
403 //  Purpose:  search workplane containing given feature
404 // ============================================================================
405 std::shared_ptr<ModelAPI_CompositeFeature> SketchSolver_Manager
406 ::findWorkplane(std::shared_ptr<SketchPlugin_Feature> theFeature) const
407 {
408   // Already verified workplanes
409   std::set<std::shared_ptr<ModelAPI_CompositeFeature> > aVerified;
410
411   std::list<SketchSolver_Group*>::const_iterator aGroupIter;
412   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
413     std::shared_ptr<ModelAPI_CompositeFeature> aWP = (*aGroupIter)->getWorkplane();
414     if (aVerified.find(aWP) != aVerified.end())
415       continue;
416
417     DataPtr aData = aWP->data();
418     if (aData->isValid()) {
419       std::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures = std::dynamic_pointer_cast<
420           ModelAPI_AttributeRefList>(aData->attribute(SketchPlugin_Sketch::FEATURES_ID()));
421       std::list<ObjectPtr> aFeaturesList = aWPFeatures->list();
422       std::list<ObjectPtr>::const_iterator anIter;
423       for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
424         if (*anIter == theFeature)
425           return aWP;  // workplane is found
426     }
427     aVerified.insert(aWP);
428   }
429
430   return std::shared_ptr<ModelAPI_CompositeFeature>();
431 }
432
433 // ============================================================================
434 //  Function: resolveConstraints
435 //  Purpose:  change entities according to available constraints
436 // ============================================================================
437 bool SketchSolver_Manager::resolveConstraints(const std::list<SketchSolver_Group*>& theGroups)
438 {
439   bool needToUpdate = false;
440   const std::list<SketchSolver_Group*>& aGroupsToResolve = theGroups.empty() ? myGroups : theGroups;
441   std::list<SketchSolver_Group*>::const_iterator aGroupIter = aGroupsToResolve.begin();
442   for (; aGroupIter != aGroupsToResolve.end(); aGroupIter++)
443     if ((*aGroupIter)->resolveConstraints())
444       needToUpdate = true;
445   return needToUpdate;
446 }
447
448 // ============================================================================
449 //  Function: degreesOfFreedom
450 //  Purpose:  calculate DoFs for each sketch
451 // ============================================================================
452 void SketchSolver_Manager::degreesOfFreedom()
453 {
454   static std::map<std::string, int> aDoFDelta; // indicates how many DoF adds or decreases a feature
455   static bool isNeedInit = true;
456   if (isNeedInit) {
457     aDoFDelta[SketchPlugin_Point::ID()] = 2;
458     aDoFDelta[SketchPlugin_Line::ID()] = 4;
459     aDoFDelta[SketchPlugin_Circle::ID()] = 3;
460     aDoFDelta[SketchPlugin_Arc::ID()] = 5;
461
462     aDoFDelta[SketchPlugin_ConstraintAngle::ID()] = -1;
463     aDoFDelta[SketchPlugin_ConstraintCollinear::ID()] = -1;
464     aDoFDelta[SketchPlugin_ConstraintDistance::ID()] = -1;
465     aDoFDelta[SketchPlugin_ConstraintEqual::ID()] = -1;
466     aDoFDelta[SketchPlugin_ConstraintHorizontal::ID()] = -1;
467     aDoFDelta[SketchPlugin_ConstraintLength::ID()] = -1;
468     aDoFDelta[SketchPlugin_ConstraintMiddle::ID()] = -1;
469     aDoFDelta[SketchPlugin_ConstraintParallel::ID()] = -1;
470     aDoFDelta[SketchPlugin_ConstraintPerpendicular::ID()] = -1;
471     aDoFDelta[SketchPlugin_ConstraintRadius::ID()] = -1;
472     aDoFDelta[SketchPlugin_ConstraintTangent::ID()] = -1;
473     aDoFDelta[SketchPlugin_ConstraintVertical::ID()] = -1;
474
475     isNeedInit = false;
476   }
477
478   std::map<CompositeFeaturePtr, int> aSketchDoF;
479
480   std::list<SketchSolver_Group*>::const_iterator aGroupIt = myGroups.begin();
481   for (; aGroupIt != myGroups.end(); ++aGroupIt) {
482     CompositeFeaturePtr aSketch = (*aGroupIt)->getWorkplane();
483
484     // check conflicting constraints in the group
485     if ((*aGroupIt)->isFailed())
486       aSketchDoF[aSketch] = -1;
487     // check the sketch is already processed
488     if (aSketchDoF.find(aSketch) != aSketchDoF.end() || aSketchDoF[aSketch] < 0)
489       continue;
490
491     int aDoF = 0;
492     int aNbSubs = aSketch->numberOfSubs();
493     for (int i = 0; i < aNbSubs; ++i) {
494       FeaturePtr aFeature = aSketch->subFeature(i);
495       // check DoF delta for invariant types
496       std::map<std::string, int>::const_iterator aFound = aDoFDelta.find(aFeature->getKind());
497       if (aFound != aDoFDelta.end()) {
498         aDoF += aFound->second;
499         continue;
500       }
501
502       // DoF delta in specific cases
503       if (aFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
504         for (int j = 0; j < 2; ++j) {
505           AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
506               aFeature->attribute(SketchPlugin_Constraint::ATTRIBUTE(j)));
507           if (!aRefAttr)
508             continue;
509           bool isPoint = !aRefAttr->isObject();
510           if (!isPoint) {
511             FeaturePtr anAttr = ModelAPI_Feature::feature(aRefAttr->object());
512             isPoint = anAttr && anAttr->getKind() == SketchPlugin_Point::ID();
513           }
514           if (isPoint)
515             aDoF -= 1;
516         }
517       }
518       else if (aFeature->getKind() == SketchPlugin_ConstraintRigid::ID()) {
519         AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
520             aFeature->attribute(SketchPlugin_Constraint::ENTITY_A()));
521         assert(aRefAttr);
522         if (!aRefAttr->isObject())
523           aDoF -= 2; // attribute is a point
524         else {
525           FeaturePtr anAttr = ModelAPI_Feature::feature(aRefAttr->object());
526           assert(anAttr);
527           aDoF -= aDoFDelta[anAttr->getKind()];
528         }
529       }
530       else if (aFeature->getKind() == SketchPlugin_ConstraintMirror::ID() ||
531                aFeature->getKind() == SketchPlugin_MultiRotation::ID() ||
532                aFeature->getKind() == SketchPlugin_MultiTranslation::ID()) {
533         int aNbCopies = 1;
534         std::string anAttrName;
535         if (aFeature->getKind() == SketchPlugin_ConstraintMirror::ID())
536           anAttrName = SketchPlugin_Constraint::ENTITY_B();
537         else {
538           if (aFeature->getKind() == SketchPlugin_MultiRotation::ID())
539             aNbCopies = aFeature->integer(SketchPlugin_MultiRotation::NUMBER_OF_OBJECTS_ID())->value() - 1;
540           else if (aFeature->getKind() == SketchPlugin_MultiTranslation::ID())
541             aNbCopies = aFeature->integer(SketchPlugin_MultiTranslation::NUMBER_OF_OBJECTS_ID())->value() - 1;
542           anAttrName = SketchPlugin_Constraint::ENTITY_A();
543         }
544
545         AttributeRefListPtr aRefListOfShapes = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
546             aFeature->attribute(anAttrName));
547         std::list<ObjectPtr> anObjList = aRefListOfShapes->list();
548         std::list<ObjectPtr>::const_iterator anObjIt = anObjList.begin();
549         for (; anObjIt != anObjList.end(); ++anObjIt) {
550           FeaturePtr aSub = ModelAPI_Feature::feature(*anObjIt);
551           aDoF -= aDoFDelta[aSub->getKind()] * aNbCopies;
552         }
553       }
554     }
555
556     aSketchDoF[aSketch] = aDoF;
557   }
558
559   // Check the degrees of freedom are changed
560   std::map<CompositeFeaturePtr, int>::const_iterator aDoFIt = aSketchDoF.begin();
561   std::map<CompositeFeaturePtr, int>::iterator aFound;
562   for (; aDoFIt != aSketchDoF.end(); ++aDoFIt) {
563     if (aDoFIt->second < 0)
564       continue; // conflicting constraints on the current sketch
565     aFound = myDoF.find(aDoFIt->first);
566     if (aFound != myDoF.end() && aFound->second == aDoFIt->second)
567       continue; // nothing is changed
568     myDoF[aDoFIt->first] = aDoFIt->second;
569     // send a message
570     std::shared_ptr<ModelAPI_SolverFailedMessage> aMessage =
571         std::shared_ptr<ModelAPI_SolverFailedMessage>(
572         new ModelAPI_SolverFailedMessage(Events_Loop::eventByName(EVENT_SOLVER_REPAIRED)));
573     aMessage->dof(aDoFIt->second);
574     Events_Loop::loop()->send(aMessage);
575   }
576 }
577
578 bool SketchSolver_Manager::stopSendUpdate() const
579 {
580   // to avoid redisplay of each segment on update by solver one by one in the viewer
581   bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
582   if (isUpdateFlushed) {
583     Events_Loop::loop()->setFlushed(anUpdateEvent, false);
584   }
585   return isUpdateFlushed;
586 }
587
588 void SketchSolver_Manager::allowSendUpdate() const
589 {
590   Events_Loop::loop()->setFlushed(anUpdateEvent, true);
591 }