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 (aFeature->getKind() == SketchPlugin_ConstraintFillet::ID())
123 continue; // skip Fillet features
124 if (SketchSolver_Group::isComplexConstraint(aFeature)) {
125 aComplexConstraints.insert(aFeature);
128 hasProperFeature = changeConstraintOrEntity(aFeature) || hasProperFeature;
130 // processing remain constraints
131 aFeatIter = aComplexConstraints.begin();
132 for (; aFeatIter != aComplexConstraints.end(); aFeatIter++) {
133 std::shared_ptr<SketchPlugin_Feature> aFeature =
134 std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
137 hasProperFeature = changeConstraintOrEntity(aFeature) || hasProperFeature;
141 // Solve the set of constraints
142 if (hasProperFeature)
143 resolveConstraints(isMovedEvt); // send update for movement in any case
144 } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
145 std::shared_ptr<ModelAPI_ObjectDeletedMessage> aDeleteMsg =
146 std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
147 const std::set<std::string>& aFeatureGroups = aDeleteMsg->groups();
149 // Find SketchPlugin_Sketch::ID() in groups. The constraint groups should be updated when an object removed from Sketch
150 std::set<std::string>::const_iterator aFGrIter;
151 for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
152 if (aFGrIter->compare(ModelAPI_ResultConstruction::group()) == 0 ||
153 aFGrIter->compare(ModelAPI_Feature::group()) == 0)
156 if (aFGrIter != aFeatureGroups.end()) {
157 std::vector<SketchSolver_Group*>::iterator aGroupIter = myGroups.begin();
158 std::vector<SketchSolver_Group*> aSeparatedGroups;
159 while (aGroupIter != myGroups.end()) {
160 if (!(*aGroupIter)->isWorkplaneValid()) { // the group should be removed
162 int aShift = aGroupIter - myGroups.begin();
163 myGroups.erase(aGroupIter);
164 aGroupIter = myGroups.begin() + aShift;
167 if (!(*aGroupIter)->isConsistent()) { // some constraints were removed, try to split the group
168 (*aGroupIter)->splitGroup(aSeparatedGroups);
172 if (aSeparatedGroups.size() > 0)
173 myGroups.insert(myGroups.end(), aSeparatedGroups.begin(), aSeparatedGroups.end());
178 // ============================================================================
179 // Function: changeWorkplane
180 // Class: SketchSolver_Session
181 // Purpose: update workplane by given parameters of the sketch
182 // ============================================================================
183 bool SketchSolver_ConstraintManager::changeWorkplane(CompositeFeaturePtr theSketch)
185 bool aResult = true; // changed when a workplane wrongly updated
186 bool isUpdated = false;
187 // Try to update specified workplane in all groups
188 std::vector<SketchSolver_Group*>::iterator aGroupIter;
189 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
190 if ((*aGroupIter)->isBaseWorkplane(theSketch)) {
192 if (!(*aGroupIter)->updateWorkplane())
195 // If the workplane is not updated, so this is a new workplane
197 SketchSolver_Group* aNewGroup = new SketchSolver_Group(theSketch);
198 // Verify that the group is created successfully
199 if (!aNewGroup->isBaseWorkplane(theSketch) || !aNewGroup->isWorkplaneValid()) {
203 myGroups.push_back(aNewGroup);
208 // ============================================================================
209 // Function: changeConstraintOrEntity
210 // Class: SketchSolver_Session
211 // Purpose: create/update the constraint or the feature and place it into appropriate group
212 // ============================================================================
213 bool SketchSolver_ConstraintManager::changeConstraintOrEntity(
214 std::shared_ptr<SketchPlugin_Feature> theFeature)
216 // Search the groups which this feature touches
217 std::set<Slvs_hGroup> aGroups;
218 findGroups(theFeature, aGroups);
220 std::shared_ptr<SketchPlugin_Constraint> aConstraint =
221 std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
223 // Process the groups list
224 if (aGroups.size() == 0) {
225 // There are no groups applicable for this constraint => create new one
226 // The group will be created only for constraints, not for features
227 if (!aConstraint) return false;
228 std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(aConstraint);
231 SketchSolver_Group* aGroup = new SketchSolver_Group(aWP);
232 if (!aGroup->changeConstraint(aConstraint)) {
236 myGroups.push_back(aGroup);
238 } else if (aGroups.size() == 1) { // Only one group => add feature into it
239 Slvs_hGroup aGroupId = *(aGroups.begin());
240 std::vector<SketchSolver_Group*>::iterator aGroupIter;
241 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
242 if ((*aGroupIter)->getId() == aGroupId) {
243 // If the group is empty, the feature is not added (the constraint only)
244 if (!aConstraint && !(*aGroupIter)->isEmpty())
245 return (*aGroupIter)->updateFeature(theFeature);
246 return (*aGroupIter)->changeConstraint(aConstraint);
248 } else if (aGroups.size() > 1) { // Several groups applicable for this feature => need to merge them
249 std::set<Slvs_hGroup>::const_iterator aGroupsIter = aGroups.begin();
251 // Search first group
252 std::vector<SketchSolver_Group*>::iterator aFirstGroupIter;
253 for (aFirstGroupIter = myGroups.begin(); aFirstGroupIter != myGroups.end(); aFirstGroupIter++)
254 if ((*aFirstGroupIter)->getId() == *aGroupsIter)
256 if (aFirstGroupIter == myGroups.end())
259 // Append other groups to the first one
260 std::vector<SketchSolver_Group*>::iterator anOtherGroupIter = aFirstGroupIter + 1;
261 for (aGroupsIter++; aGroupsIter != aGroups.end(); aGroupsIter++) {
262 for (; anOtherGroupIter != myGroups.end(); anOtherGroupIter++)
263 if ((*anOtherGroupIter)->getId() == *aGroupsIter)
265 if (anOtherGroupIter == myGroups.end()) { // Group disappears
266 anOtherGroupIter = aFirstGroupIter + 1;
270 (*aFirstGroupIter)->mergeGroups(**anOtherGroupIter);
271 int aShiftFirst = aFirstGroupIter - myGroups.begin();
272 int aShiftOther = anOtherGroupIter - myGroups.begin();
273 delete *anOtherGroupIter;
274 myGroups.erase(anOtherGroupIter);
275 aFirstGroupIter = myGroups.begin() + aShiftFirst;
276 anOtherGroupIter = myGroups.begin() + aShiftOther;
280 return (*aFirstGroupIter)->changeConstraint(aConstraint);
281 return (*aFirstGroupIter)->updateFeature(theFeature);
284 // Something goes wrong
288 // ============================================================================
289 // Function: moveEntity
290 // Class: SketchSolver_Session
291 // Purpose: update element moved on the sketch, which is used by constraints
292 // ============================================================================
293 void SketchSolver_ConstraintManager::moveEntity(
294 std::shared_ptr<SketchPlugin_Feature> theFeature)
296 std::vector<SketchSolver_Group*>::iterator aGroupIt = myGroups.begin();
297 for (; aGroupIt != myGroups.end(); aGroupIt++)
298 if (!(*aGroupIt)->isEmpty() && (*aGroupIt)->isInteract(theFeature))
299 (*aGroupIt)->moveFeature(theFeature);
302 // ============================================================================
303 // Function: findGroups
304 // Class: SketchSolver_Session
305 // Purpose: search groups of entities interacting with given feature
306 // ============================================================================
307 void SketchSolver_ConstraintManager::findGroups(
308 std::shared_ptr<SketchPlugin_Feature> theFeature,
309 std::set<Slvs_hGroup>& theGroupIDs) const
311 std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(theFeature);
313 SketchSolver_Group* anEmptyGroup = 0; // appropriate empty group for specified constraint
314 std::vector<SketchSolver_Group*>::const_iterator aGroupIter;
315 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
316 if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theFeature)) {
317 if (!(*aGroupIter)->isEmpty())
318 theGroupIDs.insert((*aGroupIter)->getId());
319 else if (!anEmptyGroup)
320 anEmptyGroup = *aGroupIter;
323 // When only empty group is found, use it
324 if (anEmptyGroup && theGroupIDs.empty())
325 theGroupIDs.insert(anEmptyGroup->getId());
328 // ============================================================================
329 // Function: findWorkplane
330 // Class: SketchSolver_Session
331 // Purpose: search workplane containing given feature
332 // ============================================================================
333 std::shared_ptr<ModelAPI_CompositeFeature> SketchSolver_ConstraintManager
334 ::findWorkplane(std::shared_ptr<SketchPlugin_Feature> theFeature) const
336 // Already verified workplanes
337 std::set<std::shared_ptr<ModelAPI_CompositeFeature> > aVerified;
339 std::vector<SketchSolver_Group*>::const_iterator aGroupIter;
340 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
341 std::shared_ptr<ModelAPI_CompositeFeature> aWP = (*aGroupIter)->getWorkplane();
342 if (aVerified.find(aWP) != aVerified.end())
345 DataPtr aData = aWP->data();
346 if (aData->isValid()) {
347 std::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures = std::dynamic_pointer_cast<
348 ModelAPI_AttributeRefList>(aData->attribute(SketchPlugin_Sketch::FEATURES_ID()));
349 std::list<ObjectPtr> aFeaturesList = aWPFeatures->list();
350 std::list<ObjectPtr>::const_iterator anIter;
351 for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
352 if (*anIter == theFeature)
353 return aWP; // workplane is found
355 aVerified.insert(aWP);
358 return std::shared_ptr<ModelAPI_CompositeFeature>();
361 // ============================================================================
362 // Function: resolveConstraints
363 // Class: SketchSolver_Session
364 // Purpose: change entities according to available constraints
365 // ============================================================================
366 void SketchSolver_ConstraintManager::resolveConstraints(const bool theForceUpdate)
369 bool needToUpdate = false;
370 static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
371 // to avoid redisplay of each segment on update by solver one by one in the viewer
372 bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
373 if (isUpdateFlushed) {
374 Events_Loop::loop()->setFlushed(anUpdateEvent, false);
377 std::vector<SketchSolver_Group*>::iterator aGroupIter;
378 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
379 if ((*aGroupIter)->resolveConstraints())
382 // Features may be updated => now send events, but for all changed at once
383 if (isUpdateFlushed) {
384 Events_Loop::loop()->setFlushed(anUpdateEvent, true);
386 // Must be before flush because on "Updated" flush the results may be produced
387 // and the creation event is appeared with many new objects. If myIsComputed these
388 // events are missed in processEvents and some elements are not added.
389 myIsComputed = false;
390 if (needToUpdate || theForceUpdate)
391 Events_Loop::loop()->flush(anUpdateEvent);