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>
26 // Initialization of constraint manager self pointer
27 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::_self = 0;
29 /// Global constraint manager object
30 SketchSolver_ConstraintManager* myManager = SketchSolver_ConstraintManager::Instance();
33 // ========================================================
34 // ========= SketchSolver_ConstraintManager ===============
35 // ========================================================
36 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::Instance()
39 _self = new SketchSolver_ConstraintManager();
43 SketchSolver_ConstraintManager::SketchSolver_ConstraintManager()
47 // Register in event loop
48 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
49 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
50 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
51 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_MOVED));
54 SketchSolver_ConstraintManager::~SketchSolver_ConstraintManager()
59 // ============================================================================
60 // Function: processEvent
61 // Class: SketchSolver_PluginManager
62 // Purpose: listen the event loop and process the message
63 // ============================================================================
64 void SketchSolver_ConstraintManager::processEvent(const Events_Message* theMessage)
66 if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED) ||
67 theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED) ||
68 theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED))
70 const ModelAPI_ObjectUpdatedMessage* anUpdateMsg =
71 dynamic_cast<const ModelAPI_ObjectUpdatedMessage*>(theMessage);
72 std::set< ObjectPtr > aFeatures = anUpdateMsg->objects();
75 theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED);
78 std::set< ObjectPtr >::iterator aFeatIter;
79 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++)
81 FeaturePtr aFeature = boost::dynamic_pointer_cast<ModelAPI_Feature>(*aFeatIter);
82 if (!aFeature) continue;
83 // Only sketches and constraints can be added by Create event
84 const std::string& aFeatureKind = aFeature->getKind();
85 if (aFeatureKind.compare(SketchPlugin_Sketch::ID()) == 0)
87 boost::shared_ptr<SketchPlugin_Feature> aSketch =
88 boost::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
90 changeWorkplane(aSketch);
93 boost::shared_ptr<SketchPlugin_Constraint> aConstraint =
94 boost::dynamic_pointer_cast<SketchPlugin_Constraint>(aFeature);
96 changeConstraint(aConstraint);
99 // Sketch plugin features can be only updated
100 boost::shared_ptr<SketchPlugin_Feature> aSFeature =
101 boost::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
103 updateEntity(aSFeature);
108 // Solve the set of constraints
109 resolveConstraints();
111 else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED))
113 const ModelAPI_ObjectDeletedMessage* aDeleteMsg =
114 dynamic_cast<const ModelAPI_ObjectDeletedMessage*>(theMessage);
115 const std::set<std::string>& aFeatureGroups = aDeleteMsg->groups();
117 // Find SketchPlugin_Sketch::ID() in groups. The constraint groups should be updated when an object removed from Sketch
118 std::set<std::string>::const_iterator aFGrIter;
119 for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
120 if (aFGrIter->compare(ModelAPI_ResultConstruction::group()) == 0)
123 if (aFGrIter != aFeatureGroups.end())
125 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter = myGroups.begin();
126 std::vector<SketchSolver_ConstraintGroup*> aSeparatedGroups;
127 while (aGroupIter != myGroups.end())
129 if (!(*aGroupIter)->isWorkplaneValid())
130 { // the group should be removed
132 int aShift = aGroupIter - myGroups.begin();
133 myGroups.erase(aGroupIter);
134 aGroupIter = myGroups.begin() + aShift;
137 if ((*aGroupIter)->updateGroup())
138 { // some constraints were removed, try to split the group
139 (*aGroupIter)->splitGroup(aSeparatedGroups);
143 if (aSeparatedGroups.size() > 0)
144 myGroups.insert(myGroups.end(), aSeparatedGroups.begin(), aSeparatedGroups.end());
149 // ============================================================================
150 // Function: changeWorkplane
151 // Class: SketchSolver_PluginManager
152 // Purpose: update workplane by given parameters of the sketch
153 // ============================================================================
154 bool SketchSolver_ConstraintManager::changeWorkplane(boost::shared_ptr<SketchPlugin_Feature> theSketch)
156 bool aResult = true; // changed when a workplane wrongly updated
157 bool isUpdated = false;
158 // Try to update specified workplane in all groups
159 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
160 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
161 if ((*aGroupIter)->isBaseWorkplane(theSketch))
164 if (!(*aGroupIter)->updateWorkplane())
167 // If the workplane is not updated, so this is a new workplane
170 SketchSolver_ConstraintGroup* aNewGroup = new SketchSolver_ConstraintGroup(theSketch);
171 // Verify that the group is created successfully
172 if (!aNewGroup->isBaseWorkplane(theSketch))
177 myGroups.push_back(aNewGroup);
182 // ============================================================================
183 // Function: changeConstraint
184 // Class: SketchSolver_PluginManager
185 // Purpose: create/update the constraint and place it into appropriate group
186 // ============================================================================
187 bool SketchSolver_ConstraintManager::changeConstraint(
188 boost::shared_ptr<SketchPlugin_Constraint> theConstraint)
190 // Search the groups which this constraint touches
191 std::set<Slvs_hGroup> aGroups;
192 findGroups(theConstraint, aGroups);
194 // Process the groups list
195 if (aGroups.size() == 0)
196 { // There are no groups applicable for this constraint => create new one
197 boost::shared_ptr<SketchPlugin_Feature> aWP = findWorkplaneForConstraint(theConstraint);
198 if (!aWP) return false;
199 SketchSolver_ConstraintGroup* aGroup = new SketchSolver_ConstraintGroup(aWP);
200 if (!aGroup->changeConstraint(theConstraint))
205 myGroups.push_back(aGroup);
208 else if (aGroups.size() == 1)
209 { // Only one group => add constraint into it
210 Slvs_hGroup aGroupId = *(aGroups.begin());
211 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
212 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
213 if ((*aGroupIter)->getId() == aGroupId)
214 return (*aGroupIter)->changeConstraint(theConstraint);
216 else if (aGroups.size() > 1)
217 { // Several groups applicable for this constraint => need to merge them
218 std::set<Slvs_hGroup>::const_iterator aGroupsIter = aGroups.begin();
220 // Search first group
221 std::vector<SketchSolver_ConstraintGroup*>::iterator aFirstGroupIter;
222 for (aFirstGroupIter = myGroups.begin(); aFirstGroupIter != myGroups.end(); aFirstGroupIter++)
223 if ((*aFirstGroupIter)->getId() == *aGroupsIter)
225 if (aFirstGroupIter == myGroups.end())
228 // Append other groups to the first one
229 std::vector<SketchSolver_ConstraintGroup*>::iterator anOtherGroupIter = aFirstGroupIter + 1;
230 for (aGroupsIter++; aGroupsIter != aGroups.end(); aGroupsIter++)
232 for ( ; anOtherGroupIter != myGroups.end(); anOtherGroupIter++)
233 if ((*anOtherGroupIter)->getId() == *aGroupsIter)
235 if (anOtherGroupIter == myGroups.end())
236 { // Group disappears
237 anOtherGroupIter = aFirstGroupIter + 1;
241 (*aFirstGroupIter)->mergeGroups(**anOtherGroupIter);
242 int aShiftFirst = aFirstGroupIter - myGroups.begin();
243 int aShiftOther = anOtherGroupIter - myGroups.begin();
244 delete *anOtherGroupIter;
245 myGroups.erase(anOtherGroupIter);
246 aFirstGroupIter = myGroups.begin() + aShiftFirst;
247 anOtherGroupIter = myGroups.begin() + aShiftOther;
250 return (*aFirstGroupIter)->changeConstraint(theConstraint);
253 // Something goes wrong
257 // ============================================================================
258 // Function: updateEntity
259 // Class: SketchSolver_PluginManager
260 // Purpose: update any element on the sketch, which is used by constraints
261 // ============================================================================
262 void SketchSolver_ConstraintManager::updateEntity(boost::shared_ptr<SketchPlugin_Feature> theFeature)
264 // Create list of attributes depending on type of the feature
265 std::vector<std::string> anAttrList;
266 const std::string& aFeatureKind = theFeature->getKind();
268 if (aFeatureKind.compare(SketchPlugin_Point::ID()) == 0)
269 anAttrList.push_back(SketchPlugin_Point::COORD_ID());
271 else if (aFeatureKind.compare(SketchPlugin_Line::ID()) == 0)
273 anAttrList.push_back(SketchPlugin_Line::START_ID());
274 anAttrList.push_back(SketchPlugin_Line::END_ID());
277 else if (aFeatureKind.compare(SketchPlugin_Circle::ID()) == 0)
279 anAttrList.push_back(SketchPlugin_Circle::CENTER_ID());
280 anAttrList.push_back(SketchPlugin_Circle::RADIUS_ID());
283 else if (aFeatureKind.compare(SketchPlugin_Arc::ID()) == 0)
285 anAttrList.push_back(SketchPlugin_Arc::CENTER_ID());
286 anAttrList.push_back(SketchPlugin_Arc::START_ID());
287 anAttrList.push_back(SketchPlugin_Arc::END_ID());
289 /// \todo Other types of features should be implemented
291 // Check changing of feature's attributes (go through the groups and search usage of the attributes)
292 std::vector<std::string>::const_iterator anAttrIter;
293 for (anAttrIter = anAttrList.begin(); anAttrIter != anAttrList.end(); anAttrIter++)
295 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
296 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
298 if ((*aGroupIter)->isEmpty())
300 boost::shared_ptr<ModelAPI_Attribute> anAttribute =
301 boost::dynamic_pointer_cast<ModelAPI_Attribute>(theFeature->data()->attribute(*anAttrIter));
302 (*aGroupIter)->updateEntityIfPossible(anAttribute);
306 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
307 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
308 if (!(*aGroupIter)->isEmpty())
309 (*aGroupIter)->updateRelatedConstraints(theFeature);
313 // ============================================================================
314 // Function: findGroups
315 // Class: SketchSolver_PluginManager
316 // Purpose: search groups of entities interacting with given constraint
317 // ============================================================================
318 void SketchSolver_ConstraintManager::findGroups(
319 boost::shared_ptr<SketchPlugin_Constraint> theConstraint,
320 std::set<Slvs_hGroup>& theGroupIDs) const
322 boost::shared_ptr<SketchPlugin_Feature> aWP = findWorkplaneForConstraint(theConstraint);
324 SketchSolver_ConstraintGroup* anEmptyGroup = 0; // appropriate empty group for specified constraint
325 std::vector<SketchSolver_ConstraintGroup*>::const_iterator aGroupIter;
326 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
327 if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theConstraint))
329 if (!(*aGroupIter)->isEmpty())
330 theGroupIDs.insert((*aGroupIter)->getId());
331 else if (!anEmptyGroup)
332 anEmptyGroup = *aGroupIter;
335 // When only empty group is found, use it
336 if (anEmptyGroup && theGroupIDs.empty())
337 theGroupIDs.insert(anEmptyGroup->getId());
340 // ============================================================================
341 // Function: findWorkplaneForConstraint
342 // Class: SketchSolver_PluginManager
343 // Purpose: search workplane containing given constraint
344 // ============================================================================
345 boost::shared_ptr<SketchPlugin_Feature> SketchSolver_ConstraintManager::findWorkplaneForConstraint(
346 boost::shared_ptr<SketchPlugin_Constraint> theConstraint) const
348 // Already verified workplanes
349 std::set< boost::shared_ptr<SketchPlugin_Feature> > aVerified;
351 std::vector<SketchSolver_ConstraintGroup*>::const_iterator aGroupIter;
352 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
354 boost::shared_ptr<SketchPlugin_Feature> aWP = (*aGroupIter)->getWorkplane();
355 if (aVerified.find(aWP) != aVerified.end())
358 boost::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures =
359 boost::dynamic_pointer_cast<ModelAPI_AttributeRefList>(aWP->data()->attribute(SketchPlugin_Sketch::FEATURES_ID()));
360 std::list<ObjectPtr> aFeaturesList = aWPFeatures->list();
361 std::list<ObjectPtr>::const_iterator anIter;
362 for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
363 if (*anIter == theConstraint)
364 return aWP; // workplane is found
365 aVerified.insert(aWP);
368 return boost::shared_ptr<SketchPlugin_Feature>();
371 // ============================================================================
372 // Function: resolveConstraints
373 // Class: SketchSolver_PluginManager
374 // Purpose: change entities according to available constraints
375 // ============================================================================
376 void SketchSolver_ConstraintManager::resolveConstraints()
378 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
379 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
380 (*aGroupIter)->resolveConstraints();
382 // Features may be updated => send events
383 Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));