Salome HOME
8e2aa58b67ccc529e56207188e713c0939a4c254
[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     // Shows the message has at least one feature applicable for solver
86     bool hasProperFeature = false;
87
88     bool isMovedEvt = theMessage->eventID()
89         == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED);
90     if (isMovedEvt) {
91       std::set<ObjectPtr>::iterator aFeatIter;
92       for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
93         std::shared_ptr<SketchPlugin_Feature> aSFeature = 
94             std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
95         if (aSFeature) {
96           moveEntity(aSFeature);
97           hasProperFeature = true;
98         }
99       }
100     } else {
101       std::set<ObjectPtr>::iterator aFeatIter;
102       // iterate sketchers fisrt to create all sketches before (on load may exist several sketches)
103       for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
104         FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*aFeatIter);
105         if (!aFeature)
106           continue;
107         const std::string& aFeatureKind = aFeature->getKind();
108         if (aFeatureKind.compare(SketchPlugin_Sketch::ID()) == 0) {
109           std::shared_ptr<ModelAPI_CompositeFeature> aSketch = std::dynamic_pointer_cast<
110               ModelAPI_CompositeFeature>(aFeature);
111           hasProperFeature = changeWorkplane(aSketch) || hasProperFeature;
112         }
113       }
114       // then get anything but not the sketch
115       std::set<ObjectPtr> aComplexConstraints;
116       // fillet and mirror an tangency constraints will be processed later
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)
121           continue;
122         if (SketchSolver_Group::isComplexConstraint(aFeature)) {
123           aComplexConstraints.insert(aFeature);
124           continue;
125         }
126         hasProperFeature = changeConstraintOrEntity(aFeature) || hasProperFeature;
127       }
128       // processing remain constraints
129       aFeatIter = aComplexConstraints.begin();
130       for (; aFeatIter != aComplexConstraints.end(); aFeatIter++) {
131         std::shared_ptr<SketchPlugin_Feature> aFeature = 
132           std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
133         if (!aFeature)
134           continue;
135         hasProperFeature = changeConstraintOrEntity(aFeature) || hasProperFeature;
136       }
137     }
138
139     // Solve the set of constraints
140     if (hasProperFeature)
141       resolveConstraints(isMovedEvt); // send update for movement in any case
142   } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
143     std::shared_ptr<ModelAPI_ObjectDeletedMessage> aDeleteMsg =
144         std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
145     const std::set<std::string>& aFeatureGroups = aDeleteMsg->groups();
146
147     // Find SketchPlugin_Sketch::ID() in groups. The constraint groups should be updated when an object removed from Sketch
148     std::set<std::string>::const_iterator aFGrIter;
149     for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
150       if (aFGrIter->compare(ModelAPI_ResultConstruction::group()) == 0 ||
151           aFGrIter->compare(ModelAPI_Feature::group()) == 0)
152         break;
153
154     if (aFGrIter != aFeatureGroups.end()) {
155       std::vector<SketchSolver_Group*>::iterator aGroupIter = myGroups.begin();
156       std::vector<SketchSolver_Group*> aSeparatedGroups;
157       while (aGroupIter != myGroups.end()) {
158         if (!(*aGroupIter)->isWorkplaneValid()) {  // the group should be removed
159           delete *aGroupIter;
160           int aShift = aGroupIter - myGroups.begin();
161           myGroups.erase(aGroupIter);
162           aGroupIter = myGroups.begin() + aShift;
163           continue;
164         }
165         if (!(*aGroupIter)->isConsistent()) {  // some constraints were removed, try to split the group
166           (*aGroupIter)->splitGroup(aSeparatedGroups);
167         }
168         aGroupIter++;
169       }
170       if (aSeparatedGroups.size() > 0)
171         myGroups.insert(myGroups.end(), aSeparatedGroups.begin(), aSeparatedGroups.end());
172     }
173   }
174 }
175
176 // ============================================================================
177 //  Function: changeWorkplane
178 //  Class:    SketchSolver_Session
179 //  Purpose:  update workplane by given parameters of the sketch
180 // ============================================================================
181 bool SketchSolver_ConstraintManager::changeWorkplane(CompositeFeaturePtr theSketch)
182 {
183   bool aResult = true;  // changed when a workplane wrongly updated
184   bool isUpdated = false;
185   // Try to update specified workplane in all groups
186   std::vector<SketchSolver_Group*>::iterator aGroupIter;
187   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
188     if ((*aGroupIter)->isBaseWorkplane(theSketch)) {
189       isUpdated = true;
190       if (!(*aGroupIter)->updateWorkplane())
191         aResult = false;
192     }
193   // If the workplane is not updated, so this is a new workplane
194   if (!isUpdated) {
195     SketchSolver_Group* aNewGroup = new SketchSolver_Group(theSketch);
196     // Verify that the group is created successfully
197     if (!aNewGroup->isBaseWorkplane(theSketch) || !aNewGroup->isWorkplaneValid()) {
198       delete aNewGroup;
199       return false;
200     }
201     myGroups.push_back(aNewGroup);
202   }
203   return aResult;
204 }
205
206 // ============================================================================
207 //  Function: changeConstraintOrEntity
208 //  Class:    SketchSolver_Session
209 //  Purpose:  create/update the constraint or the feature and place it into appropriate group
210 // ============================================================================
211 bool SketchSolver_ConstraintManager::changeConstraintOrEntity(
212     std::shared_ptr<SketchPlugin_Feature> theFeature)
213 {
214   // Search the groups which this feature touches
215   std::set<Slvs_hGroup> aGroups;
216   findGroups(theFeature, aGroups);
217
218   std::shared_ptr<SketchPlugin_Constraint> aConstraint = 
219       std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
220
221   // Process the groups list
222   if (aGroups.size() == 0) {
223     // There are no groups applicable for this constraint => create new one
224     // The group will be created only for constraints, not for features
225     if (!aConstraint) return false;
226     std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(aConstraint);
227     if (!aWP)
228       return false;
229     SketchSolver_Group* aGroup = new SketchSolver_Group(aWP);
230     if (!aGroup->changeConstraint(aConstraint)) {
231       delete aGroup;
232       return false;
233     }
234     myGroups.push_back(aGroup);
235     return true;
236   } else if (aGroups.size() == 1) {  // Only one group => add feature into it
237     Slvs_hGroup aGroupId = *(aGroups.begin());
238     std::vector<SketchSolver_Group*>::iterator aGroupIter;
239     for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
240       if ((*aGroupIter)->getId() == aGroupId) {
241         // If the group is empty, the feature is not added (the constraint only)
242         if (!aConstraint && !(*aGroupIter)->isEmpty())
243           return (*aGroupIter)->updateFeature(theFeature);
244         return (*aGroupIter)->changeConstraint(aConstraint);
245       }
246   } else if (aGroups.size() > 1) {  // Several groups applicable for this feature => need to merge them
247     std::set<Slvs_hGroup>::const_iterator aGroupsIter = aGroups.begin();
248
249     // Search first group
250     std::vector<SketchSolver_Group*>::iterator aFirstGroupIter;
251     for (aFirstGroupIter = myGroups.begin(); aFirstGroupIter != myGroups.end(); aFirstGroupIter++)
252       if ((*aFirstGroupIter)->getId() == *aGroupsIter)
253         break;
254     if (aFirstGroupIter == myGroups.end())
255       return false;
256
257     // Append other groups to the first one
258     std::vector<SketchSolver_Group*>::iterator anOtherGroupIter = aFirstGroupIter + 1;
259     for (aGroupsIter++; aGroupsIter != aGroups.end(); aGroupsIter++) {
260       for (; anOtherGroupIter != myGroups.end(); anOtherGroupIter++)
261         if ((*anOtherGroupIter)->getId() == *aGroupsIter)
262           break;
263       if (anOtherGroupIter == myGroups.end()) {  // Group disappears
264         anOtherGroupIter = aFirstGroupIter + 1;
265         continue;
266       }
267
268       (*aFirstGroupIter)->mergeGroups(**anOtherGroupIter);
269       int aShiftFirst = aFirstGroupIter - myGroups.begin();
270       int aShiftOther = anOtherGroupIter - myGroups.begin();
271       delete *anOtherGroupIter;
272       myGroups.erase(anOtherGroupIter);
273       aFirstGroupIter = myGroups.begin() + aShiftFirst;
274       anOtherGroupIter = myGroups.begin() + aShiftOther;
275     }
276
277     if (aConstraint)
278       return (*aFirstGroupIter)->changeConstraint(aConstraint);
279     return (*aFirstGroupIter)->updateFeature(theFeature);
280   }
281
282   // Something goes wrong
283   return false;
284 }
285
286 // ============================================================================
287 //  Function: moveEntity
288 //  Class:    SketchSolver_Session
289 //  Purpose:  update element moved on the sketch, which is used by constraints
290 // ============================================================================
291 void SketchSolver_ConstraintManager::moveEntity(
292     std::shared_ptr<SketchPlugin_Feature> theFeature)
293 {
294   std::vector<SketchSolver_Group*>::iterator aGroupIt = myGroups.begin();
295   for (; aGroupIt != myGroups.end(); aGroupIt++)
296     if (!(*aGroupIt)->isEmpty() && (*aGroupIt)->isInteract(theFeature))
297       (*aGroupIt)->moveFeature(theFeature);
298 }
299
300 // ============================================================================
301 //  Function: findGroups
302 //  Class:    SketchSolver_Session
303 //  Purpose:  search groups of entities interacting with given feature
304 // ============================================================================
305 void SketchSolver_ConstraintManager::findGroups(
306     std::shared_ptr<SketchPlugin_Feature> theFeature,
307     std::set<Slvs_hGroup>& theGroupIDs) const
308 {
309   std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(theFeature);
310
311   SketchSolver_Group* anEmptyGroup = 0;  // appropriate empty group for specified constraint
312   std::vector<SketchSolver_Group*>::const_iterator aGroupIter;
313   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
314     if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theFeature)) {
315       if (!(*aGroupIter)->isEmpty())
316         theGroupIDs.insert((*aGroupIter)->getId());
317       else if (!anEmptyGroup)
318         anEmptyGroup = *aGroupIter;
319     }
320
321   // When only empty group is found, use it
322   if (anEmptyGroup && theGroupIDs.empty())
323     theGroupIDs.insert(anEmptyGroup->getId());
324 }
325
326 // ============================================================================
327 //  Function: findWorkplane
328 //  Class:    SketchSolver_Session
329 //  Purpose:  search workplane containing given feature
330 // ============================================================================
331 std::shared_ptr<ModelAPI_CompositeFeature> SketchSolver_ConstraintManager
332 ::findWorkplane(std::shared_ptr<SketchPlugin_Feature> theFeature) const
333 {
334   // Already verified workplanes
335   std::set<std::shared_ptr<ModelAPI_CompositeFeature> > aVerified;
336
337   std::vector<SketchSolver_Group*>::const_iterator aGroupIter;
338   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
339     std::shared_ptr<ModelAPI_CompositeFeature> aWP = (*aGroupIter)->getWorkplane();
340     if (aVerified.find(aWP) != aVerified.end())
341       continue;
342
343     DataPtr aData = aWP->data();
344     if (aData) {
345       std::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures = std::dynamic_pointer_cast<
346           ModelAPI_AttributeRefList>(aData->attribute(SketchPlugin_Sketch::FEATURES_ID()));
347       std::list<ObjectPtr> aFeaturesList = aWPFeatures->list();
348       std::list<ObjectPtr>::const_iterator anIter;
349       for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
350         if (*anIter == theFeature)
351           return aWP;  // workplane is found
352     }
353     aVerified.insert(aWP);
354   }
355
356   return std::shared_ptr<ModelAPI_CompositeFeature>();
357 }
358
359 // ============================================================================
360 //  Function: resolveConstraints
361 //  Class:    SketchSolver_Session
362 //  Purpose:  change entities according to available constraints
363 // ============================================================================
364 void SketchSolver_ConstraintManager::resolveConstraints(const bool theForceUpdate)
365 {
366   myIsComputed = true;
367   bool needToUpdate = false;
368   static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
369   // to avoid redisplay of each segment on update by solver one by one in the viewer
370   bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
371   if (isUpdateFlushed) {
372     Events_Loop::loop()->setFlushed(anUpdateEvent, false);
373   }
374
375   std::vector<SketchSolver_Group*>::iterator aGroupIter;
376   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
377     if ((*aGroupIter)->resolveConstraints())
378       needToUpdate = true;
379
380   // Features may be updated => now send events, but for all changed at once
381   if (isUpdateFlushed) {
382     Events_Loop::loop()->setFlushed(anUpdateEvent, true);
383   }
384   // Must be before flush because on "Updated" flush the results may be produced
385   // and the creation event is appeared with many new objects. If myIsComputed these
386   // events are missed in processEvents and some elements are not added.
387   myIsComputed = false;
388   if (needToUpdate || theForceUpdate)
389     Events_Loop::loop()->flush(anUpdateEvent);
390 }