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 // Initialization of constraint manager self pointer
32 SketchSolver_Manager* SketchSolver_Manager::mySelf = 0;
34 /// Global constraint manager object
35 SketchSolver_Manager* myManager = SketchSolver_Manager::instance();
38 // ========================================================
39 // ========= SketchSolver_Manager ===============
40 // ========================================================
41 SketchSolver_Manager* SketchSolver_Manager::instance()
44 mySelf = new SketchSolver_Manager();
48 SketchSolver_Manager::SketchSolver_Manager()
53 // Register in event loop
54 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
55 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
56 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
57 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_MOVED));
60 SketchSolver_Manager::~SketchSolver_Manager()
65 void SketchSolver_Manager::setBuilder(BuilderPtr theBuilder)
67 myBuilder = theBuilder;
70 BuilderPtr SketchSolver_Manager::builder()
75 // ============================================================================
76 // Function: processEvent
77 // Purpose: listen the event loop and process the message
78 // ============================================================================
79 void SketchSolver_Manager::processEvent(
80 const std::shared_ptr<Events_Message>& theMessage)
84 if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)
85 || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED)
86 || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED)) {
87 std::shared_ptr<ModelAPI_ObjectUpdatedMessage> anUpdateMsg =
88 std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
89 std::set<ObjectPtr> aFeatures = anUpdateMsg->objects();
91 // Shows the message has at least one feature applicable for solver
92 bool hasProperFeature = false;
94 bool isMovedEvt = theMessage->eventID()
95 == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED);
97 std::set<ObjectPtr>::iterator aFeatIter;
98 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
99 std::shared_ptr<SketchPlugin_Feature> aSFeature =
100 std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
102 moveEntity(aSFeature);
103 hasProperFeature = true;
107 std::list<FeaturePtr> aSketchFeatures = SketchSolver_Group::selectApplicableFeatures(aFeatures);
108 std::list<FeaturePtr>::iterator aFeatIter = aSketchFeatures.begin();
109 for (; aFeatIter != aSketchFeatures.end(); ++aFeatIter) {
110 if ((*aFeatIter)->getKind() == SketchPlugin_Sketch::ID()) {
111 std::shared_ptr<ModelAPI_CompositeFeature> aSketch =
112 std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aFeatIter);
113 hasProperFeature = changeWorkplane(aSketch) || hasProperFeature;
116 std::shared_ptr<SketchPlugin_Feature> aFeature =
117 std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
120 hasProperFeature = changeFeature(aFeature) || hasProperFeature;
124 // Solve the set of constraints
125 if (hasProperFeature)
126 resolveConstraints(isMovedEvt); // send update for movement in any case
127 } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
128 std::shared_ptr<ModelAPI_ObjectDeletedMessage> aDeleteMsg =
129 std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
130 const std::set<std::string>& aFeatureGroups = aDeleteMsg->groups();
132 // Find SketchPlugin_Sketch::ID() in groups. The constraint groups should be updated when an object removed from Sketch
133 std::set<std::string>::const_iterator aFGrIter;
134 for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
135 if (aFGrIter->compare(ModelAPI_ResultConstruction::group()) == 0 ||
136 aFGrIter->compare(ModelAPI_Feature::group()) == 0)
139 if (aFGrIter != aFeatureGroups.end()) {
140 std::vector<SketchSolver_Group*>::iterator aGroupIter = myGroups.begin();
141 std::list<SketchSolver_Group*> aSeparatedGroups;
142 while (aGroupIter != myGroups.end()) {
143 if (!(*aGroupIter)->isWorkplaneValid()) { // the group should be removed
145 int aShift = aGroupIter - myGroups.begin();
146 myGroups.erase(aGroupIter);
147 aGroupIter = myGroups.begin() + aShift;
150 if (!(*aGroupIter)->isConsistent()) { // some constraints were removed, try to split the group
151 (*aGroupIter)->splitGroup(aSeparatedGroups);
155 if (aSeparatedGroups.size() > 0)
156 myGroups.insert(myGroups.end(), aSeparatedGroups.begin(), aSeparatedGroups.end());
161 // ============================================================================
162 // Function: changeWorkplane
163 // Purpose: update workplane by given parameters of the sketch
164 // ============================================================================
165 bool SketchSolver_Manager::changeWorkplane(CompositeFeaturePtr theSketch)
167 bool aResult = true; // changed when a workplane wrongly updated
168 bool isUpdated = false;
169 // Try to update specified workplane in all groups
170 std::vector<SketchSolver_Group*>::iterator aGroupIter;
171 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
172 if ((*aGroupIter)->isBaseWorkplane(theSketch)) {
176 // If the workplane is not updated, so this is a new workplane
178 SketchSolver_Group* aNewGroup = new SketchSolver_Group(theSketch);
179 // Verify that the group is created successfully
180 if (!aNewGroup->isBaseWorkplane(theSketch) || !aNewGroup->isWorkplaneValid()) {
184 myGroups.push_back(aNewGroup);
189 // ============================================================================
190 // Function: changeConstraintOrEntity
191 // Purpose: create/update the constraint or the feature and place it into appropriate group
192 // ============================================================================
193 bool SketchSolver_Manager::changeFeature(std::shared_ptr<SketchPlugin_Feature> theFeature)
195 // Search the groups which this feature touches
196 std::set<GroupID> aGroups;
197 findGroups(theFeature, aGroups);
199 std::shared_ptr<SketchPlugin_Constraint> aConstraint =
200 std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
202 // Process the groups list
203 if (aGroups.size() == 0) {
204 // There are no groups applicable for this constraint => create new one
205 // The group will be created only for constraints, not for features
206 if (!aConstraint) return false;
207 std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(aConstraint);
210 SketchSolver_Group* aGroup = new SketchSolver_Group(aWP);
211 if (!aGroup->changeConstraint(aConstraint)) {
215 myGroups.push_back(aGroup);
217 } else if (aGroups.size() == 1) { // Only one group => add feature into it
218 GroupID aGroupId = *(aGroups.begin());
219 std::vector<SketchSolver_Group*>::iterator aGroupIter;
220 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
221 if ((*aGroupIter)->getId() == aGroupId) {
222 // If the group is empty, the feature is not added (the constraint only)
223 if (!aConstraint && !(*aGroupIter)->isEmpty())
224 return (*aGroupIter)->updateFeature(theFeature);
225 return (*aGroupIter)->changeConstraint(aConstraint);
227 } else if (aGroups.size() > 1) { // Several groups applicable for this feature => need to merge them
228 std::set<GroupID>::const_iterator aGroupsIter = aGroups.begin();
230 // Search first group
231 std::vector<SketchSolver_Group*>::iterator aFirstGroupIter;
232 for (aFirstGroupIter = myGroups.begin(); aFirstGroupIter != myGroups.end(); aFirstGroupIter++)
233 if ((*aFirstGroupIter)->getId() == *aGroupsIter)
235 if (aFirstGroupIter == myGroups.end())
238 // Append other groups to the first one
239 std::vector<SketchSolver_Group*>::iterator anOtherGroupIter = aFirstGroupIter + 1;
240 for (aGroupsIter++; aGroupsIter != aGroups.end(); aGroupsIter++) {
241 for (; anOtherGroupIter != myGroups.end(); anOtherGroupIter++)
242 if ((*anOtherGroupIter)->getId() == *aGroupsIter)
244 if (anOtherGroupIter == myGroups.end()) { // Group disappears
245 anOtherGroupIter = aFirstGroupIter + 1;
249 (*aFirstGroupIter)->mergeGroups(**anOtherGroupIter);
250 int aShiftFirst = aFirstGroupIter - myGroups.begin();
251 int aShiftOther = anOtherGroupIter - myGroups.begin();
252 delete *anOtherGroupIter;
253 myGroups.erase(anOtherGroupIter);
254 aFirstGroupIter = myGroups.begin() + aShiftFirst;
255 anOtherGroupIter = myGroups.begin() + aShiftOther;
259 return (*aFirstGroupIter)->changeConstraint(aConstraint);
260 return (*aFirstGroupIter)->updateFeature(theFeature);
263 // Something goes wrong
267 // ============================================================================
268 // Function: moveEntity
269 // Purpose: update element moved on the sketch, which is used by constraints
270 // ============================================================================
271 void SketchSolver_Manager::moveEntity(std::shared_ptr<SketchPlugin_Feature> theFeature)
273 std::vector<SketchSolver_Group*>::iterator aGroupIt = myGroups.begin();
274 for (; aGroupIt != myGroups.end(); aGroupIt++)
275 if (!(*aGroupIt)->isEmpty() && (*aGroupIt)->isInteract(theFeature))
276 (*aGroupIt)->moveFeature(theFeature);
279 // ============================================================================
280 // Function: findGroups
281 // Purpose: search groups of entities interacting with given feature
282 // ============================================================================
283 void SketchSolver_Manager::findGroups(
284 std::shared_ptr<SketchPlugin_Feature> theFeature,
285 std::set<GroupID>& theGroupIDs) const
287 std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(theFeature);
289 SketchSolver_Group* anEmptyGroup = 0; // appropriate empty group for specified constraint
290 std::vector<SketchSolver_Group*>::const_iterator aGroupIter;
291 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
292 if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theFeature)) {
293 if (!(*aGroupIter)->isEmpty())
294 theGroupIDs.insert((*aGroupIter)->getId());
295 else if (!anEmptyGroup)
296 anEmptyGroup = *aGroupIter;
299 // When only empty group is found, use it
300 if (anEmptyGroup && theGroupIDs.empty())
301 theGroupIDs.insert(anEmptyGroup->getId());
304 // ============================================================================
305 // Function: findWorkplane
306 // Purpose: search workplane containing given feature
307 // ============================================================================
308 std::shared_ptr<ModelAPI_CompositeFeature> SketchSolver_Manager
309 ::findWorkplane(std::shared_ptr<SketchPlugin_Feature> theFeature) const
311 // Already verified workplanes
312 std::set<std::shared_ptr<ModelAPI_CompositeFeature> > aVerified;
314 std::vector<SketchSolver_Group*>::const_iterator aGroupIter;
315 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
316 std::shared_ptr<ModelAPI_CompositeFeature> aWP = (*aGroupIter)->getWorkplane();
317 if (aVerified.find(aWP) != aVerified.end())
320 DataPtr aData = aWP->data();
321 if (aData->isValid()) {
322 std::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures = std::dynamic_pointer_cast<
323 ModelAPI_AttributeRefList>(aData->attribute(SketchPlugin_Sketch::FEATURES_ID()));
324 std::list<ObjectPtr> aFeaturesList = aWPFeatures->list();
325 std::list<ObjectPtr>::const_iterator anIter;
326 for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
327 if (*anIter == theFeature)
328 return aWP; // workplane is found
330 aVerified.insert(aWP);
333 return std::shared_ptr<ModelAPI_CompositeFeature>();
336 // ============================================================================
337 // Function: resolveConstraints
338 // Purpose: change entities according to available constraints
339 // ============================================================================
340 void SketchSolver_Manager::resolveConstraints(const bool theForceUpdate)
343 bool needToUpdate = false;
344 static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
345 // to avoid redisplay of each segment on update by solver one by one in the viewer
346 bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
347 if (isUpdateFlushed) {
348 Events_Loop::loop()->setFlushed(anUpdateEvent, false);
351 std::vector<SketchSolver_Group*>::iterator aGroupIter;
352 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
353 if ((*aGroupIter)->resolveConstraints())
356 // Features may be updated => now send events, but for all changed at once
357 if (isUpdateFlushed) {
358 Events_Loop::loop()->setFlushed(anUpdateEvent, true);
360 // Must be before flush because on "Updated" flush the results may be produced
361 // and the creation event is appeared with many new objects. If myIsComputed these
362 // events are missed in processEvents and some elements are not added.
363 myIsComputed = false;
364 if (needToUpdate || theForceUpdate)
365 Events_Loop::loop()->flush(anUpdateEvent);