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