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