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