Salome HOME
2.17. Improved management of overconstraint situation: Undo correction: SOLVER_ERROR...
[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
9 #include <Events_Loop.h>
10 #include <ModelAPI_AttributeDouble.h>
11 #include <ModelAPI_AttributeRefList.h>
12 #include <ModelAPI_Data.h>
13 #include <ModelAPI_Events.h>
14 #include <ModelAPI_Object.h>
15 #include <ModelAPI_ResultConstruction.h>
16 #include <ModelAPI_Attribute.h>
17 #include <ModelAPI_AttributeString.h>
18
19 #include <SketchPlugin_Constraint.h>
20
21 #include <SketchPlugin_Arc.h>
22 #include <SketchPlugin_Circle.h>
23 #include <SketchPlugin_Line.h>
24 #include <SketchPlugin_Point.h>
25 #include <SketchPlugin_Sketch.h>
26 #include <SketchPlugin_Feature.h>
27
28 #include <list>
29 #include <set>
30 #include <memory>
31
32 static const Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
33
34 // Initialization of constraint manager self pointer
35 SketchSolver_Manager* SketchSolver_Manager::mySelf = 0;
36
37 /// Global constraint manager object
38 SketchSolver_Manager* myManager = SketchSolver_Manager::instance();
39
40
41 // ========================================================
42 // ========= SketchSolver_Manager ===============
43 // ========================================================
44 SketchSolver_Manager* SketchSolver_Manager::instance()
45 {
46   if (!mySelf)
47     mySelf = new SketchSolver_Manager();
48   return mySelf;
49 }
50
51 SketchSolver_Manager::SketchSolver_Manager()
52 {
53   myGroups.clear();
54   myIsComputed = false;
55
56   // Register in event loop
57   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
58   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
59   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
60   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_MOVED));
61 }
62
63 SketchSolver_Manager::~SketchSolver_Manager()
64 {
65   myGroups.clear();
66 }
67
68 void SketchSolver_Manager::setBuilder(BuilderPtr theBuilder)
69 {
70   myBuilder = theBuilder;
71 }
72
73 BuilderPtr SketchSolver_Manager::builder()
74 {
75   return myBuilder;
76 }
77
78 // ============================================================================
79 //  Function: processEvent
80 //  Purpose:  listen the event loop and process the message
81 // ============================================================================
82 void SketchSolver_Manager::processEvent(
83   const std::shared_ptr<Events_Message>& theMessage)
84 {
85   if (myIsComputed)
86     return;
87   myIsComputed = true;
88   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)
89       || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED)
90       || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED)) {
91     std::shared_ptr<ModelAPI_ObjectUpdatedMessage> anUpdateMsg =
92         std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
93     std::set<ObjectPtr> aFeatures = anUpdateMsg->objects();
94
95     bool isUpdateFlushed = stopSendUpdate();
96     // Shows the message has at least one feature applicable for solver
97     bool hasProperFeature = false;
98
99     bool isMovedEvt = theMessage->eventID()
100           == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED);
101     if (isMovedEvt) {
102       std::set<ObjectPtr>::iterator aFeatIter;
103       for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
104         std::shared_ptr<SketchPlugin_Feature> aSFeature = 
105             std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
106         if (aSFeature) {
107           moveEntity(aSFeature);
108           hasProperFeature = true;
109         }
110       }
111     } else {
112       std::list<FeaturePtr> aSketchFeatures = SketchSolver_Group::selectApplicableFeatures(aFeatures);
113       std::list<FeaturePtr>::iterator aFeatIter = aSketchFeatures.begin();
114       for (; aFeatIter != aSketchFeatures.end(); ++aFeatIter) {
115         if ((*aFeatIter)->getKind() == SketchPlugin_Sketch::ID()) {
116           std::shared_ptr<ModelAPI_CompositeFeature> aSketch = 
117               std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aFeatIter);
118           hasProperFeature = changeWorkplane(aSketch) || hasProperFeature;
119           continue;
120         }
121         std::shared_ptr<SketchPlugin_Feature> aFeature = 
122             std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
123         if (!aFeature)
124           continue;
125         hasProperFeature = changeFeature(aFeature) || hasProperFeature;
126       }
127     }
128
129     bool needToUpdate = false;
130     // Solve the set of constraints
131     if (hasProperFeature)
132       needToUpdate = resolveConstraints();
133
134     // Features may be updated => now send events, but for all changed at once
135     if (isUpdateFlushed)
136       allowSendUpdate();
137     // send update for movement in any case
138     if (needToUpdate || isMovedEvt)
139       Events_Loop::loop()->flush(anUpdateEvent);
140
141   } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
142     std::shared_ptr<ModelAPI_ObjectDeletedMessage> aDeleteMsg =
143       std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
144     const std::set<std::string>& aFeatureGroups = aDeleteMsg->groups();
145
146     // Find SketchPlugin_Sketch::ID() in groups. The constraint groups should be updated when an object removed from Sketch
147     std::set<std::string>::const_iterator aFGrIter;
148     for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
149       if (aFGrIter->compare(ModelAPI_ResultConstruction::group()) == 0 ||
150         aFGrIter->compare(ModelAPI_Feature::group()) == 0)
151         break;
152
153     if (aFGrIter != aFeatureGroups.end()) {
154       std::list<SketchSolver_Group*> aGroupsToResolve;
155       std::list<SketchSolver_Group*>::iterator aGroupIter = myGroups.begin();
156       std::list<SketchSolver_Group*> aSeparatedGroups;
157       while (aGroupIter != myGroups.end()) {
158         if (!(*aGroupIter)->isWorkplaneValid()) {  // the group should be removed
159           delete *aGroupIter;
160           std::list<SketchSolver_Group*>::iterator aRemoveIt = aGroupIter++;
161           myGroups.erase(aRemoveIt);
162           continue;
163         }
164         if (!(*aGroupIter)->isConsistent()) {  // some constraints were removed, try to split the group
165           (*aGroupIter)->splitGroup(aSeparatedGroups);
166           //if (!(*aGroupIter)->getWorkplane()->string(
167           //    SketchPlugin_Sketch::SOLVER_ERROR())->value().empty())
168             aGroupsToResolve.push_back(*aGroupIter);
169         }
170         aGroupIter++;
171       }
172       if (aSeparatedGroups.size() > 0) {
173         myGroups.insert(myGroups.end(), aSeparatedGroups.begin(), aSeparatedGroups.end());
174         aGroupsToResolve.insert(aGroupsToResolve.end(),
175             aSeparatedGroups.begin(), aSeparatedGroups.end());
176       }
177
178       if (!aGroupsToResolve.empty())
179         resolveConstraints(aGroupsToResolve);
180     }
181   }
182   myIsComputed = false;
183 }
184
185 // ============================================================================
186 //  Function: changeWorkplane
187 //  Purpose:  update workplane by given parameters of the sketch
188 // ============================================================================
189 bool SketchSolver_Manager::changeWorkplane(CompositeFeaturePtr theSketch)
190 {
191   bool aResult = true;  // changed when a workplane wrongly updated
192   bool isUpdated = false;
193   // Try to update specified workplane in all groups
194   std::list<SketchSolver_Group*>::iterator aGroupIter;
195   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
196     if ((*aGroupIter)->isBaseWorkplane(theSketch)) {
197       isUpdated = true;
198       aResult = false;
199     }
200   // If the workplane is not updated, so this is a new workplane
201   if (!isUpdated) {
202     SketchSolver_Group* aNewGroup = new SketchSolver_Group(theSketch);
203     // Verify that the group is created successfully
204     if (!aNewGroup->isBaseWorkplane(theSketch) || !aNewGroup->isWorkplaneValid()) {
205       delete aNewGroup;
206       return false;
207     }
208     myGroups.push_back(aNewGroup);
209   }
210   return aResult;
211 }
212
213 // ============================================================================
214 //  Function: changeConstraintOrEntity
215 //  Purpose:  create/update the constraint or the feature and place it into appropriate group
216 // ============================================================================
217 bool SketchSolver_Manager::changeFeature(std::shared_ptr<SketchPlugin_Feature> theFeature)
218 {
219   // Search the groups which this feature touches
220   std::set<GroupID> aGroups;
221   findGroups(theFeature, aGroups);
222
223   std::shared_ptr<SketchPlugin_Constraint> aConstraint = 
224       std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
225
226   // Process the groups list
227   if (aGroups.size() == 0) {
228     // There are no groups applicable for this constraint => create new one
229     // The group will be created only for constraints, not for features
230     if (!aConstraint) return false;
231     std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(aConstraint);
232     if (!aWP)
233       return false;
234     SketchSolver_Group* aGroup = new SketchSolver_Group(aWP);
235     if (!aGroup->changeConstraint(aConstraint)) {
236       delete aGroup;
237       return false;
238     }
239     myGroups.push_back(aGroup);
240     return true;
241   } else if (aGroups.size() == 1) {  // Only one group => add feature into it
242     GroupID aGroupId = *(aGroups.begin());
243     std::list<SketchSolver_Group*>::iterator aGroupIter;
244     for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
245       if ((*aGroupIter)->getId() == aGroupId) {
246         // If the group is empty, the feature is not added (the constraint only)
247         if (!aConstraint && !(*aGroupIter)->isEmpty())
248           return (*aGroupIter)->updateFeature(theFeature);
249         return (*aGroupIter)->changeConstraint(aConstraint);
250       }
251   } else if (aGroups.size() > 1) {  // Several groups applicable for this feature => need to merge them
252     std::set<GroupID>::const_iterator aGroupsIter = aGroups.begin();
253
254     // Search first group
255     std::list<SketchSolver_Group*>::iterator aFirstGroupIter;
256     for (aFirstGroupIter = myGroups.begin(); aFirstGroupIter != myGroups.end(); aFirstGroupIter++)
257       if ((*aFirstGroupIter)->getId() == *aGroupsIter)
258         break;
259     if (aFirstGroupIter == myGroups.end())
260       return false;
261
262     // Append other groups to the first one
263     std::list<SketchSolver_Group*>::iterator anOtherGroupIter = aFirstGroupIter;
264     ++anOtherGroupIter;
265     for (aGroupsIter++; aGroupsIter != aGroups.end(); aGroupsIter++) {
266       for (; anOtherGroupIter != myGroups.end(); anOtherGroupIter++)
267         if ((*anOtherGroupIter)->getId() == *aGroupsIter)
268           break;
269       if (anOtherGroupIter == myGroups.end()) {  // Group disappears
270         anOtherGroupIter = aFirstGroupIter;
271         ++anOtherGroupIter;
272         continue;
273       }
274
275       (*aFirstGroupIter)->mergeGroups(**anOtherGroupIter);
276       std::list<SketchSolver_Group*>::iterator aRemoveIt = anOtherGroupIter++;
277       delete *aRemoveIt;
278       myGroups.erase(aRemoveIt);
279     }
280
281     if (aConstraint)
282       (*aFirstGroupIter)->changeConstraint(aConstraint);
283     else
284       (*aFirstGroupIter)->updateFeature(theFeature);
285     // groups are merged => need to resolve them
286     return true;
287   }
288
289   // Something goes wrong
290   return false;
291 }
292
293 // ============================================================================
294 //  Function: moveEntity
295 //  Purpose:  update element moved on the sketch, which is used by constraints
296 // ============================================================================
297 void SketchSolver_Manager::moveEntity(std::shared_ptr<SketchPlugin_Feature> theFeature)
298 {
299   bool isMoved = false;
300   std::list<SketchSolver_Group*>::iterator aGroupIt = myGroups.begin();
301   for (; aGroupIt != myGroups.end(); aGroupIt++)
302     if (!(*aGroupIt)->isEmpty() && (*aGroupIt)->isInteract(theFeature)) {
303       (*aGroupIt)->moveFeature(theFeature);
304       isMoved = true;
305     }
306
307   if (!isMoved && theFeature->getKind() == SketchPlugin_Arc::ID()) {
308     // Workaround to move arc.
309     // If the arc has not been constrained, we will push it into empty group and apply movement.
310     for (aGroupIt = myGroups.begin(); aGroupIt != myGroups.end(); aGroupIt++)
311       if ((*aGroupIt)->isEmpty())
312         (*aGroupIt)->moveFeature(theFeature);
313   }
314 }
315
316 // ============================================================================
317 //  Function: findGroups
318 //  Purpose:  search groups of entities interacting with given feature
319 // ============================================================================
320 void SketchSolver_Manager::findGroups(
321     std::shared_ptr<SketchPlugin_Feature> theFeature,
322     std::set<GroupID>& theGroupIDs) const
323 {
324   std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(theFeature);
325
326   SketchSolver_Group* anEmptyGroup = 0;  // appropriate empty group for specified constraint
327   std::list<SketchSolver_Group*>::const_iterator aGroupIter;
328   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
329     if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theFeature)) {
330       if (!(*aGroupIter)->isEmpty())
331         theGroupIDs.insert((*aGroupIter)->getId());
332       else if (!anEmptyGroup)
333         anEmptyGroup = *aGroupIter;
334     }
335
336   // When only empty group is found, use it
337   if (anEmptyGroup && theGroupIDs.empty())
338     theGroupIDs.insert(anEmptyGroup->getId());
339 }
340
341 // ============================================================================
342 //  Function: findWorkplane
343 //  Purpose:  search workplane containing given feature
344 // ============================================================================
345 std::shared_ptr<ModelAPI_CompositeFeature> SketchSolver_Manager
346 ::findWorkplane(std::shared_ptr<SketchPlugin_Feature> theFeature) const
347 {
348   // Already verified workplanes
349   std::set<std::shared_ptr<ModelAPI_CompositeFeature> > aVerified;
350
351   std::list<SketchSolver_Group*>::const_iterator aGroupIter;
352   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
353     std::shared_ptr<ModelAPI_CompositeFeature> aWP = (*aGroupIter)->getWorkplane();
354     if (aVerified.find(aWP) != aVerified.end())
355       continue;
356
357     DataPtr aData = aWP->data();
358     if (aData->isValid()) {
359       std::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures = std::dynamic_pointer_cast<
360           ModelAPI_AttributeRefList>(aData->attribute(SketchPlugin_Sketch::FEATURES_ID()));
361       std::list<ObjectPtr> aFeaturesList = aWPFeatures->list();
362       std::list<ObjectPtr>::const_iterator anIter;
363       for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
364         if (*anIter == theFeature)
365           return aWP;  // workplane is found
366     }
367     aVerified.insert(aWP);
368   }
369
370   return std::shared_ptr<ModelAPI_CompositeFeature>();
371 }
372
373 // ============================================================================
374 //  Function: resolveConstraints
375 //  Purpose:  change entities according to available constraints
376 // ============================================================================
377 bool SketchSolver_Manager::resolveConstraints(const std::list<SketchSolver_Group*>& theGroups)
378 {
379   bool needToUpdate = false;
380   const std::list<SketchSolver_Group*>& aGroupsToResolve = theGroups.empty() ? myGroups : theGroups;
381   std::list<SketchSolver_Group*>::const_iterator aGroupIter = aGroupsToResolve.begin();
382   for (; aGroupIter != aGroupsToResolve.end(); aGroupIter++)
383     if ((*aGroupIter)->resolveConstraints())
384       needToUpdate = true;
385   return needToUpdate;
386 }
387
388 bool SketchSolver_Manager::stopSendUpdate() const
389 {
390   // to avoid redisplay of each segment on update by solver one by one in the viewer
391   bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
392   if (isUpdateFlushed) {
393     Events_Loop::loop()->setFlushed(anUpdateEvent, false);
394   }
395   return isUpdateFlushed;
396 }
397
398 void SketchSolver_Manager::allowSendUpdate() const
399 {
400   Events_Loop::loop()->setFlushed(anUpdateEvent, true);
401 }