Salome HOME
Merge remote-tracking branch 'remotes/origin/master' into azv/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 #include "SketchSolver_Error.h"
9
10 #include <Events_Loop.h>
11 #include <ModelAPI_Events.h>
12 #include <ModelAPI_ResultConstruction.h>
13 #include <ModelAPI_Session.h>
14 #include <ModelAPI_Validator.h>
15 #include <SketchPlugin_Sketch.h>
16
17 /// Global constraint manager object
18 static SketchSolver_Manager* myManager = SketchSolver_Manager::instance();
19
20 /// \brief Verifies is the feature valid
21 static bool isFeatureValid(FeaturePtr theFeature)
22 {
23   if (!theFeature || !theFeature->data() || !theFeature->data()->isValid())
24     return false;
25
26   SessionPtr aMgr = ModelAPI_Session::get();
27   ModelAPI_ValidatorsFactory* aFactory = aMgr->validators();
28   return aFactory->validate(theFeature);
29 }
30
31
32
33 // ========================================================
34 // ========= SketchSolver_Manager ===============
35 // ========================================================
36 SketchSolver_Manager* SketchSolver_Manager::instance()
37 {
38   static SketchSolver_Manager* mySelf = 0; // Self pointer to implement singleton functionality
39   if (!mySelf)
40     mySelf = new SketchSolver_Manager();
41   return mySelf;
42 }
43
44 SketchSolver_Manager::SketchSolver_Manager()
45 {
46   myGroups.clear();
47   myIsComputed = false;
48
49   // Register in event loop
50   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
51   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
52   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
53   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_MOVED));
54
55   ////Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_SOLVER_FAILED));
56   ////Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_SOLVER_REPAIRED));
57   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_SKETCH_PREPARED));
58 }
59
60 SketchSolver_Manager::~SketchSolver_Manager()
61 {
62   myGroups.clear();
63 }
64
65 bool SketchSolver_Manager::groupMessages()
66 {
67   return true;
68 }
69
70 // ============================================================================
71 //  Function: processEvent
72 //  Purpose:  listen the event loop and process the message
73 // ============================================================================
74 void SketchSolver_Manager::processEvent(
75   const std::shared_ptr<Events_Message>& theMessage)
76 {
77   static const Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
78   static const Events_ID aSketchPreparedEvent = Events_Loop::eventByName(EVENT_SKETCH_PREPARED);
79   // sketch is prepared for resolve: all the needed events
80   // are collected and must be processed by the solver
81   if (theMessage->eventID() == aSketchPreparedEvent) {
82     flushGrouped(anUpdateEvent);
83     return;
84   }
85
86   if (myIsComputed)
87     return;
88   myIsComputed = true;
89
90   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)
91       || theMessage->eventID() == anUpdateEvent
92       || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED)) {
93     std::shared_ptr<ModelAPI_ObjectUpdatedMessage> anUpdateMsg =
94         std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
95     std::set<ObjectPtr> aFeatures = anUpdateMsg->objects();
96
97     bool isUpdateFlushed = stopSendUpdate();
98
99     bool isMovedEvt = theMessage->eventID()
100           == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED);
101
102     // Shows that the message has at least one feature applicable for solver
103     bool hasProperFeature = false;
104
105     // update sketch features only
106     std::set<ObjectPtr>::iterator aFeatIter;
107     for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
108       std::shared_ptr<SketchPlugin_Feature> aFeature =
109           std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
110       if (!aFeature || aFeature->isMacro())
111         continue;
112
113       hasProperFeature = updateFeature(aFeature, isMovedEvt) || hasProperFeature;
114     }
115
116     if (isMovedEvt && !hasProperFeature) {
117       // in this iteration it will compute nothing, so, no problem with recursion
118       // it is important that solver flushes signal updated after processing move signal as there
119       // is optimization that relies on this update, might be found by key "optimization"
120       myIsComputed = false;
121     }
122
123     // Solve the set of constraints
124     bool needToUpdate = resolveConstraints();
125
126     // Features may be updated => now send events, but for all changed at once
127     if (isUpdateFlushed)
128       allowSendUpdate();
129
130     myIsComputed = false;
131
132     // send update for movement in any case
133     if (needToUpdate || isMovedEvt)
134       Events_Loop::loop()->flush(anUpdateEvent);
135
136   } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
137     std::shared_ptr<ModelAPI_ObjectDeletedMessage> aDeleteMsg =
138       std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
139     const std::set<std::string>& aFeatureGroups = aDeleteMsg->groups();
140
141     // Find SketchPlugin_Sketch::ID() in groups.
142     // The constraint groups should be updated when an object removed from Sketch
143     std::set<std::string>::const_iterator aFGrIter;
144     for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
145       if (aFGrIter->compare(ModelAPI_ResultConstruction::group()) == 0 ||
146           aFGrIter->compare(ModelAPI_Feature::group()) == 0)
147         break;
148
149     if (aFGrIter != aFeatureGroups.end()) {
150       std::list<SketchGroupPtr>::iterator aGroupIter = myGroups.begin();
151       while (aGroupIter != myGroups.end()) {
152         if (!(*aGroupIter)->isWorkplaneValid()) {  // the group should be removed
153           std::list<SketchGroupPtr>::iterator aRemoveIt = aGroupIter++;
154           myGroups.erase(aRemoveIt);
155           continue;
156         }
157
158         (*aGroupIter)->repairConsistency();
159         ++aGroupIter;
160       }
161
162       resolveConstraints();
163     }
164     myIsComputed = false;
165   }
166 }
167
168 // ============================================================================
169 //  Function: changeConstraintOrEntity
170 //  Purpose:  create/update the constraint or the feature and place it into appropriate group
171 // ============================================================================
172 bool SketchSolver_Manager::updateFeature(std::shared_ptr<SketchPlugin_Feature> theFeature,
173                                          bool theMoved)
174 {
175   // Check feature validity and find a group to place it.
176   // If the feature is not valid, the returned group will be empty.
177   // This will protect to deal with wrong (not fully initialized) features.
178   SketchGroupPtr aGroup = findGroup(theFeature);
179   if (!aGroup)
180     return false;
181   aGroup->blockEvents(true);
182
183   std::shared_ptr<SketchPlugin_Constraint> aConstraint =
184       std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
185
186   bool isOk = false;
187   if (aConstraint)
188     isOk = aGroup->changeConstraint(aConstraint);
189   else if (theMoved)
190     isOk = aGroup->moveFeature(theFeature);
191   else
192     isOk = aGroup->updateFeature(theFeature);
193   return isOk;
194 }
195
196 // ============================================================================
197 //  Function: findGroup
198 //  Purpose:  search groups of entities interacting with given feature
199 // ============================================================================
200 SketchGroupPtr SketchSolver_Manager::findGroup(
201     std::shared_ptr<SketchPlugin_Feature> theFeature)
202 {
203   if (!isFeatureValid(theFeature))
204     return SketchGroupPtr(); // do not process wrong features
205
206   // Obtain sketch, containing the feature
207   CompositeFeaturePtr aSketch;
208   const std::set<AttributePtr>& aRefsList = theFeature->data()->refsToMe();
209   std::set<AttributePtr>::const_iterator aRefIt = aRefsList.begin();
210   for (; aRefIt != aRefsList.end(); ++aRefIt) {
211     FeaturePtr anOwner = ModelAPI_Feature::feature((*aRefIt)->owner());
212     if (anOwner && anOwner->getKind() == SketchPlugin_Sketch::ID()) {
213       aSketch = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(anOwner);
214       break;
215     }
216   }
217
218   if (!aSketch)
219     return SketchGroupPtr(); // not a sketch's feature
220
221   std::list<SketchGroupPtr>::const_iterator aGroupIt;
222   for (aGroupIt = myGroups.begin(); aGroupIt != myGroups.end(); ++aGroupIt)
223     if ((*aGroupIt)->getWorkplane() == aSketch)
224       return *aGroupIt;
225
226   // group for the sketch does not created yet
227   SketchGroupPtr aNewGroup = SketchGroupPtr(new SketchSolver_Group(aSketch));
228   myGroups.push_back(aNewGroup);
229   return aNewGroup;
230 }
231
232 // ============================================================================
233 //  Function: resolveConstraints
234 //  Purpose:  change entities according to available constraints
235 // ============================================================================
236 bool SketchSolver_Manager::resolveConstraints()
237 {
238   bool needToUpdate = false;
239   std::list<SketchGroupPtr>::const_iterator aGroupIter = myGroups.begin();
240   for (; aGroupIter != myGroups.end(); ++aGroupIter) {
241     if ((*aGroupIter)->resolveConstraints())
242       needToUpdate = true;
243     (*aGroupIter)->blockEvents(false);
244   }
245   return needToUpdate;
246 }
247
248 bool SketchSolver_Manager::stopSendUpdate() const
249 {
250 static const Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
251   // to avoid redisplay of each segment on update by solver one by one in the viewer
252   bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
253   if (isUpdateFlushed) {
254     Events_Loop::loop()->setFlushed(anUpdateEvent, false);
255   }
256   return isUpdateFlushed;
257 }
258
259 void SketchSolver_Manager::allowSendUpdate() const
260 {
261   Events_Loop::loop()->setFlushed(Events_Loop::eventByName(EVENT_OBJECT_UPDATED), true);
262 }