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)
116 if (aFGrIter != aFeatureGroups.end()) {
117 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter = myGroups.begin();
118 std::vector<SketchSolver_ConstraintGroup*> aSeparatedGroups;
119 while (aGroupIter != myGroups.end()) {
120 if (!(*aGroupIter)->isWorkplaneValid()) { // the group should be removed
122 int aShift = aGroupIter - myGroups.begin();
123 myGroups.erase(aGroupIter);
124 aGroupIter = myGroups.begin() + aShift;
127 if ((*aGroupIter)->updateGroup()) { // some constraints were removed, try to split the group
128 (*aGroupIter)->splitGroup(aSeparatedGroups);
132 if (aSeparatedGroups.size() > 0)
133 myGroups.insert(myGroups.end(), aSeparatedGroups.begin(), aSeparatedGroups.end());
138 // ============================================================================
139 // Function: changeWorkplane
140 // Class: SketchSolver_Session
141 // Purpose: update workplane by given parameters of the sketch
142 // ============================================================================
143 bool SketchSolver_ConstraintManager::changeWorkplane(
144 boost::shared_ptr<SketchPlugin_Feature> theSketch)
146 bool aResult = true; // changed when a workplane wrongly updated
147 bool isUpdated = false;
148 // Try to update specified workplane in all groups
149 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
150 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
151 if ((*aGroupIter)->isBaseWorkplane(theSketch)) {
153 if (!(*aGroupIter)->updateWorkplane())
156 // If the workplane is not updated, so this is a new workplane
158 SketchSolver_ConstraintGroup* aNewGroup = new SketchSolver_ConstraintGroup(theSketch);
159 // Verify that the group is created successfully
160 if (!aNewGroup->isBaseWorkplane(theSketch)) {
164 myGroups.push_back(aNewGroup);
169 // ============================================================================
170 // Function: changeConstraint
171 // Class: SketchSolver_Session
172 // Purpose: create/update the constraint and place it into appropriate group
173 // ============================================================================
174 bool SketchSolver_ConstraintManager::changeConstraint(
175 boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
177 // Search the groups which this constraint touches
178 std::set<Slvs_hGroup> aGroups;
179 findGroups(theConstraint, aGroups);
181 // Process the groups list
182 if (aGroups.size() == 0) { // There are no groups applicable for this constraint => create new one
183 boost::shared_ptr<SketchPlugin_Feature> aWP = findWorkplaneForConstraint(theConstraint);
186 SketchSolver_ConstraintGroup* aGroup = new SketchSolver_ConstraintGroup(aWP);
187 if (!aGroup->changeConstraint(theConstraint)) {
191 myGroups.push_back(aGroup);
193 } else if (aGroups.size() == 1) { // Only one group => add constraint into it
194 Slvs_hGroup aGroupId = *(aGroups.begin());
195 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
196 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
197 if ((*aGroupIter)->getId() == aGroupId)
198 return (*aGroupIter)->changeConstraint(theConstraint);
199 } else if (aGroups.size() > 1) { // Several groups applicable for this constraint => need to merge them
200 std::set<Slvs_hGroup>::const_iterator aGroupsIter = aGroups.begin();
202 // Search first group
203 std::vector<SketchSolver_ConstraintGroup*>::iterator aFirstGroupIter;
204 for (aFirstGroupIter = myGroups.begin(); aFirstGroupIter != myGroups.end(); aFirstGroupIter++)
205 if ((*aFirstGroupIter)->getId() == *aGroupsIter)
207 if (aFirstGroupIter == myGroups.end())
210 // Append other groups to the first one
211 std::vector<SketchSolver_ConstraintGroup*>::iterator anOtherGroupIter = aFirstGroupIter + 1;
212 for (aGroupsIter++; aGroupsIter != aGroups.end(); aGroupsIter++) {
213 for (; anOtherGroupIter != myGroups.end(); anOtherGroupIter++)
214 if ((*anOtherGroupIter)->getId() == *aGroupsIter)
216 if (anOtherGroupIter == myGroups.end()) { // Group disappears
217 anOtherGroupIter = aFirstGroupIter + 1;
221 (*aFirstGroupIter)->mergeGroups(**anOtherGroupIter);
222 int aShiftFirst = aFirstGroupIter - myGroups.begin();
223 int aShiftOther = anOtherGroupIter - myGroups.begin();
224 delete *anOtherGroupIter;
225 myGroups.erase(anOtherGroupIter);
226 aFirstGroupIter = myGroups.begin() + aShiftFirst;
227 anOtherGroupIter = myGroups.begin() + aShiftOther;
230 return (*aFirstGroupIter)->changeConstraint(theConstraint);
233 // Something goes wrong
237 // ============================================================================
238 // Function: updateEntity
239 // Class: SketchSolver_Session
240 // Purpose: update any element on the sketch, which is used by constraints
241 // ============================================================================
242 void SketchSolver_ConstraintManager::updateEntity(
243 boost::shared_ptr<SketchPlugin_Feature> theFeature)
245 // Create list of attributes depending on type of the feature
246 std::vector<std::string> anAttrList;
247 const std::string& aFeatureKind = theFeature->getKind();
249 if (aFeatureKind.compare(SketchPlugin_Point::ID()) == 0)
250 anAttrList.push_back(SketchPlugin_Point::COORD_ID());
252 else if (aFeatureKind.compare(SketchPlugin_Line::ID()) == 0) {
253 anAttrList.push_back(SketchPlugin_Line::START_ID());
254 anAttrList.push_back(SketchPlugin_Line::END_ID());
257 else if (aFeatureKind.compare(SketchPlugin_Circle::ID()) == 0) {
258 anAttrList.push_back(SketchPlugin_Circle::CENTER_ID());
259 anAttrList.push_back(SketchPlugin_Circle::RADIUS_ID());
262 else if (aFeatureKind.compare(SketchPlugin_Arc::ID()) == 0) {
263 anAttrList.push_back(SketchPlugin_Arc::CENTER_ID());
264 anAttrList.push_back(SketchPlugin_Arc::START_ID());
265 anAttrList.push_back(SketchPlugin_Arc::END_ID());
267 /// \todo Other types of features should be implemented
269 // Check changing of feature's attributes (go through the groups and search usage of the attributes)
270 std::vector<std::string>::const_iterator anAttrIter;
271 for (anAttrIter = anAttrList.begin(); anAttrIter != anAttrList.end(); anAttrIter++) {
272 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
273 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
274 if ((*aGroupIter)->isEmpty())
276 boost::shared_ptr<ModelAPI_Attribute> anAttribute = boost::dynamic_pointer_cast<
277 ModelAPI_Attribute>(theFeature->data()->attribute(*anAttrIter));
278 (*aGroupIter)->updateEntityIfPossible(anAttribute);
282 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
283 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
284 if (!(*aGroupIter)->isEmpty())
285 (*aGroupIter)->updateRelatedConstraints(theFeature);
288 // ============================================================================
289 // Function: findGroups
290 // Class: SketchSolver_Session
291 // Purpose: search groups of entities interacting with given constraint
292 // ============================================================================
293 void SketchSolver_ConstraintManager::findGroups(
294 boost::shared_ptr<SketchPlugin_Constraint> theConstraint,
295 std::set<Slvs_hGroup>& theGroupIDs) const
297 boost::shared_ptr<SketchPlugin_Feature> aWP = findWorkplaneForConstraint(theConstraint);
299 SketchSolver_ConstraintGroup* anEmptyGroup = 0; // appropriate empty group for specified constraint
300 std::vector<SketchSolver_ConstraintGroup*>::const_iterator aGroupIter;
301 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
302 if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theConstraint)) {
303 if (!(*aGroupIter)->isEmpty())
304 theGroupIDs.insert((*aGroupIter)->getId());
305 else if (!anEmptyGroup)
306 anEmptyGroup = *aGroupIter;
309 // When only empty group is found, use it
310 if (anEmptyGroup && theGroupIDs.empty())
311 theGroupIDs.insert(anEmptyGroup->getId());
314 // ============================================================================
315 // Function: findWorkplaneForConstraint
316 // Class: SketchSolver_Session
317 // Purpose: search workplane containing given constraint
318 // ============================================================================
319 boost::shared_ptr<SketchPlugin_Feature> SketchSolver_ConstraintManager::findWorkplaneForConstraint(
320 boost::shared_ptr<SketchPlugin_Constraint> theConstraint) const
322 // Already verified workplanes
323 std::set<boost::shared_ptr<SketchPlugin_Feature> > aVerified;
325 std::vector<SketchSolver_ConstraintGroup*>::const_iterator aGroupIter;
326 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
327 boost::shared_ptr<SketchPlugin_Feature> aWP = (*aGroupIter)->getWorkplane();
328 if (aVerified.find(aWP) != aVerified.end())
331 boost::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures = boost::dynamic_pointer_cast<
332 ModelAPI_AttributeRefList>(aWP->data()->attribute(SketchPlugin_Sketch::FEATURES_ID()));
333 std::list<ObjectPtr> aFeaturesList = aWPFeatures->list();
334 std::list<ObjectPtr>::const_iterator anIter;
335 for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
336 if (*anIter == theConstraint)
337 return aWP; // workplane is found
338 aVerified.insert(aWP);
341 return boost::shared_ptr<SketchPlugin_Feature>();
344 // ============================================================================
345 // Function: resolveConstraints
346 // Class: SketchSolver_Session
347 // Purpose: change entities according to available constraints
348 // ============================================================================
349 void SketchSolver_ConstraintManager::resolveConstraints()
351 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
352 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
353 (*aGroupIter)->resolveConstraints();
355 // Features may be updated => send events
356 Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));