1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
3 // File: SketchSolver_Manager.cpp
4 // Created: 08 May 2014
5 // Author: Artem ZHIDKOV
7 #include "SketchSolver_Manager.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>
20 #include <SketchPlugin_Arc.h>
21 #include <SketchPlugin_Circle.h>
22 #include <SketchPlugin_Line.h>
23 #include <SketchPlugin_Point.h>
24 #include <SketchPlugin_Sketch.h>
25 #include <SketchPlugin_Feature.h>
31 // Initialization of constraint manager self pointer
32 SketchSolver_Manager* SketchSolver_Manager::mySelf = 0;
34 /// Global constraint manager object
35 SketchSolver_Manager* myManager = SketchSolver_Manager::instance();
38 // ========================================================
39 // ========= SketchSolver_Manager ===============
40 // ========================================================
41 SketchSolver_Manager* SketchSolver_Manager::instance()
44 mySelf = new SketchSolver_Manager();
48 SketchSolver_Manager::SketchSolver_Manager()
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_Manager::~SketchSolver_Manager()
65 void SketchSolver_Manager::setBuilder(BuilderPtr theBuilder)
67 myBuilder = theBuilder;
70 BuilderPtr SketchSolver_Manager::builder()
75 // ============================================================================
76 // Function: processEvent
77 // Purpose: listen the event loop and process the message
78 // ============================================================================
79 void SketchSolver_Manager::processEvent(
80 const std::shared_ptr<Events_Message>& theMessage)
85 if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)
86 || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED)
87 || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED)) {
88 std::shared_ptr<ModelAPI_ObjectUpdatedMessage> anUpdateMsg =
89 std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
90 std::set<ObjectPtr> aFeatures = anUpdateMsg->objects();
92 // Shows the message has at least one feature applicable for solver
93 bool hasProperFeature = false;
95 bool isMovedEvt = theMessage->eventID()
96 == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED);
98 std::set<ObjectPtr>::iterator aFeatIter;
99 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
100 std::shared_ptr<SketchPlugin_Feature> aSFeature =
101 std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
103 moveEntity(aSFeature);
104 hasProperFeature = true;
108 std::list<FeaturePtr> aSketchFeatures = SketchSolver_Group::selectApplicableFeatures(aFeatures);
109 std::list<FeaturePtr>::iterator aFeatIter = aSketchFeatures.begin();
110 for (; aFeatIter != aSketchFeatures.end(); ++aFeatIter) {
111 if ((*aFeatIter)->getKind() == SketchPlugin_Sketch::ID()) {
112 std::shared_ptr<ModelAPI_CompositeFeature> aSketch =
113 std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aFeatIter);
114 hasProperFeature = changeWorkplane(aSketch) || hasProperFeature;
117 std::shared_ptr<SketchPlugin_Feature> aFeature =
118 std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
121 hasProperFeature = changeFeature(aFeature) || hasProperFeature;
125 // Solve the set of constraints
126 if (hasProperFeature)
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_Group*>::iterator aGroupIter = myGroups.begin();
142 std::list<SketchSolver_Group*> 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)->isConsistent()) { // 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());
160 myIsComputed = false;
163 // ============================================================================
164 // Function: changeWorkplane
165 // Purpose: update workplane by given parameters of the sketch
166 // ============================================================================
167 bool SketchSolver_Manager::changeWorkplane(CompositeFeaturePtr theSketch)
169 bool aResult = true; // changed when a workplane wrongly updated
170 bool isUpdated = false;
171 // Try to update specified workplane in all groups
172 std::vector<SketchSolver_Group*>::iterator aGroupIter;
173 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
174 if ((*aGroupIter)->isBaseWorkplane(theSketch)) {
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 // Purpose: create/update the constraint or the feature and place it into appropriate group
194 // ============================================================================
195 bool SketchSolver_Manager::changeFeature(std::shared_ptr<SketchPlugin_Feature> theFeature)
197 // Search the groups which this feature touches
198 std::set<GroupID> aGroups;
199 findGroups(theFeature, aGroups);
201 std::shared_ptr<SketchPlugin_Constraint> aConstraint =
202 std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
204 // Process the groups list
205 if (aGroups.size() == 0) {
206 // There are no groups applicable for this constraint => create new one
207 // The group will be created only for constraints, not for features
208 if (!aConstraint) return false;
209 std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(aConstraint);
212 SketchSolver_Group* aGroup = new SketchSolver_Group(aWP);
213 if (!aGroup->changeConstraint(aConstraint)) {
217 myGroups.push_back(aGroup);
219 } else if (aGroups.size() == 1) { // Only one group => add feature into it
220 GroupID aGroupId = *(aGroups.begin());
221 std::vector<SketchSolver_Group*>::iterator aGroupIter;
222 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
223 if ((*aGroupIter)->getId() == aGroupId) {
224 // If the group is empty, the feature is not added (the constraint only)
225 if (!aConstraint && !(*aGroupIter)->isEmpty())
226 return (*aGroupIter)->updateFeature(theFeature);
227 return (*aGroupIter)->changeConstraint(aConstraint);
229 } else if (aGroups.size() > 1) { // Several groups applicable for this feature => need to merge them
230 std::set<GroupID>::const_iterator aGroupsIter = aGroups.begin();
232 // Search first group
233 std::vector<SketchSolver_Group*>::iterator aFirstGroupIter;
234 for (aFirstGroupIter = myGroups.begin(); aFirstGroupIter != myGroups.end(); aFirstGroupIter++)
235 if ((*aFirstGroupIter)->getId() == *aGroupsIter)
237 if (aFirstGroupIter == myGroups.end())
240 // Append other groups to the first one
241 std::vector<SketchSolver_Group*>::iterator anOtherGroupIter = aFirstGroupIter + 1;
242 for (aGroupsIter++; aGroupsIter != aGroups.end(); aGroupsIter++) {
243 for (; anOtherGroupIter != myGroups.end(); anOtherGroupIter++)
244 if ((*anOtherGroupIter)->getId() == *aGroupsIter)
246 if (anOtherGroupIter == myGroups.end()) { // Group disappears
247 anOtherGroupIter = aFirstGroupIter + 1;
251 (*aFirstGroupIter)->mergeGroups(**anOtherGroupIter);
252 int aShiftFirst = aFirstGroupIter - myGroups.begin();
253 int aShiftOther = anOtherGroupIter - myGroups.begin();
254 delete *anOtherGroupIter;
255 myGroups.erase(anOtherGroupIter);
256 aFirstGroupIter = myGroups.begin() + aShiftFirst;
257 anOtherGroupIter = myGroups.begin() + aShiftOther;
261 return (*aFirstGroupIter)->changeConstraint(aConstraint);
262 return (*aFirstGroupIter)->updateFeature(theFeature);
265 // Something goes wrong
269 // ============================================================================
270 // Function: moveEntity
271 // Purpose: update element moved on the sketch, which is used by constraints
272 // ============================================================================
273 void SketchSolver_Manager::moveEntity(std::shared_ptr<SketchPlugin_Feature> theFeature)
275 std::vector<SketchSolver_Group*>::iterator aGroupIt = myGroups.begin();
276 for (; aGroupIt != myGroups.end(); aGroupIt++)
277 if (!(*aGroupIt)->isEmpty() && (*aGroupIt)->isInteract(theFeature))
278 (*aGroupIt)->moveFeature(theFeature);
281 // ============================================================================
282 // Function: findGroups
283 // Purpose: search groups of entities interacting with given feature
284 // ============================================================================
285 void SketchSolver_Manager::findGroups(
286 std::shared_ptr<SketchPlugin_Feature> theFeature,
287 std::set<GroupID>& theGroupIDs) const
289 std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(theFeature);
291 SketchSolver_Group* anEmptyGroup = 0; // appropriate empty group for specified constraint
292 std::vector<SketchSolver_Group*>::const_iterator aGroupIter;
293 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
294 if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theFeature)) {
295 if (!(*aGroupIter)->isEmpty())
296 theGroupIDs.insert((*aGroupIter)->getId());
297 else if (!anEmptyGroup)
298 anEmptyGroup = *aGroupIter;
301 // When only empty group is found, use it
302 if (anEmptyGroup && theGroupIDs.empty())
303 theGroupIDs.insert(anEmptyGroup->getId());
306 // ============================================================================
307 // Function: findWorkplane
308 // Purpose: search workplane containing given feature
309 // ============================================================================
310 std::shared_ptr<ModelAPI_CompositeFeature> SketchSolver_Manager
311 ::findWorkplane(std::shared_ptr<SketchPlugin_Feature> theFeature) const
313 // Already verified workplanes
314 std::set<std::shared_ptr<ModelAPI_CompositeFeature> > aVerified;
316 std::vector<SketchSolver_Group*>::const_iterator aGroupIter;
317 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
318 std::shared_ptr<ModelAPI_CompositeFeature> aWP = (*aGroupIter)->getWorkplane();
319 if (aVerified.find(aWP) != aVerified.end())
322 DataPtr aData = aWP->data();
323 if (aData->isValid()) {
324 std::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures = std::dynamic_pointer_cast<
325 ModelAPI_AttributeRefList>(aData->attribute(SketchPlugin_Sketch::FEATURES_ID()));
326 std::list<ObjectPtr> aFeaturesList = aWPFeatures->list();
327 std::list<ObjectPtr>::const_iterator anIter;
328 for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
329 if (*anIter == theFeature)
330 return aWP; // workplane is found
332 aVerified.insert(aWP);
335 return std::shared_ptr<ModelAPI_CompositeFeature>();
338 // ============================================================================
339 // Function: resolveConstraints
340 // Purpose: change entities according to available constraints
341 // ============================================================================
342 void SketchSolver_Manager::resolveConstraints(const bool theForceUpdate)
344 //myIsComputed = true;
345 bool needToUpdate = false;
346 static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
347 // to avoid redisplay of each segment on update by solver one by one in the viewer
348 bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
349 if (isUpdateFlushed) {
350 Events_Loop::loop()->setFlushed(anUpdateEvent, false);
353 std::vector<SketchSolver_Group*>::iterator aGroupIter;
354 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
355 if ((*aGroupIter)->resolveConstraints())
358 // Features may be updated => now send events, but for all changed at once
359 if (isUpdateFlushed) {
360 Events_Loop::loop()->setFlushed(anUpdateEvent, true);
362 // Must be before flush because on "Updated" flush the results may be produced
363 // and the creation event is appeared with many new objects. If myIsComputed these
364 // events are missed in processEvents and some elements are not added.
365 //myIsComputed = false;
366 if (needToUpdate || theForceUpdate)
367 Events_Loop::loop()->flush(anUpdateEvent);