1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
3 // File: SketchSolver_ConstraintManager.cpp
4 // Created: 08 May 2014
5 // Author: Artem ZHIDKOV
7 #include "SketchSolver_ConstraintManager.h"
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>
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>
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>
47 // Initialization of constraint manager self pointer
48 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::_self = 0;
50 /// Global constraint manager object
51 SketchSolver_ConstraintManager* myManager = SketchSolver_ConstraintManager::Instance();
54 /// \brief Select and sort features applicable for SketchSolver
55 static std::list<FeaturePtr> selectApplicableFeatures(const std::set<ObjectPtr>& theObjects);
57 // ========================================================
58 // ========= SketchSolver_ConstraintManager ===============
59 // ========================================================
60 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::Instance()
63 _self = new SketchSolver_ConstraintManager();
67 SketchSolver_ConstraintManager::SketchSolver_ConstraintManager()
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));
79 SketchSolver_ConstraintManager::~SketchSolver_ConstraintManager()
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)
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();
101 // Shows the message has at least one feature applicable for solver
102 bool hasProperFeature = false;
104 bool isMovedEvt = theMessage->eventID()
105 == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED);
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);
112 moveEntity(aSFeature);
113 hasProperFeature = true;
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;
126 std::shared_ptr<SketchPlugin_Feature> aFeature =
127 std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
130 hasProperFeature = changeConstraintOrEntity(aFeature) || hasProperFeature;
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();
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)
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
155 int aShift = aGroupIter - myGroups.begin();
156 myGroups.erase(aGroupIter);
157 aGroupIter = myGroups.begin() + aShift;
160 if (!(*aGroupIter)->isConsistent()) { // some constraints were removed, try to split the group
161 (*aGroupIter)->splitGroup(aSeparatedGroups);
165 if (aSeparatedGroups.size() > 0)
166 myGroups.insert(myGroups.end(), aSeparatedGroups.begin(), aSeparatedGroups.end());
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)
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)) {
185 if (!(*aGroupIter)->updateWorkplane())
188 // If the workplane is not updated, so this is a new workplane
190 SketchSolver_Group* aNewGroup = new SketchSolver_Group(theSketch);
191 // Verify that the group is created successfully
192 if (!aNewGroup->isBaseWorkplane(theSketch) || !aNewGroup->isWorkplaneValid()) {
196 myGroups.push_back(aNewGroup);
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)
209 // Search the groups which this feature touches
210 std::set<Slvs_hGroup> aGroups;
211 findGroups(theFeature, aGroups);
213 std::shared_ptr<SketchPlugin_Constraint> aConstraint =
214 std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
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);
224 SketchSolver_Group* aGroup = new SketchSolver_Group(aWP);
225 if (!aGroup->changeConstraint(aConstraint)) {
229 myGroups.push_back(aGroup);
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);
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();
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)
249 if (aFirstGroupIter == myGroups.end())
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)
258 if (anOtherGroupIter == myGroups.end()) { // Group disappears
259 anOtherGroupIter = aFirstGroupIter + 1;
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;
273 return (*aFirstGroupIter)->changeConstraint(aConstraint);
274 return (*aFirstGroupIter)->updateFeature(theFeature);
277 // Something goes wrong
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)
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);
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
304 std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(theFeature);
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;
316 // When only empty group is found, use it
317 if (anEmptyGroup && theGroupIDs.empty())
318 theGroupIDs.insert(anEmptyGroup->getId());
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
329 // Already verified workplanes
330 std::set<std::shared_ptr<ModelAPI_CompositeFeature> > aVerified;
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())
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
348 aVerified.insert(aWP);
351 return std::shared_ptr<ModelAPI_CompositeFeature>();
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)
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);
370 std::vector<SketchSolver_Group*>::iterator aGroupIter;
371 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
372 if ((*aGroupIter)->resolveConstraints())
375 // Features may be updated => now send events, but for all changed at once
376 if (isUpdateFlushed) {
377 Events_Loop::loop()->setFlushed(anUpdateEvent, true);
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);
390 // =========== Auxiliary functions ========================================
391 static double featureToVal(FeaturePtr theFeature)
393 if (theFeature->getKind() == SketchPlugin_Sketch::ID())
394 return 0.0; // sketch
395 ConstraintPtr aConstraint = std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
397 return 1.0; // features (arc, circle, line, point)
399 const std::string& anID = aConstraint->getKind();
400 if (anID == SketchPlugin_ConstraintCoincidence::ID())
402 if (anID == SketchPlugin_ConstraintDistance::ID() ||
403 anID == SketchPlugin_ConstraintLength::ID() ||
404 anID == SketchPlugin_ConstraintRadius::ID() ||
405 anID == SketchPlugin_ConstraintAngle::ID())
407 if (anID == SketchPlugin_ConstraintHorizontal::ID() ||
408 anID == SketchPlugin_ConstraintVertical::ID() ||
409 anID == SketchPlugin_ConstraintParallel::ID() ||
410 anID == SketchPlugin_ConstraintPerpendicular::ID())
412 if (anID == SketchPlugin_ConstraintEqual::ID())
414 if (anID == SketchPlugin_ConstraintTangent::ID() ||
415 anID == SketchPlugin_ConstraintMirror::ID())
417 if (anID == SketchPlugin_ConstraintRigid::ID())
419 if (anID == SketchPlugin_MultiRotation::ID() ||
420 anID == SketchPlugin_MultiTranslation::ID())
423 // all other constraints are placed between Equal and Tangent constraints
427 static bool operator< (FeaturePtr theFeature1, FeaturePtr theFeature2)
429 return featureToVal(theFeature1) < featureToVal(theFeature2);
432 std::list<FeaturePtr> selectApplicableFeatures(const std::set<ObjectPtr>& theObjects)
434 std::list<FeaturePtr> aResult;
435 std::list<FeaturePtr>::iterator aResIt;
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);
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())
450 // Find the place where to insert a feature
451 for (aResIt = aResult.begin(); aResIt != aResult.end(); ++aResIt)
452 if (aFeature < *aResIt)
454 aResult.insert(aResIt, aFeature);