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