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