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"
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 #include <ModelAPI_AttributeString.h>
19 #include <SketchPlugin_Constraint.h>
21 #include <SketchPlugin_Arc.h>
22 #include <SketchPlugin_Circle.h>
23 #include <SketchPlugin_Line.h>
24 #include <SketchPlugin_Point.h>
25 #include <SketchPlugin_Sketch.h>
26 #include <SketchPlugin_Feature.h>
32 static const Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
34 // Initialization of constraint manager self pointer
35 SketchSolver_Manager* SketchSolver_Manager::mySelf = 0;
37 /// Global constraint manager object
38 SketchSolver_Manager* myManager = SketchSolver_Manager::instance();
41 // ========================================================
42 // ========= SketchSolver_Manager ===============
43 // ========================================================
44 SketchSolver_Manager* SketchSolver_Manager::instance()
47 mySelf = new SketchSolver_Manager();
51 SketchSolver_Manager::SketchSolver_Manager()
56 // Register in event loop
57 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
58 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
59 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
60 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_MOVED));
63 SketchSolver_Manager::~SketchSolver_Manager()
68 void SketchSolver_Manager::setBuilder(BuilderPtr theBuilder)
70 myBuilder = theBuilder;
73 BuilderPtr SketchSolver_Manager::builder()
78 // ============================================================================
79 // Function: processEvent
80 // Purpose: listen the event loop and process the message
81 // ============================================================================
82 void SketchSolver_Manager::processEvent(
83 const std::shared_ptr<Events_Message>& theMessage)
88 if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)
89 || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED)
90 || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED)) {
91 std::shared_ptr<ModelAPI_ObjectUpdatedMessage> anUpdateMsg =
92 std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
93 std::set<ObjectPtr> aFeatures = anUpdateMsg->objects();
95 bool isUpdateFlushed = stopSendUpdate();
96 // Shows the message has at least one feature applicable for solver
97 bool hasProperFeature = false;
99 bool isMovedEvt = theMessage->eventID()
100 == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED);
102 std::set<ObjectPtr>::iterator aFeatIter;
103 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
104 std::shared_ptr<SketchPlugin_Feature> aSFeature =
105 std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
107 moveEntity(aSFeature);
108 hasProperFeature = true;
112 std::list<FeaturePtr> aSketchFeatures = SketchSolver_Group::selectApplicableFeatures(aFeatures);
113 std::list<FeaturePtr>::iterator aFeatIter = aSketchFeatures.begin();
114 for (; aFeatIter != aSketchFeatures.end(); ++aFeatIter) {
115 if ((*aFeatIter)->getKind() == SketchPlugin_Sketch::ID()) {
116 std::shared_ptr<ModelAPI_CompositeFeature> aSketch =
117 std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aFeatIter);
118 hasProperFeature = changeWorkplane(aSketch) || hasProperFeature;
121 std::shared_ptr<SketchPlugin_Feature> aFeature =
122 std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
125 hasProperFeature = changeFeature(aFeature) || hasProperFeature;
129 bool needToUpdate = false;
130 // Solve the set of constraints
131 if (hasProperFeature)
132 needToUpdate = resolveConstraints();
134 // Features may be updated => now send events, but for all changed at once
137 // send update for movement in any case
138 if (needToUpdate || isMovedEvt)
139 Events_Loop::loop()->flush(anUpdateEvent);
141 } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
142 std::shared_ptr<ModelAPI_ObjectDeletedMessage> aDeleteMsg =
143 std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
144 const std::set<std::string>& aFeatureGroups = aDeleteMsg->groups();
146 // Find SketchPlugin_Sketch::ID() in groups. The constraint groups should be updated when an object removed from Sketch
147 std::set<std::string>::const_iterator aFGrIter;
148 for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
149 if (aFGrIter->compare(ModelAPI_ResultConstruction::group()) == 0 ||
150 aFGrIter->compare(ModelAPI_Feature::group()) == 0)
153 if (aFGrIter != aFeatureGroups.end()) {
154 std::list<SketchSolver_Group*> aGroupsToResolve;
155 std::list<SketchSolver_Group*>::iterator aGroupIter = myGroups.begin();
156 std::list<SketchSolver_Group*> aSeparatedGroups;
157 while (aGroupIter != myGroups.end()) {
158 if (!(*aGroupIter)->isWorkplaneValid()) { // the group should be removed
160 std::list<SketchSolver_Group*>::iterator aRemoveIt = aGroupIter++;
161 myGroups.erase(aRemoveIt);
164 if (!(*aGroupIter)->isConsistent()) { // some constraints were removed, try to split the group
165 (*aGroupIter)->splitGroup(aSeparatedGroups);
166 if (!(*aGroupIter)->getWorkplane()->string(
167 SketchPlugin_Sketch::SOLVER_ERROR())->value().empty())
168 aGroupsToResolve.push_back(*aGroupIter);
172 if (aSeparatedGroups.size() > 0) {
173 myGroups.insert(myGroups.end(), aSeparatedGroups.begin(), aSeparatedGroups.end());
174 aGroupsToResolve.insert(aGroupsToResolve.end(),
175 aSeparatedGroups.begin(), aSeparatedGroups.end());
178 if (!aGroupsToResolve.empty())
179 resolveConstraints(aGroupsToResolve);
182 myIsComputed = false;
185 // ============================================================================
186 // Function: changeWorkplane
187 // Purpose: update workplane by given parameters of the sketch
188 // ============================================================================
189 bool SketchSolver_Manager::changeWorkplane(CompositeFeaturePtr theSketch)
191 bool aResult = true; // changed when a workplane wrongly updated
192 bool isUpdated = false;
193 // Try to update specified workplane in all groups
194 std::list<SketchSolver_Group*>::iterator aGroupIter;
195 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
196 if ((*aGroupIter)->isBaseWorkplane(theSketch)) {
200 // If the workplane is not updated, so this is a new workplane
202 SketchSolver_Group* aNewGroup = new SketchSolver_Group(theSketch);
203 // Verify that the group is created successfully
204 if (!aNewGroup->isBaseWorkplane(theSketch) || !aNewGroup->isWorkplaneValid()) {
208 myGroups.push_back(aNewGroup);
213 // ============================================================================
214 // Function: changeConstraintOrEntity
215 // Purpose: create/update the constraint or the feature and place it into appropriate group
216 // ============================================================================
217 bool SketchSolver_Manager::changeFeature(std::shared_ptr<SketchPlugin_Feature> theFeature)
219 // Search the groups which this feature touches
220 std::set<GroupID> aGroups;
221 findGroups(theFeature, aGroups);
223 std::shared_ptr<SketchPlugin_Constraint> aConstraint =
224 std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
226 // Process the groups list
227 if (aGroups.size() == 0) {
228 // There are no groups applicable for this constraint => create new one
229 // The group will be created only for constraints, not for features
230 if (!aConstraint) return false;
231 std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(aConstraint);
234 SketchSolver_Group* aGroup = new SketchSolver_Group(aWP);
235 if (!aGroup->changeConstraint(aConstraint)) {
239 myGroups.push_back(aGroup);
241 } else if (aGroups.size() == 1) { // Only one group => add feature into it
242 GroupID aGroupId = *(aGroups.begin());
243 std::list<SketchSolver_Group*>::iterator aGroupIter;
244 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
245 if ((*aGroupIter)->getId() == aGroupId) {
246 // If the group is empty, the feature is not added (the constraint only)
247 if (!aConstraint && !(*aGroupIter)->isEmpty())
248 return (*aGroupIter)->updateFeature(theFeature);
249 return (*aGroupIter)->changeConstraint(aConstraint);
251 } else if (aGroups.size() > 1) { // Several groups applicable for this feature => need to merge them
252 std::set<GroupID>::const_iterator aGroupsIter = aGroups.begin();
254 // Search first group
255 std::list<SketchSolver_Group*>::iterator aFirstGroupIter;
256 for (aFirstGroupIter = myGroups.begin(); aFirstGroupIter != myGroups.end(); aFirstGroupIter++)
257 if ((*aFirstGroupIter)->getId() == *aGroupsIter)
259 if (aFirstGroupIter == myGroups.end())
262 // Append other groups to the first one
263 std::list<SketchSolver_Group*>::iterator anOtherGroupIter = aFirstGroupIter;
265 for (aGroupsIter++; aGroupsIter != aGroups.end(); aGroupsIter++) {
266 for (; anOtherGroupIter != myGroups.end(); anOtherGroupIter++)
267 if ((*anOtherGroupIter)->getId() == *aGroupsIter)
269 if (anOtherGroupIter == myGroups.end()) { // Group disappears
270 anOtherGroupIter = aFirstGroupIter;
275 (*aFirstGroupIter)->mergeGroups(**anOtherGroupIter);
276 std::list<SketchSolver_Group*>::iterator aRemoveIt = anOtherGroupIter++;
278 myGroups.erase(aRemoveIt);
282 (*aFirstGroupIter)->changeConstraint(aConstraint);
284 (*aFirstGroupIter)->updateFeature(theFeature);
285 // groups are merged => need to resolve them
289 // Something goes wrong
293 // ============================================================================
294 // Function: moveEntity
295 // Purpose: update element moved on the sketch, which is used by constraints
296 // ============================================================================
297 void SketchSolver_Manager::moveEntity(std::shared_ptr<SketchPlugin_Feature> theFeature)
299 bool isMoved = false;
300 std::list<SketchSolver_Group*>::iterator aGroupIt = myGroups.begin();
301 for (; aGroupIt != myGroups.end(); aGroupIt++)
302 if (!(*aGroupIt)->isEmpty() && (*aGroupIt)->isInteract(theFeature)) {
303 (*aGroupIt)->moveFeature(theFeature);
307 if (!isMoved && theFeature->getKind() == SketchPlugin_Arc::ID()) {
308 // Workaround to move arc.
309 // If the arc has not been constrained, we will push it into empty group and apply movement.
310 for (aGroupIt = myGroups.begin(); aGroupIt != myGroups.end(); aGroupIt++)
311 if ((*aGroupIt)->isEmpty())
312 (*aGroupIt)->moveFeature(theFeature);
316 // ============================================================================
317 // Function: findGroups
318 // Purpose: search groups of entities interacting with given feature
319 // ============================================================================
320 void SketchSolver_Manager::findGroups(
321 std::shared_ptr<SketchPlugin_Feature> theFeature,
322 std::set<GroupID>& theGroupIDs) const
324 std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(theFeature);
326 SketchSolver_Group* anEmptyGroup = 0; // appropriate empty group for specified constraint
327 std::list<SketchSolver_Group*>::const_iterator aGroupIter;
328 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
329 if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theFeature)) {
330 if (!(*aGroupIter)->isEmpty())
331 theGroupIDs.insert((*aGroupIter)->getId());
332 else if (!anEmptyGroup)
333 anEmptyGroup = *aGroupIter;
336 // When only empty group is found, use it
337 if (anEmptyGroup && theGroupIDs.empty())
338 theGroupIDs.insert(anEmptyGroup->getId());
341 // ============================================================================
342 // Function: findWorkplane
343 // Purpose: search workplane containing given feature
344 // ============================================================================
345 std::shared_ptr<ModelAPI_CompositeFeature> SketchSolver_Manager
346 ::findWorkplane(std::shared_ptr<SketchPlugin_Feature> theFeature) const
348 // Already verified workplanes
349 std::set<std::shared_ptr<ModelAPI_CompositeFeature> > aVerified;
351 std::list<SketchSolver_Group*>::const_iterator aGroupIter;
352 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
353 std::shared_ptr<ModelAPI_CompositeFeature> aWP = (*aGroupIter)->getWorkplane();
354 if (aVerified.find(aWP) != aVerified.end())
357 DataPtr aData = aWP->data();
358 if (aData->isValid()) {
359 std::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures = std::dynamic_pointer_cast<
360 ModelAPI_AttributeRefList>(aData->attribute(SketchPlugin_Sketch::FEATURES_ID()));
361 std::list<ObjectPtr> aFeaturesList = aWPFeatures->list();
362 std::list<ObjectPtr>::const_iterator anIter;
363 for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
364 if (*anIter == theFeature)
365 return aWP; // workplane is found
367 aVerified.insert(aWP);
370 return std::shared_ptr<ModelAPI_CompositeFeature>();
373 // ============================================================================
374 // Function: resolveConstraints
375 // Purpose: change entities according to available constraints
376 // ============================================================================
377 bool SketchSolver_Manager::resolveConstraints(const std::list<SketchSolver_Group*>& theGroups)
379 bool needToUpdate = false;
380 const std::list<SketchSolver_Group*>& aGroupsToResolve = theGroups.empty() ? myGroups : theGroups;
381 std::list<SketchSolver_Group*>::const_iterator aGroupIter = aGroupsToResolve.begin();
382 for (; aGroupIter != aGroupsToResolve.end(); aGroupIter++)
383 if ((*aGroupIter)->resolveConstraints())
388 bool SketchSolver_Manager::stopSendUpdate() const
390 // to avoid redisplay of each segment on update by solver one by one in the viewer
391 bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
392 if (isUpdateFlushed) {
393 Events_Loop::loop()->setFlushed(anUpdateEvent, false);
395 return isUpdateFlushed;
398 void SketchSolver_Manager::allowSendUpdate() const
400 Events_Loop::loop()->setFlushed(anUpdateEvent, true);