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