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