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