1 // File: SketchSolver_ConstraintManager.cpp
2 // Created: 08 May 2014
3 // Author: Artem ZHIDKOV
5 #include "SketchSolver_ConstraintManager.h"
7 #include <Events_Loop.h>
8 #include <ModelAPI_AttributeDouble.h>
9 #include <ModelAPI_AttributeRefList.h>
10 #include <ModelAPI_Data.h>
11 #include <ModelAPI_Events.h>
12 #include <ModelAPI_Object.h>
13 #include <ModelAPI_ResultConstruction.h>
15 #include <SketchPlugin_Constraint.h>
17 #include <SketchPlugin_Arc.h>
18 #include <SketchPlugin_Circle.h>
19 #include <SketchPlugin_Line.h>
20 #include <SketchPlugin_Point.h>
21 #include <SketchPlugin_Sketch.h>
25 // Initialization of constraint manager self pointer
26 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::_self = 0;
28 /// Global constraint manager object
29 SketchSolver_ConstraintManager* myManager = SketchSolver_ConstraintManager::Instance();
31 // ========================================================
32 // ========= SketchSolver_ConstraintManager ===============
33 // ========================================================
34 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::Instance()
37 _self = new SketchSolver_ConstraintManager();
41 SketchSolver_ConstraintManager::SketchSolver_ConstraintManager()
45 // Register in event loop
46 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
47 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
48 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
49 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_MOVED));
52 SketchSolver_ConstraintManager::~SketchSolver_ConstraintManager()
57 // ============================================================================
58 // Function: processEvent
59 // Class: SketchSolver_Session
60 // Purpose: listen the event loop and process the message
61 // ============================================================================
62 void SketchSolver_ConstraintManager::processEvent(
63 const boost::shared_ptr<Events_Message>& theMessage)
65 if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)
66 || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED)
67 || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED)) {
68 boost::shared_ptr<ModelAPI_ObjectUpdatedMessage> anUpdateMsg =
69 boost::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
70 std::set<ObjectPtr> aFeatures = anUpdateMsg->objects();
72 bool isModifiedEvt = theMessage->eventID()
73 == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED);
75 std::set<ObjectPtr>::iterator aFeatIter;
76 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
77 FeaturePtr aFeature = boost::dynamic_pointer_cast<ModelAPI_Feature>(*aFeatIter);
80 // Only sketches and constraints can be added by Create event
81 const std::string& aFeatureKind = aFeature->getKind();
82 if (aFeatureKind.compare(SketchPlugin_Sketch::ID()) == 0) {
83 boost::shared_ptr<SketchPlugin_Feature> aSketch = boost::dynamic_pointer_cast<
84 SketchPlugin_Feature>(aFeature);
86 changeWorkplane(aSketch);
89 boost::shared_ptr<SketchPlugin_Constraint> aConstraint = boost::dynamic_pointer_cast<
90 SketchPlugin_Constraint>(aFeature);
92 changeConstraint(aConstraint);
94 // Sketch plugin features can be only updated
95 boost::shared_ptr<SketchPlugin_Feature> aSFeature = boost::dynamic_pointer_cast<
96 SketchPlugin_Feature>(aFeature);
98 updateEntity(aSFeature);
103 // Solve the set of constraints
104 resolveConstraints();
105 } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
106 boost::shared_ptr<ModelAPI_ObjectDeletedMessage> aDeleteMsg =
107 boost::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
108 const std::set<std::string>& aFeatureGroups = aDeleteMsg->groups();
110 // Find SketchPlugin_Sketch::ID() in groups. The constraint groups should be updated when an object removed from Sketch
111 std::set<std::string>::const_iterator aFGrIter;
112 for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
113 if (aFGrIter->compare(ModelAPI_ResultConstruction::group()) == 0 ||
114 aFGrIter->compare(ModelAPI_Feature::group()) == 0)
117 if (aFGrIter != aFeatureGroups.end()) {
118 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter = myGroups.begin();
119 std::vector<SketchSolver_ConstraintGroup*> aSeparatedGroups;
120 while (aGroupIter != myGroups.end()) {
121 if (!(*aGroupIter)->isWorkplaneValid()) { // the group should be removed
123 int aShift = aGroupIter - myGroups.begin();
124 myGroups.erase(aGroupIter);
125 aGroupIter = myGroups.begin() + aShift;
128 if ((*aGroupIter)->updateGroup()) { // some constraints were removed, try to split the group
129 (*aGroupIter)->splitGroup(aSeparatedGroups);
133 if (aSeparatedGroups.size() > 0)
134 myGroups.insert(myGroups.end(), aSeparatedGroups.begin(), aSeparatedGroups.end());
139 // ============================================================================
140 // Function: changeWorkplane
141 // Class: SketchSolver_Session
142 // Purpose: update workplane by given parameters of the sketch
143 // ============================================================================
144 bool SketchSolver_ConstraintManager::changeWorkplane(
145 boost::shared_ptr<SketchPlugin_Feature> theSketch)
147 bool aResult = true; // changed when a workplane wrongly updated
148 bool isUpdated = false;
149 // Try to update specified workplane in all groups
150 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
151 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
152 if ((*aGroupIter)->isBaseWorkplane(theSketch)) {
154 if (!(*aGroupIter)->updateWorkplane())
157 // If the workplane is not updated, so this is a new workplane
159 SketchSolver_ConstraintGroup* aNewGroup = new SketchSolver_ConstraintGroup(theSketch);
160 // Verify that the group is created successfully
161 if (!aNewGroup->isBaseWorkplane(theSketch)) {
165 myGroups.push_back(aNewGroup);
170 // ============================================================================
171 // Function: changeConstraint
172 // Class: SketchSolver_Session
173 // Purpose: create/update the constraint and place it into appropriate group
174 // ============================================================================
175 bool SketchSolver_ConstraintManager::changeConstraint(
176 boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
178 // Search the groups which this constraint touches
179 std::set<Slvs_hGroup> aGroups;
180 findGroups(theConstraint, aGroups);
182 // Process the groups list
183 if (aGroups.size() == 0) { // There are no groups applicable for this constraint => create new one
184 boost::shared_ptr<SketchPlugin_Feature> aWP = findWorkplaneForConstraint(theConstraint);
187 SketchSolver_ConstraintGroup* aGroup = new SketchSolver_ConstraintGroup(aWP);
188 if (!aGroup->changeConstraint(theConstraint)) {
192 myGroups.push_back(aGroup);
194 } else if (aGroups.size() == 1) { // Only one group => add constraint into it
195 Slvs_hGroup aGroupId = *(aGroups.begin());
196 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
197 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
198 if ((*aGroupIter)->getId() == aGroupId)
199 return (*aGroupIter)->changeConstraint(theConstraint);
200 } else if (aGroups.size() > 1) { // Several groups applicable for this constraint => need to merge them
201 std::set<Slvs_hGroup>::const_iterator aGroupsIter = aGroups.begin();
203 // Search first group
204 std::vector<SketchSolver_ConstraintGroup*>::iterator aFirstGroupIter;
205 for (aFirstGroupIter = myGroups.begin(); aFirstGroupIter != myGroups.end(); aFirstGroupIter++)
206 if ((*aFirstGroupIter)->getId() == *aGroupsIter)
208 if (aFirstGroupIter == myGroups.end())
211 // Append other groups to the first one
212 std::vector<SketchSolver_ConstraintGroup*>::iterator anOtherGroupIter = aFirstGroupIter + 1;
213 for (aGroupsIter++; aGroupsIter != aGroups.end(); aGroupsIter++) {
214 for (; anOtherGroupIter != myGroups.end(); anOtherGroupIter++)
215 if ((*anOtherGroupIter)->getId() == *aGroupsIter)
217 if (anOtherGroupIter == myGroups.end()) { // Group disappears
218 anOtherGroupIter = aFirstGroupIter + 1;
222 (*aFirstGroupIter)->mergeGroups(**anOtherGroupIter);
223 int aShiftFirst = aFirstGroupIter - myGroups.begin();
224 int aShiftOther = anOtherGroupIter - myGroups.begin();
225 delete *anOtherGroupIter;
226 myGroups.erase(anOtherGroupIter);
227 aFirstGroupIter = myGroups.begin() + aShiftFirst;
228 anOtherGroupIter = myGroups.begin() + aShiftOther;
231 return (*aFirstGroupIter)->changeConstraint(theConstraint);
234 // Something goes wrong
238 // ============================================================================
239 // Function: updateEntity
240 // Class: SketchSolver_Session
241 // Purpose: update any element on the sketch, which is used by constraints
242 // ============================================================================
243 void SketchSolver_ConstraintManager::updateEntity(
244 boost::shared_ptr<SketchPlugin_Feature> theFeature)
246 // Create list of attributes depending on type of the feature
247 std::vector<std::string> anAttrList;
248 const std::string& aFeatureKind = theFeature->getKind();
250 if (aFeatureKind.compare(SketchPlugin_Point::ID()) == 0)
251 anAttrList.push_back(SketchPlugin_Point::COORD_ID());
253 else if (aFeatureKind.compare(SketchPlugin_Line::ID()) == 0) {
254 anAttrList.push_back(SketchPlugin_Line::START_ID());
255 anAttrList.push_back(SketchPlugin_Line::END_ID());
258 else if (aFeatureKind.compare(SketchPlugin_Circle::ID()) == 0) {
259 anAttrList.push_back(SketchPlugin_Circle::CENTER_ID());
260 anAttrList.push_back(SketchPlugin_Circle::RADIUS_ID());
263 else if (aFeatureKind.compare(SketchPlugin_Arc::ID()) == 0) {
264 anAttrList.push_back(SketchPlugin_Arc::CENTER_ID());
265 anAttrList.push_back(SketchPlugin_Arc::START_ID());
266 anAttrList.push_back(SketchPlugin_Arc::END_ID());
268 /// \todo Other types of features should be implemented
270 // Check changing of feature's attributes (go through the groups and search usage of the attributes)
271 std::vector<std::string>::const_iterator anAttrIter;
272 for (anAttrIter = anAttrList.begin(); anAttrIter != anAttrList.end(); anAttrIter++) {
273 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
274 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
275 if ((*aGroupIter)->isEmpty())
277 boost::shared_ptr<ModelAPI_Attribute> anAttribute = boost::dynamic_pointer_cast<
278 ModelAPI_Attribute>(theFeature->data()->attribute(*anAttrIter));
279 (*aGroupIter)->updateEntityIfPossible(anAttribute);
283 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
284 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
285 if (!(*aGroupIter)->isEmpty())
286 (*aGroupIter)->updateRelatedConstraints(theFeature);
289 // ============================================================================
290 // Function: findGroups
291 // Class: SketchSolver_Session
292 // Purpose: search groups of entities interacting with given constraint
293 // ============================================================================
294 void SketchSolver_ConstraintManager::findGroups(
295 boost::shared_ptr<SketchPlugin_Constraint> theConstraint,
296 std::set<Slvs_hGroup>& theGroupIDs) const
298 boost::shared_ptr<SketchPlugin_Feature> aWP = findWorkplaneForConstraint(theConstraint);
300 SketchSolver_ConstraintGroup* anEmptyGroup = 0; // appropriate empty group for specified constraint
301 std::vector<SketchSolver_ConstraintGroup*>::const_iterator aGroupIter;
302 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
303 if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theConstraint)) {
304 if (!(*aGroupIter)->isEmpty())
305 theGroupIDs.insert((*aGroupIter)->getId());
306 else if (!anEmptyGroup)
307 anEmptyGroup = *aGroupIter;
310 // When only empty group is found, use it
311 if (anEmptyGroup && theGroupIDs.empty())
312 theGroupIDs.insert(anEmptyGroup->getId());
315 // ============================================================================
316 // Function: findWorkplaneForConstraint
317 // Class: SketchSolver_Session
318 // Purpose: search workplane containing given constraint
319 // ============================================================================
320 boost::shared_ptr<SketchPlugin_Feature> SketchSolver_ConstraintManager::findWorkplaneForConstraint(
321 boost::shared_ptr<SketchPlugin_Constraint> theConstraint) const
323 // Already verified workplanes
324 std::set<boost::shared_ptr<SketchPlugin_Feature> > aVerified;
326 std::vector<SketchSolver_ConstraintGroup*>::const_iterator aGroupIter;
327 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
328 boost::shared_ptr<SketchPlugin_Feature> aWP = (*aGroupIter)->getWorkplane();
329 if (aVerified.find(aWP) != aVerified.end())
332 boost::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures = boost::dynamic_pointer_cast<
333 ModelAPI_AttributeRefList>(aWP->data()->attribute(SketchPlugin_Sketch::FEATURES_ID()));
334 std::list<ObjectPtr> aFeaturesList = aWPFeatures->list();
335 std::list<ObjectPtr>::const_iterator anIter;
336 for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
337 if (*anIter == theConstraint)
338 return aWP; // workplane is found
339 aVerified.insert(aWP);
342 return boost::shared_ptr<SketchPlugin_Feature>();
345 // ============================================================================
346 // Function: resolveConstraints
347 // Class: SketchSolver_Session
348 // Purpose: change entities according to available constraints
349 // ============================================================================
350 void SketchSolver_ConstraintManager::resolveConstraints()
352 bool needToUpdate = false;
353 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
354 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
355 if ((*aGroupIter)->resolveConstraints())
358 // Features may be updated => send events
360 Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));