Salome HOME
Fix undo-redo problems on Fillet construction
[modules/shaper.git] / src / SketchSolver / SketchSolver_ConstraintManager.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 // File:    SketchSolver_ConstraintManager.cpp
4 // Created: 08 May 2014
5 // Author:  Artem ZHIDKOV
6
7 #include "SketchSolver_ConstraintManager.h"
8
9 #include <Events_Loop.h>
10 #include <ModelAPI_AttributeDouble.h>
11 #include <ModelAPI_AttributeRefList.h>
12 #include <ModelAPI_Data.h>
13 #include <ModelAPI_Events.h>
14 #include <ModelAPI_Object.h>
15 #include <ModelAPI_ResultConstruction.h>
16 #include <ModelAPI_Attribute.h>
17
18 #include <SketchPlugin_Constraint.h>
19 #include <SketchPlugin_ConstraintCoincidence.h>
20 #include <SketchPlugin_ConstraintFillet.h>
21 #include <SketchPlugin_ConstraintMirror.h>
22 #include <SketchPlugin_ConstraintTangent.h>
23
24 #include <SketchPlugin_Arc.h>
25 #include <SketchPlugin_Circle.h>
26 #include <SketchPlugin_Line.h>
27 #include <SketchPlugin_Point.h>
28 #include <SketchPlugin_Sketch.h>
29 #include <SketchPlugin_Feature.h>
30
31 #include <list>
32 #include <set>
33 #include <memory>
34
35 // Initialization of constraint manager self pointer
36 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::_self = 0;
37
38 /// Global constraint manager object
39 SketchSolver_ConstraintManager* myManager = SketchSolver_ConstraintManager::Instance();
40
41 // ========================================================
42 // ========= SketchSolver_ConstraintManager ===============
43 // ========================================================
44 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::Instance()
45 {
46   if (!_self)
47     _self = new SketchSolver_ConstraintManager();
48   return _self;
49 }
50
51 SketchSolver_ConstraintManager::SketchSolver_ConstraintManager()
52 {
53   myGroups.clear();
54   myIsComputed = false;
55
56   // Register in event loop
57   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
58   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
59   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
60   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_MOVED));
61 }
62
63 SketchSolver_ConstraintManager::~SketchSolver_ConstraintManager()
64 {
65   myGroups.clear();
66 }
67
68 // ============================================================================
69 //  Function: processEvent
70 //  Class:    SketchSolver_Session
71 //  Purpose:  listen the event loop and process the message
72 // ============================================================================
73 void SketchSolver_ConstraintManager::processEvent(
74   const std::shared_ptr<Events_Message>& theMessage)
75 {
76   if (myIsComputed)
77     return;
78   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)
79       || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED)
80       || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED)) {
81     std::shared_ptr<ModelAPI_ObjectUpdatedMessage> anUpdateMsg =
82         std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
83     std::set<ObjectPtr> aFeatures = anUpdateMsg->objects();
84
85     bool isMovedEvt = theMessage->eventID()
86         == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED);
87     if (isMovedEvt) {
88       std::set<ObjectPtr>::iterator aFeatIter;
89       for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
90         std::shared_ptr<SketchPlugin_Feature> aSFeature = 
91             std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
92         if (aSFeature)
93           moveEntity(aSFeature);
94       }
95     } else {
96       std::set<ObjectPtr>::iterator aFeatIter;
97       // iterate sketchers fisrt to create all sketches before (on load may exist several sketches)
98       for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
99         FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*aFeatIter);
100         if (!aFeature)
101           continue;
102         const std::string& aFeatureKind = aFeature->getKind();
103         if (aFeatureKind.compare(SketchPlugin_Sketch::ID()) == 0) {
104           std::shared_ptr<ModelAPI_CompositeFeature> aSketch = std::dynamic_pointer_cast<
105               ModelAPI_CompositeFeature>(aFeature);
106           changeWorkplane(aSketch);
107         }
108       }
109       // then get anything but not the sketch
110       std::set<ObjectPtr> aComplexConstraints;
111       // fillet and mirror an tangency constraints will be processed later
112       for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
113         std::shared_ptr<SketchPlugin_Feature> aFeature = 
114           std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
115         if (!aFeature)
116           continue;
117         if (SketchSolver_Group::isComplexConstraint(aFeature)) {
118           aComplexConstraints.insert(aFeature);
119           continue;
120         }
121         changeConstraintOrEntity(aFeature);
122       }
123       // processing remain constraints
124       aFeatIter = aComplexConstraints.begin();
125       for (; aFeatIter != aComplexConstraints.end(); aFeatIter++) {
126         std::shared_ptr<SketchPlugin_Feature> aFeature = 
127           std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
128         if (!aFeature)
129           continue;
130         changeConstraintOrEntity(aFeature);
131       }
132     }
133
134     // Solve the set of constraints
135     resolveConstraints(isMovedEvt); // send update for movement in any case
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. The constraint groups should be updated when an object removed from Sketch
142     std::set<std::string>::const_iterator aFGrIter;
143     for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
144       if (aFGrIter->compare(ModelAPI_ResultConstruction::group()) == 0 ||
145           aFGrIter->compare(ModelAPI_Feature::group()) == 0)
146         break;
147
148     if (aFGrIter != aFeatureGroups.end()) {
149       std::vector<SketchSolver_Group*>::iterator aGroupIter = myGroups.begin();
150       std::vector<SketchSolver_Group*> aSeparatedGroups;
151       while (aGroupIter != myGroups.end()) {
152         if (!(*aGroupIter)->isWorkplaneValid()) {  // the group should be removed
153           delete *aGroupIter;
154           int aShift = aGroupIter - myGroups.begin();
155           myGroups.erase(aGroupIter);
156           aGroupIter = myGroups.begin() + aShift;
157           continue;
158         }
159         if (!(*aGroupIter)->isConsistent()) {  // some constraints were removed, try to split the group
160           (*aGroupIter)->splitGroup(aSeparatedGroups);
161         }
162         aGroupIter++;
163       }
164       if (aSeparatedGroups.size() > 0)
165         myGroups.insert(myGroups.end(), aSeparatedGroups.begin(), aSeparatedGroups.end());
166     }
167   }
168 }
169
170 // ============================================================================
171 //  Function: changeWorkplane
172 //  Class:    SketchSolver_Session
173 //  Purpose:  update workplane by given parameters of the sketch
174 // ============================================================================
175 bool SketchSolver_ConstraintManager::changeWorkplane(CompositeFeaturePtr theSketch)
176 {
177   bool aResult = true;  // changed when a workplane wrongly updated
178   bool isUpdated = false;
179   // Try to update specified workplane in all groups
180   std::vector<SketchSolver_Group*>::iterator aGroupIter;
181   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
182     if ((*aGroupIter)->isBaseWorkplane(theSketch)) {
183       isUpdated = true;
184       if (!(*aGroupIter)->updateWorkplane())
185         aResult = false;
186     }
187   // If the workplane is not updated, so this is a new workplane
188   if (!isUpdated) {
189     SketchSolver_Group* aNewGroup = new SketchSolver_Group(theSketch);
190     // Verify that the group is created successfully
191     if (!aNewGroup->isBaseWorkplane(theSketch) || !aNewGroup->isWorkplaneValid()) {
192       delete aNewGroup;
193       return false;
194     }
195     myGroups.push_back(aNewGroup);
196   }
197   return aResult;
198 }
199
200 // ============================================================================
201 //  Function: changeConstraintOrEntity
202 //  Class:    SketchSolver_Session
203 //  Purpose:  create/update the constraint or the feature and place it into appropriate group
204 // ============================================================================
205 bool SketchSolver_ConstraintManager::changeConstraintOrEntity(
206     std::shared_ptr<SketchPlugin_Feature> theFeature)
207 {
208   // Search the groups which this feature touches
209   std::set<Slvs_hGroup> aGroups;
210   findGroups(theFeature, aGroups);
211
212   std::shared_ptr<SketchPlugin_Constraint> aConstraint = 
213       std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
214
215   // Process the groups list
216   if (aGroups.size() == 0) {
217     // There are no groups applicable for this constraint => create new one
218     // The group will be created only for constraints, not for features
219     if (!aConstraint) return false;
220     std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(aConstraint);
221     if (!aWP)
222       return false;
223     SketchSolver_Group* aGroup = new SketchSolver_Group(aWP);
224     if (!aGroup->changeConstraint(aConstraint)) {
225       delete aGroup;
226       return false;
227     }
228     myGroups.push_back(aGroup);
229     return true;
230   } else if (aGroups.size() == 1) {  // Only one group => add feature into it
231     Slvs_hGroup aGroupId = *(aGroups.begin());
232     std::vector<SketchSolver_Group*>::iterator aGroupIter;
233     for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
234       if ((*aGroupIter)->getId() == aGroupId) {
235         // If the group is empty, the feature is not added (the constraint only)
236         if (!aConstraint && !(*aGroupIter)->isEmpty())
237           return (*aGroupIter)->updateFeature(theFeature);
238         return (*aGroupIter)->changeConstraint(aConstraint);
239       }
240   } else if (aGroups.size() > 1) {  // Several groups applicable for this feature => need to merge them
241     std::set<Slvs_hGroup>::const_iterator aGroupsIter = aGroups.begin();
242
243     // Search first group
244     std::vector<SketchSolver_Group*>::iterator aFirstGroupIter;
245     for (aFirstGroupIter = myGroups.begin(); aFirstGroupIter != myGroups.end(); aFirstGroupIter++)
246       if ((*aFirstGroupIter)->getId() == *aGroupsIter)
247         break;
248     if (aFirstGroupIter == myGroups.end())
249       return false;
250
251     // Append other groups to the first one
252     std::vector<SketchSolver_Group*>::iterator anOtherGroupIter = aFirstGroupIter + 1;
253     for (aGroupsIter++; aGroupsIter != aGroups.end(); aGroupsIter++) {
254       for (; anOtherGroupIter != myGroups.end(); anOtherGroupIter++)
255         if ((*anOtherGroupIter)->getId() == *aGroupsIter)
256           break;
257       if (anOtherGroupIter == myGroups.end()) {  // Group disappears
258         anOtherGroupIter = aFirstGroupIter + 1;
259         continue;
260       }
261
262       (*aFirstGroupIter)->mergeGroups(**anOtherGroupIter);
263       int aShiftFirst = aFirstGroupIter - myGroups.begin();
264       int aShiftOther = anOtherGroupIter - myGroups.begin();
265       delete *anOtherGroupIter;
266       myGroups.erase(anOtherGroupIter);
267       aFirstGroupIter = myGroups.begin() + aShiftFirst;
268       anOtherGroupIter = myGroups.begin() + aShiftOther;
269     }
270
271     if (aConstraint)
272       return (*aFirstGroupIter)->changeConstraint(aConstraint);
273     return (*aFirstGroupIter)->updateFeature(theFeature);
274   }
275
276   // Something goes wrong
277   return false;
278 }
279
280 // ============================================================================
281 //  Function: moveEntity
282 //  Class:    SketchSolver_Session
283 //  Purpose:  update element moved on the sketch, which is used by constraints
284 // ============================================================================
285 void SketchSolver_ConstraintManager::moveEntity(
286     std::shared_ptr<SketchPlugin_Feature> theFeature)
287 {
288   std::vector<SketchSolver_Group*>::iterator aGroupIt = myGroups.begin();
289   for (; aGroupIt != myGroups.end(); aGroupIt++)
290     if (!(*aGroupIt)->isEmpty() && (*aGroupIt)->isInteract(theFeature))
291       (*aGroupIt)->moveFeature(theFeature);
292 }
293
294 // ============================================================================
295 //  Function: findGroups
296 //  Class:    SketchSolver_Session
297 //  Purpose:  search groups of entities interacting with given feature
298 // ============================================================================
299 void SketchSolver_ConstraintManager::findGroups(
300     std::shared_ptr<SketchPlugin_Feature> theFeature,
301     std::set<Slvs_hGroup>& theGroupIDs) const
302 {
303   std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(theFeature);
304
305   SketchSolver_Group* anEmptyGroup = 0;  // appropriate empty group for specified constraint
306   std::vector<SketchSolver_Group*>::const_iterator aGroupIter;
307   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
308     if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theFeature)) {
309       if (!(*aGroupIter)->isEmpty())
310         theGroupIDs.insert((*aGroupIter)->getId());
311       else if (!anEmptyGroup)
312         anEmptyGroup = *aGroupIter;
313     }
314
315   // When only empty group is found, use it
316   if (anEmptyGroup && theGroupIDs.empty())
317     theGroupIDs.insert(anEmptyGroup->getId());
318 }
319
320 // ============================================================================
321 //  Function: findWorkplane
322 //  Class:    SketchSolver_Session
323 //  Purpose:  search workplane containing given feature
324 // ============================================================================
325 std::shared_ptr<ModelAPI_CompositeFeature> SketchSolver_ConstraintManager
326 ::findWorkplane(std::shared_ptr<SketchPlugin_Feature> theFeature) const
327 {
328   // Already verified workplanes
329   std::set<std::shared_ptr<ModelAPI_CompositeFeature> > aVerified;
330
331   std::vector<SketchSolver_Group*>::const_iterator aGroupIter;
332   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
333     std::shared_ptr<ModelAPI_CompositeFeature> aWP = (*aGroupIter)->getWorkplane();
334     if (aVerified.find(aWP) != aVerified.end())
335       continue;
336
337     DataPtr aData = aWP->data();
338     if (aData) {
339       std::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures = std::dynamic_pointer_cast<
340           ModelAPI_AttributeRefList>(aData->attribute(SketchPlugin_Sketch::FEATURES_ID()));
341       std::list<ObjectPtr> aFeaturesList = aWPFeatures->list();
342       std::list<ObjectPtr>::const_iterator anIter;
343       for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
344         if (*anIter == theFeature)
345           return aWP;  // workplane is found
346     }
347     aVerified.insert(aWP);
348   }
349
350   return std::shared_ptr<ModelAPI_CompositeFeature>();
351 }
352
353 // ============================================================================
354 //  Function: resolveConstraints
355 //  Class:    SketchSolver_Session
356 //  Purpose:  change entities according to available constraints
357 // ============================================================================
358 void SketchSolver_ConstraintManager::resolveConstraints(const bool theForceUpdate)
359 {
360   myIsComputed = true;
361   bool needToUpdate = false;
362   static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
363   // to avoid redisplay of each segment on update by solver one by one in the viewer
364   bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
365   if (isUpdateFlushed) {
366     Events_Loop::loop()->setFlushed(anUpdateEvent, false);
367   }
368
369   std::vector<SketchSolver_Group*>::iterator aGroupIter;
370   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
371     if ((*aGroupIter)->resolveConstraints())
372       needToUpdate = true;
373
374   // Features may be updated => now send events, but for all changed at once
375   if (isUpdateFlushed) {
376     Events_Loop::loop()->setFlushed(anUpdateEvent, true);
377   }
378   // Must be before flush because on "Updated" flush the results may be produced
379   // and the creation event is appeared with many new objects. If myIsComputed these
380   // events are missed in processEvents and some elements are not added.
381   myIsComputed = false;
382   if (needToUpdate || theForceUpdate)
383     Events_Loop::loop()->flush(anUpdateEvent);
384 }