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