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