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