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