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 isMovedEvt = 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 boost::shared_ptr<SketchPlugin_Feature> aSFeature =
78 boost::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
80 updateEntity(aSFeature);
83 std::set<ObjectPtr>::iterator aFeatIter;
84 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
85 FeaturePtr aFeature = boost::dynamic_pointer_cast<ModelAPI_Feature>(*aFeatIter);
88 // Only sketches and constraints can be added by Create event
89 const std::string& aFeatureKind = aFeature->getKind();
90 if (aFeatureKind.compare(SketchPlugin_Sketch::ID()) == 0) {
91 boost::shared_ptr<SketchPlugin_Feature> aSketch = boost::dynamic_pointer_cast<
92 SketchPlugin_Feature>(aFeature);
94 changeWorkplane(aSketch);
97 // Sketch plugin features can be only updated
98 boost::shared_ptr<SketchPlugin_Feature> aSFeature = boost::dynamic_pointer_cast<
99 SketchPlugin_Feature>(aFeature);
101 changeConstraintOrEntity(aSFeature);
105 // Solve the set of constraints
106 resolveConstraints();
107 } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
108 boost::shared_ptr<ModelAPI_ObjectDeletedMessage> aDeleteMsg =
109 boost::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
110 const std::set<std::string>& aFeatureGroups = aDeleteMsg->groups();
112 // Find SketchPlugin_Sketch::ID() in groups. The constraint groups should be updated when an object removed from Sketch
113 std::set<std::string>::const_iterator aFGrIter;
114 for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
115 if (aFGrIter->compare(ModelAPI_ResultConstruction::group()) == 0 ||
116 aFGrIter->compare(ModelAPI_Feature::group()) == 0)
119 if (aFGrIter != aFeatureGroups.end()) {
120 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter = myGroups.begin();
121 std::vector<SketchSolver_ConstraintGroup*> aSeparatedGroups;
122 while (aGroupIter != myGroups.end()) {
123 if (!(*aGroupIter)->isWorkplaneValid()) { // the group should be removed
125 int aShift = aGroupIter - myGroups.begin();
126 myGroups.erase(aGroupIter);
127 aGroupIter = myGroups.begin() + aShift;
130 if ((*aGroupIter)->updateGroup()) { // some constraints were removed, try to split the group
131 (*aGroupIter)->splitGroup(aSeparatedGroups);
135 if (aSeparatedGroups.size() > 0)
136 myGroups.insert(myGroups.end(), aSeparatedGroups.begin(), aSeparatedGroups.end());
141 // ============================================================================
142 // Function: changeWorkplane
143 // Class: SketchSolver_Session
144 // Purpose: update workplane by given parameters of the sketch
145 // ============================================================================
146 bool SketchSolver_ConstraintManager::changeWorkplane(
147 boost::shared_ptr<SketchPlugin_Feature> theSketch)
149 bool aResult = true; // changed when a workplane wrongly updated
150 bool isUpdated = false;
151 // Try to update specified workplane in all groups
152 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
153 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
154 if ((*aGroupIter)->isBaseWorkplane(theSketch)) {
156 if (!(*aGroupIter)->updateWorkplane())
159 // If the workplane is not updated, so this is a new workplane
161 SketchSolver_ConstraintGroup* aNewGroup = new SketchSolver_ConstraintGroup(theSketch);
162 // Verify that the group is created successfully
163 if (!aNewGroup->isBaseWorkplane(theSketch)) {
167 myGroups.push_back(aNewGroup);
172 // ============================================================================
173 // Function: changeConstraintOrEntity
174 // Class: SketchSolver_Session
175 // Purpose: create/update the constraint or the feature and place it into appropriate group
176 // ============================================================================
177 bool SketchSolver_ConstraintManager::changeConstraintOrEntity(
178 boost::shared_ptr<SketchPlugin_Feature> theFeature)
180 // Search the groups which this feature touches
181 std::set<Slvs_hGroup> aGroups;
182 findGroups(theFeature, aGroups);
184 boost::shared_ptr<SketchPlugin_Constraint> aConstraint =
185 boost::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
187 // Process the groups list
188 if (aGroups.size() == 0) {
189 // There are no groups applicable for this constraint => create new one
190 // The group will be created only for constraints, not for features
191 if (!aConstraint) return false;
192 boost::shared_ptr<SketchPlugin_Feature> aWP = findWorkplane(aConstraint);
195 SketchSolver_ConstraintGroup* aGroup = new SketchSolver_ConstraintGroup(aWP);
196 if (!aGroup->changeConstraint(aConstraint)) {
200 myGroups.push_back(aGroup);
202 } else if (aGroups.size() == 1) { // Only one group => add feature into it
203 Slvs_hGroup aGroupId = *(aGroups.begin());
204 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
205 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
206 if ((*aGroupIter)->getId() == aGroupId) {
207 // If the group is empty, the feature is not added (the constraint only)
208 if (!aConstraint && !(*aGroupIter)->isEmpty())
209 return (*aGroupIter)->changeEntity(theFeature) != SLVS_E_UNKNOWN;
210 return (*aGroupIter)->changeConstraint(aConstraint);
212 } else if (aGroups.size() > 1) { // Several groups applicable for this feature => need to merge them
213 std::set<Slvs_hGroup>::const_iterator aGroupsIter = aGroups.begin();
215 // Search first group
216 std::vector<SketchSolver_ConstraintGroup*>::iterator aFirstGroupIter;
217 for (aFirstGroupIter = myGroups.begin(); aFirstGroupIter != myGroups.end(); aFirstGroupIter++)
218 if ((*aFirstGroupIter)->getId() == *aGroupsIter)
220 if (aFirstGroupIter == myGroups.end())
223 // Append other groups to the first one
224 std::vector<SketchSolver_ConstraintGroup*>::iterator anOtherGroupIter = aFirstGroupIter + 1;
225 for (aGroupsIter++; aGroupsIter != aGroups.end(); aGroupsIter++) {
226 for (; anOtherGroupIter != myGroups.end(); anOtherGroupIter++)
227 if ((*anOtherGroupIter)->getId() == *aGroupsIter)
229 if (anOtherGroupIter == myGroups.end()) { // Group disappears
230 anOtherGroupIter = aFirstGroupIter + 1;
234 (*aFirstGroupIter)->mergeGroups(**anOtherGroupIter);
235 int aShiftFirst = aFirstGroupIter - myGroups.begin();
236 int aShiftOther = anOtherGroupIter - myGroups.begin();
237 delete *anOtherGroupIter;
238 myGroups.erase(anOtherGroupIter);
239 aFirstGroupIter = myGroups.begin() + aShiftFirst;
240 anOtherGroupIter = myGroups.begin() + aShiftOther;
244 return (*aFirstGroupIter)->changeConstraint(aConstraint);
245 return (*aFirstGroupIter)->changeEntity(theFeature) != SLVS_E_UNKNOWN;
248 // Something goes wrong
252 // ============================================================================
253 // Function: updateEntity
254 // Class: SketchSolver_Session
255 // Purpose: update any element on the sketch, which is used by constraints
256 // ============================================================================
257 void SketchSolver_ConstraintManager::updateEntity(
258 boost::shared_ptr<SketchPlugin_Feature> theFeature)
260 // Create list of attributes depending on type of the feature
261 std::vector<std::string> anAttrList;
262 const std::string& aFeatureKind = theFeature->getKind();
264 if (aFeatureKind.compare(SketchPlugin_Point::ID()) == 0)
265 anAttrList.push_back(SketchPlugin_Point::COORD_ID());
267 else if (aFeatureKind.compare(SketchPlugin_Line::ID()) == 0) {
268 anAttrList.push_back(SketchPlugin_Line::START_ID());
269 anAttrList.push_back(SketchPlugin_Line::END_ID());
272 else if (aFeatureKind.compare(SketchPlugin_Circle::ID()) == 0) {
273 anAttrList.push_back(SketchPlugin_Circle::CENTER_ID());
274 anAttrList.push_back(SketchPlugin_Circle::RADIUS_ID());
277 else if (aFeatureKind.compare(SketchPlugin_Arc::ID()) == 0) {
278 anAttrList.push_back(SketchPlugin_Arc::CENTER_ID());
279 anAttrList.push_back(SketchPlugin_Arc::START_ID());
280 anAttrList.push_back(SketchPlugin_Arc::END_ID());
282 /// \todo Other types of features should be implemented
284 // Check changing of feature's attributes (go through the groups and search usage of the attributes)
285 std::vector<std::string>::const_iterator anAttrIter;
286 for (anAttrIter = anAttrList.begin(); anAttrIter != anAttrList.end(); anAttrIter++) {
287 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
288 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
289 if ((*aGroupIter)->isEmpty())
291 boost::shared_ptr<ModelAPI_Attribute> anAttribute = boost::dynamic_pointer_cast<
292 ModelAPI_Attribute>(theFeature->data()->attribute(*anAttrIter));
293 (*aGroupIter)->updateEntityIfPossible(anAttribute);
297 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
298 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
299 if (!(*aGroupIter)->isEmpty())
300 (*aGroupIter)->updateRelatedConstraints(theFeature);
303 // ============================================================================
304 // Function: findGroups
305 // Class: SketchSolver_Session
306 // Purpose: search groups of entities interacting with given feature
307 // ============================================================================
308 void SketchSolver_ConstraintManager::findGroups(
309 boost::shared_ptr<SketchPlugin_Feature> theFeature,
310 std::set<Slvs_hGroup>& theGroupIDs) const
312 boost::shared_ptr<SketchPlugin_Feature> aWP = findWorkplane(theFeature);
314 SketchSolver_ConstraintGroup* anEmptyGroup = 0; // appropriate empty group for specified constraint
315 std::vector<SketchSolver_ConstraintGroup*>::const_iterator aGroupIter;
316 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
317 if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theFeature)) {
318 if (!(*aGroupIter)->isEmpty())
319 theGroupIDs.insert((*aGroupIter)->getId());
320 else if (!anEmptyGroup)
321 anEmptyGroup = *aGroupIter;
324 // When only empty group is found, use it
325 if (anEmptyGroup && theGroupIDs.empty())
326 theGroupIDs.insert(anEmptyGroup->getId());
329 // ============================================================================
330 // Function: findWorkplane
331 // Class: SketchSolver_Session
332 // Purpose: search workplane containing given feature
333 // ============================================================================
334 boost::shared_ptr<SketchPlugin_Feature> SketchSolver_ConstraintManager::findWorkplane(
335 boost::shared_ptr<SketchPlugin_Feature> theFeature) const
337 // Already verified workplanes
338 std::set<boost::shared_ptr<SketchPlugin_Feature> > aVerified;
340 std::vector<SketchSolver_ConstraintGroup*>::const_iterator aGroupIter;
341 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
342 boost::shared_ptr<SketchPlugin_Feature> aWP = (*aGroupIter)->getWorkplane();
343 if (aVerified.find(aWP) != aVerified.end())
346 boost::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures = boost::dynamic_pointer_cast<
347 ModelAPI_AttributeRefList>(aWP->data()->attribute(SketchPlugin_Sketch::FEATURES_ID()));
348 std::list<ObjectPtr> aFeaturesList = aWPFeatures->list();
349 std::list<ObjectPtr>::const_iterator anIter;
350 for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
351 if (*anIter == theFeature)
352 return aWP; // workplane is found
353 aVerified.insert(aWP);
356 return boost::shared_ptr<SketchPlugin_Feature>();
359 // ============================================================================
360 // Function: resolveConstraints
361 // Class: SketchSolver_Session
362 // Purpose: change entities according to available constraints
363 // ============================================================================
364 void SketchSolver_ConstraintManager::resolveConstraints()
366 bool needToUpdate = false;
367 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
368 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
369 if ((*aGroupIter)->resolveConstraints())
372 // Features may be updated => send events
374 Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));