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