1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
3 // File: SketchSolver_Manager.cpp
4 // Created: 08 May 2014
5 // Author: Artem ZHIDKOV
7 #include "SketchSolver_Manager.h"
8 #include "SketchSolver_Error.h"
10 #include <Events_Loop.h>
11 #include <ModelAPI_AttributeDouble.h>
12 #include <ModelAPI_AttributeRefList.h>
13 #include <ModelAPI_Data.h>
14 #include <ModelAPI_Events.h>
15 #include <ModelAPI_Object.h>
16 #include <ModelAPI_ResultConstruction.h>
17 #include <ModelAPI_Attribute.h>
18 #include <ModelAPI_AttributeString.h>
20 #include <SketchPlugin_Constraint.h>
22 #include <SketchPlugin_Arc.h>
23 #include <SketchPlugin_Circle.h>
24 #include <SketchPlugin_Line.h>
25 #include <SketchPlugin_Point.h>
26 #include <SketchPlugin_Sketch.h>
27 #include <SketchPlugin_Feature.h>
33 static const Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
35 // Initialization of constraint manager self pointer
36 SketchSolver_Manager* SketchSolver_Manager::mySelf = 0;
38 /// Global constraint manager object
39 SketchSolver_Manager* myManager = SketchSolver_Manager::instance();
42 // ========================================================
43 // ========= SketchSolver_Manager ===============
44 // ========================================================
45 SketchSolver_Manager* SketchSolver_Manager::instance()
48 mySelf = new SketchSolver_Manager();
52 SketchSolver_Manager::SketchSolver_Manager()
57 // Register in event loop
58 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
59 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
60 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
61 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_MOVED));
63 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_SOLVER_FAILED));
64 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_SOLVER_REPAIRED));
67 SketchSolver_Manager::~SketchSolver_Manager()
72 void SketchSolver_Manager::setBuilder(BuilderPtr theBuilder)
74 myBuilder = theBuilder;
77 BuilderPtr SketchSolver_Manager::builder()
82 // ============================================================================
83 // Function: processEvent
84 // Purpose: listen the event loop and process the message
85 // ============================================================================
86 void SketchSolver_Manager::processEvent(
87 const std::shared_ptr<Events_Message>& theMessage)
89 checkConflictingConstraints(theMessage);
93 if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)
94 || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED)
95 || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED)) {
96 std::shared_ptr<ModelAPI_ObjectUpdatedMessage> anUpdateMsg =
97 std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
98 std::set<ObjectPtr> aFeatures = anUpdateMsg->objects();
100 bool isUpdateFlushed = stopSendUpdate();
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 = SketchSolver_Group::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 = changeFeature(aFeature) || hasProperFeature;
134 bool needToUpdate = false;
135 // Solve the set of constraints
136 if (hasProperFeature)
137 needToUpdate = resolveConstraints();
139 // Features may be updated => now send events, but for all changed at once
142 // send update for movement in any case
143 if (needToUpdate || isMovedEvt)
144 Events_Loop::loop()->flush(anUpdateEvent);
146 } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
147 std::shared_ptr<ModelAPI_ObjectDeletedMessage> aDeleteMsg =
148 std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
149 const std::set<std::string>& aFeatureGroups = aDeleteMsg->groups();
151 // Find SketchPlugin_Sketch::ID() in groups. The constraint groups should be updated when an object removed from Sketch
152 std::set<std::string>::const_iterator aFGrIter;
153 for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
154 if (aFGrIter->compare(ModelAPI_ResultConstruction::group()) == 0 ||
155 aFGrIter->compare(ModelAPI_Feature::group()) == 0)
158 if (aFGrIter != aFeatureGroups.end()) {
159 std::list<SketchSolver_Group*> aGroupsToResolve;
160 std::list<SketchSolver_Group*>::iterator aGroupIter = myGroups.begin();
161 std::list<SketchSolver_Group*> aSeparatedGroups;
162 while (aGroupIter != myGroups.end()) {
163 if (!(*aGroupIter)->isWorkplaneValid()) { // the group should be removed
165 std::list<SketchSolver_Group*>::iterator aRemoveIt = aGroupIter++;
166 myGroups.erase(aRemoveIt);
169 if (!(*aGroupIter)->isConsistent()) { // some constraints were removed, try to split the group
170 (*aGroupIter)->splitGroup(aSeparatedGroups);
171 //if (!(*aGroupIter)->getWorkplane()->string(
172 // SketchPlugin_Sketch::SOLVER_ERROR())->value().empty())
173 aGroupsToResolve.push_back(*aGroupIter);
177 if (aSeparatedGroups.size() > 0) {
178 myGroups.insert(myGroups.end(), aSeparatedGroups.begin(), aSeparatedGroups.end());
179 aGroupsToResolve.insert(aGroupsToResolve.end(),
180 aSeparatedGroups.begin(), aSeparatedGroups.end());
183 if (!aGroupsToResolve.empty())
184 resolveConstraints(aGroupsToResolve);
187 myIsComputed = false;
190 void SketchSolver_Manager::checkConflictingConstraints(const std::shared_ptr<Events_Message>& theMessage)
192 if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_SOLVER_REPAIRED)) {
193 std::shared_ptr<ModelAPI_SolverFailedMessage> aMessage =
194 std::dynamic_pointer_cast<ModelAPI_SolverFailedMessage>(theMessage);
195 std::set<ObjectPtr> aSentObjs = aMessage->objects();
196 if (!aSentObjs.empty()) {
197 // Obtain sketch where the constraints are placed.
198 // It is enough to check only one constraint.
199 CompositeFeaturePtr aSketch;
200 FeaturePtr aConstraint = ModelAPI_Feature::feature(*aSentObjs.begin());
201 std::list<SketchSolver_Group*>::const_iterator aGrIt = myGroups.begin();
202 for (; aGrIt != myGroups.end(); ++aGrIt)
203 if ((*aGrIt)->isInteract(aConstraint)) {
204 aSketch = (*aGrIt)->getWorkplane();
208 // Search failed groups built on the same sketch
210 for (aGrIt = myGroups.begin(); aGrIt != myGroups.end(); ++aGrIt) {
211 SketchSolver_Group* aGroup = *aGrIt;
212 if (aGroup->isBaseWorkplane(aSketch) && aGroup->isFailed() &&
213 !aGroup->isInteract(aConstraint)) {
214 // reset error message on the sketch
215 aGroup->getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue(
216 SketchSolver_Error::CONSTRAINTS());
225 // ============================================================================
226 // Function: changeWorkplane
227 // Purpose: update workplane by given parameters of the sketch
228 // ============================================================================
229 bool SketchSolver_Manager::changeWorkplane(CompositeFeaturePtr theSketch)
231 bool aResult = true; // changed when a workplane wrongly updated
232 bool isUpdated = false;
233 // Try to update specified workplane in all groups
234 std::list<SketchSolver_Group*>::iterator aGroupIter;
235 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
236 if ((*aGroupIter)->isBaseWorkplane(theSketch)) {
240 // If the workplane is not updated, so this is a new workplane
242 SketchSolver_Group* aNewGroup = new SketchSolver_Group(theSketch);
243 // Verify that the group is created successfully
244 if (!aNewGroup->isBaseWorkplane(theSketch) || !aNewGroup->isWorkplaneValid()) {
248 myGroups.push_back(aNewGroup);
253 // ============================================================================
254 // Function: changeConstraintOrEntity
255 // Purpose: create/update the constraint or the feature and place it into appropriate group
256 // ============================================================================
257 bool SketchSolver_Manager::changeFeature(std::shared_ptr<SketchPlugin_Feature> theFeature)
259 // Search the groups which this feature touches
260 std::set<GroupID> aGroups;
261 findGroups(theFeature, aGroups);
263 std::shared_ptr<SketchPlugin_Constraint> aConstraint =
264 std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
266 // Process the groups list
267 if (aGroups.size() == 0) {
268 // There are no groups applicable for this constraint => create new one
269 // The group will be created only for constraints, not for features
270 if (!aConstraint) return false;
271 std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(aConstraint);
274 SketchSolver_Group* aGroup = new SketchSolver_Group(aWP);
275 if (!aGroup->changeConstraint(aConstraint)) {
279 myGroups.push_back(aGroup);
281 } else if (aGroups.size() == 1) { // Only one group => add feature into it
282 GroupID aGroupId = *(aGroups.begin());
283 std::list<SketchSolver_Group*>::iterator aGroupIter;
284 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
285 if ((*aGroupIter)->getId() == aGroupId) {
286 // If the group is empty, the feature is not added (the constraint only)
287 if (!aConstraint && !(*aGroupIter)->isEmpty())
288 return (*aGroupIter)->updateFeature(theFeature);
289 return (*aGroupIter)->changeConstraint(aConstraint);
291 } else if (aGroups.size() > 1) { // Several groups applicable for this feature => need to merge them
292 std::set<GroupID>::const_iterator aGroupsIter = aGroups.begin();
294 // Search first group
295 std::list<SketchSolver_Group*>::iterator aFirstGroupIter;
296 for (aFirstGroupIter = myGroups.begin(); aFirstGroupIter != myGroups.end(); aFirstGroupIter++)
297 if ((*aFirstGroupIter)->getId() == *aGroupsIter)
299 if (aFirstGroupIter == myGroups.end())
302 // Append other groups to the first one
303 std::list<SketchSolver_Group*>::iterator anOtherGroupIter = aFirstGroupIter;
305 for (aGroupsIter++; aGroupsIter != aGroups.end(); aGroupsIter++) {
306 for (; anOtherGroupIter != myGroups.end(); anOtherGroupIter++)
307 if ((*anOtherGroupIter)->getId() == *aGroupsIter)
309 if (anOtherGroupIter == myGroups.end()) { // Group disappears
310 anOtherGroupIter = aFirstGroupIter;
315 (*aFirstGroupIter)->mergeGroups(**anOtherGroupIter);
316 std::list<SketchSolver_Group*>::iterator aRemoveIt = anOtherGroupIter++;
318 myGroups.erase(aRemoveIt);
322 (*aFirstGroupIter)->changeConstraint(aConstraint);
324 (*aFirstGroupIter)->updateFeature(theFeature);
325 // groups are merged => need to resolve them
329 // Something goes wrong
333 // ============================================================================
334 // Function: moveEntity
335 // Purpose: update element moved on the sketch, which is used by constraints
336 // ============================================================================
337 void SketchSolver_Manager::moveEntity(std::shared_ptr<SketchPlugin_Feature> theFeature)
339 bool isMoved = false;
340 std::list<SketchSolver_Group*>::iterator aGroupIt = myGroups.begin();
341 for (; aGroupIt != myGroups.end(); aGroupIt++)
342 if (!(*aGroupIt)->isEmpty() && (*aGroupIt)->isInteract(theFeature)) {
343 (*aGroupIt)->moveFeature(theFeature);
347 if (!isMoved && theFeature->getKind() == SketchPlugin_Arc::ID()) {
348 // Workaround to move arc.
349 // If the arc has not been constrained, we will push it into empty group and apply movement.
350 for (aGroupIt = myGroups.begin(); aGroupIt != myGroups.end(); aGroupIt++)
351 if ((*aGroupIt)->isEmpty())
352 (*aGroupIt)->moveFeature(theFeature);
356 // ============================================================================
357 // Function: findGroups
358 // Purpose: search groups of entities interacting with given feature
359 // ============================================================================
360 void SketchSolver_Manager::findGroups(
361 std::shared_ptr<SketchPlugin_Feature> theFeature,
362 std::set<GroupID>& theGroupIDs) const
364 std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(theFeature);
366 SketchSolver_Group* anEmptyGroup = 0; // appropriate empty group for specified constraint
367 std::list<SketchSolver_Group*>::const_iterator aGroupIter;
368 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
369 if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theFeature)) {
370 if (!(*aGroupIter)->isEmpty())
371 theGroupIDs.insert((*aGroupIter)->getId());
372 else if (!anEmptyGroup)
373 anEmptyGroup = *aGroupIter;
376 // When only empty group is found, use it
377 if (anEmptyGroup && theGroupIDs.empty())
378 theGroupIDs.insert(anEmptyGroup->getId());
381 // ============================================================================
382 // Function: findWorkplane
383 // Purpose: search workplane containing given feature
384 // ============================================================================
385 std::shared_ptr<ModelAPI_CompositeFeature> SketchSolver_Manager
386 ::findWorkplane(std::shared_ptr<SketchPlugin_Feature> theFeature) const
388 // Already verified workplanes
389 std::set<std::shared_ptr<ModelAPI_CompositeFeature> > aVerified;
391 std::list<SketchSolver_Group*>::const_iterator aGroupIter;
392 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
393 std::shared_ptr<ModelAPI_CompositeFeature> aWP = (*aGroupIter)->getWorkplane();
394 if (aVerified.find(aWP) != aVerified.end())
397 DataPtr aData = aWP->data();
398 if (aData->isValid()) {
399 std::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures = std::dynamic_pointer_cast<
400 ModelAPI_AttributeRefList>(aData->attribute(SketchPlugin_Sketch::FEATURES_ID()));
401 std::list<ObjectPtr> aFeaturesList = aWPFeatures->list();
402 std::list<ObjectPtr>::const_iterator anIter;
403 for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
404 if (*anIter == theFeature)
405 return aWP; // workplane is found
407 aVerified.insert(aWP);
410 return std::shared_ptr<ModelAPI_CompositeFeature>();
413 // ============================================================================
414 // Function: resolveConstraints
415 // Purpose: change entities according to available constraints
416 // ============================================================================
417 bool SketchSolver_Manager::resolveConstraints(const std::list<SketchSolver_Group*>& theGroups)
419 bool needToUpdate = false;
420 const std::list<SketchSolver_Group*>& aGroupsToResolve = theGroups.empty() ? myGroups : theGroups;
421 std::list<SketchSolver_Group*>::const_iterator aGroupIter = aGroupsToResolve.begin();
422 for (; aGroupIter != aGroupsToResolve.end(); aGroupIter++)
423 if ((*aGroupIter)->resolveConstraints())
428 bool SketchSolver_Manager::stopSendUpdate() const
430 // to avoid redisplay of each segment on update by solver one by one in the viewer
431 bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
432 if (isUpdateFlushed) {
433 Events_Loop::loop()->setFlushed(anUpdateEvent, false);
435 return isUpdateFlushed;
438 void SketchSolver_Manager::allowSendUpdate() const
440 Events_Loop::loop()->setFlushed(anUpdateEvent, true);