Salome HOME
Task #2997: Show objects with DoF
[modules/shaper.git] / src / SketchSolver / SketchSolver_Manager.cpp
1 // Copyright (C) 2014-2019  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
18 //
19
20 #include "SketchSolver_Manager.h"
21 #include "SketchSolver_Error.h"
22
23 #include <Events_Loop.h>
24 #include <GeomDataAPI_Point2D.h>
25 #include <ModelAPI_Events.h>
26 #include <ModelAPI_ResultConstruction.h>
27 #include <ModelAPI_Session.h>
28 #include <ModelAPI_Validator.h>
29 #include <SketchPlugin_Sketch.h>
30
31 /// Global constraint manager object
32 static SketchSolver_Manager* myManager = SketchSolver_Manager::instance();
33
34 /// \brief Verifies is the feature valid
35 static bool isFeatureValid(FeaturePtr theFeature)
36 {
37   if (!theFeature || !theFeature->data() || !theFeature->data()->isValid())
38     return false;
39
40   SessionPtr aMgr = ModelAPI_Session::get();
41   ModelAPI_ValidatorsFactory* aFactory = aMgr->validators();
42   return aFactory->validate(theFeature);
43 }
44
45 typedef std::map<int, std::shared_ptr<SketchPlugin_Feature>> IndexedFeatureMap;
46
47 static void featuresOrderedByCreation(const std::set<ObjectPtr>& theOriginalFeatures,
48                                       IndexedFeatureMap& theOrderedFeatures)
49 {
50   std::set<ObjectPtr>::iterator aFeatIter = theOriginalFeatures.begin();
51   for (; aFeatIter != theOriginalFeatures.end(); aFeatIter++) {
52     std::shared_ptr<SketchPlugin_Feature> aFeature =
53         std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
54     if (aFeature && !aFeature->isMacro() && aFeature->data() && aFeature->data()->isValid()) {
55       theOrderedFeatures[aFeature->data()->featureId()] = aFeature;
56     }
57   }
58 }
59
60 static void featuresOrderedByType(const std::set<ObjectPtr>& theOriginalFeatures,
61                                   IndexedFeatureMap& theOrderedFeatures,
62                                   CompositeFeaturePtr& theSketch)
63 {
64   int aFeatureIndex = 0;
65   int aConstraintIndex = (int)theOriginalFeatures.size();
66
67   std::set<ObjectPtr>::iterator aFeatIter = theOriginalFeatures.begin();
68   for (; aFeatIter != theOriginalFeatures.end(); aFeatIter++) {
69     std::shared_ptr<SketchPlugin_Feature> aFeature =
70         std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
71     if (aFeature) {
72       if (!aFeature->isMacro() && aFeature->data() && aFeature->data()->isValid()) {
73         std::shared_ptr<SketchPlugin_Constraint> aConstraint =
74             std::dynamic_pointer_cast<SketchPlugin_Constraint>(aFeature);
75         if (aConstraint)
76           theOrderedFeatures[++aConstraintIndex] = aFeature;
77         else
78           theOrderedFeatures[++aFeatureIndex] = aFeature;
79       }
80     }
81     else {
82       CompositeFeaturePtr aSketch =
83           std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aFeatIter);
84       if (aSketch && aSketch->getKind() == SketchPlugin_Sketch::ID())
85         theSketch = aSketch;
86     }
87   }
88 }
89
90
91
92 // ========================================================
93 // ========= SketchSolver_Manager ===============
94 // ========================================================
95 SketchSolver_Manager* SketchSolver_Manager::instance()
96 {
97   static SketchSolver_Manager* mySelf = 0; // Self pointer to implement singleton functionality
98   if (!mySelf)
99     mySelf = new SketchSolver_Manager();
100   return mySelf;
101 }
102
103 SketchSolver_Manager::SketchSolver_Manager()
104 {
105   myGroups.clear();
106   myIsComputed = false;
107
108   // Register in event loop
109   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
110   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
111   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
112   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_MOVED));
113
114   ////Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_SOLVER_FAILED));
115   ////Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_SOLVER_REPAIRED));
116   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_SKETCH_PREPARED));
117   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_GET_DOF_OBJECTS));
118 }
119
120 SketchSolver_Manager::~SketchSolver_Manager()
121 {
122   myGroups.clear();
123 }
124
125 bool SketchSolver_Manager::groupMessages()
126 {
127   return true;
128 }
129
130 // ============================================================================
131 //  Function: processEvent
132 //  Purpose:  listen the event loop and process the message
133 // ============================================================================
134 void SketchSolver_Manager::processEvent(
135   const std::shared_ptr<Events_Message>& theMessage)
136 {
137   bool needToResolve = false;
138   bool isUpdateFlushed = false;
139   bool isMovedEvt = false;
140
141   static const Events_ID aCreatedEvent = Events_Loop::eventByName(EVENT_OBJECT_CREATED);
142   static const Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
143   static const Events_ID aSketchPreparedEvent = Events_Loop::eventByName(EVENT_SKETCH_PREPARED);
144   // sketch is prepared for resolve: all the needed events
145   // are collected and must be processed by the solver
146   if (theMessage->eventID() == aSketchPreparedEvent) {
147     flushGrouped(anUpdateEvent);
148     needToResolve = true;
149   }
150
151   if (myIsComputed)
152     return;
153   myIsComputed = true;
154
155   if (theMessage->eventID() == aCreatedEvent || theMessage->eventID() == anUpdateEvent) {
156     std::shared_ptr<ModelAPI_ObjectUpdatedMessage> anUpdateMsg =
157         std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
158
159     isUpdateFlushed = stopSendUpdate();
160
161     // update sketch features only
162     const std::set<ObjectPtr>& aFeatures = anUpdateMsg->objects();
163     IndexedFeatureMap anOrderedFeatures;
164     CompositeFeaturePtr aSketchFeature;
165     // try to keep order as features were created if there are several created features: #2229
166     if (theMessage->eventID() == aCreatedEvent && aFeatures.size() > 1) {
167       featuresOrderedByCreation(aFeatures, anOrderedFeatures);
168     } else { // order is not important, just process features before constraints
169       featuresOrderedByType(aFeatures, anOrderedFeatures, aSketchFeature);
170     }
171
172     IndexedFeatureMap::iterator aFeat;
173     for (aFeat = anOrderedFeatures.begin(); aFeat != anOrderedFeatures.end(); aFeat++) {
174       updateFeature(aFeat->second);
175     }
176     updateSketch(aSketchFeature);
177
178   } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED)) {
179     std::shared_ptr<ModelAPI_ObjectMovedMessage> aMoveMsg =
180         std::dynamic_pointer_cast<ModelAPI_ObjectMovedMessage>(theMessage);
181
182     ObjectPtr aMovedObject = aMoveMsg->movedObject();
183     std::shared_ptr<GeomDataAPI_Point2D> aMovedPoint =
184         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aMoveMsg->movedAttribute());
185
186     const std::shared_ptr<GeomAPI_Pnt2d>& aFrom = aMoveMsg->originalPosition();
187     const std::shared_ptr<GeomAPI_Pnt2d>& aTo = aMoveMsg->currentPosition();
188
189     if (aMovedObject) {
190       FeaturePtr aMovedFeature = ModelAPI_Feature::feature(aMovedObject);
191       std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
192           std::dynamic_pointer_cast<SketchPlugin_Feature>(aMovedFeature);
193       if (aSketchFeature && !aSketchFeature->isMacro())
194         needToResolve = moveFeature(aSketchFeature, aFrom, aTo);
195     } else if (aMovedPoint)
196       needToResolve = moveAttribute(aMovedPoint, aFrom, aTo);
197
198   } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
199     std::shared_ptr<ModelAPI_ObjectDeletedMessage> aDeleteMsg =
200       std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
201     const std::list<std::pair<std::shared_ptr<ModelAPI_Document>, std::string>>& aFeatureGroups =
202       aDeleteMsg->groups();
203
204     // Find SketchPlugin_Sketch::ID() in groups.
205     // The constraint groups should be updated when an object removed from Sketch
206     std::list<std::pair<std::shared_ptr<ModelAPI_Document>, std::string>>::const_iterator aFGrIter;
207     for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
208       if (aFGrIter->second == ModelAPI_ResultConstruction::group() ||
209         aFGrIter->second == ModelAPI_Feature::group())
210         break;
211
212     if (aFGrIter != aFeatureGroups.end()) {
213       std::list<SketchGroupPtr>::iterator aGroupIter = myGroups.begin();
214       while (aGroupIter != myGroups.end()) {
215         if (!(*aGroupIter)->isWorkplaneValid()) {  // the group should be removed
216           std::list<SketchGroupPtr>::iterator aRemoveIt = aGroupIter++;
217           myGroups.erase(aRemoveIt);
218           continue;
219         }
220
221         (*aGroupIter)->repairConsistency();
222         ++aGroupIter;
223       }
224     }
225     myIsComputed = false;
226   }
227   else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_GET_DOF_OBJECTS)) {
228     std::shared_ptr<ModelAPI_ObjectUpdatedMessage> anUpdateMsg =
229       std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
230     std::set<ObjectPtr> aObjects = anUpdateMsg->objects();
231     if (aObjects.size() == 1) {
232       std::set<ObjectPtr>::const_iterator aIt;
233       for (aIt = aObjects.cbegin(); aIt != aObjects.cend(); aIt++) {
234         std::shared_ptr<SketchPlugin_Feature> aFeature =
235           std::dynamic_pointer_cast<SketchPlugin_Feature>(*aIt);
236         if (aFeature) {
237           SketchGroupPtr aGroup = findGroup(aFeature);
238
239           std::set<ObjectPtr> aFreeFeatures;
240           aGroup->underconstrainedFeatures(aFreeFeatures);
241
242           std::list<ObjectPtr> aFeatures;
243           std::set<ObjectPtr>::const_iterator aIt;
244           for (aIt = aFreeFeatures.cbegin(); aIt != aFreeFeatures.cend(); ++aIt) {
245             aFeatures.push_back(*aIt);
246           }
247
248           // TODO: send features to GUI
249           static const Events_ID anEvent = Events_Loop::eventByName(EVENT_DOF_OBJECTS);
250           ModelAPI_EventCreator::get()->sendUpdated(aFeatures, anEvent);
251           Events_Loop::loop()->flush(anEvent);
252         }
253       }
254     }
255   }
256
257   // resolve constraints if needed
258   bool needToUpdate = needToResolve && resolveConstraints();
259   releaseFeaturesIfEventsBlocked();
260
261   // Features may be updated => now send events, but for all changed at once
262   if (isUpdateFlushed)
263     allowSendUpdate();
264
265   myIsComputed = false;
266
267   // send update for movement in any case
268   if (needToUpdate || isMovedEvt)
269     Events_Loop::loop()->flush(anUpdateEvent);
270 }
271
272 // ============================================================================
273 //  Function: updateSketch
274 //  Purpose:  update sketch plane in appropriate group
275 // ============================================================================
276 bool SketchSolver_Manager::updateSketch(const CompositeFeaturePtr& theSketch)
277 {
278   if (!theSketch)
279     return true;
280
281   bool isOk = true;
282   std::list<SketchGroupPtr>::const_iterator aGroupIt;
283   for (aGroupIt = myGroups.begin(); aGroupIt != myGroups.end(); ++aGroupIt)
284     if ((*aGroupIt)->getWorkplane() == theSketch) {
285       (*aGroupIt)->updateSketch(theSketch);
286       break;
287     }
288   return isOk;
289 }
290
291 // ============================================================================
292 //  Function: updateFeature
293 //  Purpose:  create/update constraint or feature in appropriate group
294 // ============================================================================
295 bool SketchSolver_Manager::updateFeature(const std::shared_ptr<SketchPlugin_Feature>& theFeature)
296 {
297   // Check feature validity and find a group to place it.
298   // If the feature is not valid, the returned group will be empty.
299   // This will protect to deal with wrong (not fully initialized) features.
300   SketchGroupPtr aGroup = findGroup(theFeature);
301   if (!aGroup)
302     return false;
303   aGroup->blockEvents(true);
304
305   std::shared_ptr<SketchPlugin_Constraint> aConstraint =
306       std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
307
308   bool isOk = false;
309   if (aConstraint)
310     isOk = aGroup->changeConstraint(aConstraint);
311   else
312     isOk = aGroup->updateFeature(theFeature);
313   return isOk;
314 }
315
316 // ============================================================================
317 //  Function: moveFeature
318 //  Purpose:  move given feature in appropriate group
319 // ============================================================================
320 bool SketchSolver_Manager::moveFeature(
321     const std::shared_ptr<SketchPlugin_Feature>& theMovedFeature,
322     const std::shared_ptr<GeomAPI_Pnt2d>& theFrom,
323     const std::shared_ptr<GeomAPI_Pnt2d>& theTo)
324 {
325   SketchGroupPtr aGroup = findGroup(theMovedFeature);
326   if (!aGroup)
327     return false;
328
329   std::shared_ptr<SketchPlugin_Constraint> aConstraint =
330       std::dynamic_pointer_cast<SketchPlugin_Constraint>(theMovedFeature);
331   if (aConstraint)
332   {
333     std::shared_ptr<GeomDataAPI_Point2D> aPntAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>
334       (aConstraint->attribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT()));
335     if (aPntAttr)
336     {
337       aPntAttr->setValue(theTo);
338       Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
339     }
340     return true;
341   }
342
343   aGroup->blockEvents(true);
344   return aGroup->moveFeature(theMovedFeature, theFrom, theTo);
345 }
346
347 // ============================================================================
348 //  Function: moveAttribute
349 //  Purpose:  move given attribute in appropriate group
350 // ============================================================================
351 bool SketchSolver_Manager::moveAttribute(
352     const std::shared_ptr<GeomDataAPI_Point2D>& theMovedAttribute,
353     const std::shared_ptr<GeomAPI_Pnt2d>& theFrom,
354     const std::shared_ptr<GeomAPI_Pnt2d>& theTo)
355 {
356   FeaturePtr anOwner = ModelAPI_Feature::feature(theMovedAttribute->owner());
357   std::shared_ptr<SketchPlugin_Constraint> aConstraint =
358       std::dynamic_pointer_cast<SketchPlugin_Constraint>(anOwner);
359   if (aConstraint)
360   {
361     theMovedAttribute->setValue(theTo);
362     Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
363     return true;
364   }
365
366   std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
367       std::dynamic_pointer_cast<SketchPlugin_Feature>(anOwner);
368   SketchGroupPtr aGroup;
369   if (aSketchFeature)
370     aGroup = findGroup(aSketchFeature);
371   if (!aGroup) {
372     theMovedAttribute->setValue(theTo);
373     return false;
374   }
375
376   aGroup->blockEvents(true);
377   return aGroup->movePoint(theMovedAttribute, theFrom, theTo);
378 }
379
380 // ============================================================================
381 //  Function: findGroup
382 //  Purpose:  search groups of entities interacting with given feature
383 // ============================================================================
384 SketchGroupPtr SketchSolver_Manager::findGroup(
385     std::shared_ptr<SketchPlugin_Feature> theFeature)
386 {
387   if (!isFeatureValid(theFeature))
388     return SketchGroupPtr(); // do not process wrong features
389
390   // Obtain sketch, containing the feature
391   CompositeFeaturePtr aSketch;
392   const std::set<AttributePtr>& aRefsList = theFeature->data()->refsToMe();
393   std::set<AttributePtr>::const_iterator aRefIt = aRefsList.begin();
394   for (; aRefIt != aRefsList.end(); ++aRefIt) {
395     FeaturePtr anOwner = ModelAPI_Feature::feature((*aRefIt)->owner());
396     if (anOwner && anOwner->getKind() == SketchPlugin_Sketch::ID()) {
397       aSketch = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(anOwner);
398       break;
399     }
400   }
401
402   if (!aSketch)
403     return SketchGroupPtr(); // not a sketch's feature
404
405   std::list<SketchGroupPtr>::const_iterator aGroupIt;
406   for (aGroupIt = myGroups.begin(); aGroupIt != myGroups.end(); ++aGroupIt)
407     if ((*aGroupIt)->getWorkplane() == aSketch)
408       return *aGroupIt;
409
410   // group for the sketch does not created yet
411   SketchGroupPtr aNewGroup = SketchGroupPtr(new SketchSolver_Group(aSketch));
412   myGroups.push_back(aNewGroup);
413   return aNewGroup;
414 }
415
416 // ============================================================================
417 //  Function: resolveConstraints
418 //  Purpose:  change entities according to available constraints
419 // ============================================================================
420 bool SketchSolver_Manager::resolveConstraints()
421 {
422   bool needToUpdate = false;
423   std::list<SketchGroupPtr>::const_iterator aGroupIter = myGroups.begin();
424   for (; aGroupIter != myGroups.end(); ++aGroupIter) {
425     if ((*aGroupIter)->resolveConstraints())
426       needToUpdate = true;
427   }
428   return needToUpdate;
429 }
430
431 void SketchSolver_Manager::releaseFeaturesIfEventsBlocked() const
432 {
433   std::list<SketchGroupPtr>::const_iterator aGroupIter = myGroups.begin();
434   for (; aGroupIter != myGroups.end(); ++aGroupIter)
435     (*aGroupIter)->blockEvents(false);
436 }
437
438 bool SketchSolver_Manager::stopSendUpdate() const
439 {
440 static const Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
441   // to avoid redisplay of each segment on update by solver one by one in the viewer
442   bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
443   if (isUpdateFlushed) {
444     Events_Loop::loop()->setFlushed(anUpdateEvent, false);
445   }
446   return isUpdateFlushed;
447 }
448
449 void SketchSolver_Manager::allowSendUpdate() const
450 {
451   Events_Loop::loop()->setFlushed(Events_Loop::eventByName(EVENT_OBJECT_UPDATED), true);
452 }