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