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