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()
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_Session
61 // Purpose: listen the event loop and process the message
62 // ============================================================================
63 void SketchSolver_ConstraintManager::processEvent(
64 const boost::shared_ptr<Events_Message>& theMessage)
68 if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)
69 || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED)
70 || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED)) {
71 boost::shared_ptr<ModelAPI_ObjectUpdatedMessage> anUpdateMsg =
72 boost::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
73 std::set<ObjectPtr> aFeatures = anUpdateMsg->objects();
75 bool isMovedEvt = theMessage->eventID()
76 == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED);
78 std::set<ObjectPtr>::iterator aFeatIter;
79 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
80 boost::shared_ptr<SketchPlugin_Feature> aSFeature =
81 boost::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
83 updateEntity(aSFeature);
86 std::set<ObjectPtr>::iterator aFeatIter;
87 // iterate sketchers fisrt to create all sketches before (on load may exist several sketches)
88 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
89 FeaturePtr aFeature = boost::dynamic_pointer_cast<ModelAPI_Feature>(*aFeatIter);
92 const std::string& aFeatureKind = aFeature->getKind();
93 if (aFeatureKind.compare(SketchPlugin_Sketch::ID()) == 0) {
94 boost::shared_ptr<ModelAPI_CompositeFeature> aSketch = boost::dynamic_pointer_cast<
95 ModelAPI_CompositeFeature>(aFeature);
96 changeWorkplane(aSketch);
99 // then get anything but not the sketch
100 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
101 boost::shared_ptr<SketchPlugin_Feature> aFeature =
102 boost::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
105 changeConstraintOrEntity(aFeature);
109 // Solve the set of constraints
110 resolveConstraints();
111 } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
112 boost::shared_ptr<ModelAPI_ObjectDeletedMessage> aDeleteMsg =
113 boost::dynamic_pointer_cast<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(ModelAPI_ResultConstruction::group()) == 0 ||
120 aFGrIter->compare(ModelAPI_Feature::group()) == 0)
123 if (aFGrIter != aFeatureGroups.end()) {
124 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter = myGroups.begin();
125 std::vector<SketchSolver_ConstraintGroup*> aSeparatedGroups;
126 while (aGroupIter != myGroups.end()) {
127 if (!(*aGroupIter)->isWorkplaneValid()) { // the group should be removed
129 int aShift = aGroupIter - myGroups.begin();
130 myGroups.erase(aGroupIter);
131 aGroupIter = myGroups.begin() + aShift;
134 if ((*aGroupIter)->updateGroup()) { // some constraints were removed, try to split the group
135 (*aGroupIter)->splitGroup(aSeparatedGroups);
139 if (aSeparatedGroups.size() > 0)
140 myGroups.insert(myGroups.end(), aSeparatedGroups.begin(), aSeparatedGroups.end());
145 // ============================================================================
146 // Function: changeWorkplane
147 // Class: SketchSolver_Session
148 // Purpose: update workplane by given parameters of the sketch
149 // ============================================================================
150 bool SketchSolver_ConstraintManager::changeWorkplane(
151 boost::shared_ptr<ModelAPI_CompositeFeature> theSketch)
153 bool aResult = true; // changed when a workplane wrongly updated
154 bool isUpdated = false;
155 // Try to update specified workplane in all groups
156 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
157 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
158 if ((*aGroupIter)->isBaseWorkplane(theSketch)) {
160 if (!(*aGroupIter)->updateWorkplane())
163 // If the workplane is not updated, so this is a new workplane
165 SketchSolver_ConstraintGroup* aNewGroup = new SketchSolver_ConstraintGroup(theSketch);
166 // Verify that the group is created successfully
167 if (!aNewGroup->isBaseWorkplane(theSketch)) {
171 myGroups.push_back(aNewGroup);
176 // ============================================================================
177 // Function: changeConstraintOrEntity
178 // Class: SketchSolver_Session
179 // Purpose: create/update the constraint or the feature and place it into appropriate group
180 // ============================================================================
181 bool SketchSolver_ConstraintManager::changeConstraintOrEntity(
182 boost::shared_ptr<SketchPlugin_Feature> theFeature)
184 // Search the groups which this feature touches
185 std::set<Slvs_hGroup> aGroups;
186 findGroups(theFeature, aGroups);
188 boost::shared_ptr<SketchPlugin_Constraint> aConstraint =
189 boost::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
191 // Process the groups list
192 if (aGroups.size() == 0) {
193 // There are no groups applicable for this constraint => create new one
194 // The group will be created only for constraints, not for features
195 if (!aConstraint) return false;
196 boost::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(aConstraint);
199 SketchSolver_ConstraintGroup* aGroup = new SketchSolver_ConstraintGroup(aWP);
200 if (!aGroup->changeConstraint(aConstraint)) {
204 myGroups.push_back(aGroup);
206 } else if (aGroups.size() == 1) { // Only one group => add feature into it
207 Slvs_hGroup aGroupId = *(aGroups.begin());
208 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
209 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
210 if ((*aGroupIter)->getId() == aGroupId) {
211 // If the group is empty, the feature is not added (the constraint only)
212 if (!aConstraint && !(*aGroupIter)->isEmpty())
213 return (*aGroupIter)->changeEntity(theFeature) != SLVS_E_UNKNOWN;
214 return (*aGroupIter)->changeConstraint(aConstraint);
216 } else if (aGroups.size() > 1) { // Several groups applicable for this feature => 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++) {
230 for (; anOtherGroupIter != myGroups.end(); anOtherGroupIter++)
231 if ((*anOtherGroupIter)->getId() == *aGroupsIter)
233 if (anOtherGroupIter == myGroups.end()) { // Group disappears
234 anOtherGroupIter = aFirstGroupIter + 1;
238 (*aFirstGroupIter)->mergeGroups(**anOtherGroupIter);
239 int aShiftFirst = aFirstGroupIter - myGroups.begin();
240 int aShiftOther = anOtherGroupIter - myGroups.begin();
241 delete *anOtherGroupIter;
242 myGroups.erase(anOtherGroupIter);
243 aFirstGroupIter = myGroups.begin() + aShiftFirst;
244 anOtherGroupIter = myGroups.begin() + aShiftOther;
248 return (*aFirstGroupIter)->changeConstraint(aConstraint);
249 return (*aFirstGroupIter)->changeEntity(theFeature) != SLVS_E_UNKNOWN;
252 // Something goes wrong
256 // ============================================================================
257 // Function: updateEntity
258 // Class: SketchSolver_Session
259 // Purpose: update any element on the sketch, which is used by constraints
260 // ============================================================================
261 void SketchSolver_ConstraintManager::updateEntity(
262 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) {
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) {
277 anAttrList.push_back(SketchPlugin_Circle::CENTER_ID());
278 anAttrList.push_back(SketchPlugin_Circle::RADIUS_ID());
281 else if (aFeatureKind.compare(SketchPlugin_Arc::ID()) == 0) {
282 anAttrList.push_back(SketchPlugin_Arc::CENTER_ID());
283 anAttrList.push_back(SketchPlugin_Arc::START_ID());
284 anAttrList.push_back(SketchPlugin_Arc::END_ID());
286 /// \todo Other types of features should be implemented
288 // Check changing of feature's attributes (go through the groups and search usage of the attributes)
289 std::vector<std::string>::const_iterator anAttrIter;
290 for (anAttrIter = anAttrList.begin(); anAttrIter != anAttrList.end(); anAttrIter++) {
291 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
292 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
293 if ((*aGroupIter)->isEmpty())
295 boost::shared_ptr<ModelAPI_Attribute> anAttribute = boost::dynamic_pointer_cast<
296 ModelAPI_Attribute>(theFeature->data()->attribute(*anAttrIter));
297 (*aGroupIter)->updateEntityIfPossible(anAttribute);
301 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
302 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
303 if (!(*aGroupIter)->isEmpty())
304 (*aGroupIter)->updateRelatedConstraints(theFeature);
307 // ============================================================================
308 // Function: findGroups
309 // Class: SketchSolver_Session
310 // Purpose: search groups of entities interacting with given feature
311 // ============================================================================
312 void SketchSolver_ConstraintManager::findGroups(
313 boost::shared_ptr<SketchPlugin_Feature> theFeature,
314 std::set<Slvs_hGroup>& theGroupIDs) const
316 boost::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(theFeature);
318 SketchSolver_ConstraintGroup* anEmptyGroup = 0; // appropriate empty group for specified constraint
319 std::vector<SketchSolver_ConstraintGroup*>::const_iterator aGroupIter;
320 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
321 if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theFeature)) {
322 if (!(*aGroupIter)->isEmpty())
323 theGroupIDs.insert((*aGroupIter)->getId());
324 else if (!anEmptyGroup)
325 anEmptyGroup = *aGroupIter;
328 // When only empty group is found, use it
329 if (anEmptyGroup && theGroupIDs.empty())
330 theGroupIDs.insert(anEmptyGroup->getId());
333 // ============================================================================
334 // Function: findWorkplane
335 // Class: SketchSolver_Session
336 // Purpose: search workplane containing given feature
337 // ============================================================================
338 boost::shared_ptr<ModelAPI_CompositeFeature> SketchSolver_ConstraintManager::findWorkplane(
339 boost::shared_ptr<SketchPlugin_Feature> theFeature) const
341 // Already verified workplanes
342 std::set<boost::shared_ptr<ModelAPI_CompositeFeature> > aVerified;
344 std::vector<SketchSolver_ConstraintGroup*>::const_iterator aGroupIter;
345 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
346 boost::shared_ptr<ModelAPI_CompositeFeature> aWP = (*aGroupIter)->getWorkplane();
347 if (aVerified.find(aWP) != aVerified.end())
350 boost::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures = boost::dynamic_pointer_cast<
351 ModelAPI_AttributeRefList>(aWP->data()->attribute(SketchPlugin_Sketch::FEATURES_ID()));
352 std::list<ObjectPtr> aFeaturesList = aWPFeatures->list();
353 std::list<ObjectPtr>::const_iterator anIter;
354 for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
355 if (*anIter == theFeature)
356 return aWP; // workplane is found
357 aVerified.insert(aWP);
360 return boost::shared_ptr<ModelAPI_CompositeFeature>();
363 // ============================================================================
364 // Function: resolveConstraints
365 // Class: SketchSolver_Session
366 // Purpose: change entities according to available constraints
367 // ============================================================================
368 void SketchSolver_ConstraintManager::resolveConstraints()
371 bool needToUpdate = false;
372 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
373 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
374 if ((*aGroupIter)->resolveConstraints())
377 // Features may be updated => send events
379 Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
380 myIsComputed = false;