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"
9 #include <Events_Loop.h>
10 #include <ModelAPI_AttributeDouble.h>
11 #include <ModelAPI_AttributeRefList.h>
12 #include <ModelAPI_Data.h>
13 #include <ModelAPI_Events.h>
14 #include <ModelAPI_Object.h>
15 #include <ModelAPI_ResultConstruction.h>
16 #include <ModelAPI_Attribute.h>
18 #include <SketchPlugin_Constraint.h>
19 #include <SketchPlugin_ConstraintCoincidence.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 moveEntity(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 // at first, add coincidence constraints, because they may be used by other constraints
108 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
109 std::shared_ptr<SketchPlugin_Feature> aFeature =
110 std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
111 if (!aFeature || aFeature->getKind() != SketchPlugin_ConstraintCoincidence::ID())
113 changeConstraintOrEntity(aFeature);
115 // after that, add all features except coincidence
116 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
117 std::shared_ptr<SketchPlugin_Feature> aFeature =
118 std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
119 if (!aFeature /*|| aFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()*/)
121 changeConstraintOrEntity(aFeature);
125 // Solve the set of constraints
126 resolveConstraints(isMovedEvt); // send update for movement in any case
127 } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
128 std::shared_ptr<ModelAPI_ObjectDeletedMessage> aDeleteMsg =
129 std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
130 const std::set<std::string>& aFeatureGroups = aDeleteMsg->groups();
132 // Find SketchPlugin_Sketch::ID() in groups. The constraint groups should be updated when an object removed from Sketch
133 std::set<std::string>::const_iterator aFGrIter;
134 for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
135 if (aFGrIter->compare(ModelAPI_ResultConstruction::group()) == 0 ||
136 aFGrIter->compare(ModelAPI_Feature::group()) == 0)
139 if (aFGrIter != aFeatureGroups.end()) {
140 std::vector<SketchSolver_Group*>::iterator aGroupIter = myGroups.begin();
141 std::vector<SketchSolver_Group*> aSeparatedGroups;
142 while (aGroupIter != myGroups.end()) {
143 if (!(*aGroupIter)->isWorkplaneValid()) { // the group should be removed
145 int aShift = aGroupIter - myGroups.begin();
146 myGroups.erase(aGroupIter);
147 aGroupIter = myGroups.begin() + aShift;
150 if (!(*aGroupIter)->isConsistent()) { // some constraints were removed, try to split the group
151 (*aGroupIter)->splitGroup(aSeparatedGroups);
155 if (aSeparatedGroups.size() > 0)
156 myGroups.insert(myGroups.end(), aSeparatedGroups.begin(), aSeparatedGroups.end());
161 // ============================================================================
162 // Function: changeWorkplane
163 // Class: SketchSolver_Session
164 // Purpose: update workplane by given parameters of the sketch
165 // ============================================================================
166 bool SketchSolver_ConstraintManager::changeWorkplane(CompositeFeaturePtr theSketch)
168 bool aResult = true; // changed when a workplane wrongly updated
169 bool isUpdated = false;
170 // Try to update specified workplane in all groups
171 std::vector<SketchSolver_Group*>::iterator aGroupIter;
172 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
173 if ((*aGroupIter)->isBaseWorkplane(theSketch)) {
175 if (!(*aGroupIter)->updateWorkplane())
178 // If the workplane is not updated, so this is a new workplane
180 SketchSolver_Group* aNewGroup = new SketchSolver_Group(theSketch);
181 // Verify that the group is created successfully
182 if (!aNewGroup->isBaseWorkplane(theSketch) || !aNewGroup->isWorkplaneValid()) {
186 myGroups.push_back(aNewGroup);
191 // ============================================================================
192 // Function: changeConstraintOrEntity
193 // Class: SketchSolver_Session
194 // Purpose: create/update the constraint or the feature and place it into appropriate group
195 // ============================================================================
196 bool SketchSolver_ConstraintManager::changeConstraintOrEntity(
197 std::shared_ptr<SketchPlugin_Feature> theFeature)
199 // Search the groups which this feature touches
200 std::set<Slvs_hGroup> aGroups;
201 findGroups(theFeature, aGroups);
203 std::shared_ptr<SketchPlugin_Constraint> aConstraint =
204 std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
206 // Process the groups list
207 if (aGroups.size() == 0) {
208 // There are no groups applicable for this constraint => create new one
209 // The group will be created only for constraints, not for features
210 if (!aConstraint) return false;
211 std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(aConstraint);
214 SketchSolver_Group* aGroup = new SketchSolver_Group(aWP);
215 if (!aGroup->changeConstraint(aConstraint)) {
219 myGroups.push_back(aGroup);
221 } else if (aGroups.size() == 1) { // Only one group => add feature into it
222 Slvs_hGroup aGroupId = *(aGroups.begin());
223 std::vector<SketchSolver_Group*>::iterator aGroupIter;
224 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
225 if ((*aGroupIter)->getId() == aGroupId) {
226 // If the group is empty, the feature is not added (the constraint only)
227 if (!aConstraint && !(*aGroupIter)->isEmpty())
228 return (*aGroupIter)->updateFeature(theFeature);
229 //// return (*aGroupIter)->changeEntityFeature(theFeature) != SLVS_E_UNKNOWN;
230 return (*aGroupIter)->changeConstraint(aConstraint);
232 } else if (aGroups.size() > 1) { // Several groups applicable for this feature => need to merge them
233 std::set<Slvs_hGroup>::const_iterator aGroupsIter = aGroups.begin();
235 // Search first group
236 std::vector<SketchSolver_Group*>::iterator aFirstGroupIter;
237 for (aFirstGroupIter = myGroups.begin(); aFirstGroupIter != myGroups.end(); aFirstGroupIter++)
238 if ((*aFirstGroupIter)->getId() == *aGroupsIter)
240 if (aFirstGroupIter == myGroups.end())
243 // Append other groups to the first one
244 std::vector<SketchSolver_Group*>::iterator anOtherGroupIter = aFirstGroupIter + 1;
245 for (aGroupsIter++; aGroupsIter != aGroups.end(); aGroupsIter++) {
246 for (; anOtherGroupIter != myGroups.end(); anOtherGroupIter++)
247 if ((*anOtherGroupIter)->getId() == *aGroupsIter)
249 if (anOtherGroupIter == myGroups.end()) { // Group disappears
250 anOtherGroupIter = aFirstGroupIter + 1;
254 (*aFirstGroupIter)->mergeGroups(**anOtherGroupIter);
255 int aShiftFirst = aFirstGroupIter - myGroups.begin();
256 int aShiftOther = anOtherGroupIter - myGroups.begin();
257 delete *anOtherGroupIter;
258 myGroups.erase(anOtherGroupIter);
259 aFirstGroupIter = myGroups.begin() + aShiftFirst;
260 anOtherGroupIter = myGroups.begin() + aShiftOther;
264 return (*aFirstGroupIter)->changeConstraint(aConstraint);
265 return (*aFirstGroupIter)->updateFeature(theFeature);
266 //// return (*aFirstGroupIter)->changeEntityFeature(theFeature) != SLVS_E_UNKNOWN;
269 // Something goes wrong
273 // ============================================================================
274 // Function: moveEntity
275 // Class: SketchSolver_Session
276 // Purpose: update element moved on the sketch, which is used by constraints
277 // ============================================================================
278 void SketchSolver_ConstraintManager::moveEntity(
279 std::shared_ptr<SketchPlugin_Feature> theFeature)
281 std::vector<SketchSolver_Group*>::iterator aGroupIt = myGroups.begin();
282 for (; aGroupIt != myGroups.end(); aGroupIt++)
283 if (!(*aGroupIt)->isEmpty() && (*aGroupIt)->isInteract(theFeature))
284 (*aGroupIt)->moveFeature(theFeature);
286 //// // Create list of attributes depending on type of the feature
287 //// std::vector<std::string> anAttrList;
288 //// const std::string& aFeatureKind = theFeature->getKind();
290 //// if (aFeatureKind.compare(SketchPlugin_Point::ID()) == 0)
291 //// anAttrList.push_back(SketchPlugin_Point::COORD_ID());
293 //// else if (aFeatureKind.compare(SketchPlugin_Line::ID()) == 0) {
294 //// anAttrList.push_back(SketchPlugin_Line::START_ID());
295 //// anAttrList.push_back(SketchPlugin_Line::END_ID());
298 //// else if (aFeatureKind.compare(SketchPlugin_Circle::ID()) == 0) {
299 //// anAttrList.push_back(SketchPlugin_Circle::CENTER_ID());
300 //// anAttrList.push_back(SketchPlugin_Circle::RADIUS_ID());
303 //// else if (aFeatureKind.compare(SketchPlugin_Arc::ID()) == 0) {
304 //// anAttrList.push_back(SketchPlugin_Arc::CENTER_ID());
305 //// anAttrList.push_back(SketchPlugin_Arc::START_ID());
306 //// anAttrList.push_back(SketchPlugin_Arc::END_ID());
308 //// /// \todo Other types of features should be implemented
310 //// // Check changing of feature's attributes (go through the groups and search usage of the attributes)
311 //// std::vector<std::string>::const_iterator anAttrIter;
312 //// for (anAttrIter = anAttrList.begin(); anAttrIter != anAttrList.end(); anAttrIter++) {
313 //// std::vector<SketchSolver_Group*>::iterator aGroupIter;
314 //// for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
315 //// if ((*aGroupIter)->isEmpty())
317 //// std::shared_ptr<ModelAPI_Attribute> anAttribute = std::dynamic_pointer_cast<
318 //// ModelAPI_Attribute>(theFeature->data()->attribute(*anAttrIter));
319 //// (*aGroupIter)->updateEntityIfPossible(anAttribute);
323 //// std::vector<SketchSolver_Group*>::iterator aGroupIter;
324 //// for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
325 //// if (!(*aGroupIter)->isEmpty())
326 //// (*aGroupIter)->updateRelatedConstraintsFeature(theFeature);
329 // ============================================================================
330 // Function: findGroups
331 // Class: SketchSolver_Session
332 // Purpose: search groups of entities interacting with given feature
333 // ============================================================================
334 void SketchSolver_ConstraintManager::findGroups(
335 std::shared_ptr<SketchPlugin_Feature> theFeature,
336 std::set<Slvs_hGroup>& theGroupIDs) const
338 std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(theFeature);
340 SketchSolver_Group* anEmptyGroup = 0; // appropriate empty group for specified constraint
341 std::vector<SketchSolver_Group*>::const_iterator aGroupIter;
342 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
343 if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theFeature)) {
344 if (!(*aGroupIter)->isEmpty())
345 theGroupIDs.insert((*aGroupIter)->getId());
346 else if (!anEmptyGroup)
347 anEmptyGroup = *aGroupIter;
350 // When only empty group is found, use it
351 if (anEmptyGroup && theGroupIDs.empty())
352 theGroupIDs.insert(anEmptyGroup->getId());
355 // ============================================================================
356 // Function: findWorkplane
357 // Class: SketchSolver_Session
358 // Purpose: search workplane containing given feature
359 // ============================================================================
360 std::shared_ptr<ModelAPI_CompositeFeature> SketchSolver_ConstraintManager
361 ::findWorkplane(std::shared_ptr<SketchPlugin_Feature> theFeature) const
363 // Already verified workplanes
364 std::set<std::shared_ptr<ModelAPI_CompositeFeature> > aVerified;
366 std::vector<SketchSolver_Group*>::const_iterator aGroupIter;
367 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
368 std::shared_ptr<ModelAPI_CompositeFeature> aWP = (*aGroupIter)->getWorkplane();
369 if (aVerified.find(aWP) != aVerified.end())
372 DataPtr aData = aWP->data();
374 std::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures = std::dynamic_pointer_cast<
375 ModelAPI_AttributeRefList>(aData->attribute(SketchPlugin_Sketch::FEATURES_ID()));
376 std::list<ObjectPtr> aFeaturesList = aWPFeatures->list();
377 std::list<ObjectPtr>::const_iterator anIter;
378 for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
379 if (*anIter == theFeature)
380 return aWP; // workplane is found
382 aVerified.insert(aWP);
385 return std::shared_ptr<ModelAPI_CompositeFeature>();
388 // ============================================================================
389 // Function: resolveConstraints
390 // Class: SketchSolver_Session
391 // Purpose: change entities according to available constraints
392 // ============================================================================
393 void SketchSolver_ConstraintManager::resolveConstraints(const bool theForceUpdate)
396 bool needToUpdate = false;
397 static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
398 // to avoid redisplay of each segment on update by solver one by one in the viewer
399 bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
400 if (isUpdateFlushed) {
401 Events_Loop::loop()->setFlushed(anUpdateEvent, false);
404 std::vector<SketchSolver_Group*>::iterator aGroupIter;
405 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
406 if ((*aGroupIter)->resolveConstraints())
409 // Features may be updated => now send events, but for all changed at once
410 if (isUpdateFlushed) {
411 Events_Loop::loop()->setFlushed(anUpdateEvent, true);
413 // Must be before flush because on "Updated" flush the results may be produced
414 // and the creation event is appeared with many new objects. If myIsComputed these
415 // events are missed in processEvents and some elements are not added.
416 myIsComputed = false;
417 if (needToUpdate || theForceUpdate)
418 Events_Loop::loop()->flush(anUpdateEvent);