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>
20 #include <SketchPlugin_ConstraintCoincidence.h>
22 #include <SketchPlugin_Arc.h>
23 #include <SketchPlugin_Circle.h>
24 #include <SketchPlugin_Line.h>
25 #include <SketchPlugin_Point.h>
26 #include <SketchPlugin_Sketch.h>
27 #include <SketchPlugin_Feature.h>
33 // Initialization of constraint manager self pointer
34 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::_self = 0;
36 /// Global constraint manager object
37 SketchSolver_ConstraintManager* myManager = SketchSolver_ConstraintManager::Instance();
39 // ========================================================
40 // ========= SketchSolver_ConstraintManager ===============
41 // ========================================================
42 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::Instance()
45 _self = new SketchSolver_ConstraintManager();
49 SketchSolver_ConstraintManager::SketchSolver_ConstraintManager()
54 // Register in event loop
55 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
56 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
57 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
58 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_MOVED));
61 SketchSolver_ConstraintManager::~SketchSolver_ConstraintManager()
66 // ============================================================================
67 // Function: processEvent
68 // Class: SketchSolver_Session
69 // Purpose: listen the event loop and process the message
70 // ============================================================================
71 void SketchSolver_ConstraintManager::processEvent(
72 const std::shared_ptr<Events_Message>& theMessage)
76 if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)
77 || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED)
78 || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED)) {
79 std::shared_ptr<ModelAPI_ObjectUpdatedMessage> anUpdateMsg =
80 std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
81 std::set<ObjectPtr> aFeatures = anUpdateMsg->objects();
83 bool isMovedEvt = theMessage->eventID()
84 == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED);
86 std::set<ObjectPtr>::iterator aFeatIter;
87 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
88 std::shared_ptr<SketchPlugin_Feature> aSFeature =
89 std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
91 updateEntity(aSFeature);
94 std::set<ObjectPtr>::iterator aFeatIter;
95 // iterate sketchers fisrt to create all sketches before (on load may exist several sketches)
96 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
97 FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*aFeatIter);
100 const std::string& aFeatureKind = aFeature->getKind();
101 if (aFeatureKind.compare(SketchPlugin_Sketch::ID()) == 0) {
102 std::shared_ptr<ModelAPI_CompositeFeature> aSketch = std::dynamic_pointer_cast<
103 ModelAPI_CompositeFeature>(aFeature);
104 changeWorkplane(aSketch);
107 // then get anything but not the sketch
108 // at first, add coincidence constraints, because they may be used by other constraints
109 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
110 std::shared_ptr<SketchPlugin_Feature> aFeature =
111 std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
112 if (!aFeature || aFeature->getKind() != SketchPlugin_ConstraintCoincidence::ID())
114 changeConstraintOrEntity(aFeature);
116 // after that, add all features except coincidence
117 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
118 std::shared_ptr<SketchPlugin_Feature> aFeature =
119 std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
120 if (!aFeature /*|| aFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()*/)
122 changeConstraintOrEntity(aFeature);
126 // Solve the set of constraints
127 resolveConstraints(isMovedEvt); // send update for movement in any case
128 } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
129 std::shared_ptr<ModelAPI_ObjectDeletedMessage> aDeleteMsg =
130 std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
131 const std::set<std::string>& aFeatureGroups = aDeleteMsg->groups();
133 // Find SketchPlugin_Sketch::ID() in groups. The constraint groups should be updated when an object removed from Sketch
134 std::set<std::string>::const_iterator aFGrIter;
135 for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
136 if (aFGrIter->compare(ModelAPI_ResultConstruction::group()) == 0 ||
137 aFGrIter->compare(ModelAPI_Feature::group()) == 0)
140 if (aFGrIter != aFeatureGroups.end()) {
141 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter = myGroups.begin();
142 std::vector<SketchSolver_ConstraintGroup*> aSeparatedGroups;
143 while (aGroupIter != myGroups.end()) {
144 if (!(*aGroupIter)->isWorkplaneValid()) { // the group should be removed
146 int aShift = aGroupIter - myGroups.begin();
147 myGroups.erase(aGroupIter);
148 aGroupIter = myGroups.begin() + aShift;
151 if ((*aGroupIter)->updateGroup()) { // some constraints were removed, try to split the group
152 (*aGroupIter)->splitGroup(aSeparatedGroups);
156 if (aSeparatedGroups.size() > 0)
157 myGroups.insert(myGroups.end(), aSeparatedGroups.begin(), aSeparatedGroups.end());
162 // ============================================================================
163 // Function: changeWorkplane
164 // Class: SketchSolver_Session
165 // Purpose: update workplane by given parameters of the sketch
166 // ============================================================================
167 bool SketchSolver_ConstraintManager::changeWorkplane(
168 std::shared_ptr<ModelAPI_CompositeFeature> theSketch)
170 bool aResult = true; // changed when a workplane wrongly updated
171 bool isUpdated = false;
172 // Try to update specified workplane in all groups
173 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
174 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
175 if ((*aGroupIter)->isBaseWorkplane(theSketch)) {
177 if (!(*aGroupIter)->updateWorkplane())
180 // If the workplane is not updated, so this is a new workplane
182 SketchSolver_ConstraintGroup* aNewGroup = new SketchSolver_ConstraintGroup(theSketch);
183 // Verify that the group is created successfully
184 if (!aNewGroup->isBaseWorkplane(theSketch) || !aNewGroup->isWorkplaneValid()) {
188 myGroups.push_back(aNewGroup);
193 // ============================================================================
194 // Function: changeConstraintOrEntity
195 // Class: SketchSolver_Session
196 // Purpose: create/update the constraint or the feature and place it into appropriate group
197 // ============================================================================
198 bool SketchSolver_ConstraintManager::changeConstraintOrEntity(
199 std::shared_ptr<SketchPlugin_Feature> theFeature)
201 // Search the groups which this feature touches
202 std::set<Slvs_hGroup> aGroups;
203 findGroups(theFeature, aGroups);
205 std::shared_ptr<SketchPlugin_Constraint> aConstraint =
206 std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
208 // Process the groups list
209 if (aGroups.size() == 0) {
210 // There are no groups applicable for this constraint => create new one
211 // The group will be created only for constraints, not for features
212 if (!aConstraint) return false;
213 std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(aConstraint);
216 SketchSolver_ConstraintGroup* aGroup = new SketchSolver_ConstraintGroup(aWP);
217 if (!aGroup->changeConstraint(aConstraint)) {
221 myGroups.push_back(aGroup);
223 } else if (aGroups.size() == 1) { // Only one group => add feature into it
224 Slvs_hGroup aGroupId = *(aGroups.begin());
225 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
226 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
227 if ((*aGroupIter)->getId() == aGroupId) {
228 // If the group is empty, the feature is not added (the constraint only)
229 if (!aConstraint && !(*aGroupIter)->isEmpty())
230 return (*aGroupIter)->changeEntityFeature(theFeature) != SLVS_E_UNKNOWN;
231 return (*aGroupIter)->changeConstraint(aConstraint);
233 } else if (aGroups.size() > 1) { // Several groups applicable for this feature => need to merge them
234 std::set<Slvs_hGroup>::const_iterator aGroupsIter = aGroups.begin();
236 // Search first group
237 std::vector<SketchSolver_ConstraintGroup*>::iterator aFirstGroupIter;
238 for (aFirstGroupIter = myGroups.begin(); aFirstGroupIter != myGroups.end(); aFirstGroupIter++)
239 if ((*aFirstGroupIter)->getId() == *aGroupsIter)
241 if (aFirstGroupIter == myGroups.end())
244 // Append other groups to the first one
245 std::vector<SketchSolver_ConstraintGroup*>::iterator anOtherGroupIter = aFirstGroupIter + 1;
246 for (aGroupsIter++; aGroupsIter != aGroups.end(); aGroupsIter++) {
247 for (; anOtherGroupIter != myGroups.end(); anOtherGroupIter++)
248 if ((*anOtherGroupIter)->getId() == *aGroupsIter)
250 if (anOtherGroupIter == myGroups.end()) { // Group disappears
251 anOtherGroupIter = aFirstGroupIter + 1;
255 (*aFirstGroupIter)->mergeGroups(**anOtherGroupIter);
256 int aShiftFirst = aFirstGroupIter - myGroups.begin();
257 int aShiftOther = anOtherGroupIter - myGroups.begin();
258 delete *anOtherGroupIter;
259 myGroups.erase(anOtherGroupIter);
260 aFirstGroupIter = myGroups.begin() + aShiftFirst;
261 anOtherGroupIter = myGroups.begin() + aShiftOther;
265 return (*aFirstGroupIter)->changeConstraint(aConstraint);
266 return (*aFirstGroupIter)->changeEntityFeature(theFeature) != SLVS_E_UNKNOWN;
269 // Something goes wrong
273 // ============================================================================
274 // Function: updateEntity
275 // Class: SketchSolver_Session
276 // Purpose: update any element on the sketch, which is used by constraints
277 // ============================================================================
278 void SketchSolver_ConstraintManager::updateEntity(
279 std::shared_ptr<SketchPlugin_Feature> theFeature)
281 // Create list of attributes depending on type of the feature
282 std::vector<std::string> anAttrList;
283 const std::string& aFeatureKind = theFeature->getKind();
285 if (aFeatureKind.compare(SketchPlugin_Point::ID()) == 0)
286 anAttrList.push_back(SketchPlugin_Point::COORD_ID());
288 else if (aFeatureKind.compare(SketchPlugin_Line::ID()) == 0) {
289 anAttrList.push_back(SketchPlugin_Line::START_ID());
290 anAttrList.push_back(SketchPlugin_Line::END_ID());
293 else if (aFeatureKind.compare(SketchPlugin_Circle::ID()) == 0) {
294 anAttrList.push_back(SketchPlugin_Circle::CENTER_ID());
295 anAttrList.push_back(SketchPlugin_Circle::RADIUS_ID());
298 else if (aFeatureKind.compare(SketchPlugin_Arc::ID()) == 0) {
299 anAttrList.push_back(SketchPlugin_Arc::CENTER_ID());
300 anAttrList.push_back(SketchPlugin_Arc::START_ID());
301 anAttrList.push_back(SketchPlugin_Arc::END_ID());
303 /// \todo Other types of features should be implemented
305 // Check changing of feature's attributes (go through the groups and search usage of the attributes)
306 std::vector<std::string>::const_iterator anAttrIter;
307 for (anAttrIter = anAttrList.begin(); anAttrIter != anAttrList.end(); anAttrIter++) {
308 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
309 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
310 if ((*aGroupIter)->isEmpty())
312 std::shared_ptr<ModelAPI_Attribute> anAttribute = std::dynamic_pointer_cast<
313 ModelAPI_Attribute>(theFeature->data()->attribute(*anAttrIter));
314 (*aGroupIter)->updateEntityIfPossible(anAttribute);
318 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
319 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
320 if (!(*aGroupIter)->isEmpty())
321 (*aGroupIter)->updateRelatedConstraintsFeature(theFeature);
324 // ============================================================================
325 // Function: findGroups
326 // Class: SketchSolver_Session
327 // Purpose: search groups of entities interacting with given feature
328 // ============================================================================
329 void SketchSolver_ConstraintManager::findGroups(
330 std::shared_ptr<SketchPlugin_Feature> theFeature,
331 std::set<Slvs_hGroup>& theGroupIDs) const
333 std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(theFeature);
335 SketchSolver_ConstraintGroup* anEmptyGroup = 0; // appropriate empty group for specified constraint
336 std::vector<SketchSolver_ConstraintGroup*>::const_iterator aGroupIter;
337 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
338 if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theFeature)) {
339 if (!(*aGroupIter)->isEmpty())
340 theGroupIDs.insert((*aGroupIter)->getId());
341 else if (!anEmptyGroup)
342 anEmptyGroup = *aGroupIter;
345 // When only empty group is found, use it
346 if (anEmptyGroup && theGroupIDs.empty())
347 theGroupIDs.insert(anEmptyGroup->getId());
350 // ============================================================================
351 // Function: findWorkplane
352 // Class: SketchSolver_Session
353 // Purpose: search workplane containing given feature
354 // ============================================================================
355 std::shared_ptr<ModelAPI_CompositeFeature> SketchSolver_ConstraintManager
356 ::findWorkplane(std::shared_ptr<SketchPlugin_Feature> theFeature) const
358 // Already verified workplanes
359 std::set<std::shared_ptr<ModelAPI_CompositeFeature> > aVerified;
361 std::vector<SketchSolver_ConstraintGroup*>::const_iterator aGroupIter;
362 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
363 std::shared_ptr<ModelAPI_CompositeFeature> aWP = (*aGroupIter)->getWorkplane();
364 if (aVerified.find(aWP) != aVerified.end())
367 DataPtr aData = aWP->data();
369 std::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures = std::dynamic_pointer_cast<
370 ModelAPI_AttributeRefList>(aData->attribute(SketchPlugin_Sketch::FEATURES_ID()));
371 std::list<ObjectPtr> aFeaturesList = aWPFeatures->list();
372 std::list<ObjectPtr>::const_iterator anIter;
373 for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
374 if (*anIter == theFeature)
375 return aWP; // workplane is found
377 aVerified.insert(aWP);
380 return std::shared_ptr<ModelAPI_CompositeFeature>();
383 // ============================================================================
384 // Function: resolveConstraints
385 // Class: SketchSolver_Session
386 // Purpose: change entities according to available constraints
387 // ============================================================================
388 void SketchSolver_ConstraintManager::resolveConstraints(const bool theForceUpdate)
391 bool needToUpdate = false;
392 static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
393 // to avoid redisplay of each segment on update by solver one by one in the viewer
394 bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
395 if (isUpdateFlushed) {
396 Events_Loop::loop()->setFlushed(anUpdateEvent, false);
399 std::vector<SketchSolver_ConstraintGroup*>::iterator aGroupIter;
400 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
401 if ((*aGroupIter)->resolveConstraints())
404 // Features may be updated => now send events, but for all changed at once
405 if (isUpdateFlushed) {
406 Events_Loop::loop()->setFlushed(anUpdateEvent, true);
408 // Must be before flush because on "Updated" flush the results may be produced
409 // and the creation event is appeared with many new objects. If myIsComputed these
410 // events are missed in processEvents and some elements are not added.
411 myIsComputed = false;
412 if (needToUpdate || theForceUpdate)
413 Events_Loop::loop()->flush(anUpdateEvent);