Salome HOME
SketchSolver Refactoring: Eliminate SolveSpace as a sketch solver.
[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 void SketchSolver_Manager::setBuilder(BuilderPtr theBuilder)
66 {
67   myBuilder = theBuilder;
68 }
69
70 BuilderPtr SketchSolver_Manager::builder()
71 {
72   return myBuilder;
73 }
74
75 bool SketchSolver_Manager::groupMessages()
76 {
77   return true;
78 }
79
80 // ============================================================================
81 //  Function: processEvent
82 //  Purpose:  listen the event loop and process the message
83 // ============================================================================
84 void SketchSolver_Manager::processEvent(
85   const std::shared_ptr<Events_Message>& theMessage)
86 {
87   static const Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
88   static const Events_ID aSketchPreparedEvent = Events_Loop::eventByName(EVENT_SKETCH_PREPARED);
89   // sketch is prepared for resolve: all the needed events
90   // are collected and must be processed by the solver
91   if (theMessage->eventID() == aSketchPreparedEvent) {
92     flushGrouped(anUpdateEvent);
93     return;
94   }
95
96   if (myIsComputed)
97     return;
98   myIsComputed = true;
99
100   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)
101       || theMessage->eventID() == anUpdateEvent
102       || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED)) {
103     std::shared_ptr<ModelAPI_ObjectUpdatedMessage> anUpdateMsg =
104         std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
105     std::set<ObjectPtr> aFeatures = anUpdateMsg->objects();
106
107     bool isUpdateFlushed = stopSendUpdate();
108
109     bool isMovedEvt = theMessage->eventID()
110           == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED);
111
112     // Shows that the message has at least one feature applicable for solver
113     bool hasProperFeature = false;
114
115     // update sketch features only
116     std::set<ObjectPtr>::iterator aFeatIter;
117     for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
118       std::shared_ptr<SketchPlugin_Feature> aFeature =
119           std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
120       if (!aFeature || aFeature->isMacro())
121         continue;
122
123       hasProperFeature = updateFeature(aFeature, isMovedEvt) || hasProperFeature;
124     }
125
126     if (isMovedEvt && !hasProperFeature) {
127       // in this iteration it will compute nothing, so, no problem with recursion
128       // it is important that solver flushes signal updated after processing move signal as there
129       // is optimization that relies on this update, might be found by key "optimization"
130       myIsComputed = false;
131     }
132
133     // Solve the set of constraints
134     bool needToUpdate = resolveConstraints();
135
136     // Features may be updated => now send events, but for all changed at once
137     if (isUpdateFlushed)
138       allowSendUpdate();
139
140     myIsComputed = false;
141
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.
152     // The constraint groups should be updated when an object removed from Sketch
153     std::set<std::string>::const_iterator aFGrIter;
154     for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
155       if (aFGrIter->compare(ModelAPI_ResultConstruction::group()) == 0 ||
156           aFGrIter->compare(ModelAPI_Feature::group()) == 0)
157         break;
158
159     if (aFGrIter != aFeatureGroups.end()) {
160       std::list<SketchGroupPtr>::iterator aGroupIter = myGroups.begin();
161       while (aGroupIter != myGroups.end()) {
162         if (!(*aGroupIter)->isWorkplaneValid()) {  // the group should be removed
163           std::list<SketchGroupPtr>::iterator aRemoveIt = aGroupIter++;
164           myGroups.erase(aRemoveIt);
165           continue;
166         }
167
168         (*aGroupIter)->repairConsistency();
169         ++aGroupIter;
170       }
171
172       resolveConstraints();
173     }
174     myIsComputed = false;
175   }
176 }
177
178 // ============================================================================
179 //  Function: changeConstraintOrEntity
180 //  Purpose:  create/update the constraint or the feature and place it into appropriate group
181 // ============================================================================
182 bool SketchSolver_Manager::updateFeature(std::shared_ptr<SketchPlugin_Feature> theFeature,
183                                          bool theMoved)
184 {
185   // Check feature validity and find a group to place it.
186   // If the feature is not valid, the returned group will be empty.
187   // This will protect to deal with wrong (not fully initialized) features.
188   SketchGroupPtr aGroup = findGroup(theFeature);
189   if (!aGroup)
190     return false;
191   aGroup->blockEvents(true);
192
193   std::shared_ptr<SketchPlugin_Constraint> aConstraint =
194       std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
195
196   bool isOk = false;
197   if (aConstraint)
198     isOk = aGroup->changeConstraint(aConstraint);
199   else if (theMoved)
200     isOk = aGroup->moveFeature(theFeature);
201   else
202     isOk = aGroup->updateFeature(theFeature);
203   return isOk;
204 }
205
206 // ============================================================================
207 //  Function: findGroup
208 //  Purpose:  search groups of entities interacting with given feature
209 // ============================================================================
210 SketchGroupPtr SketchSolver_Manager::findGroup(
211     std::shared_ptr<SketchPlugin_Feature> theFeature)
212 {
213   if (!isFeatureValid(theFeature))
214     return SketchGroupPtr(); // do not process wrong features
215
216   // Obtain sketch, containing the feature
217   CompositeFeaturePtr aSketch;
218   const std::set<AttributePtr>& aRefsList = theFeature->data()->refsToMe();
219   std::set<AttributePtr>::const_iterator aRefIt = aRefsList.begin();
220   for (; aRefIt != aRefsList.end(); ++aRefIt) {
221     FeaturePtr anOwner = ModelAPI_Feature::feature((*aRefIt)->owner());
222     if (anOwner && anOwner->getKind() == SketchPlugin_Sketch::ID()) {
223       aSketch = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(anOwner);
224       break;
225     }
226   }
227
228   if (!aSketch)
229     return SketchGroupPtr(); // not a sketch's feature
230
231   std::list<SketchGroupPtr>::const_iterator aGroupIt;
232   for (aGroupIt = myGroups.begin(); aGroupIt != myGroups.end(); ++aGroupIt)
233     if ((*aGroupIt)->getWorkplane() == aSketch)
234       return *aGroupIt;
235
236   // group for the sketch does not created yet
237   SketchGroupPtr aNewGroup = SketchGroupPtr(new SketchSolver_Group(aSketch));
238   myGroups.push_back(aNewGroup);
239   return aNewGroup;
240 }
241
242 // ============================================================================
243 //  Function: resolveConstraints
244 //  Purpose:  change entities according to available constraints
245 // ============================================================================
246 bool SketchSolver_Manager::resolveConstraints()
247 {
248   bool needToUpdate = false;
249   std::list<SketchGroupPtr>::const_iterator aGroupIter = myGroups.begin();
250   for (; aGroupIter != myGroups.end(); ++aGroupIter) {
251     if ((*aGroupIter)->resolveConstraints())
252       needToUpdate = true;
253     (*aGroupIter)->blockEvents(false);
254   }
255   return needToUpdate;
256 }
257
258 bool SketchSolver_Manager::stopSendUpdate() const
259 {
260 static const Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
261   // to avoid redisplay of each segment on update by solver one by one in the viewer
262   bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
263   if (isUpdateFlushed) {
264     Events_Loop::loop()->setFlushed(anUpdateEvent, false);
265   }
266   return isUpdateFlushed;
267 }
268
269 void SketchSolver_Manager::allowSendUpdate() const
270 {
271   Events_Loop::loop()->setFlushed(Events_Loop::eventByName(EVENT_OBJECT_UPDATED), true);
272 }