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>
18 #include <SketchPlugin_Constraint.h>
20 #include <SketchPlugin_Arc.h>
21 #include <SketchPlugin_Circle.h>
22 #include <SketchPlugin_Line.h>
23 #include <SketchPlugin_Point.h>
24 #include <SketchPlugin_Sketch.h>
25 #include <SketchPlugin_Feature.h>
31 static const Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
33 // Initialization of constraint manager self pointer
34 SketchSolver_Manager* SketchSolver_Manager::mySelf = 0;
36 /// Global constraint manager object
37 SketchSolver_Manager* myManager = SketchSolver_Manager::instance();
40 // ========================================================
41 // ========= SketchSolver_Manager ===============
42 // ========================================================
43 SketchSolver_Manager* SketchSolver_Manager::instance()
46 mySelf = new SketchSolver_Manager();
50 SketchSolver_Manager::SketchSolver_Manager()
55 // Register in event loop
56 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
57 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
58 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
59 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_MOVED));
62 SketchSolver_Manager::~SketchSolver_Manager()
67 void SketchSolver_Manager::setBuilder(BuilderPtr theBuilder)
69 myBuilder = theBuilder;
72 BuilderPtr SketchSolver_Manager::builder()
77 // ============================================================================
78 // Function: processEvent
79 // Purpose: listen the event loop and process the message
80 // ============================================================================
81 void SketchSolver_Manager::processEvent(
82 const std::shared_ptr<Events_Message>& theMessage)
87 if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)
88 || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED)
89 || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED)) {
90 std::shared_ptr<ModelAPI_ObjectUpdatedMessage> anUpdateMsg =
91 std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
92 std::set<ObjectPtr> aFeatures = anUpdateMsg->objects();
94 bool isUpdateFlushed = stopSendUpdate();
95 // Shows the message has at least one feature applicable for solver
96 bool hasProperFeature = false;
98 bool isMovedEvt = theMessage->eventID()
99 == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED);
101 std::set<ObjectPtr>::iterator aFeatIter;
102 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
103 std::shared_ptr<SketchPlugin_Feature> aSFeature =
104 std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
106 moveEntity(aSFeature);
107 hasProperFeature = true;
111 std::list<FeaturePtr> aSketchFeatures = SketchSolver_Group::selectApplicableFeatures(aFeatures);
112 std::list<FeaturePtr>::iterator aFeatIter = aSketchFeatures.begin();
113 for (; aFeatIter != aSketchFeatures.end(); ++aFeatIter) {
114 if ((*aFeatIter)->getKind() == SketchPlugin_Sketch::ID()) {
115 std::shared_ptr<ModelAPI_CompositeFeature> aSketch =
116 std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aFeatIter);
117 hasProperFeature = changeWorkplane(aSketch) || hasProperFeature;
120 std::shared_ptr<SketchPlugin_Feature> aFeature =
121 std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
124 hasProperFeature = changeFeature(aFeature) || hasProperFeature;
128 bool needToUpdate = false;
129 // Solve the set of constraints
130 if (hasProperFeature)
131 needToUpdate = resolveConstraints();
133 // Features may be updated => now send events, but for all changed at once
136 // send update for movement in any case
137 if (needToUpdate || isMovedEvt)
138 Events_Loop::loop()->flush(anUpdateEvent);
140 } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
141 std::shared_ptr<ModelAPI_ObjectDeletedMessage> aDeleteMsg =
142 std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
143 const std::set<std::string>& aFeatureGroups = aDeleteMsg->groups();
145 // Find SketchPlugin_Sketch::ID() in groups. The constraint groups should be updated when an object removed from Sketch
146 std::set<std::string>::const_iterator aFGrIter;
147 for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
148 if (aFGrIter->compare(ModelAPI_ResultConstruction::group()) == 0 ||
149 aFGrIter->compare(ModelAPI_Feature::group()) == 0)
152 if (aFGrIter != aFeatureGroups.end()) {
153 std::vector<SketchSolver_Group*>::iterator aGroupIter = myGroups.begin();
154 std::list<SketchSolver_Group*> aSeparatedGroups;
155 while (aGroupIter != myGroups.end()) {
156 if (!(*aGroupIter)->isWorkplaneValid()) { // the group should be removed
158 int aShift = aGroupIter - myGroups.begin();
159 myGroups.erase(aGroupIter);
160 aGroupIter = myGroups.begin() + aShift;
163 if (!(*aGroupIter)->isConsistent()) { // some constraints were removed, try to split the group
164 (*aGroupIter)->splitGroup(aSeparatedGroups);
168 if (aSeparatedGroups.size() > 0)
169 myGroups.insert(myGroups.end(), aSeparatedGroups.begin(), aSeparatedGroups.end());
172 myIsComputed = false;
175 // ============================================================================
176 // Function: changeWorkplane
177 // Purpose: update workplane by given parameters of the sketch
178 // ============================================================================
179 bool SketchSolver_Manager::changeWorkplane(CompositeFeaturePtr theSketch)
181 bool aResult = true; // changed when a workplane wrongly updated
182 bool isUpdated = false;
183 // Try to update specified workplane in all groups
184 std::vector<SketchSolver_Group*>::iterator aGroupIter;
185 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
186 if ((*aGroupIter)->isBaseWorkplane(theSketch)) {
190 // If the workplane is not updated, so this is a new workplane
192 SketchSolver_Group* aNewGroup = new SketchSolver_Group(theSketch);
193 // Verify that the group is created successfully
194 if (!aNewGroup->isBaseWorkplane(theSketch) || !aNewGroup->isWorkplaneValid()) {
198 myGroups.push_back(aNewGroup);
203 // ============================================================================
204 // Function: changeConstraintOrEntity
205 // Purpose: create/update the constraint or the feature and place it into appropriate group
206 // ============================================================================
207 bool SketchSolver_Manager::changeFeature(std::shared_ptr<SketchPlugin_Feature> theFeature)
209 // Search the groups which this feature touches
210 std::set<GroupID> 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 GroupID 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<GroupID>::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 (*aFirstGroupIter)->changeConstraint(aConstraint);
275 (*aFirstGroupIter)->updateFeature(theFeature);
276 // groups are merged => need to resolve them
280 // Something goes wrong
284 // ============================================================================
285 // Function: moveEntity
286 // Purpose: update element moved on the sketch, which is used by constraints
287 // ============================================================================
288 void SketchSolver_Manager::moveEntity(std::shared_ptr<SketchPlugin_Feature> theFeature)
290 bool isMoved = false;
291 std::vector<SketchSolver_Group*>::iterator aGroupIt = myGroups.begin();
292 for (; aGroupIt != myGroups.end(); aGroupIt++)
293 if (!(*aGroupIt)->isEmpty() && (*aGroupIt)->isInteract(theFeature)) {
294 (*aGroupIt)->moveFeature(theFeature);
298 if (!isMoved && theFeature->getKind() == SketchPlugin_Arc::ID()) {
299 // Workaround to move arc.
300 // If the arc has not been constrained, we will push it into empty group and apply movement.
301 for (aGroupIt = myGroups.begin(); aGroupIt != myGroups.end(); aGroupIt++)
302 if ((*aGroupIt)->isEmpty())
303 (*aGroupIt)->moveFeature(theFeature);
307 // ============================================================================
308 // Function: findGroups
309 // Purpose: search groups of entities interacting with given feature
310 // ============================================================================
311 void SketchSolver_Manager::findGroups(
312 std::shared_ptr<SketchPlugin_Feature> theFeature,
313 std::set<GroupID>& theGroupIDs) const
315 std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(theFeature);
317 SketchSolver_Group* anEmptyGroup = 0; // appropriate empty group for specified constraint
318 std::vector<SketchSolver_Group*>::const_iterator aGroupIter;
319 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
320 if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theFeature)) {
321 if (!(*aGroupIter)->isEmpty())
322 theGroupIDs.insert((*aGroupIter)->getId());
323 else if (!anEmptyGroup)
324 anEmptyGroup = *aGroupIter;
327 // When only empty group is found, use it
328 if (anEmptyGroup && theGroupIDs.empty())
329 theGroupIDs.insert(anEmptyGroup->getId());
332 // ============================================================================
333 // Function: findWorkplane
334 // Purpose: search workplane containing given feature
335 // ============================================================================
336 std::shared_ptr<ModelAPI_CompositeFeature> SketchSolver_Manager
337 ::findWorkplane(std::shared_ptr<SketchPlugin_Feature> theFeature) const
339 // Already verified workplanes
340 std::set<std::shared_ptr<ModelAPI_CompositeFeature> > aVerified;
342 std::vector<SketchSolver_Group*>::const_iterator aGroupIter;
343 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
344 std::shared_ptr<ModelAPI_CompositeFeature> aWP = (*aGroupIter)->getWorkplane();
345 if (aVerified.find(aWP) != aVerified.end())
348 DataPtr aData = aWP->data();
349 if (aData->isValid()) {
350 std::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures = std::dynamic_pointer_cast<
351 ModelAPI_AttributeRefList>(aData->attribute(SketchPlugin_Sketch::FEATURES_ID()));
352 std::list<ObjectPtr> aFeaturesList = aWPFeatures->list();
353 std::list<ObjectPtr>::const_iterator anIter;
354 for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
355 if (*anIter == theFeature)
356 return aWP; // workplane is found
358 aVerified.insert(aWP);
361 return std::shared_ptr<ModelAPI_CompositeFeature>();
364 // ============================================================================
365 // Function: resolveConstraints
366 // Purpose: change entities according to available constraints
367 // ============================================================================
368 bool SketchSolver_Manager::resolveConstraints()
370 bool needToUpdate = false;
371 std::vector<SketchSolver_Group*>::iterator aGroupIter;
372 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
373 if ((*aGroupIter)->resolveConstraints())
378 bool SketchSolver_Manager::stopSendUpdate() const
380 // to avoid redisplay of each segment on update by solver one by one in the viewer
381 bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
382 if (isUpdateFlushed) {
383 Events_Loop::loop()->setFlushed(anUpdateEvent, false);
385 return isUpdateFlushed;
388 void SketchSolver_Manager::allowSendUpdate() const
390 Events_Loop::loop()->setFlushed(anUpdateEvent, true);