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