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