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