Salome HOME
Task 2.4. Ability to modify the radius of circles and arcs of circle with the mouse
[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 <GeomDataAPI_Point2D.h>
12 #include <ModelAPI_Events.h>
13 #include <ModelAPI_ResultConstruction.h>
14 #include <ModelAPI_Session.h>
15 #include <ModelAPI_Validator.h>
16 #include <SketchPlugin_Constraint.h>
17 #include <SketchPlugin_Sketch.h>
18
19 /// Global constraint manager object
20 static SketchSolver_Manager* myManager = SketchSolver_Manager::instance();
21
22 /// \brief Verifies is the feature valid
23 static bool isFeatureValid(FeaturePtr theFeature)
24 {
25   if (!theFeature || !theFeature->data() || !theFeature->data()->isValid())
26     return false;
27
28   SessionPtr aMgr = ModelAPI_Session::get();
29   ModelAPI_ValidatorsFactory* aFactory = aMgr->validators();
30   return aFactory->validate(theFeature);
31 }
32
33
34
35 // ========================================================
36 // ========= SketchSolver_Manager ===============
37 // ========================================================
38 SketchSolver_Manager* SketchSolver_Manager::instance()
39 {
40   static SketchSolver_Manager* mySelf = 0; // Self pointer to implement singleton functionality
41   if (!mySelf)
42     mySelf = new SketchSolver_Manager();
43   return mySelf;
44 }
45
46 SketchSolver_Manager::SketchSolver_Manager()
47 {
48   myGroups.clear();
49   myIsComputed = false;
50
51   // Register in event loop
52   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
53   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
54   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
55   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_MOVED));
56
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   bool needToResolve = false;
78   bool isUpdateFlushed = false;
79   bool isMovedEvt = false;
80
81   static const Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
82   static const Events_ID aSketchPreparedEvent = Events_Loop::eventByName(EVENT_SKETCH_PREPARED);
83   // sketch is prepared for resolve: all the needed events
84   // are collected and must be processed by the solver
85   if (theMessage->eventID() == aSketchPreparedEvent) {
86     flushGrouped(anUpdateEvent);
87     needToResolve = true;
88   }
89
90   if (myIsComputed)
91     return;
92   myIsComputed = true;
93
94 #ifdef SUPPORT_NEW_MOVE
95   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)
96       || theMessage->eventID() == anUpdateEvent) {
97 #else
98   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)
99       || theMessage->eventID() == anUpdateEvent
100       || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED)) {
101 #endif
102     std::shared_ptr<ModelAPI_ObjectUpdatedMessage> anUpdateMsg =
103         std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
104     std::set<ObjectPtr> aFeatures = anUpdateMsg->objects();
105
106     isUpdateFlushed = stopSendUpdate();
107
108 #ifndef SUPPORT_NEW_MOVE
109     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 #endif
115
116     // update sketch features only
117     std::set<ObjectPtr>::iterator aFeatIter;
118     for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
119       std::shared_ptr<SketchPlugin_Feature> aFeature =
120           std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
121       if (!aFeature || aFeature->isMacro())
122         continue;
123
124 #ifdef SUPPORT_NEW_MOVE
125       updateFeature(aFeature);
126 #else
127       hasProperFeature = updateFeature(aFeature, isMovedEvt) || hasProperFeature;
128 #endif
129     }
130
131 #ifndef SUPPORT_NEW_MOVE
132     if (isMovedEvt && hasProperFeature)
133       needToResolve = true;
134
135 #else
136   } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED)) {
137     std::shared_ptr<ModelAPI_ObjectMovedMessage> aMoveMsg =
138         std::dynamic_pointer_cast<ModelAPI_ObjectMovedMessage>(theMessage);
139
140     ObjectPtr aMovedObject = aMoveMsg->movedObject();
141     std::shared_ptr<GeomDataAPI_Point2D> aMovedPoint =
142         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aMoveMsg->movedAttribute());
143
144     const std::shared_ptr<GeomAPI_Pnt2d>& aFrom = aMoveMsg->originalPosition();
145     const std::shared_ptr<GeomAPI_Pnt2d>& aTo = aMoveMsg->currentPosition();
146
147     if (aMovedObject) {
148       FeaturePtr aMovedFeature = ModelAPI_Feature::feature(aMovedObject);
149       std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
150           std::dynamic_pointer_cast<SketchPlugin_Feature>(aMovedFeature);
151       if (aSketchFeature && !aSketchFeature->isMacro())
152         needToResolve = moveFeature(aSketchFeature, aFrom, aTo);
153     } else if (aMovedPoint)
154       needToResolve = moveAttribute(aMovedPoint, aFrom, aTo);
155
156 #endif
157   } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
158     std::shared_ptr<ModelAPI_ObjectDeletedMessage> aDeleteMsg =
159       std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
160     const std::set<std::string>& aFeatureGroups = aDeleteMsg->groups();
161
162     // Find SketchPlugin_Sketch::ID() in groups.
163     // The constraint groups should be updated when an object removed from Sketch
164     std::set<std::string>::const_iterator aFGrIter;
165     for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
166       if (aFGrIter->compare(ModelAPI_ResultConstruction::group()) == 0 ||
167           aFGrIter->compare(ModelAPI_Feature::group()) == 0)
168         break;
169
170     if (aFGrIter != aFeatureGroups.end()) {
171       std::list<SketchGroupPtr>::iterator aGroupIter = myGroups.begin();
172       while (aGroupIter != myGroups.end()) {
173         if (!(*aGroupIter)->isWorkplaneValid()) {  // the group should be removed
174           std::list<SketchGroupPtr>::iterator aRemoveIt = aGroupIter++;
175           myGroups.erase(aRemoveIt);
176           continue;
177         }
178
179         (*aGroupIter)->repairConsistency();
180         ++aGroupIter;
181       }
182     }
183     myIsComputed = false;
184   }
185
186   // resolve constraints if needed
187   bool needToUpdate = needToResolve && resolveConstraints();
188   releaseFeaturesIfEventsBlocked();
189
190   // Features may be updated => now send events, but for all changed at once
191   if (isUpdateFlushed)
192     allowSendUpdate();
193
194   myIsComputed = false;
195
196   // send update for movement in any case
197   if (needToUpdate || isMovedEvt)
198     Events_Loop::loop()->flush(anUpdateEvent);
199 }
200
201 // ============================================================================
202 //  Function: updateFeature
203 //  Purpose:  create/update constraint or feature in appropriate group
204 // ============================================================================
205 #ifdef SUPPORT_NEW_MOVE
206 bool SketchSolver_Manager::updateFeature(const std::shared_ptr<SketchPlugin_Feature>& theFeature)
207 #else
208 bool SketchSolver_Manager::updateFeature(std::shared_ptr<SketchPlugin_Feature> theFeature,
209                                          bool theMoved)
210 #endif
211 {
212   // Check feature validity and find a group to place it.
213   // If the feature is not valid, the returned group will be empty.
214   // This will protect to deal with wrong (not fully initialized) features.
215   SketchGroupPtr aGroup = findGroup(theFeature);
216   if (!aGroup)
217     return false;
218   aGroup->blockEvents(true);
219
220   std::shared_ptr<SketchPlugin_Constraint> aConstraint =
221       std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
222
223   bool isOk = false;
224   if (aConstraint)
225     isOk = aGroup->changeConstraint(aConstraint);
226 #ifndef SUPPORT_NEW_MOVE
227   else if (theMoved)
228     isOk = aGroup->moveFeature(theFeature);
229 #endif
230   else
231     isOk = aGroup->updateFeature(theFeature);
232   return isOk;
233 }
234
235 #ifdef SUPPORT_NEW_MOVE
236 // ============================================================================
237 //  Function: moveFeature
238 //  Purpose:  move given feature in appropriate group
239 // ============================================================================
240 bool SketchSolver_Manager::moveFeature(
241     const std::shared_ptr<SketchPlugin_Feature>& theMovedFeature,
242     const std::shared_ptr<GeomAPI_Pnt2d>& theFrom,
243     const std::shared_ptr<GeomAPI_Pnt2d>& theTo)
244 {
245   SketchGroupPtr aGroup = findGroup(theMovedFeature);
246   if (!aGroup)
247     return false;
248
249   aGroup->blockEvents(true);
250   return aGroup->moveFeature(theMovedFeature, theFrom, theTo);
251 }
252
253 // ============================================================================
254 //  Function: moveAttribute
255 //  Purpose:  move given attribute in appropriate group
256 // ============================================================================
257 bool SketchSolver_Manager::moveAttribute(
258     const std::shared_ptr<GeomDataAPI_Point2D>& theMovedAttribute,
259     const std::shared_ptr<GeomAPI_Pnt2d>& theFrom,
260     const std::shared_ptr<GeomAPI_Pnt2d>& theTo)
261 {
262   FeaturePtr anOwner = ModelAPI_Feature::feature(theMovedAttribute->owner());
263   std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
264       std::dynamic_pointer_cast<SketchPlugin_Feature>(anOwner);
265   SketchGroupPtr aGroup;
266   if (aSketchFeature)
267     aGroup = findGroup(aSketchFeature);
268   if (!aGroup)
269     return false;
270
271   aGroup->blockEvents(true);
272   return aGroup->movePoint(theMovedAttribute, theFrom, theTo);
273 }
274 #endif
275
276 // ============================================================================
277 //  Function: findGroup
278 //  Purpose:  search groups of entities interacting with given feature
279 // ============================================================================
280 SketchGroupPtr SketchSolver_Manager::findGroup(
281     std::shared_ptr<SketchPlugin_Feature> theFeature)
282 {
283   if (!isFeatureValid(theFeature))
284     return SketchGroupPtr(); // do not process wrong features
285
286   // Obtain sketch, containing the feature
287   CompositeFeaturePtr aSketch;
288   const std::set<AttributePtr>& aRefsList = theFeature->data()->refsToMe();
289   std::set<AttributePtr>::const_iterator aRefIt = aRefsList.begin();
290   for (; aRefIt != aRefsList.end(); ++aRefIt) {
291     FeaturePtr anOwner = ModelAPI_Feature::feature((*aRefIt)->owner());
292     if (anOwner && anOwner->getKind() == SketchPlugin_Sketch::ID()) {
293       aSketch = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(anOwner);
294       break;
295     }
296   }
297
298   if (!aSketch)
299     return SketchGroupPtr(); // not a sketch's feature
300
301   std::list<SketchGroupPtr>::const_iterator aGroupIt;
302   for (aGroupIt = myGroups.begin(); aGroupIt != myGroups.end(); ++aGroupIt)
303     if ((*aGroupIt)->getWorkplane() == aSketch)
304       return *aGroupIt;
305
306   // group for the sketch does not created yet
307   SketchGroupPtr aNewGroup = SketchGroupPtr(new SketchSolver_Group(aSketch));
308   myGroups.push_back(aNewGroup);
309   return aNewGroup;
310 }
311
312 // ============================================================================
313 //  Function: resolveConstraints
314 //  Purpose:  change entities according to available constraints
315 // ============================================================================
316 bool SketchSolver_Manager::resolveConstraints()
317 {
318   bool needToUpdate = false;
319   std::list<SketchGroupPtr>::const_iterator aGroupIter = myGroups.begin();
320   for (; aGroupIter != myGroups.end(); ++aGroupIter) {
321     if ((*aGroupIter)->resolveConstraints())
322       needToUpdate = true;
323   }
324   return needToUpdate;
325 }
326
327 void SketchSolver_Manager::releaseFeaturesIfEventsBlocked() const
328 {
329   std::list<SketchGroupPtr>::const_iterator aGroupIter = myGroups.begin();
330   for (; aGroupIter != myGroups.end(); ++aGroupIter)
331     (*aGroupIter)->blockEvents(false);
332 }
333
334 bool SketchSolver_Manager::stopSendUpdate() const
335 {
336 static const Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
337   // to avoid redisplay of each segment on update by solver one by one in the viewer
338   bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
339   if (isUpdateFlushed) {
340     Events_Loop::loop()->setFlushed(anUpdateEvent, false);
341   }
342   return isUpdateFlushed;
343 }
344
345 void SketchSolver_Manager::allowSendUpdate() const
346 {
347   Events_Loop::loop()->setFlushed(Events_Loop::eventByName(EVENT_OBJECT_UPDATED), true);
348 }