Salome HOME
Add copyright header according to request of CEA from 06.06.2017
[modules/shaper.git] / src / SketchSolver / SketchSolver_Manager.cpp
1 // Copyright (C) 2014-2017  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
18 //
19
20 #include "SketchSolver_Manager.h"
21 #include "SketchSolver_Error.h"
22
23 #include <Events_Loop.h>
24 #include <ModelAPI_Events.h>
25 #include <ModelAPI_ResultConstruction.h>
26 #include <ModelAPI_Session.h>
27 #include <ModelAPI_Validator.h>
28 #include <SketchPlugin_Sketch.h>
29
30 /// Global constraint manager object
31 static SketchSolver_Manager* myManager = SketchSolver_Manager::instance();
32
33 /// \brief Verifies is the feature valid
34 static bool isFeatureValid(FeaturePtr theFeature)
35 {
36   if (!theFeature || !theFeature->data() || !theFeature->data()->isValid())
37     return false;
38
39   SessionPtr aMgr = ModelAPI_Session::get();
40   ModelAPI_ValidatorsFactory* aFactory = aMgr->validators();
41   return aFactory->validate(theFeature);
42 }
43
44
45
46 // ========================================================
47 // ========= SketchSolver_Manager ===============
48 // ========================================================
49 SketchSolver_Manager* SketchSolver_Manager::instance()
50 {
51   static SketchSolver_Manager* mySelf = 0; // Self pointer to implement singleton functionality
52   if (!mySelf)
53     mySelf = new SketchSolver_Manager();
54   return mySelf;
55 }
56
57 SketchSolver_Manager::SketchSolver_Manager()
58 {
59   myGroups.clear();
60   myIsComputed = false;
61
62   // Register in event loop
63   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
64   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
65   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
66   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_MOVED));
67
68   ////Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_SOLVER_FAILED));
69   ////Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_SOLVER_REPAIRED));
70   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_SKETCH_PREPARED));
71 }
72
73 SketchSolver_Manager::~SketchSolver_Manager()
74 {
75   myGroups.clear();
76 }
77
78 bool SketchSolver_Manager::groupMessages()
79 {
80   return true;
81 }
82
83 // ============================================================================
84 //  Function: processEvent
85 //  Purpose:  listen the event loop and process the message
86 // ============================================================================
87 void SketchSolver_Manager::processEvent(
88   const std::shared_ptr<Events_Message>& theMessage)
89 {
90   bool needToResolve = false;
91   bool isUpdateFlushed = false;
92   bool isMovedEvt = false;
93
94   static const Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
95   static const Events_ID aSketchPreparedEvent = Events_Loop::eventByName(EVENT_SKETCH_PREPARED);
96   // sketch is prepared for resolve: all the needed events
97   // are collected and must be processed by the solver
98   if (theMessage->eventID() == aSketchPreparedEvent) {
99     flushGrouped(anUpdateEvent);
100     needToResolve = true;
101   }
102
103   if (myIsComputed)
104     return;
105   myIsComputed = true;
106
107   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)
108       || theMessage->eventID() == anUpdateEvent
109       || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED)) {
110     std::shared_ptr<ModelAPI_ObjectUpdatedMessage> anUpdateMsg =
111         std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
112     std::set<ObjectPtr> aFeatures = anUpdateMsg->objects();
113
114     isUpdateFlushed = stopSendUpdate();
115
116     isMovedEvt = theMessage->eventID()
117           == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED);
118
119     // Shows that the message has at least one feature applicable for solver
120     bool hasProperFeature = false;
121
122     // update sketch features only
123     std::set<ObjectPtr>::iterator aFeatIter;
124     for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
125       std::shared_ptr<SketchPlugin_Feature> aFeature =
126           std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
127       if (!aFeature || aFeature->isMacro())
128         continue;
129
130       hasProperFeature = updateFeature(aFeature, isMovedEvt) || hasProperFeature;
131     }
132
133     if (isMovedEvt && hasProperFeature)
134       needToResolve = true;
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     myIsComputed = false;
163   }
164
165   // resolve constraints if needed
166   bool needToUpdate = needToResolve && resolveConstraints();
167   releaseFeaturesIfEventsBlocked();
168
169   // Features may be updated => now send events, but for all changed at once
170   if (isUpdateFlushed)
171     allowSendUpdate();
172
173   myIsComputed = false;
174
175   // send update for movement in any case
176   if (needToUpdate || isMovedEvt)
177     Events_Loop::loop()->flush(anUpdateEvent);
178 }
179
180 // ============================================================================
181 //  Function: changeConstraintOrEntity
182 //  Purpose:  create/update the constraint or the feature and place it into appropriate group
183 // ============================================================================
184 bool SketchSolver_Manager::updateFeature(std::shared_ptr<SketchPlugin_Feature> theFeature,
185                                          bool theMoved)
186 {
187   // Check feature validity and find a group to place it.
188   // If the feature is not valid, the returned group will be empty.
189   // This will protect to deal with wrong (not fully initialized) features.
190   SketchGroupPtr aGroup = findGroup(theFeature);
191   if (!aGroup)
192     return false;
193   aGroup->blockEvents(true);
194
195   std::shared_ptr<SketchPlugin_Constraint> aConstraint =
196       std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
197
198   bool isOk = false;
199   if (aConstraint)
200     isOk = aGroup->changeConstraint(aConstraint);
201   else if (theMoved)
202     isOk = aGroup->moveFeature(theFeature);
203   else
204     isOk = aGroup->updateFeature(theFeature);
205   return isOk;
206 }
207
208 // ============================================================================
209 //  Function: findGroup
210 //  Purpose:  search groups of entities interacting with given feature
211 // ============================================================================
212 SketchGroupPtr SketchSolver_Manager::findGroup(
213     std::shared_ptr<SketchPlugin_Feature> theFeature)
214 {
215   if (!isFeatureValid(theFeature))
216     return SketchGroupPtr(); // do not process wrong features
217
218   // Obtain sketch, containing the feature
219   CompositeFeaturePtr aSketch;
220   const std::set<AttributePtr>& aRefsList = theFeature->data()->refsToMe();
221   std::set<AttributePtr>::const_iterator aRefIt = aRefsList.begin();
222   for (; aRefIt != aRefsList.end(); ++aRefIt) {
223     FeaturePtr anOwner = ModelAPI_Feature::feature((*aRefIt)->owner());
224     if (anOwner && anOwner->getKind() == SketchPlugin_Sketch::ID()) {
225       aSketch = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(anOwner);
226       break;
227     }
228   }
229
230   if (!aSketch)
231     return SketchGroupPtr(); // not a sketch's feature
232
233   std::list<SketchGroupPtr>::const_iterator aGroupIt;
234   for (aGroupIt = myGroups.begin(); aGroupIt != myGroups.end(); ++aGroupIt)
235     if ((*aGroupIt)->getWorkplane() == aSketch)
236       return *aGroupIt;
237
238   // group for the sketch does not created yet
239   SketchGroupPtr aNewGroup = SketchGroupPtr(new SketchSolver_Group(aSketch));
240   myGroups.push_back(aNewGroup);
241   return aNewGroup;
242 }
243
244 // ============================================================================
245 //  Function: resolveConstraints
246 //  Purpose:  change entities according to available constraints
247 // ============================================================================
248 bool SketchSolver_Manager::resolveConstraints()
249 {
250   bool needToUpdate = false;
251   std::list<SketchGroupPtr>::const_iterator aGroupIter = myGroups.begin();
252   for (; aGroupIter != myGroups.end(); ++aGroupIter) {
253     if ((*aGroupIter)->resolveConstraints())
254       needToUpdate = true;
255   }
256   return needToUpdate;
257 }
258
259 void SketchSolver_Manager::releaseFeaturesIfEventsBlocked() const
260 {
261   std::list<SketchGroupPtr>::const_iterator aGroupIter = myGroups.begin();
262   for (; aGroupIter != myGroups.end(); ++aGroupIter)
263     (*aGroupIter)->blockEvents(false);
264 }
265
266 bool SketchSolver_Manager::stopSendUpdate() const
267 {
268 static const Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
269   // to avoid redisplay of each segment on update by solver one by one in the viewer
270   bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
271   if (isUpdateFlushed) {
272     Events_Loop::loop()->setFlushed(anUpdateEvent, false);
273   }
274   return isUpdateFlushed;
275 }
276
277 void SketchSolver_Manager::allowSendUpdate() const
278 {
279   Events_Loop::loop()->setFlushed(Events_Loop::eventByName(EVENT_OBJECT_UPDATED), true);
280 }