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>
14 #include <SketchPlugin_Constraint.h>
16 #include <SketchPlugin_Arc.h>
17 #include <SketchPlugin_Circle.h>
18 #include <SketchPlugin_Line.h>
19 #include <SketchPlugin_Point.h>
20 #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();
32 // ========================================================
33 // ========= SketchSolver_ConstraintManager ===============
34 // ========================================================
35 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::Instance()
38 _self = new SketchSolver_ConstraintManager();
42 SketchSolver_ConstraintManager::SketchSolver_ConstraintManager()
46 // Register in event loop
47 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
48 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
49 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
50 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_MOVED));
53 SketchSolver_ConstraintManager::~SketchSolver_ConstraintManager()
58 // ============================================================================
59 // Function: processEvent
60 // Class: SketchSolver_PluginManager
61 // Purpose: listen the event loop and process the message
62 // ============================================================================
63 void SketchSolver_ConstraintManager::processEvent(const 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))
69 const ModelAPI_ObjectUpdatedMessage* anUpdateMsg =
70 dynamic_cast<const ModelAPI_ObjectUpdatedMessage*>(theMessage);
71 std::set< ObjectPtr > aFeatures = anUpdateMsg->objects();
74 theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED);
77 std::set< ObjectPtr >::iterator aFeatIter;
78 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++)
80 FeaturePtr aFeature = boost::dynamic_pointer_cast<ModelAPI_Feature>(*aFeatIter);
81 if (!aFeature) continue;
82 // Only sketches and constraints can be added by Create event
83 const std::string& aFeatureKind = aFeature->getKind();
84 if (aFeatureKind.compare(SketchPlugin_Sketch::ID()) == 0)
86 boost::shared_ptr<SketchPlugin_Feature> aSketch =
87 boost::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
89 changeWorkplane(aSketch);
92 boost::shared_ptr<SketchPlugin_Constraint> aConstraint =
93 boost::dynamic_pointer_cast<SketchPlugin_Constraint>(aFeature);
95 changeConstraint(aConstraint);
98 // Sketch plugin features can be only updated
99 boost::shared_ptr<SketchPlugin_Feature> aSFeature =
100 boost::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
102 updateEntity(aSFeature);
107 // Solve the set of constraints
108 resolveConstraints();
110 else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED))
112 const ModelAPI_ObjectDeletedMessage* aDeleteMsg =
113 dynamic_cast<const ModelAPI_ObjectDeletedMessage*>(theMessage);
114 const std::set<std::string>& aFeatureGroups = aDeleteMsg->groups();
116 // Find SketchPlugin_Sketch::ID() in groups. The constraint groups should be updated when an object removed from Sketch
117 std::set<std::string>::const_iterator aFGrIter;
118 for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
119 if (aFGrIter->compare(SketchPlugin_Sketch::ID()) == 0)
122 if (aFGrIter != aFeatureGroups.end())
124 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter = myGroups.begin();
125 std::vector<SketchSolver_ConstraintGroup*> aSeparatedGroups;
126 while (aGroupIter != myGroups.end())
128 if (!(*aGroupIter)->isWorkplaneValid())
129 { // the group should be removed
131 int aShift = aGroupIter - myGroups.begin();
132 myGroups.erase(aGroupIter);
133 aGroupIter = myGroups.begin() + aShift;
136 if ((*aGroupIter)->updateGroup())
137 { // some constraints were removed, try to split the group
138 (*aGroupIter)->splitGroup(aSeparatedGroups);
142 if (aSeparatedGroups.size() > 0)
143 myGroups.insert(myGroups.end(), aSeparatedGroups.begin(), aSeparatedGroups.end());
148 // ============================================================================
149 // Function: changeWorkplane
150 // Class: SketchSolver_PluginManager
151 // Purpose: update workplane by given parameters of the sketch
152 // ============================================================================
153 bool SketchSolver_ConstraintManager::changeWorkplane(boost::shared_ptr<SketchPlugin_Feature> theSketch)
155 bool aResult = true; // changed when a workplane wrongly updated
156 bool isUpdated = false;
157 // Try to update specified workplane in all groups
158 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
159 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
160 if ((*aGroupIter)->isBaseWorkplane(theSketch))
163 if (!(*aGroupIter)->updateWorkplane())
166 // If the workplane is not updated, so this is a new workplane
169 SketchSolver_ConstraintGroup* aNewGroup = new SketchSolver_ConstraintGroup(theSketch);
170 // Verify that the group is created successfully
171 if (!aNewGroup->isBaseWorkplane(theSketch))
176 myGroups.push_back(aNewGroup);
181 // ============================================================================
182 // Function: changeConstraint
183 // Class: SketchSolver_PluginManager
184 // Purpose: create/update the constraint and place it into appropriate group
185 // ============================================================================
186 bool SketchSolver_ConstraintManager::changeConstraint(
187 boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
189 // Search the groups which this constraint touches
190 std::set<Slvs_hGroup> aGroups;
191 findGroups(theConstraint, aGroups);
193 // Process the groups list
194 if (aGroups.size() == 0)
195 { // There are no groups applicable for this constraint => create new one
196 boost::shared_ptr<SketchPlugin_Feature> aWP = findWorkplaneForConstraint(theConstraint);
197 if (!aWP) return false;
198 SketchSolver_ConstraintGroup* aGroup = new SketchSolver_ConstraintGroup(aWP);
199 if (!aGroup->changeConstraint(theConstraint))
204 myGroups.push_back(aGroup);
207 else if (aGroups.size() == 1)
208 { // Only one group => add constraint into it
209 Slvs_hGroup aGroupId = *(aGroups.begin());
210 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
211 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
212 if ((*aGroupIter)->getId() == aGroupId)
213 return (*aGroupIter)->changeConstraint(theConstraint);
215 else if (aGroups.size() > 1)
216 { // Several groups applicable for this constraint => need to merge them
217 std::set<Slvs_hGroup>::const_iterator aGroupsIter = aGroups.begin();
219 // Search first group
220 std::vector<SketchSolver_ConstraintGroup*>::iterator aFirstGroupIter;
221 for (aFirstGroupIter = myGroups.begin(); aFirstGroupIter != myGroups.end(); aFirstGroupIter++)
222 if ((*aFirstGroupIter)->getId() == *aGroupsIter)
224 if (aFirstGroupIter == myGroups.end())
227 // Append other groups to the first one
228 std::vector<SketchSolver_ConstraintGroup*>::iterator anOtherGroupIter = aFirstGroupIter + 1;
229 for (aGroupsIter++; aGroupsIter != aGroups.end(); aGroupsIter++)
231 for ( ; anOtherGroupIter != myGroups.end(); anOtherGroupIter++)
232 if ((*anOtherGroupIter)->getId() == *aGroupsIter)
234 if (anOtherGroupIter == myGroups.end())
235 { // Group disappears
236 anOtherGroupIter = aFirstGroupIter + 1;
240 (*aFirstGroupIter)->mergeGroups(**anOtherGroupIter);
241 int aShiftFirst = aFirstGroupIter - myGroups.begin();
242 int aShiftOther = anOtherGroupIter - myGroups.begin();
243 delete *anOtherGroupIter;
244 myGroups.erase(anOtherGroupIter);
245 aFirstGroupIter = myGroups.begin() + aShiftFirst;
246 anOtherGroupIter = myGroups.begin() + aShiftOther;
249 return (*aFirstGroupIter)->changeConstraint(theConstraint);
252 // Something goes wrong
256 // ============================================================================
257 // Function: updateEntity
258 // Class: SketchSolver_PluginManager
259 // Purpose: update any element on the sketch, which is used by constraints
260 // ============================================================================
261 void SketchSolver_ConstraintManager::updateEntity(boost::shared_ptr<SketchPlugin_Feature> theFeature)
263 // Create list of attributes depending on type of the feature
264 std::vector<std::string> anAttrList;
265 const std::string& aFeatureKind = theFeature->getKind();
267 if (aFeatureKind.compare(SketchPlugin_Point::ID()) == 0)
268 anAttrList.push_back(SketchPlugin_Point::COORD_ID());
270 else if (aFeatureKind.compare(SketchPlugin_Line::ID()) == 0)
272 anAttrList.push_back(SketchPlugin_Line::START_ID());
273 anAttrList.push_back(SketchPlugin_Line::END_ID());
276 else if (aFeatureKind.compare(SketchPlugin_Circle::ID()) == 0)
278 anAttrList.push_back(SketchPlugin_Circle::CENTER_ID());
279 anAttrList.push_back(SketchPlugin_Circle::RADIUS_ID());
282 else if (aFeatureKind.compare(SketchPlugin_Arc::ID()) == 0)
284 anAttrList.push_back(SketchPlugin_Arc::CENTER_ID());
285 anAttrList.push_back(SketchPlugin_Arc::START_ID());
286 anAttrList.push_back(SketchPlugin_Arc::END_ID());
288 /// \todo Other types of features should be implemented
290 // Check changing of feature's attributes (go through the groups and search usage of the attributes)
291 std::vector<std::string>::const_iterator anAttrIter;
292 for (anAttrIter = anAttrList.begin(); anAttrIter != anAttrList.end(); anAttrIter++)
294 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
295 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
297 if ((*aGroupIter)->isEmpty())
299 boost::shared_ptr<ModelAPI_Attribute> anAttribute =
300 boost::dynamic_pointer_cast<ModelAPI_Attribute>(theFeature->data()->attribute(*anAttrIter));
301 (*aGroupIter)->updateEntityIfPossible(anAttribute);
305 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
306 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
307 if (!(*aGroupIter)->isEmpty())
308 (*aGroupIter)->updateRelatedConstraints(theFeature);
312 // ============================================================================
313 // Function: findGroups
314 // Class: SketchSolver_PluginManager
315 // Purpose: search groups of entities interacting with given constraint
316 // ============================================================================
317 void SketchSolver_ConstraintManager::findGroups(
318 boost::shared_ptr<SketchPlugin_Constraint> theConstraint,
319 std::set<Slvs_hGroup>& theGroupIDs) const
321 boost::shared_ptr<SketchPlugin_Feature> aWP = findWorkplaneForConstraint(theConstraint);
323 SketchSolver_ConstraintGroup* anEmptyGroup = 0; // appropriate empty group for specified constraint
324 std::vector<SketchSolver_ConstraintGroup*>::const_iterator aGroupIter;
325 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
326 if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theConstraint))
328 if (!(*aGroupIter)->isEmpty())
329 theGroupIDs.insert((*aGroupIter)->getId());
330 else if (!anEmptyGroup)
331 anEmptyGroup = *aGroupIter;
334 // When only empty group is found, use it
335 if (anEmptyGroup && theGroupIDs.empty())
336 theGroupIDs.insert(anEmptyGroup->getId());
339 // ============================================================================
340 // Function: findWorkplaneForConstraint
341 // Class: SketchSolver_PluginManager
342 // Purpose: search workplane containing given constraint
343 // ============================================================================
344 boost::shared_ptr<SketchPlugin_Feature> SketchSolver_ConstraintManager::findWorkplaneForConstraint(
345 boost::shared_ptr<SketchPlugin_Constraint> theConstraint) const
347 // Already verified workplanes
348 std::set< boost::shared_ptr<SketchPlugin_Feature> > aVerified;
350 std::vector<SketchSolver_ConstraintGroup*>::const_iterator aGroupIter;
351 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
353 boost::shared_ptr<SketchPlugin_Feature> aWP = (*aGroupIter)->getWorkplane();
354 if (aVerified.find(aWP) != aVerified.end())
357 boost::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures =
358 boost::dynamic_pointer_cast<ModelAPI_AttributeRefList>(aWP->data()->attribute(SketchPlugin_Sketch::FEATURES_ID()));
359 std::list<ObjectPtr> aFeaturesList = aWPFeatures->list();
360 std::list<ObjectPtr>::const_iterator anIter;
361 for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
362 if (*anIter == theConstraint)
363 return aWP; // workplane is found
364 aVerified.insert(aWP);
367 return boost::shared_ptr<SketchPlugin_Feature>();
370 // ============================================================================
371 // Function: resolveConstraints
372 // Class: SketchSolver_PluginManager
373 // Purpose: change entities according to available constraints
374 // ============================================================================
375 void SketchSolver_ConstraintManager::resolveConstraints()
377 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
378 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
379 (*aGroupIter)->resolveConstraints();
381 // Features may be updated => send events
382 Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));