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