Salome HOME
Movement of arc in ward of sketch solver (improvement #730, issue #1172)
[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   bool isMoved = false;
291   std::vector<SketchSolver_Group*>::iterator aGroupIt = myGroups.begin();
292   for (; aGroupIt != myGroups.end(); aGroupIt++)
293     if (!(*aGroupIt)->isEmpty() && (*aGroupIt)->isInteract(theFeature)) {
294       (*aGroupIt)->moveFeature(theFeature);
295       isMoved = true;
296     }
297
298   if (!isMoved && theFeature->getKind() == SketchPlugin_Arc::ID()) {
299     // Workaround to move arc.
300     // If the arc has not been constrained, we will push it into empty group and apply movement.
301     for (aGroupIt = myGroups.begin(); aGroupIt != myGroups.end(); aGroupIt++)
302       if ((*aGroupIt)->isEmpty())
303         (*aGroupIt)->moveFeature(theFeature);
304   }
305 }
306
307 // ============================================================================
308 //  Function: findGroups
309 //  Purpose:  search groups of entities interacting with given feature
310 // ============================================================================
311 void SketchSolver_Manager::findGroups(
312     std::shared_ptr<SketchPlugin_Feature> theFeature,
313     std::set<GroupID>& theGroupIDs) const
314 {
315   std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(theFeature);
316
317   SketchSolver_Group* anEmptyGroup = 0;  // appropriate empty group for specified constraint
318   std::vector<SketchSolver_Group*>::const_iterator aGroupIter;
319   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
320     if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theFeature)) {
321       if (!(*aGroupIter)->isEmpty())
322         theGroupIDs.insert((*aGroupIter)->getId());
323       else if (!anEmptyGroup)
324         anEmptyGroup = *aGroupIter;
325     }
326
327   // When only empty group is found, use it
328   if (anEmptyGroup && theGroupIDs.empty())
329     theGroupIDs.insert(anEmptyGroup->getId());
330 }
331
332 // ============================================================================
333 //  Function: findWorkplane
334 //  Purpose:  search workplane containing given feature
335 // ============================================================================
336 std::shared_ptr<ModelAPI_CompositeFeature> SketchSolver_Manager
337 ::findWorkplane(std::shared_ptr<SketchPlugin_Feature> theFeature) const
338 {
339   // Already verified workplanes
340   std::set<std::shared_ptr<ModelAPI_CompositeFeature> > aVerified;
341
342   std::vector<SketchSolver_Group*>::const_iterator aGroupIter;
343   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
344     std::shared_ptr<ModelAPI_CompositeFeature> aWP = (*aGroupIter)->getWorkplane();
345     if (aVerified.find(aWP) != aVerified.end())
346       continue;
347
348     DataPtr aData = aWP->data();
349     if (aData->isValid()) {
350       std::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures = std::dynamic_pointer_cast<
351           ModelAPI_AttributeRefList>(aData->attribute(SketchPlugin_Sketch::FEATURES_ID()));
352       std::list<ObjectPtr> aFeaturesList = aWPFeatures->list();
353       std::list<ObjectPtr>::const_iterator anIter;
354       for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
355         if (*anIter == theFeature)
356           return aWP;  // workplane is found
357     }
358     aVerified.insert(aWP);
359   }
360
361   return std::shared_ptr<ModelAPI_CompositeFeature>();
362 }
363
364 // ============================================================================
365 //  Function: resolveConstraints
366 //  Purpose:  change entities according to available constraints
367 // ============================================================================
368 bool SketchSolver_Manager::resolveConstraints()
369 {
370   bool needToUpdate = false;
371   std::vector<SketchSolver_Group*>::iterator aGroupIter;
372   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
373     if ((*aGroupIter)->resolveConstraints())
374       needToUpdate = true;
375   return needToUpdate;
376 }
377
378 bool SketchSolver_Manager::stopSendUpdate() const
379 {
380   // to avoid redisplay of each segment on update by solver one by one in the viewer
381   bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
382   if (isUpdateFlushed) {
383     Events_Loop::loop()->setFlushed(anUpdateEvent, false);
384   }
385   return isUpdateFlushed;
386 }
387
388 void SketchSolver_Manager::allowSendUpdate() const
389 {
390   Events_Loop::loop()->setFlushed(anUpdateEvent, true);
391 }