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