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