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