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>
20 #include <SketchPlugin_ConstraintFillet.h>
21 #include <SketchPlugin_ConstraintMirror.h>
22 #include <SketchPlugin_ConstraintTangent.h>
24 #include <SketchPlugin_Arc.h>
25 #include <SketchPlugin_Circle.h>
26 #include <SketchPlugin_Line.h>
27 #include <SketchPlugin_Point.h>
28 #include <SketchPlugin_Sketch.h>
29 #include <SketchPlugin_Feature.h>
35 // Initialization of constraint manager self pointer
36 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::_self = 0;
38 /// Global constraint manager object
39 SketchSolver_ConstraintManager* myManager = SketchSolver_ConstraintManager::Instance();
41 // ========================================================
42 // ========= SketchSolver_ConstraintManager ===============
43 // ========================================================
44 SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::Instance()
47 _self = new SketchSolver_ConstraintManager();
51 SketchSolver_ConstraintManager::SketchSolver_ConstraintManager()
56 // Register in event loop
57 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
58 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
59 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
60 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_MOVED));
63 SketchSolver_ConstraintManager::~SketchSolver_ConstraintManager()
68 // ============================================================================
69 // Function: processEvent
70 // Class: SketchSolver_Session
71 // Purpose: listen the event loop and process the message
72 // ============================================================================
73 void SketchSolver_ConstraintManager::processEvent(
74 const std::shared_ptr<Events_Message>& theMessage)
78 if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)
79 || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED)
80 || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED)) {
81 std::shared_ptr<ModelAPI_ObjectUpdatedMessage> anUpdateMsg =
82 std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
83 std::set<ObjectPtr> aFeatures = anUpdateMsg->objects();
85 // Shows the message has at least one feature applicable for solver
86 bool hasProperFeature = false;
88 bool isMovedEvt = theMessage->eventID()
89 == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED);
91 std::set<ObjectPtr>::iterator aFeatIter;
92 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
93 std::shared_ptr<SketchPlugin_Feature> aSFeature =
94 std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
96 moveEntity(aSFeature);
97 hasProperFeature = true;
101 std::set<ObjectPtr>::iterator aFeatIter;
102 // iterate sketchers fisrt to create all sketches before (on load may exist several sketches)
103 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
104 FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*aFeatIter);
107 const std::string& aFeatureKind = aFeature->getKind();
108 if (aFeatureKind.compare(SketchPlugin_Sketch::ID()) == 0) {
109 std::shared_ptr<ModelAPI_CompositeFeature> aSketch = std::dynamic_pointer_cast<
110 ModelAPI_CompositeFeature>(aFeature);
111 hasProperFeature = changeWorkplane(aSketch) || hasProperFeature;
114 // then get anything but not the sketch
115 std::set<ObjectPtr> aComplexConstraints;
116 // fillet and mirror an tangency constraints will be processed later
117 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
118 std::shared_ptr<SketchPlugin_Feature> aFeature =
119 std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
122 if (SketchSolver_Group::isComplexConstraint(aFeature)) {
123 aComplexConstraints.insert(aFeature);
126 hasProperFeature = changeConstraintOrEntity(aFeature) || hasProperFeature;
128 // processing remain constraints
129 aFeatIter = aComplexConstraints.begin();
130 for (; aFeatIter != aComplexConstraints.end(); aFeatIter++) {
131 std::shared_ptr<SketchPlugin_Feature> aFeature =
132 std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
135 hasProperFeature = changeConstraintOrEntity(aFeature) || hasProperFeature;
139 // Solve the set of constraints
140 if (hasProperFeature)
141 resolveConstraints(isMovedEvt); // send update for movement in any case
142 } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
143 std::shared_ptr<ModelAPI_ObjectDeletedMessage> aDeleteMsg =
144 std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
145 const std::set<std::string>& aFeatureGroups = aDeleteMsg->groups();
147 // Find SketchPlugin_Sketch::ID() in groups. The constraint groups should be updated when an object removed from Sketch
148 std::set<std::string>::const_iterator aFGrIter;
149 for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
150 if (aFGrIter->compare(ModelAPI_ResultConstruction::group()) == 0 ||
151 aFGrIter->compare(ModelAPI_Feature::group()) == 0)
154 if (aFGrIter != aFeatureGroups.end()) {
155 std::vector<SketchSolver_Group*>::iterator aGroupIter = myGroups.begin();
156 std::vector<SketchSolver_Group*> aSeparatedGroups;
157 while (aGroupIter != myGroups.end()) {
158 if (!(*aGroupIter)->isWorkplaneValid()) { // the group should be removed
160 int aShift = aGroupIter - myGroups.begin();
161 myGroups.erase(aGroupIter);
162 aGroupIter = myGroups.begin() + aShift;
165 if (!(*aGroupIter)->isConsistent()) { // some constraints were removed, try to split the group
166 (*aGroupIter)->splitGroup(aSeparatedGroups);
170 if (aSeparatedGroups.size() > 0)
171 myGroups.insert(myGroups.end(), aSeparatedGroups.begin(), aSeparatedGroups.end());
176 // ============================================================================
177 // Function: changeWorkplane
178 // Class: SketchSolver_Session
179 // Purpose: update workplane by given parameters of the sketch
180 // ============================================================================
181 bool SketchSolver_ConstraintManager::changeWorkplane(CompositeFeaturePtr theSketch)
183 bool aResult = true; // changed when a workplane wrongly updated
184 bool isUpdated = false;
185 // Try to update specified workplane in all groups
186 std::vector<SketchSolver_Group*>::iterator aGroupIter;
187 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
188 if ((*aGroupIter)->isBaseWorkplane(theSketch)) {
190 if (!(*aGroupIter)->updateWorkplane())
193 // If the workplane is not updated, so this is a new workplane
195 SketchSolver_Group* aNewGroup = new SketchSolver_Group(theSketch);
196 // Verify that the group is created successfully
197 if (!aNewGroup->isBaseWorkplane(theSketch) || !aNewGroup->isWorkplaneValid()) {
201 myGroups.push_back(aNewGroup);
206 // ============================================================================
207 // Function: changeConstraintOrEntity
208 // Class: SketchSolver_Session
209 // Purpose: create/update the constraint or the feature and place it into appropriate group
210 // ============================================================================
211 bool SketchSolver_ConstraintManager::changeConstraintOrEntity(
212 std::shared_ptr<SketchPlugin_Feature> theFeature)
214 // Search the groups which this feature touches
215 std::set<Slvs_hGroup> aGroups;
216 findGroups(theFeature, aGroups);
218 std::shared_ptr<SketchPlugin_Constraint> aConstraint =
219 std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
221 // Process the groups list
222 if (aGroups.size() == 0) {
223 // There are no groups applicable for this constraint => create new one
224 // The group will be created only for constraints, not for features
225 if (!aConstraint) return false;
226 std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(aConstraint);
229 SketchSolver_Group* aGroup = new SketchSolver_Group(aWP);
230 if (!aGroup->changeConstraint(aConstraint)) {
234 myGroups.push_back(aGroup);
236 } else if (aGroups.size() == 1) { // Only one group => add feature into it
237 Slvs_hGroup aGroupId = *(aGroups.begin());
238 std::vector<SketchSolver_Group*>::iterator aGroupIter;
239 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
240 if ((*aGroupIter)->getId() == aGroupId) {
241 // If the group is empty, the feature is not added (the constraint only)
242 if (!aConstraint && !(*aGroupIter)->isEmpty())
243 return (*aGroupIter)->updateFeature(theFeature);
244 return (*aGroupIter)->changeConstraint(aConstraint);
246 } else if (aGroups.size() > 1) { // Several groups applicable for this feature => need to merge them
247 std::set<Slvs_hGroup>::const_iterator aGroupsIter = aGroups.begin();
249 // Search first group
250 std::vector<SketchSolver_Group*>::iterator aFirstGroupIter;
251 for (aFirstGroupIter = myGroups.begin(); aFirstGroupIter != myGroups.end(); aFirstGroupIter++)
252 if ((*aFirstGroupIter)->getId() == *aGroupsIter)
254 if (aFirstGroupIter == myGroups.end())
257 // Append other groups to the first one
258 std::vector<SketchSolver_Group*>::iterator anOtherGroupIter = aFirstGroupIter + 1;
259 for (aGroupsIter++; aGroupsIter != aGroups.end(); aGroupsIter++) {
260 for (; anOtherGroupIter != myGroups.end(); anOtherGroupIter++)
261 if ((*anOtherGroupIter)->getId() == *aGroupsIter)
263 if (anOtherGroupIter == myGroups.end()) { // Group disappears
264 anOtherGroupIter = aFirstGroupIter + 1;
268 (*aFirstGroupIter)->mergeGroups(**anOtherGroupIter);
269 int aShiftFirst = aFirstGroupIter - myGroups.begin();
270 int aShiftOther = anOtherGroupIter - myGroups.begin();
271 delete *anOtherGroupIter;
272 myGroups.erase(anOtherGroupIter);
273 aFirstGroupIter = myGroups.begin() + aShiftFirst;
274 anOtherGroupIter = myGroups.begin() + aShiftOther;
278 return (*aFirstGroupIter)->changeConstraint(aConstraint);
279 return (*aFirstGroupIter)->updateFeature(theFeature);
282 // Something goes wrong
286 // ============================================================================
287 // Function: moveEntity
288 // Class: SketchSolver_Session
289 // Purpose: update element moved on the sketch, which is used by constraints
290 // ============================================================================
291 void SketchSolver_ConstraintManager::moveEntity(
292 std::shared_ptr<SketchPlugin_Feature> theFeature)
294 std::vector<SketchSolver_Group*>::iterator aGroupIt = myGroups.begin();
295 for (; aGroupIt != myGroups.end(); aGroupIt++)
296 if (!(*aGroupIt)->isEmpty() && (*aGroupIt)->isInteract(theFeature))
297 (*aGroupIt)->moveFeature(theFeature);
300 // ============================================================================
301 // Function: findGroups
302 // Class: SketchSolver_Session
303 // Purpose: search groups of entities interacting with given feature
304 // ============================================================================
305 void SketchSolver_ConstraintManager::findGroups(
306 std::shared_ptr<SketchPlugin_Feature> theFeature,
307 std::set<Slvs_hGroup>& theGroupIDs) const
309 std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(theFeature);
311 SketchSolver_Group* anEmptyGroup = 0; // appropriate empty group for specified constraint
312 std::vector<SketchSolver_Group*>::const_iterator aGroupIter;
313 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
314 if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theFeature)) {
315 if (!(*aGroupIter)->isEmpty())
316 theGroupIDs.insert((*aGroupIter)->getId());
317 else if (!anEmptyGroup)
318 anEmptyGroup = *aGroupIter;
321 // When only empty group is found, use it
322 if (anEmptyGroup && theGroupIDs.empty())
323 theGroupIDs.insert(anEmptyGroup->getId());
326 // ============================================================================
327 // Function: findWorkplane
328 // Class: SketchSolver_Session
329 // Purpose: search workplane containing given feature
330 // ============================================================================
331 std::shared_ptr<ModelAPI_CompositeFeature> SketchSolver_ConstraintManager
332 ::findWorkplane(std::shared_ptr<SketchPlugin_Feature> theFeature) const
334 // Already verified workplanes
335 std::set<std::shared_ptr<ModelAPI_CompositeFeature> > aVerified;
337 std::vector<SketchSolver_Group*>::const_iterator aGroupIter;
338 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
339 std::shared_ptr<ModelAPI_CompositeFeature> aWP = (*aGroupIter)->getWorkplane();
340 if (aVerified.find(aWP) != aVerified.end())
343 DataPtr aData = aWP->data();
345 std::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures = std::dynamic_pointer_cast<
346 ModelAPI_AttributeRefList>(aData->attribute(SketchPlugin_Sketch::FEATURES_ID()));
347 std::list<ObjectPtr> aFeaturesList = aWPFeatures->list();
348 std::list<ObjectPtr>::const_iterator anIter;
349 for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
350 if (*anIter == theFeature)
351 return aWP; // workplane is found
353 aVerified.insert(aWP);
356 return std::shared_ptr<ModelAPI_CompositeFeature>();
359 // ============================================================================
360 // Function: resolveConstraints
361 // Class: SketchSolver_Session
362 // Purpose: change entities according to available constraints
363 // ============================================================================
364 void SketchSolver_ConstraintManager::resolveConstraints(const bool theForceUpdate)
367 bool needToUpdate = false;
368 static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
369 // to avoid redisplay of each segment on update by solver one by one in the viewer
370 bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
371 if (isUpdateFlushed) {
372 Events_Loop::loop()->setFlushed(anUpdateEvent, false);
375 std::vector<SketchSolver_Group*>::iterator aGroupIter;
376 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
377 if ((*aGroupIter)->resolveConstraints())
380 // Features may be updated => now send events, but for all changed at once
381 if (isUpdateFlushed) {
382 Events_Loop::loop()->setFlushed(anUpdateEvent, true);
384 // Must be before flush because on "Updated" flush the results may be produced
385 // and the creation event is appeared with many new objects. If myIsComputed these
386 // events are missed in processEvents and some elements are not added.
387 myIsComputed = false;
388 if (needToUpdate || theForceUpdate)
389 Events_Loop::loop()->flush(anUpdateEvent);