Salome HOME
5867d6b94ed72ee786f611536ad14829f3363c8a
[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
12 #include <GeomDataAPI_Point2D.h>
13
14 #include <ModelAPI_AttributeDouble.h>
15 #include <ModelAPI_AttributeRefList.h>
16 #include <ModelAPI_Data.h>
17 #include <ModelAPI_Events.h>
18 #include <ModelAPI_Object.h>
19 #include <ModelAPI_ResultConstruction.h>
20 #include <ModelAPI_Attribute.h>
21 #include <ModelAPI_AttributeInteger.h>
22 #include <ModelAPI_AttributeString.h>
23
24 #include <SketchPlugin_Constraint.h>
25 #include <SketchPlugin_ConstraintAngle.h>
26 #include <SketchPlugin_ConstraintCoincidence.h>
27 #include <SketchPlugin_ConstraintCollinear.h>
28 #include <SketchPlugin_ConstraintDistance.h>
29 #include <SketchPlugin_ConstraintEqual.h>
30 #include <SketchPlugin_ConstraintHorizontal.h>
31 #include <SketchPlugin_ConstraintLength.h>
32 #include <SketchPlugin_ConstraintMiddle.h>
33 #include <SketchPlugin_ConstraintMirror.h>
34 #include <SketchPlugin_ConstraintParallel.h>
35 #include <SketchPlugin_ConstraintPerpendicular.h>
36 #include <SketchPlugin_ConstraintRadius.h>
37 #include <SketchPlugin_ConstraintRigid.h>
38 #include <SketchPlugin_ConstraintTangent.h>
39 #include <SketchPlugin_ConstraintVertical.h>
40 #include <SketchPlugin_MultiRotation.h>
41 #include <SketchPlugin_MultiTranslation.h>
42
43 #include <SketchPlugin_Arc.h>
44 #include <SketchPlugin_Circle.h>
45 #include <SketchPlugin_Line.h>
46 #include <SketchPlugin_Point.h>
47 #include <SketchPlugin_Sketch.h>
48 #include <SketchPlugin_Feature.h>
49
50 #include <assert.h>
51 #include <list>
52 #include <set>
53 #include <memory>
54 #include <sstream>
55
56 static const Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
57
58 // Initialization of constraint manager self pointer
59 SketchSolver_Manager* SketchSolver_Manager::mySelf = 0;
60
61 /// Global constraint manager object
62 SketchSolver_Manager* myManager = SketchSolver_Manager::instance();
63
64
65 // ========================================================
66 // ========= SketchSolver_Manager ===============
67 // ========================================================
68 SketchSolver_Manager* SketchSolver_Manager::instance()
69 {
70   if (!mySelf)
71     mySelf = new SketchSolver_Manager();
72   return mySelf;
73 }
74
75 SketchSolver_Manager::SketchSolver_Manager()
76 {
77   myGroups.clear();
78   myIsComputed = false;
79
80   // Register in event loop
81   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
82   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
83   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
84   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_MOVED));
85
86   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_SOLVER_FAILED));
87   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_SOLVER_REPAIRED));
88 }
89
90 SketchSolver_Manager::~SketchSolver_Manager()
91 {
92   myGroups.clear();
93 }
94
95 void SketchSolver_Manager::setBuilder(BuilderPtr theBuilder)
96 {
97   myBuilder = theBuilder;
98 }
99
100 BuilderPtr SketchSolver_Manager::builder()
101 {
102   return myBuilder;
103 }
104
105 // ============================================================================
106 //  Function: processEvent
107 //  Purpose:  listen the event loop and process the message
108 // ============================================================================
109 void SketchSolver_Manager::processEvent(
110   const std::shared_ptr<Events_Message>& theMessage)
111 {
112   checkConflictingConstraints(theMessage);
113   if (myIsComputed)
114     return;
115   myIsComputed = true;
116
117   // Shows that the message has at least one feature applicable for solver
118   bool hasProperFeature = false;
119
120   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)
121       || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED)
122       || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED)) {
123     std::shared_ptr<ModelAPI_ObjectUpdatedMessage> anUpdateMsg =
124         std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
125     std::set<ObjectPtr> aFeatures = anUpdateMsg->objects();
126
127     bool isUpdateFlushed = stopSendUpdate();
128
129     bool isMovedEvt = theMessage->eventID()
130           == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED);
131     if (isMovedEvt) {
132       std::set<ObjectPtr>::iterator aFeatIter;
133       for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
134         std::shared_ptr<SketchPlugin_Feature> aSFeature = 
135             std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
136         if (aSFeature && moveEntity(aSFeature)) {
137           // Want to avoid recalculation of DoF too frequently.
138           // So, set the flag when the feature is really moved.
139           hasProperFeature = true;
140         }
141       }
142       if (!hasProperFeature) // in this iteration it will compute nothing, so, no problem with recursion
143         // it is important that solver flushes signal updated after processing move signal as there is
144         // optimization that relies on this update, might be found by key "optimization"
145         myIsComputed = false;
146     } else {
147       std::list<FeaturePtr> aSketchFeatures = SketchSolver_Group::selectApplicableFeatures(aFeatures);
148       std::list<FeaturePtr>::iterator aFeatIter = aSketchFeatures.begin();
149       for (; aFeatIter != aSketchFeatures.end(); ++aFeatIter) {
150         if ((*aFeatIter)->getKind() == SketchPlugin_Sketch::ID()) {
151           std::shared_ptr<ModelAPI_CompositeFeature> aSketch = 
152               std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aFeatIter);
153           hasProperFeature = changeWorkplane(aSketch) || hasProperFeature;
154           continue;
155         }
156         std::shared_ptr<SketchPlugin_Feature> aFeature = 
157             std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
158         if (!aFeature)
159           continue;
160         hasProperFeature = changeFeature(aFeature) || hasProperFeature;
161       }
162     }
163
164     bool needToUpdate = false;
165     // Solve the set of constraints
166     if (hasProperFeature)
167       needToUpdate = resolveConstraints();
168
169     // Features may be updated => now send events, but for all changed at once
170     if (isUpdateFlushed)
171       allowSendUpdate();
172     // send update for movement in any case
173     if (needToUpdate || isMovedEvt)
174       Events_Loop::loop()->flush(anUpdateEvent);
175
176   } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
177     std::shared_ptr<ModelAPI_ObjectDeletedMessage> aDeleteMsg =
178       std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
179     const std::set<std::string>& aFeatureGroups = aDeleteMsg->groups();
180
181     // Find SketchPlugin_Sketch::ID() in groups. The constraint groups should be updated when an object removed from Sketch
182     std::set<std::string>::const_iterator aFGrIter;
183     for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
184       if (aFGrIter->compare(ModelAPI_ResultConstruction::group()) == 0 ||
185         aFGrIter->compare(ModelAPI_Feature::group()) == 0)
186         break;
187
188     if (aFGrIter != aFeatureGroups.end()) {
189       hasProperFeature = true;
190       std::list<SketchSolver_Group*> aGroupsToResolve;
191       std::list<SketchSolver_Group*>::iterator aGroupIter = myGroups.begin();
192       std::list<SketchSolver_Group*> aSeparatedGroups;
193       while (aGroupIter != myGroups.end()) {
194         if (!(*aGroupIter)->isWorkplaneValid()) {  // the group should be removed
195           delete *aGroupIter;
196           std::list<SketchSolver_Group*>::iterator aRemoveIt = aGroupIter++;
197           myGroups.erase(aRemoveIt);
198           continue;
199         }
200         if (!(*aGroupIter)->isConsistent()) {  // some constraints were removed, try to split the group
201           (*aGroupIter)->splitGroup(aSeparatedGroups);
202           if (!(*aGroupIter)->getWorkplane()->string(
203               SketchPlugin_Sketch::SOLVER_ERROR())->value().empty() ||
204               (*aGroupIter)->isFailed())
205             aGroupsToResolve.push_back(*aGroupIter);
206         }
207         aGroupIter++;
208       }
209       if (aSeparatedGroups.size() > 0) {
210         myGroups.insert(myGroups.end(), aSeparatedGroups.begin(), aSeparatedGroups.end());
211         aGroupsToResolve.insert(aGroupsToResolve.end(),
212             aSeparatedGroups.begin(), aSeparatedGroups.end());
213       }
214
215       if (!aGroupsToResolve.empty())
216         resolveConstraints(aGroupsToResolve);
217     }
218   }
219
220   if (hasProperFeature)
221     degreesOfFreedom();
222   myIsComputed = false;
223 }
224
225 void SketchSolver_Manager::checkConflictingConstraints(const std::shared_ptr<Events_Message>& theMessage)
226 {
227   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_SOLVER_REPAIRED)) {
228     std::shared_ptr<ModelAPI_SolverFailedMessage> aMessage =
229         std::dynamic_pointer_cast<ModelAPI_SolverFailedMessage>(theMessage);
230     std::set<ObjectPtr> aSentObjs = aMessage->objects();
231     if (!aSentObjs.empty()) {
232       // Obtain sketch where the constraints are placed.
233       // It is enough to check only one constraint.
234       CompositeFeaturePtr aSketch;
235       FeaturePtr aConstraint = ModelAPI_Feature::feature(*aSentObjs.begin());
236       std::list<SketchSolver_Group*>::const_iterator aGrIt = myGroups.begin();
237       for (; aGrIt != myGroups.end(); ++aGrIt)
238         if ((*aGrIt)->isInteract(aConstraint)) {
239           aSketch = (*aGrIt)->getWorkplane();
240           break;
241         }
242
243       // Search failed groups built on the same sketch
244       if (aSketch) {
245         for (aGrIt = myGroups.begin(); aGrIt != myGroups.end(); ++aGrIt) {
246           SketchSolver_Group* aGroup = *aGrIt;
247           if (aGroup->isBaseWorkplane(aSketch) && aGroup->isFailed() &&
248               !aGroup->isInteract(aConstraint)) {
249             // reset error message on the sketch
250             aGroup->getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue(
251                 SketchSolver_Error::CONSTRAINTS());
252             break;
253           }
254         }
255       }
256     }
257   }
258 }
259
260 // ============================================================================
261 //  Function: changeWorkplane
262 //  Purpose:  update workplane by given parameters of the sketch
263 // ============================================================================
264 bool SketchSolver_Manager::changeWorkplane(CompositeFeaturePtr theSketch)
265 {
266   bool aResult = true;  // changed when a workplane wrongly updated
267   bool isUpdated = false;
268   // Try to update specified workplane in all groups
269   std::list<SketchSolver_Group*>::iterator aGroupIter;
270   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
271     if ((*aGroupIter)->isBaseWorkplane(theSketch)) {
272       isUpdated = true;
273       aResult = false;
274     }
275   // If the workplane is not updated, so this is a new workplane
276   if (!isUpdated) {
277     SketchSolver_Group* aNewGroup = new SketchSolver_Group(theSketch);
278     // Verify that the group is created successfully
279     if (!aNewGroup->isBaseWorkplane(theSketch) || !aNewGroup->isWorkplaneValid()) {
280       delete aNewGroup;
281       return false;
282     }
283     myGroups.push_back(aNewGroup);
284   }
285   return aResult || isUpdated;
286 }
287
288 // ============================================================================
289 //  Function: changeConstraintOrEntity
290 //  Purpose:  create/update the constraint or the feature and place it into appropriate group
291 // ============================================================================
292 bool SketchSolver_Manager::changeFeature(std::shared_ptr<SketchPlugin_Feature> theFeature)
293 {
294   // Search the groups which this feature touches
295   std::set<GroupID> aGroups;
296   findGroups(theFeature, aGroups);
297
298   std::shared_ptr<SketchPlugin_Constraint> aConstraint = 
299       std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
300
301   // Process the groups list
302   if (aGroups.size() == 0) {
303     // There are no groups applicable for this constraint => create new one
304     // The group will be created only for constraints, not for features
305     if (!aConstraint) return false;
306     std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(aConstraint);
307     if (!aWP)
308       return false;
309     SketchSolver_Group* aGroup = new SketchSolver_Group(aWP);
310     if (!aGroup->changeConstraint(aConstraint)) {
311       delete aGroup;
312       return false;
313     }
314     myGroups.push_back(aGroup);
315     return true;
316   } else if (aGroups.size() == 1) {  // Only one group => add feature into it
317     GroupID aGroupId = *(aGroups.begin());
318     std::list<SketchSolver_Group*>::iterator aGroupIter;
319     for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
320       if ((*aGroupIter)->getId() == aGroupId) {
321         // If the group is empty, the feature is not added (the constraint only)
322         if (!aConstraint && !(*aGroupIter)->isEmpty())
323           return (*aGroupIter)->updateFeature(theFeature);
324         return (*aGroupIter)->changeConstraint(aConstraint);
325       }
326   } else if (aGroups.size() > 1) {  // Several groups applicable for this feature => need to merge them
327     std::set<GroupID>::const_iterator aGroupsIter = aGroups.begin();
328
329     // Search first group
330     std::list<SketchSolver_Group*>::iterator aFirstGroupIter;
331     for (aFirstGroupIter = myGroups.begin(); aFirstGroupIter != myGroups.end(); aFirstGroupIter++)
332       if ((*aFirstGroupIter)->getId() == *aGroupsIter)
333         break;
334     if (aFirstGroupIter == myGroups.end())
335       return false;
336
337     // Append other groups to the first one
338     std::list<SketchSolver_Group*>::iterator anOtherGroupIter = aFirstGroupIter;
339     ++anOtherGroupIter;
340     for (aGroupsIter++; aGroupsIter != aGroups.end(); aGroupsIter++) {
341       for (; anOtherGroupIter != myGroups.end(); anOtherGroupIter++)
342         if ((*anOtherGroupIter)->getId() == *aGroupsIter)
343           break;
344       if (anOtherGroupIter == myGroups.end()) {  // Group disappears
345         anOtherGroupIter = aFirstGroupIter;
346         ++anOtherGroupIter;
347         continue;
348       }
349
350       (*aFirstGroupIter)->mergeGroups(**anOtherGroupIter);
351       std::list<SketchSolver_Group*>::iterator aRemoveIt = anOtherGroupIter++;
352       delete *aRemoveIt;
353       myGroups.erase(aRemoveIt);
354     }
355
356     if (aConstraint)
357       (*aFirstGroupIter)->changeConstraint(aConstraint);
358     else
359       (*aFirstGroupIter)->updateFeature(theFeature);
360     // groups are merged => need to resolve them
361     return true;
362   }
363
364   // Something goes wrong
365   return false;
366 }
367
368 // ============================================================================
369 //  Function: moveEntity
370 //  Purpose:  update element moved on the sketch, which is used by constraints
371 // ============================================================================
372 bool SketchSolver_Manager::moveEntity(std::shared_ptr<SketchPlugin_Feature> theFeature)
373 {
374   bool isMoved = false;
375   std::list<SketchSolver_Group*>::iterator aGroupIt = myGroups.begin();
376   for (; aGroupIt != myGroups.end(); aGroupIt++)
377     if (!(*aGroupIt)->isEmpty() && (*aGroupIt)->isInteract(theFeature))
378       isMoved = (*aGroupIt)->moveFeature(theFeature) || isMoved;
379
380   if (!isMoved && theFeature->getKind() == SketchPlugin_Arc::ID()) {
381     // Workaround to move arc.
382     // If the arc has not been constrained, we will push it into empty group and apply movement.
383     bool hasEmptyGroup = false;
384     for (aGroupIt = myGroups.begin(); aGroupIt != myGroups.end(); aGroupIt++)
385       if ((*aGroupIt)->isEmpty()) {
386         isMoved = (*aGroupIt)->moveFeature(theFeature) || isMoved;
387         hasEmptyGroup = true;
388       }
389     // There is no empty group, create it explicitly
390     if (!hasEmptyGroup) {
391       // find sketch containing the arc
392       CompositeFeaturePtr aWP;
393       const std::set<AttributePtr>& aRefs = theFeature->data()->refsToMe();
394       std::set<AttributePtr>::const_iterator aRefIt = aRefs.begin();
395       for (; aRefIt != aRefs.end(); ++aRefIt) {
396         FeaturePtr anOwner = ModelAPI_Feature::feature((*aRefIt)->owner());
397         if (anOwner && anOwner->getKind() == SketchPlugin_Sketch::ID()) {
398           aWP = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(anOwner);
399           break;
400         }
401       }
402       if (aWP) {
403         SketchSolver_Group* aGroup = new SketchSolver_Group(aWP);
404         isMoved = aGroup->moveFeature(theFeature) || isMoved;
405         myGroups.push_back(aGroup);
406       }
407     }
408   }
409   return isMoved;
410 }
411
412 // ============================================================================
413 //  Function: findGroups
414 //  Purpose:  search groups of entities interacting with given feature
415 // ============================================================================
416 void SketchSolver_Manager::findGroups(
417     std::shared_ptr<SketchPlugin_Feature> theFeature,
418     std::set<GroupID>& theGroupIDs) const
419 {
420   std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(theFeature);
421
422   SketchSolver_Group* anEmptyGroup = 0;  // appropriate empty group for specified constraint
423   std::list<SketchSolver_Group*>::const_iterator aGroupIter;
424   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
425     if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theFeature)) {
426       if (!(*aGroupIter)->isEmpty())
427         theGroupIDs.insert((*aGroupIter)->getId());
428       else if (!anEmptyGroup)
429         anEmptyGroup = *aGroupIter;
430     }
431
432   // When only empty group is found, use it
433   if (anEmptyGroup && theGroupIDs.empty())
434     theGroupIDs.insert(anEmptyGroup->getId());
435 }
436
437 // ============================================================================
438 //  Function: findWorkplane
439 //  Purpose:  search workplane containing given feature
440 // ============================================================================
441 std::shared_ptr<ModelAPI_CompositeFeature> SketchSolver_Manager
442 ::findWorkplane(std::shared_ptr<SketchPlugin_Feature> theFeature) const
443 {
444   // Already verified workplanes
445   std::set<std::shared_ptr<ModelAPI_CompositeFeature> > aVerified;
446
447   std::list<SketchSolver_Group*>::const_iterator aGroupIter;
448   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
449     std::shared_ptr<ModelAPI_CompositeFeature> aWP = (*aGroupIter)->getWorkplane();
450     if (aVerified.find(aWP) != aVerified.end())
451       continue;
452
453     DataPtr aData = aWP->data();
454     if (aData->isValid()) {
455       std::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures = std::dynamic_pointer_cast<
456           ModelAPI_AttributeRefList>(aData->attribute(SketchPlugin_Sketch::FEATURES_ID()));
457       std::list<ObjectPtr> aFeaturesList = aWPFeatures->list();
458       std::list<ObjectPtr>::const_iterator anIter;
459       for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
460         if (*anIter == theFeature)
461           return aWP;  // workplane is found
462     }
463     aVerified.insert(aWP);
464   }
465
466   return std::shared_ptr<ModelAPI_CompositeFeature>();
467 }
468
469 // ============================================================================
470 //  Function: resolveConstraints
471 //  Purpose:  change entities according to available constraints
472 // ============================================================================
473 bool SketchSolver_Manager::resolveConstraints(const std::list<SketchSolver_Group*>& theGroups)
474 {
475   bool needToUpdate = false;
476   const std::list<SketchSolver_Group*>& aGroupsToResolve = theGroups.empty() ? myGroups : theGroups;
477   std::list<SketchSolver_Group*>::const_iterator aGroupIter = aGroupsToResolve.begin();
478   for (; aGroupIter != aGroupsToResolve.end(); aGroupIter++)
479     if ((*aGroupIter)->resolveConstraints())
480       needToUpdate = true;
481   return needToUpdate;
482 }
483
484
485 // Obtain points and their copies for Mirror, Multi-Rotation and Multi-Translation constraints
486 static void collectPointsAndCopies(FeaturePtr theConstraint, std::list<std::set<AttributePtr> >& thePoints)
487 {
488   typedef std::list<std::string> strlist;
489   static strlist aPointAttributes(1, SketchPlugin_Point::COORD_ID());
490   static strlist aLineAttributes;
491   if (aLineAttributes.empty()) {
492     aLineAttributes.push_back(SketchPlugin_Line::START_ID());
493     aLineAttributes.push_back(SketchPlugin_Line::END_ID());
494   };
495   static strlist aCircleAttributes(1, SketchPlugin_Circle::CENTER_ID());
496   static strlist anArcAttributes;
497   if (anArcAttributes.empty()) {
498     anArcAttributes.push_back(SketchPlugin_Arc::CENTER_ID());
499     anArcAttributes.push_back(SketchPlugin_Arc::START_ID());
500     anArcAttributes.push_back(SketchPlugin_Arc::END_ID());
501   };
502
503   static std::map<std::string, strlist> aFeatureAttributes;
504   if (aFeatureAttributes.empty()) {
505     aFeatureAttributes[SketchPlugin_Point::ID()] = aPointAttributes;
506     aFeatureAttributes[SketchPlugin_Line::ID()] = aLineAttributes;
507     aFeatureAttributes[SketchPlugin_Circle::ID()] = aCircleAttributes;
508     aFeatureAttributes[SketchPlugin_Arc::ID()] = anArcAttributes;
509   }
510
511
512   std::set<AttributePtr> aPoints;
513   if (theConstraint->getKind() == SketchPlugin_ConstraintMirror::ID()) {
514     AttributeRefListPtr aBaseRefList = theConstraint->reflist(SketchPlugin_Constraint::ENTITY_B());
515     AttributeRefListPtr aMirrRefList = theConstraint->reflist(SketchPlugin_Constraint::ENTITY_C());
516
517     std::list<ObjectPtr> aBaseList = aBaseRefList->list();
518     std::list<ObjectPtr> aMirrList = aMirrRefList->list();
519     std::list<ObjectPtr>::const_iterator aBIt, aMIt;
520     for (aBIt = aBaseList.begin(), aMIt = aMirrList.begin();
521          aBIt != aBaseList.end() && aMIt != aMirrList.end(); ++aBIt, ++aMIt) {
522       FeaturePtr aBaseFeature = ModelAPI_Feature::feature(*aBIt);
523       FeaturePtr aMirrFeature = ModelAPI_Feature::feature(*aMIt);
524
525       strlist anAttrList = aFeatureAttributes[aBaseFeature->getKind()];
526       strlist::iterator anIt = anAttrList.begin();
527       for (; anIt != anAttrList.end(); ++anIt) {
528         aPoints.clear();
529         aPoints.insert(aBaseFeature->attribute(*anIt));
530         aPoints.insert(aMirrFeature->attribute(*anIt));
531         thePoints.push_back(aPoints);
532       }
533     }
534   }
535   else { // the "Multi" constraints
536     std::string aNbObjName;
537     if (theConstraint->getKind() == SketchPlugin_MultiRotation::ID())
538       aNbObjName = SketchPlugin_MultiRotation::NUMBER_OF_OBJECTS_ID();
539     else
540       aNbObjName = SketchPlugin_MultiTranslation::NUMBER_OF_OBJECTS_ID();
541     int aNbCopies = theConstraint->integer(aNbObjName)->value();
542
543     AttributeRefListPtr aRefList = theConstraint->reflist(SketchPlugin_Constraint::ENTITY_B());
544     std::list<ObjectPtr> aFullList = aRefList->list();
545     std::list<ObjectPtr>::const_iterator anObjIt = aFullList.begin();
546     std::list<ObjectPtr>::const_iterator aCopyIt;
547     while (anObjIt != aFullList.end()) {
548       FeaturePtr aBaseFeature = ModelAPI_Feature::feature(*anObjIt);
549       strlist anAttrList = aFeatureAttributes[aBaseFeature->getKind()];
550       strlist::iterator anIt = anAttrList.begin();
551       for (; anIt != anAttrList.end(); ++anIt) {
552         aPoints.clear();
553         aCopyIt = anObjIt;
554         for (int i = 0; i < aNbCopies && aCopyIt != aFullList.end(); ++i, ++aCopyIt) {
555           FeaturePtr aFeature = ModelAPI_Feature::feature(*aCopyIt);
556           aPoints.insert(aFeature->attribute(*anIt));
557         }
558         thePoints.push_back(aPoints);
559       }
560       anObjIt = aCopyIt;
561     }
562   }
563 }
564
565 // ============================================================================
566 //  Function: degreesOfFreedom
567 //  Purpose:  calculate DoFs for each sketch
568 // ============================================================================
569 void SketchSolver_Manager::degreesOfFreedom()
570 {
571   static std::map<std::string, int> aDoFDelta; // indicates how many DoF adds or decreases a feature
572   static bool isNeedInit = true;
573   if (isNeedInit) {
574     aDoFDelta[SketchPlugin_Point::ID()] = 2;
575     aDoFDelta[SketchPlugin_Line::ID()] = 4;
576     aDoFDelta[SketchPlugin_Circle::ID()] = 3;
577     aDoFDelta[SketchPlugin_Arc::ID()] = 5;
578
579     aDoFDelta[SketchPlugin_ConstraintAngle::ID()] = -1;
580     aDoFDelta[SketchPlugin_ConstraintCollinear::ID()] = -1;
581     aDoFDelta[SketchPlugin_ConstraintDistance::ID()] = -1;
582     aDoFDelta[SketchPlugin_ConstraintEqual::ID()] = -1;
583     aDoFDelta[SketchPlugin_ConstraintHorizontal::ID()] = -1;
584     aDoFDelta[SketchPlugin_ConstraintLength::ID()] = -1;
585     aDoFDelta[SketchPlugin_ConstraintParallel::ID()] = -1;
586     aDoFDelta[SketchPlugin_ConstraintPerpendicular::ID()] = -1;
587     aDoFDelta[SketchPlugin_ConstraintRadius::ID()] = -1;
588     aDoFDelta[SketchPlugin_ConstraintTangent::ID()] = -1;
589     aDoFDelta[SketchPlugin_ConstraintVertical::ID()] = -1;
590
591     isNeedInit = false;
592   }
593
594   std::map<CompositeFeaturePtr, int> aSketchDoF;
595
596   std::list<SketchSolver_Group*>::const_iterator aGroupIt = myGroups.begin();
597   for (; aGroupIt != myGroups.end(); ++aGroupIt) {
598     CompositeFeaturePtr aSketch = (*aGroupIt)->getWorkplane();
599     bool isSketchValid = aSketch->data() && aSketch->data()->isValid();
600
601     if (isSketchValid) {
602       std::shared_ptr<GeomDataAPI_Dir> aNormal =
603           std::dynamic_pointer_cast<GeomDataAPI_Dir>(aSketch->data()->attribute(SketchPlugin_Sketch::NORM_ID()));
604       isSketchValid = aNormal && aNormal->isInitialized();
605     }
606
607     if (!isSketchValid) {
608       myDoF.erase(aSketch);
609       continue;
610     }
611
612     // check conflicting constraints in the group
613     if ((*aGroupIt)->isFailed())
614       aSketchDoF[aSketch] = -1;
615     // check the sketch is already processed
616     if (aSketchDoF.find(aSketch) != aSketchDoF.end() || aSketchDoF[aSketch] < 0)
617       continue;
618
619     std::set<AttributePtr> aCoincidentPoints;
620     std::set<AttributePtr> aFixedPoints;
621     std::map<AttributePtr, std::set<FeaturePtr> > aPointOnLine;
622     std::list<std::set<AttributePtr> > aPointsInMultiConstraints;
623     int aDoF = 0;
624     int aNbSubs = aSketch->numberOfSubs();
625     for (int i = 0; i < aNbSubs; ++i) {
626       FeaturePtr aFeature = aSketch->subFeature(i);
627       // check DoF delta for invariant types
628       std::map<std::string, int>::const_iterator aFound = aDoFDelta.find(aFeature->getKind());
629       if (aFound != aDoFDelta.end()) {
630         aDoF += aFound->second;
631         continue;
632       }
633
634       // DoF delta in specific cases
635       if (aFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
636         AttributePtr aCoincPoint[2] = {AttributePtr(), AttributePtr()};
637         FeaturePtr aCoincLine;
638         for (int j = 0; j < 2; ++j) {
639           AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
640               aFeature->attribute(SketchPlugin_Constraint::ATTRIBUTE(j)));
641           if (!aRefAttr)
642             continue;
643           if (!aRefAttr->isObject())
644             aCoincPoint[j] = aRefAttr->attr();
645           else {
646             FeaturePtr anAttr = ModelAPI_Feature::feature(aRefAttr->object());
647             if (!anAttr)
648               continue;
649             if (anAttr->getKind() == SketchPlugin_Point::ID())
650               aCoincPoint[j] = anAttr->attribute(SketchPlugin_Point::COORD_ID());
651             else if (anAttr->getKind() == SketchPlugin_Line::ID())
652               aCoincLine = anAttr;
653           }
654         }
655         if (aCoincPoint[0] && aCoincPoint[1]) {
656           bool isDoFDecreased = false;
657           // point-point coincidence
658           if (aCoincidentPoints.find(aCoincPoint[0]) == aCoincidentPoints.end() ||
659               aCoincidentPoints.find(aCoincPoint[1]) == aCoincidentPoints.end()) {
660             aDoF -= 2;
661             isDoFDecreased = true;
662           }
663           // check the coincident point is used in "multi" constraints
664           std::list<std::set<AttributePtr> >::const_iterator
665               aPtIt = aPointsInMultiConstraints.begin();
666           bool isFound[2] = {false, false};
667           for (; aPtIt != aPointsInMultiConstraints.end(); ++aPtIt) {
668             if ((!isFound[0] && (isFound[0] = (aPtIt->find(aCoincPoint[0]) != aPtIt->end())))
669              || (!isFound[1] && (isFound[1] = (aPtIt->find(aCoincPoint[1]) != aPtIt->end()))))
670               aCoincidentPoints.insert(aPtIt->begin(), aPtIt->end());
671             if (isFound[0] && isFound[1])
672               break;
673           }
674           // check both points are fixed => not need to decrease DoF
675           bool isFixed[2] = { aFixedPoints.find(aCoincPoint[0]) != aFixedPoints.end(),
676                               aFixedPoints.find(aCoincPoint[1]) != aFixedPoints.end() };
677           if (isFixed[0] && isFixed[1] && isDoFDecreased)
678             aDoF += 2; // revert decrease of DoF
679           else if (isFixed[0] && !isFixed[1])
680             aFixedPoints.insert(aCoincPoint[1]);
681           else if (!isFixed[0] && isFixed[1])
682             aFixedPoints.insert(aCoincPoint[0]);
683         } else {
684           aDoF -= 1;
685           if (aCoincPoint[0] && aCoincLine) {
686             // if the point is already coincident to a line (by middle point constraint), do not decrease DoF
687             std::map<AttributePtr, std::set<FeaturePtr> >::iterator
688                 aPtFound = aPointOnLine.find(aCoincPoint[0]);
689             if (aPtFound != aPointOnLine.end() &&
690                 aPtFound->second.find(aCoincLine) != aPtFound->second.end())
691               aDoF += 1; // restore value decreased above
692             else
693               aPointOnLine[aCoincPoint[0]].insert(aCoincLine);
694           }
695         }
696         for (int j = 0; j < 2; ++j)
697           if (aCoincPoint[j])
698             aCoincidentPoints.insert(aCoincPoint[j]);
699       }
700       else if (aFeature->getKind() == SketchPlugin_ConstraintMiddle::ID()) {
701         AttributePtr aPoint;
702         FeaturePtr aLine;
703         for (int j = 0; j < 2; ++j) {
704           AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
705               aFeature->attribute(SketchPlugin_Constraint::ATTRIBUTE(j)));
706           if (!aRefAttr)
707             continue;
708           if (aRefAttr->isObject())
709             aLine = ModelAPI_Feature::feature(aRefAttr->object());
710           else
711             aPoint = aRefAttr->attr();
712         }
713         if (aPoint && aLine) {
714           // if the point is already on the line, decrease 1 DoF, instead decrease 2 DoF
715           std::map<AttributePtr, std::set<FeaturePtr> >::iterator
716               aPtFound = aPointOnLine.find(aPoint);
717           if (aPtFound != aPointOnLine.end() &&
718               aPtFound->second.find(aLine) != aPtFound->second.end())
719             aDoF -= 1;
720           else {
721             aDoF -= 2;
722             aPointOnLine[aPoint].insert(aLine);
723           }
724         }
725       }
726       else if (aFeature->getKind() == SketchPlugin_ConstraintRigid::ID()) {
727         AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
728             aFeature->attribute(SketchPlugin_Constraint::ENTITY_A()));
729         assert(aRefAttr);
730         std::set<AttributePtr> aPoints;
731         if (!aRefAttr->isObject()) {
732           aDoF -= 2; // attribute is a point
733           aPoints.insert(aRefAttr->attr());
734         } else {
735           FeaturePtr anAttr = ModelAPI_Feature::feature(aRefAttr->object());
736           if (anAttr)
737             aDoF -= aDoFDelta[anAttr->getKind()];
738           std::list<AttributePtr> aPtAttrs = anAttr->data()->attributes(GeomDataAPI_Point2D::typeId());
739           aPoints.insert(aPtAttrs.begin(), aPtAttrs.end());
740         }
741
742         // Check whether feature's points are already coincident with fixed points.
743         // In this case we need to revert decrease of DoF for these points.
744         // If the coordinates of fixed points are different, it will be processed by solver.
745         for (int k = 0; k < i; ++k) {
746           FeaturePtr aFeature = aSketch->subFeature(k);
747           if (aFeature->getKind() != SketchPlugin_ConstraintCoincidence::ID())
748             continue;
749           AttributePtr aCoincPoint[2] = {AttributePtr(), AttributePtr()};
750           for (int j = 0; j < 2; ++j) {
751             AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
752                 aFeature->attribute(SketchPlugin_Constraint::ATTRIBUTE(j)));
753             if (!aRefAttr)
754               continue;
755             if (!aRefAttr->isObject())
756               aCoincPoint[j] = aRefAttr->attr();
757             else {
758               FeaturePtr anAttr = ModelAPI_Feature::feature(aRefAttr->object());
759               if (anAttr && anAttr->getKind() == SketchPlugin_Point::ID())
760                 aCoincPoint[j] = anAttr->attribute(SketchPlugin_Point::COORD_ID());
761             }
762           }
763           if (aCoincPoint[0] && aCoincPoint[1]) {
764             if ((aFixedPoints.find(aCoincPoint[0]) != aFixedPoints.end() &&
765                  aPoints.find(aCoincPoint[1]) != aPoints.end()) ||
766                 (aFixedPoints.find(aCoincPoint[1]) != aFixedPoints.end() &&
767                  aPoints.find(aCoincPoint[0]) != aPoints.end()))
768               aDoF += 2; // point already fixed
769           }
770         }
771         // store fixed points
772         aFixedPoints.insert(aPoints.begin(), aPoints.end());
773       }
774       else if (aFeature->getKind() == SketchPlugin_ConstraintMirror::ID() ||
775                aFeature->getKind() == SketchPlugin_MultiRotation::ID() ||
776                aFeature->getKind() == SketchPlugin_MultiTranslation::ID()) {
777         int aNbCopies = 1;
778         std::string anAttrName;
779         if (aFeature->getKind() == SketchPlugin_ConstraintMirror::ID())
780           anAttrName = SketchPlugin_Constraint::ENTITY_B();
781         else {
782           if (aFeature->getKind() == SketchPlugin_MultiRotation::ID())
783             aNbCopies = aFeature->integer(SketchPlugin_MultiRotation::NUMBER_OF_OBJECTS_ID())->value() - 1;
784           else if (aFeature->getKind() == SketchPlugin_MultiTranslation::ID())
785             aNbCopies = aFeature->integer(SketchPlugin_MultiTranslation::NUMBER_OF_OBJECTS_ID())->value() - 1;
786           anAttrName = SketchPlugin_Constraint::ENTITY_A();
787         }
788
789         AttributeRefListPtr aRefListOfShapes = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
790             aFeature->attribute(anAttrName));
791         std::list<ObjectPtr> anObjList = aRefListOfShapes->list();
792         std::list<ObjectPtr>::const_iterator anObjIt = anObjList.begin();
793         for (; anObjIt != anObjList.end(); ++anObjIt) {
794           FeaturePtr aSub = ModelAPI_Feature::feature(*anObjIt);
795           aDoF -= aDoFDelta[aSub->getKind()] * aNbCopies;
796         }
797         // collect points and their copies for correct calculation of DoF for coincident points
798         collectPointsAndCopies(aFeature, aPointsInMultiConstraints);
799       }
800     }
801
802     aSketchDoF[aSketch] = aDoF;
803   }
804
805   // Check the degrees of freedom are changed
806   std::map<CompositeFeaturePtr, int>::const_iterator aDoFIt = aSketchDoF.begin();
807   std::map<CompositeFeaturePtr, int>::iterator aFound;
808   for (; aDoFIt != aSketchDoF.end(); ++aDoFIt) {
809     if (aDoFIt->second < 0)
810       continue; // conflicting constraints on the current sketch
811     aFound = myDoF.find(aDoFIt->first);
812     if (aFound != myDoF.end() && aFound->second == aDoFIt->second)
813       continue; // nothing is changed
814     myDoF[aDoFIt->first] = aDoFIt->second;
815     // change attribute value
816     std::ostringstream aStream;
817     if (aDoFIt->second == 0)
818       aStream << "Sketch fully fixed (DOF = " << aDoFIt->second << ")";
819     else
820       aStream << "DOF (degree of freedom) = " << aDoFIt->second;
821     aDoFIt->first->data()->string(SketchPlugin_Sketch::SOLVER_DOF())->setValue(aStream.str());
822   }
823 }
824
825 bool SketchSolver_Manager::stopSendUpdate() const
826 {
827   // to avoid redisplay of each segment on update by solver one by one in the viewer
828   bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
829   if (isUpdateFlushed) {
830     Events_Loop::loop()->setFlushed(anUpdateEvent, false);
831   }
832   return isUpdateFlushed;
833 }
834
835 void SketchSolver_Manager::allowSendUpdate() const
836 {
837   Events_Loop::loop()->setFlushed(anUpdateEvent, true);
838 }