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