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