1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
3 // File: SketchSolver_ConstraintManager.cpp
4 // Created: 08 May 2014
5 // Author: Artem ZHIDKOV
7 #include "SketchSolver_ConstraintManager.h"
8 #include <SketchSolver_ConstraintGroup.h>
10 #include <Events_Loop.h>
11 #include <ModelAPI_AttributeDouble.h>
12 #include <ModelAPI_AttributeRefList.h>
13 #include <ModelAPI_Data.h>
14 #include <ModelAPI_Events.h>
15 #include <ModelAPI_Object.h>
16 #include <ModelAPI_ResultConstruction.h>
17 #include <ModelAPI_Attribute.h>
19 #include <SketchPlugin_Constraint.h>
21 #include <SketchPlugin_Arc.h>
22 #include <SketchPlugin_Circle.h>
23 #include <SketchPlugin_Line.h>
24 #include <SketchPlugin_Point.h>
25 #include <SketchPlugin_Sketch.h>
26 #include <SketchPlugin_Feature.h>
32 // Initialization of constraint manager self pointer
33 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::_self = 0;
35 /// Global constraint manager object
36 SketchSolver_ConstraintManager* myManager = SketchSolver_ConstraintManager::Instance();
38 // ========================================================
39 // ========= SketchSolver_ConstraintManager ===============
40 // ========================================================
41 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::Instance()
44 _self = new SketchSolver_ConstraintManager();
48 SketchSolver_ConstraintManager::SketchSolver_ConstraintManager()
53 // Register in event loop
54 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
55 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
56 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
57 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_MOVED));
60 SketchSolver_ConstraintManager::~SketchSolver_ConstraintManager()
65 // ============================================================================
66 // Function: processEvent
67 // Class: SketchSolver_Session
68 // Purpose: listen the event loop and process the message
69 // ============================================================================
70 void SketchSolver_ConstraintManager::processEvent(
71 const std::shared_ptr<Events_Message>& theMessage)
75 if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)
76 || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED)
77 || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED)) {
78 std::shared_ptr<ModelAPI_ObjectUpdatedMessage> anUpdateMsg =
79 std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
80 std::set<ObjectPtr> aFeatures = anUpdateMsg->objects();
82 bool isMovedEvt = theMessage->eventID()
83 == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED);
85 std::set<ObjectPtr>::iterator aFeatIter;
86 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
87 std::shared_ptr<SketchPlugin_Feature> aSFeature =
88 std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
90 updateEntity(aSFeature);
93 std::set<ObjectPtr>::iterator aFeatIter;
94 // iterate sketchers fisrt to create all sketches before (on load may exist several sketches)
95 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
96 FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*aFeatIter);
99 const std::string& aFeatureKind = aFeature->getKind();
100 if (aFeatureKind.compare(SketchPlugin_Sketch::ID()) == 0) {
101 std::shared_ptr<ModelAPI_CompositeFeature> aSketch = std::dynamic_pointer_cast<
102 ModelAPI_CompositeFeature>(aFeature);
103 changeWorkplane(aSketch);
106 // then get anything but not the sketch
107 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
108 std::shared_ptr<SketchPlugin_Feature> aFeature =
109 std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
112 changeConstraintOrEntity(aFeature);
116 // Solve the set of constraints
117 resolveConstraints(isMovedEvt); // send update for movement in any case
118 } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
119 std::shared_ptr<ModelAPI_ObjectDeletedMessage> aDeleteMsg =
120 std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
121 const std::set<std::string>& aFeatureGroups = aDeleteMsg->groups();
123 // Find SketchPlugin_Sketch::ID() in groups. The constraint groups should be updated when an object removed from Sketch
124 std::set<std::string>::const_iterator aFGrIter;
125 for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
126 if (aFGrIter->compare(ModelAPI_ResultConstruction::group()) == 0 ||
127 aFGrIter->compare(ModelAPI_Feature::group()) == 0)
130 if (aFGrIter != aFeatureGroups.end()) {
131 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter = myGroups.begin();
132 std::vector<SketchSolver_ConstraintGroup*> aSeparatedGroups;
133 while (aGroupIter != myGroups.end()) {
134 if (!(*aGroupIter)->isWorkplaneValid()) { // the group should be removed
136 int aShift = aGroupIter - myGroups.begin();
137 myGroups.erase(aGroupIter);
138 aGroupIter = myGroups.begin() + aShift;
141 if ((*aGroupIter)->updateGroup()) { // some constraints were removed, try to split the group
142 (*aGroupIter)->splitGroup(aSeparatedGroups);
146 if (aSeparatedGroups.size() > 0)
147 myGroups.insert(myGroups.end(), aSeparatedGroups.begin(), aSeparatedGroups.end());
152 // ============================================================================
153 // Function: changeWorkplane
154 // Class: SketchSolver_Session
155 // Purpose: update workplane by given parameters of the sketch
156 // ============================================================================
157 bool SketchSolver_ConstraintManager::changeWorkplane(
158 std::shared_ptr<ModelAPI_CompositeFeature> theSketch)
160 bool aResult = true; // changed when a workplane wrongly updated
161 bool isUpdated = false;
162 // Try to update specified workplane in all groups
163 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
164 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
165 if ((*aGroupIter)->isBaseWorkplane(theSketch)) {
167 if (!(*aGroupIter)->updateWorkplane())
170 // If the workplane is not updated, so this is a new workplane
172 SketchSolver_ConstraintGroup* aNewGroup = new SketchSolver_ConstraintGroup(theSketch);
173 // Verify that the group is created successfully
174 if (!aNewGroup->isBaseWorkplane(theSketch) || !aNewGroup->isWorkplaneValid()) {
178 myGroups.push_back(aNewGroup);
183 // ============================================================================
184 // Function: changeConstraintOrEntity
185 // Class: SketchSolver_Session
186 // Purpose: create/update the constraint or the feature and place it into appropriate group
187 // ============================================================================
188 bool SketchSolver_ConstraintManager::changeConstraintOrEntity(
189 std::shared_ptr<SketchPlugin_Feature> theFeature)
191 // Search the groups which this feature touches
192 std::set<Slvs_hGroup> aGroups;
193 findGroups(theFeature, aGroups);
195 std::shared_ptr<SketchPlugin_Constraint> aConstraint =
196 std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
198 // Process the groups list
199 if (aGroups.size() == 0) {
200 // There are no groups applicable for this constraint => create new one
201 // The group will be created only for constraints, not for features
202 if (!aConstraint) return false;
203 std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(aConstraint);
206 SketchSolver_ConstraintGroup* aGroup = new SketchSolver_ConstraintGroup(aWP);
207 if (!aGroup->changeConstraint(aConstraint)) {
211 myGroups.push_back(aGroup);
213 } else if (aGroups.size() == 1) { // Only one group => add feature into it
214 Slvs_hGroup aGroupId = *(aGroups.begin());
215 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
216 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
217 if ((*aGroupIter)->getId() == aGroupId) {
218 // If the group is empty, the feature is not added (the constraint only)
219 if (!aConstraint && !(*aGroupIter)->isEmpty())
220 return (*aGroupIter)->changeEntityFeature(theFeature) != SLVS_E_UNKNOWN;
221 return (*aGroupIter)->changeConstraint(aConstraint);
223 } else if (aGroups.size() > 1) { // Several groups applicable for this feature => need to merge them
224 std::set<Slvs_hGroup>::const_iterator aGroupsIter = aGroups.begin();
226 // Search first group
227 std::vector<SketchSolver_ConstraintGroup*>::iterator aFirstGroupIter;
228 for (aFirstGroupIter = myGroups.begin(); aFirstGroupIter != myGroups.end(); aFirstGroupIter++)
229 if ((*aFirstGroupIter)->getId() == *aGroupsIter)
231 if (aFirstGroupIter == myGroups.end())
234 // Append other groups to the first one
235 std::vector<SketchSolver_ConstraintGroup*>::iterator anOtherGroupIter = aFirstGroupIter + 1;
236 for (aGroupsIter++; aGroupsIter != aGroups.end(); aGroupsIter++) {
237 for (; anOtherGroupIter != myGroups.end(); anOtherGroupIter++)
238 if ((*anOtherGroupIter)->getId() == *aGroupsIter)
240 if (anOtherGroupIter == myGroups.end()) { // Group disappears
241 anOtherGroupIter = aFirstGroupIter + 1;
245 (*aFirstGroupIter)->mergeGroups(**anOtherGroupIter);
246 int aShiftFirst = aFirstGroupIter - myGroups.begin();
247 int aShiftOther = anOtherGroupIter - myGroups.begin();
248 delete *anOtherGroupIter;
249 myGroups.erase(anOtherGroupIter);
250 aFirstGroupIter = myGroups.begin() + aShiftFirst;
251 anOtherGroupIter = myGroups.begin() + aShiftOther;
255 return (*aFirstGroupIter)->changeConstraint(aConstraint);
256 return (*aFirstGroupIter)->changeEntityFeature(theFeature) != SLVS_E_UNKNOWN;
259 // Something goes wrong
263 // ============================================================================
264 // Function: updateEntity
265 // Class: SketchSolver_Session
266 // Purpose: update any element on the sketch, which is used by constraints
267 // ============================================================================
268 void SketchSolver_ConstraintManager::updateEntity(
269 std::shared_ptr<SketchPlugin_Feature> theFeature)
271 // Create list of attributes depending on type of the feature
272 std::vector<std::string> anAttrList;
273 const std::string& aFeatureKind = theFeature->getKind();
275 if (aFeatureKind.compare(SketchPlugin_Point::ID()) == 0)
276 anAttrList.push_back(SketchPlugin_Point::COORD_ID());
278 else if (aFeatureKind.compare(SketchPlugin_Line::ID()) == 0) {
279 anAttrList.push_back(SketchPlugin_Line::START_ID());
280 anAttrList.push_back(SketchPlugin_Line::END_ID());
283 else if (aFeatureKind.compare(SketchPlugin_Circle::ID()) == 0) {
284 anAttrList.push_back(SketchPlugin_Circle::CENTER_ID());
285 anAttrList.push_back(SketchPlugin_Circle::RADIUS_ID());
288 else if (aFeatureKind.compare(SketchPlugin_Arc::ID()) == 0) {
289 anAttrList.push_back(SketchPlugin_Arc::CENTER_ID());
290 anAttrList.push_back(SketchPlugin_Arc::START_ID());
291 anAttrList.push_back(SketchPlugin_Arc::END_ID());
293 /// \todo Other types of features should be implemented
295 // Check changing of feature's attributes (go through the groups and search usage of the attributes)
296 std::vector<std::string>::const_iterator anAttrIter;
297 for (anAttrIter = anAttrList.begin(); anAttrIter != anAttrList.end(); anAttrIter++) {
298 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
299 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
300 if ((*aGroupIter)->isEmpty())
302 std::shared_ptr<ModelAPI_Attribute> anAttribute = std::dynamic_pointer_cast<
303 ModelAPI_Attribute>(theFeature->data()->attribute(*anAttrIter));
304 (*aGroupIter)->updateEntityIfPossible(anAttribute);
308 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
309 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
310 if (!(*aGroupIter)->isEmpty())
311 (*aGroupIter)->updateRelatedConstraintsFeature(theFeature);
314 // ============================================================================
315 // Function: findGroups
316 // Class: SketchSolver_Session
317 // Purpose: search groups of entities interacting with given feature
318 // ============================================================================
319 void SketchSolver_ConstraintManager::findGroups(
320 std::shared_ptr<SketchPlugin_Feature> theFeature,
321 std::set<Slvs_hGroup>& theGroupIDs) const
323 std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(theFeature);
325 SketchSolver_ConstraintGroup* anEmptyGroup = 0; // appropriate empty group for specified constraint
326 std::vector<SketchSolver_ConstraintGroup*>::const_iterator aGroupIter;
327 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
328 if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theFeature)) {
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: findWorkplane
342 // Class: SketchSolver_Session
343 // Purpose: search workplane containing given feature
344 // ============================================================================
345 std::shared_ptr<ModelAPI_CompositeFeature> SketchSolver_ConstraintManager
346 ::findWorkplane(std::shared_ptr<SketchPlugin_Feature> theFeature) const
348 // Already verified workplanes
349 std::set<std::shared_ptr<ModelAPI_CompositeFeature> > aVerified;
351 std::vector<SketchSolver_ConstraintGroup*>::const_iterator aGroupIter;
352 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
353 std::shared_ptr<ModelAPI_CompositeFeature> aWP = (*aGroupIter)->getWorkplane();
354 if (aVerified.find(aWP) != aVerified.end())
357 std::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures = std::dynamic_pointer_cast<
358 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 == theFeature)
363 return aWP; // workplane is found
364 aVerified.insert(aWP);
367 return std::shared_ptr<ModelAPI_CompositeFeature>();
370 // ============================================================================
371 // Function: resolveConstraints
372 // Class: SketchSolver_Session
373 // Purpose: change entities according to available constraints
374 // ============================================================================
375 void SketchSolver_ConstraintManager::resolveConstraints(const bool theForceUpdate)
378 bool needToUpdate = false;
379 static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
380 // to avoid redisplay of each segment on update by solver one by one in the viewer
381 bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
382 if (isUpdateFlushed) {
383 Events_Loop::loop()->setFlushed(anUpdateEvent, false);
386 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
387 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
388 if ((*aGroupIter)->resolveConstraints())
391 // Features may be updated => now send events, but for all changed at once
392 if (isUpdateFlushed) {
393 Events_Loop::loop()->setFlushed(anUpdateEvent, true);
395 // Must be before flush because on "Updated" flush the results may be produced
396 // and the creation event is appeared with many new objects. If myIsComputed these
397 // events are missed in processEvents and some elements are not added.
398 myIsComputed = false;
399 if (needToUpdate || theForceUpdate)
400 Events_Loop::loop()->flush(anUpdateEvent);