Salome HOME
Issue #901 - It is possible to define empty name for parameter
[modules/shaper.git] / src / SketchSolver / SketchSolver_ConstraintManager.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 // File:    SketchSolver_ConstraintManager.cpp
4 // Created: 08 May 2014
5 // Author:  Artem ZHIDKOV
6
7 #include "SketchSolver_ConstraintManager.h"
8
9 #include <Events_Loop.h>
10 #include <ModelAPI_AttributeDouble.h>
11 #include <ModelAPI_AttributeRefList.h>
12 #include <ModelAPI_Data.h>
13 #include <ModelAPI_Events.h>
14 #include <ModelAPI_Object.h>
15 #include <ModelAPI_ResultConstruction.h>
16 #include <ModelAPI_Attribute.h>
17
18 #include <SketchPlugin_Constraint.h>
19 #include <SketchPlugin_ConstraintAngle.h>
20 #include <SketchPlugin_ConstraintCoincidence.h>
21 #include <SketchPlugin_ConstraintDistance.h>
22 #include <SketchPlugin_ConstraintEqual.h>
23 #include <SketchPlugin_ConstraintHorizontal.h>
24 #include <SketchPlugin_ConstraintLength.h>
25 #include <SketchPlugin_ConstraintFillet.h>
26 #include <SketchPlugin_ConstraintMirror.h>
27 #include <SketchPlugin_ConstraintParallel.h>
28 #include <SketchPlugin_ConstraintPerpendicular.h>
29 #include <SketchPlugin_ConstraintRadius.h>
30 #include <SketchPlugin_ConstraintRigid.h>
31 #include <SketchPlugin_ConstraintTangent.h>
32 #include <SketchPlugin_ConstraintVertical.h>
33 #include <SketchPlugin_MultiRotation.h>
34 #include <SketchPlugin_MultiTranslation.h>
35
36 #include <SketchPlugin_Arc.h>
37 #include <SketchPlugin_Circle.h>
38 #include <SketchPlugin_Line.h>
39 #include <SketchPlugin_Point.h>
40 #include <SketchPlugin_Sketch.h>
41 #include <SketchPlugin_Feature.h>
42
43 #include <list>
44 #include <set>
45 #include <memory>
46
47 // Initialization of constraint manager self pointer
48 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::_self = 0;
49
50 /// Global constraint manager object
51 SketchSolver_ConstraintManager* myManager = SketchSolver_ConstraintManager::Instance();
52
53
54 /// \brief Select and sort features applicable for SketchSolver
55 static std::list<FeaturePtr> selectApplicableFeatures(const std::set<ObjectPtr>& theObjects);
56
57 // ========================================================
58 // ========= SketchSolver_ConstraintManager ===============
59 // ========================================================
60 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::Instance()
61 {
62   if (!_self)
63     _self = new SketchSolver_ConstraintManager();
64   return _self;
65 }
66
67 SketchSolver_ConstraintManager::SketchSolver_ConstraintManager()
68 {
69   myGroups.clear();
70   myIsComputed = false;
71
72   // Register in event loop
73   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
74   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
75   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
76   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_MOVED));
77 }
78
79 SketchSolver_ConstraintManager::~SketchSolver_ConstraintManager()
80 {
81   myGroups.clear();
82 }
83
84 // ============================================================================
85 //  Function: processEvent
86 //  Class:    SketchSolver_Session
87 //  Purpose:  listen the event loop and process the message
88 // ============================================================================
89 void SketchSolver_ConstraintManager::processEvent(
90   const std::shared_ptr<Events_Message>& theMessage)
91 {
92   if (myIsComputed)
93     return;
94   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)
95       || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED)
96       || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED)) {
97     std::shared_ptr<ModelAPI_ObjectUpdatedMessage> anUpdateMsg =
98         std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
99     std::set<ObjectPtr> aFeatures = anUpdateMsg->objects();
100
101     // Shows the message has at least one feature applicable for solver
102     bool hasProperFeature = false;
103
104     bool isMovedEvt = theMessage->eventID()
105           == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED);
106     if (isMovedEvt) {
107       std::set<ObjectPtr>::iterator aFeatIter;
108       for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
109         std::shared_ptr<SketchPlugin_Feature> aSFeature = 
110             std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
111         if (aSFeature) {
112           moveEntity(aSFeature);
113           hasProperFeature = true;
114         }
115       }
116     } else {
117       std::list<FeaturePtr> aSketchFeatures = selectApplicableFeatures(aFeatures);
118       std::list<FeaturePtr>::iterator aFeatIter = aSketchFeatures.begin();
119       for (; aFeatIter != aSketchFeatures.end(); ++aFeatIter) {
120         if ((*aFeatIter)->getKind() == SketchPlugin_Sketch::ID()) {
121           std::shared_ptr<ModelAPI_CompositeFeature> aSketch = 
122               std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aFeatIter);
123           hasProperFeature = changeWorkplane(aSketch) || hasProperFeature;
124           continue;
125         }
126         std::shared_ptr<SketchPlugin_Feature> aFeature = 
127             std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
128         if (!aFeature)
129           continue;
130         hasProperFeature = changeConstraintOrEntity(aFeature) || hasProperFeature;
131       }
132     }
133
134     // Solve the set of constraints
135     if (hasProperFeature)
136       resolveConstraints(isMovedEvt); // send update for movement in any case
137   } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
138     std::shared_ptr<ModelAPI_ObjectDeletedMessage> aDeleteMsg =
139       std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
140     const std::set<std::string>& aFeatureGroups = aDeleteMsg->groups();
141
142     // Find SketchPlugin_Sketch::ID() in groups. The constraint groups should be updated when an object removed from Sketch
143     std::set<std::string>::const_iterator aFGrIter;
144     for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
145       if (aFGrIter->compare(ModelAPI_ResultConstruction::group()) == 0 ||
146         aFGrIter->compare(ModelAPI_Feature::group()) == 0)
147         break;
148
149     if (aFGrIter != aFeatureGroups.end()) {
150       std::vector<SketchSolver_Group*>::iterator aGroupIter = myGroups.begin();
151       std::vector<SketchSolver_Group*> aSeparatedGroups;
152       while (aGroupIter != myGroups.end()) {
153         if (!(*aGroupIter)->isWorkplaneValid()) {  // the group should be removed
154           delete *aGroupIter;
155           int aShift = aGroupIter - myGroups.begin();
156           myGroups.erase(aGroupIter);
157           aGroupIter = myGroups.begin() + aShift;
158           continue;
159         }
160         if (!(*aGroupIter)->isConsistent()) {  // some constraints were removed, try to split the group
161           (*aGroupIter)->splitGroup(aSeparatedGroups);
162         }
163         aGroupIter++;
164       }
165       if (aSeparatedGroups.size() > 0)
166         myGroups.insert(myGroups.end(), aSeparatedGroups.begin(), aSeparatedGroups.end());
167     }
168   }
169 }
170
171 // ============================================================================
172 //  Function: changeWorkplane
173 //  Class:    SketchSolver_Session
174 //  Purpose:  update workplane by given parameters of the sketch
175 // ============================================================================
176 bool SketchSolver_ConstraintManager::changeWorkplane(CompositeFeaturePtr theSketch)
177 {
178   bool aResult = true;  // changed when a workplane wrongly updated
179   bool isUpdated = false;
180   // Try to update specified workplane in all groups
181   std::vector<SketchSolver_Group*>::iterator aGroupIter;
182   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
183     if ((*aGroupIter)->isBaseWorkplane(theSketch)) {
184       isUpdated = true;
185       if (!(*aGroupIter)->updateWorkplane())
186         aResult = false;
187     }
188   // If the workplane is not updated, so this is a new workplane
189   if (!isUpdated) {
190     SketchSolver_Group* aNewGroup = new SketchSolver_Group(theSketch);
191     // Verify that the group is created successfully
192     if (!aNewGroup->isBaseWorkplane(theSketch) || !aNewGroup->isWorkplaneValid()) {
193       delete aNewGroup;
194       return false;
195     }
196     myGroups.push_back(aNewGroup);
197   }
198   return aResult;
199 }
200
201 // ============================================================================
202 //  Function: changeConstraintOrEntity
203 //  Class:    SketchSolver_Session
204 //  Purpose:  create/update the constraint or the feature and place it into appropriate group
205 // ============================================================================
206 bool SketchSolver_ConstraintManager::changeConstraintOrEntity(
207     std::shared_ptr<SketchPlugin_Feature> theFeature)
208 {
209   // Search the groups which this feature touches
210   std::set<Slvs_hGroup> aGroups;
211   findGroups(theFeature, aGroups);
212
213   std::shared_ptr<SketchPlugin_Constraint> aConstraint = 
214       std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
215
216   // Process the groups list
217   if (aGroups.size() == 0) {
218     // There are no groups applicable for this constraint => create new one
219     // The group will be created only for constraints, not for features
220     if (!aConstraint) return false;
221     std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(aConstraint);
222     if (!aWP)
223       return false;
224     SketchSolver_Group* aGroup = new SketchSolver_Group(aWP);
225     if (!aGroup->changeConstraint(aConstraint)) {
226       delete aGroup;
227       return false;
228     }
229     myGroups.push_back(aGroup);
230     return true;
231   } else if (aGroups.size() == 1) {  // Only one group => add feature into it
232     Slvs_hGroup aGroupId = *(aGroups.begin());
233     std::vector<SketchSolver_Group*>::iterator aGroupIter;
234     for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
235       if ((*aGroupIter)->getId() == aGroupId) {
236         // If the group is empty, the feature is not added (the constraint only)
237         if (!aConstraint && !(*aGroupIter)->isEmpty())
238           return (*aGroupIter)->updateFeature(theFeature);
239         return (*aGroupIter)->changeConstraint(aConstraint);
240       }
241   } else if (aGroups.size() > 1) {  // Several groups applicable for this feature => need to merge them
242     std::set<Slvs_hGroup>::const_iterator aGroupsIter = aGroups.begin();
243
244     // Search first group
245     std::vector<SketchSolver_Group*>::iterator aFirstGroupIter;
246     for (aFirstGroupIter = myGroups.begin(); aFirstGroupIter != myGroups.end(); aFirstGroupIter++)
247       if ((*aFirstGroupIter)->getId() == *aGroupsIter)
248         break;
249     if (aFirstGroupIter == myGroups.end())
250       return false;
251
252     // Append other groups to the first one
253     std::vector<SketchSolver_Group*>::iterator anOtherGroupIter = aFirstGroupIter + 1;
254     for (aGroupsIter++; aGroupsIter != aGroups.end(); aGroupsIter++) {
255       for (; anOtherGroupIter != myGroups.end(); anOtherGroupIter++)
256         if ((*anOtherGroupIter)->getId() == *aGroupsIter)
257           break;
258       if (anOtherGroupIter == myGroups.end()) {  // Group disappears
259         anOtherGroupIter = aFirstGroupIter + 1;
260         continue;
261       }
262
263       (*aFirstGroupIter)->mergeGroups(**anOtherGroupIter);
264       int aShiftFirst = aFirstGroupIter - myGroups.begin();
265       int aShiftOther = anOtherGroupIter - myGroups.begin();
266       delete *anOtherGroupIter;
267       myGroups.erase(anOtherGroupIter);
268       aFirstGroupIter = myGroups.begin() + aShiftFirst;
269       anOtherGroupIter = myGroups.begin() + aShiftOther;
270     }
271
272     if (aConstraint)
273       return (*aFirstGroupIter)->changeConstraint(aConstraint);
274     return (*aFirstGroupIter)->updateFeature(theFeature);
275   }
276
277   // Something goes wrong
278   return false;
279 }
280
281 // ============================================================================
282 //  Function: moveEntity
283 //  Class:    SketchSolver_Session
284 //  Purpose:  update element moved on the sketch, which is used by constraints
285 // ============================================================================
286 void SketchSolver_ConstraintManager::moveEntity(
287     std::shared_ptr<SketchPlugin_Feature> theFeature)
288 {
289   std::vector<SketchSolver_Group*>::iterator aGroupIt = myGroups.begin();
290   for (; aGroupIt != myGroups.end(); aGroupIt++)
291     if (!(*aGroupIt)->isEmpty() && (*aGroupIt)->isInteract(theFeature))
292       (*aGroupIt)->moveFeature(theFeature);
293 }
294
295 // ============================================================================
296 //  Function: findGroups
297 //  Class:    SketchSolver_Session
298 //  Purpose:  search groups of entities interacting with given feature
299 // ============================================================================
300 void SketchSolver_ConstraintManager::findGroups(
301     std::shared_ptr<SketchPlugin_Feature> theFeature,
302     std::set<Slvs_hGroup>& theGroupIDs) const
303 {
304   std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(theFeature);
305
306   SketchSolver_Group* anEmptyGroup = 0;  // appropriate empty group for specified constraint
307   std::vector<SketchSolver_Group*>::const_iterator aGroupIter;
308   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
309     if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theFeature)) {
310       if (!(*aGroupIter)->isEmpty())
311         theGroupIDs.insert((*aGroupIter)->getId());
312       else if (!anEmptyGroup)
313         anEmptyGroup = *aGroupIter;
314     }
315
316   // When only empty group is found, use it
317   if (anEmptyGroup && theGroupIDs.empty())
318     theGroupIDs.insert(anEmptyGroup->getId());
319 }
320
321 // ============================================================================
322 //  Function: findWorkplane
323 //  Class:    SketchSolver_Session
324 //  Purpose:  search workplane containing given feature
325 // ============================================================================
326 std::shared_ptr<ModelAPI_CompositeFeature> SketchSolver_ConstraintManager
327 ::findWorkplane(std::shared_ptr<SketchPlugin_Feature> theFeature) const
328 {
329   // Already verified workplanes
330   std::set<std::shared_ptr<ModelAPI_CompositeFeature> > aVerified;
331
332   std::vector<SketchSolver_Group*>::const_iterator aGroupIter;
333   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
334     std::shared_ptr<ModelAPI_CompositeFeature> aWP = (*aGroupIter)->getWorkplane();
335     if (aVerified.find(aWP) != aVerified.end())
336       continue;
337
338     DataPtr aData = aWP->data();
339     if (aData->isValid()) {
340       std::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures = std::dynamic_pointer_cast<
341           ModelAPI_AttributeRefList>(aData->attribute(SketchPlugin_Sketch::FEATURES_ID()));
342       std::list<ObjectPtr> aFeaturesList = aWPFeatures->list();
343       std::list<ObjectPtr>::const_iterator anIter;
344       for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
345         if (*anIter == theFeature)
346           return aWP;  // workplane is found
347     }
348     aVerified.insert(aWP);
349   }
350
351   return std::shared_ptr<ModelAPI_CompositeFeature>();
352 }
353
354 // ============================================================================
355 //  Function: resolveConstraints
356 //  Class:    SketchSolver_Session
357 //  Purpose:  change entities according to available constraints
358 // ============================================================================
359 void SketchSolver_ConstraintManager::resolveConstraints(const bool theForceUpdate)
360 {
361   myIsComputed = true;
362   bool needToUpdate = false;
363   static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
364   // to avoid redisplay of each segment on update by solver one by one in the viewer
365   bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
366   if (isUpdateFlushed) {
367     Events_Loop::loop()->setFlushed(anUpdateEvent, false);
368   }
369
370   std::vector<SketchSolver_Group*>::iterator aGroupIter;
371   for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
372     if ((*aGroupIter)->resolveConstraints())
373       needToUpdate = true;
374
375   // Features may be updated => now send events, but for all changed at once
376   if (isUpdateFlushed) {
377     Events_Loop::loop()->setFlushed(anUpdateEvent, true);
378   }
379   // Must be before flush because on "Updated" flush the results may be produced
380   // and the creation event is appeared with many new objects. If myIsComputed these
381   // events are missed in processEvents and some elements are not added.
382   myIsComputed = false;
383   if (needToUpdate || theForceUpdate)
384     Events_Loop::loop()->flush(anUpdateEvent);
385 }
386
387
388
389
390 // ===========   Auxiliary functions   ========================================
391 static double featureToVal(FeaturePtr theFeature)
392 {
393   if (theFeature->getKind() == SketchPlugin_Sketch::ID())
394     return 0.0; // sketch
395   ConstraintPtr aConstraint = std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
396   if (!aConstraint)
397     return 1.0; // features (arc, circle, line, point)
398
399   const std::string& anID = aConstraint->getKind();
400   if (anID == SketchPlugin_ConstraintCoincidence::ID())
401     return 2.0;
402   if (anID == SketchPlugin_ConstraintDistance::ID() ||
403       anID == SketchPlugin_ConstraintLength::ID() ||
404       anID == SketchPlugin_ConstraintRadius::ID() ||
405       anID == SketchPlugin_ConstraintAngle::ID())
406     return 3.0;
407   if (anID == SketchPlugin_ConstraintHorizontal::ID() ||
408       anID == SketchPlugin_ConstraintVertical::ID() ||
409       anID == SketchPlugin_ConstraintParallel::ID() ||
410       anID == SketchPlugin_ConstraintPerpendicular::ID())
411     return 4.0;
412   if (anID == SketchPlugin_ConstraintEqual::ID())
413     return 5.0;
414   if (anID == SketchPlugin_ConstraintTangent::ID() ||
415       anID == SketchPlugin_ConstraintMirror::ID())
416     return 6.0;
417   if (anID == SketchPlugin_ConstraintRigid::ID())
418     return 7.0;
419   if (anID == SketchPlugin_MultiRotation::ID() ||
420       anID == SketchPlugin_MultiTranslation::ID())
421     return 8.0;
422
423   // all other constraints are placed between Equal and Tangent constraints
424   return 5.5;
425 }
426
427 static bool operator< (FeaturePtr theFeature1, FeaturePtr theFeature2)
428 {
429   return featureToVal(theFeature1) < featureToVal(theFeature2);
430 }
431
432 std::list<FeaturePtr> selectApplicableFeatures(const std::set<ObjectPtr>& theObjects)
433 {
434   std::list<FeaturePtr> aResult;
435   std::list<FeaturePtr>::iterator aResIt;
436
437   std::set<ObjectPtr>::const_iterator anObjIter = theObjects.begin();
438   for (; anObjIter != theObjects.end(); ++anObjIter) {
439     // Operate sketch itself and SketchPlugin features only.
440     // Also, the Fillet need to be skipped, because there are several separated constraints composing it.
441     FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anObjIter);
442     if (!aFeature)
443       continue;
444     std::shared_ptr<SketchPlugin_Feature> aSketchFeature = 
445         std::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
446     if ((aFeature->getKind() != SketchPlugin_Sketch::ID() && !aSketchFeature) ||
447         aFeature->getKind() == SketchPlugin_ConstraintFillet::ID())
448       continue;
449
450     // Find the place where to insert a feature
451     for (aResIt = aResult.begin(); aResIt != aResult.end(); ++aResIt)
452       if (aFeature < *aResIt)
453         break;
454     aResult.insert(aResIt, aFeature);
455   }
456
457   return aResult;
458 }
459