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"
8 #include "SketchSolver_Error.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>
18 #include <ModelAPI_AttributeInteger.h>
19 #include <ModelAPI_AttributeString.h>
21 #include <SketchPlugin_Constraint.h>
22 #include <SketchPlugin_ConstraintAngle.h>
23 #include <SketchPlugin_ConstraintCoincidence.h>
24 #include <SketchPlugin_ConstraintCollinear.h>
25 #include <SketchPlugin_ConstraintDistance.h>
26 #include <SketchPlugin_ConstraintEqual.h>
27 #include <SketchPlugin_ConstraintHorizontal.h>
28 #include <SketchPlugin_ConstraintLength.h>
29 #include <SketchPlugin_ConstraintMiddle.h>
30 #include <SketchPlugin_ConstraintMirror.h>
31 #include <SketchPlugin_ConstraintParallel.h>
32 #include <SketchPlugin_ConstraintPerpendicular.h>
33 #include <SketchPlugin_ConstraintRadius.h>
34 #include <SketchPlugin_ConstraintRigid.h>
35 #include <SketchPlugin_ConstraintTangent.h>
36 #include <SketchPlugin_ConstraintVertical.h>
37 #include <SketchPlugin_MultiRotation.h>
38 #include <SketchPlugin_MultiTranslation.h>
40 #include <SketchPlugin_Arc.h>
41 #include <SketchPlugin_Circle.h>
42 #include <SketchPlugin_Line.h>
43 #include <SketchPlugin_Point.h>
44 #include <SketchPlugin_Sketch.h>
45 #include <SketchPlugin_Feature.h>
53 static const Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
55 // Initialization of constraint manager self pointer
56 SketchSolver_Manager* SketchSolver_Manager::mySelf = 0;
58 /// Global constraint manager object
59 SketchSolver_Manager* myManager = SketchSolver_Manager::instance();
62 // ========================================================
63 // ========= SketchSolver_Manager ===============
64 // ========================================================
65 SketchSolver_Manager* SketchSolver_Manager::instance()
68 mySelf = new SketchSolver_Manager();
72 SketchSolver_Manager::SketchSolver_Manager()
77 // Register in event loop
78 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
79 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
80 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
81 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_MOVED));
83 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_SOLVER_FAILED));
84 Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_SOLVER_REPAIRED));
87 SketchSolver_Manager::~SketchSolver_Manager()
92 void SketchSolver_Manager::setBuilder(BuilderPtr theBuilder)
94 myBuilder = theBuilder;
97 BuilderPtr SketchSolver_Manager::builder()
102 // ============================================================================
103 // Function: processEvent
104 // Purpose: listen the event loop and process the message
105 // ============================================================================
106 void SketchSolver_Manager::processEvent(
107 const std::shared_ptr<Events_Message>& theMessage)
109 checkConflictingConstraints(theMessage);
114 // Shows that the message has at least one feature applicable for solver
115 bool hasProperFeature = false;
117 if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)
118 || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED)
119 || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED)) {
120 std::shared_ptr<ModelAPI_ObjectUpdatedMessage> anUpdateMsg =
121 std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
122 std::set<ObjectPtr> aFeatures = anUpdateMsg->objects();
124 bool isUpdateFlushed = stopSendUpdate();
126 bool isMovedEvt = theMessage->eventID()
127 == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED);
129 std::set<ObjectPtr>::iterator aFeatIter;
130 for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) {
131 std::shared_ptr<SketchPlugin_Feature> aSFeature =
132 std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
133 if (aSFeature && moveEntity(aSFeature)) {
134 // Want to avoid recalculation of DoF too frequently.
135 // So, set the flag when the feature is really moved.
136 hasProperFeature = true;
139 if (!hasProperFeature) // in this iteration it will compute nothing, so, no problem with recursion
140 // it is important that solver flushes signal updated after processing move signal as there is
141 // optimization that relies on this update, might be found by key "optimization"
142 myIsComputed = false;
144 std::list<FeaturePtr> aSketchFeatures = SketchSolver_Group::selectApplicableFeatures(aFeatures);
145 std::list<FeaturePtr>::iterator aFeatIter = aSketchFeatures.begin();
146 for (; aFeatIter != aSketchFeatures.end(); ++aFeatIter) {
147 if ((*aFeatIter)->getKind() == SketchPlugin_Sketch::ID()) {
148 std::shared_ptr<ModelAPI_CompositeFeature> aSketch =
149 std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aFeatIter);
150 hasProperFeature = changeWorkplane(aSketch) || hasProperFeature;
153 std::shared_ptr<SketchPlugin_Feature> aFeature =
154 std::dynamic_pointer_cast<SketchPlugin_Feature>(*aFeatIter);
157 hasProperFeature = changeFeature(aFeature) || hasProperFeature;
161 bool needToUpdate = false;
162 // Solve the set of constraints
163 if (hasProperFeature)
164 needToUpdate = resolveConstraints();
166 // Features may be updated => now send events, but for all changed at once
169 // send update for movement in any case
170 if (needToUpdate || isMovedEvt)
171 Events_Loop::loop()->flush(anUpdateEvent);
173 } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
174 std::shared_ptr<ModelAPI_ObjectDeletedMessage> aDeleteMsg =
175 std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
176 const std::set<std::string>& aFeatureGroups = aDeleteMsg->groups();
178 // Find SketchPlugin_Sketch::ID() in groups. The constraint groups should be updated when an object removed from Sketch
179 std::set<std::string>::const_iterator aFGrIter;
180 for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++)
181 if (aFGrIter->compare(ModelAPI_ResultConstruction::group()) == 0 ||
182 aFGrIter->compare(ModelAPI_Feature::group()) == 0)
185 if (aFGrIter != aFeatureGroups.end()) {
186 hasProperFeature = true;
187 std::list<SketchSolver_Group*> aGroupsToResolve;
188 std::list<SketchSolver_Group*>::iterator aGroupIter = myGroups.begin();
189 std::list<SketchSolver_Group*> aSeparatedGroups;
190 while (aGroupIter != myGroups.end()) {
191 if (!(*aGroupIter)->isWorkplaneValid()) { // the group should be removed
193 std::list<SketchSolver_Group*>::iterator aRemoveIt = aGroupIter++;
194 myGroups.erase(aRemoveIt);
197 if (!(*aGroupIter)->isConsistent()) { // some constraints were removed, try to split the group
198 (*aGroupIter)->splitGroup(aSeparatedGroups);
199 if (!(*aGroupIter)->getWorkplane()->string(
200 SketchPlugin_Sketch::SOLVER_ERROR())->value().empty() ||
201 (*aGroupIter)->isFailed())
202 aGroupsToResolve.push_back(*aGroupIter);
206 if (aSeparatedGroups.size() > 0) {
207 myGroups.insert(myGroups.end(), aSeparatedGroups.begin(), aSeparatedGroups.end());
208 aGroupsToResolve.insert(aGroupsToResolve.end(),
209 aSeparatedGroups.begin(), aSeparatedGroups.end());
212 if (!aGroupsToResolve.empty())
213 resolveConstraints(aGroupsToResolve);
217 if (hasProperFeature)
219 myIsComputed = false;
222 void SketchSolver_Manager::checkConflictingConstraints(const std::shared_ptr<Events_Message>& theMessage)
224 if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_SOLVER_REPAIRED)) {
225 std::shared_ptr<ModelAPI_SolverFailedMessage> aMessage =
226 std::dynamic_pointer_cast<ModelAPI_SolverFailedMessage>(theMessage);
227 std::set<ObjectPtr> aSentObjs = aMessage->objects();
228 if (!aSentObjs.empty()) {
229 // Obtain sketch where the constraints are placed.
230 // It is enough to check only one constraint.
231 CompositeFeaturePtr aSketch;
232 FeaturePtr aConstraint = ModelAPI_Feature::feature(*aSentObjs.begin());
233 std::list<SketchSolver_Group*>::const_iterator aGrIt = myGroups.begin();
234 for (; aGrIt != myGroups.end(); ++aGrIt)
235 if ((*aGrIt)->isInteract(aConstraint)) {
236 aSketch = (*aGrIt)->getWorkplane();
240 // Search failed groups built on the same sketch
242 for (aGrIt = myGroups.begin(); aGrIt != myGroups.end(); ++aGrIt) {
243 SketchSolver_Group* aGroup = *aGrIt;
244 if (aGroup->isBaseWorkplane(aSketch) && aGroup->isFailed() &&
245 !aGroup->isInteract(aConstraint)) {
246 // reset error message on the sketch
247 aGroup->getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue(
248 SketchSolver_Error::CONSTRAINTS());
257 // ============================================================================
258 // Function: changeWorkplane
259 // Purpose: update workplane by given parameters of the sketch
260 // ============================================================================
261 bool SketchSolver_Manager::changeWorkplane(CompositeFeaturePtr theSketch)
263 bool aResult = true; // changed when a workplane wrongly updated
264 bool isUpdated = false;
265 // Try to update specified workplane in all groups
266 std::list<SketchSolver_Group*>::iterator aGroupIter;
267 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
268 if ((*aGroupIter)->isBaseWorkplane(theSketch)) {
272 // If the workplane is not updated, so this is a new workplane
274 SketchSolver_Group* aNewGroup = new SketchSolver_Group(theSketch);
275 // Verify that the group is created successfully
276 if (!aNewGroup->isBaseWorkplane(theSketch) || !aNewGroup->isWorkplaneValid()) {
280 myGroups.push_back(aNewGroup);
282 return aResult || isUpdated;
285 // ============================================================================
286 // Function: changeConstraintOrEntity
287 // Purpose: create/update the constraint or the feature and place it into appropriate group
288 // ============================================================================
289 bool SketchSolver_Manager::changeFeature(std::shared_ptr<SketchPlugin_Feature> theFeature)
291 // Search the groups which this feature touches
292 std::set<GroupID> aGroups;
293 findGroups(theFeature, aGroups);
295 std::shared_ptr<SketchPlugin_Constraint> aConstraint =
296 std::dynamic_pointer_cast<SketchPlugin_Constraint>(theFeature);
298 // Process the groups list
299 if (aGroups.size() == 0) {
300 // There are no groups applicable for this constraint => create new one
301 // The group will be created only for constraints, not for features
302 if (!aConstraint) return false;
303 std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(aConstraint);
306 SketchSolver_Group* aGroup = new SketchSolver_Group(aWP);
307 if (!aGroup->changeConstraint(aConstraint)) {
311 myGroups.push_back(aGroup);
313 } else if (aGroups.size() == 1) { // Only one group => add feature into it
314 GroupID aGroupId = *(aGroups.begin());
315 std::list<SketchSolver_Group*>::iterator aGroupIter;
316 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
317 if ((*aGroupIter)->getId() == aGroupId) {
318 // If the group is empty, the feature is not added (the constraint only)
319 if (!aConstraint && !(*aGroupIter)->isEmpty())
320 return (*aGroupIter)->updateFeature(theFeature);
321 return (*aGroupIter)->changeConstraint(aConstraint);
323 } else if (aGroups.size() > 1) { // Several groups applicable for this feature => need to merge them
324 std::set<GroupID>::const_iterator aGroupsIter = aGroups.begin();
326 // Search first group
327 std::list<SketchSolver_Group*>::iterator aFirstGroupIter;
328 for (aFirstGroupIter = myGroups.begin(); aFirstGroupIter != myGroups.end(); aFirstGroupIter++)
329 if ((*aFirstGroupIter)->getId() == *aGroupsIter)
331 if (aFirstGroupIter == myGroups.end())
334 // Append other groups to the first one
335 std::list<SketchSolver_Group*>::iterator anOtherGroupIter = aFirstGroupIter;
337 for (aGroupsIter++; aGroupsIter != aGroups.end(); aGroupsIter++) {
338 for (; anOtherGroupIter != myGroups.end(); anOtherGroupIter++)
339 if ((*anOtherGroupIter)->getId() == *aGroupsIter)
341 if (anOtherGroupIter == myGroups.end()) { // Group disappears
342 anOtherGroupIter = aFirstGroupIter;
347 (*aFirstGroupIter)->mergeGroups(**anOtherGroupIter);
348 std::list<SketchSolver_Group*>::iterator aRemoveIt = anOtherGroupIter++;
350 myGroups.erase(aRemoveIt);
354 (*aFirstGroupIter)->changeConstraint(aConstraint);
356 (*aFirstGroupIter)->updateFeature(theFeature);
357 // groups are merged => need to resolve them
361 // Something goes wrong
365 // ============================================================================
366 // Function: moveEntity
367 // Purpose: update element moved on the sketch, which is used by constraints
368 // ============================================================================
369 bool SketchSolver_Manager::moveEntity(std::shared_ptr<SketchPlugin_Feature> theFeature)
371 bool isMoved = false;
372 std::list<SketchSolver_Group*>::iterator aGroupIt = myGroups.begin();
373 for (; aGroupIt != myGroups.end(); aGroupIt++)
374 if (!(*aGroupIt)->isEmpty() && (*aGroupIt)->isInteract(theFeature))
375 isMoved = (*aGroupIt)->moveFeature(theFeature) || isMoved;
377 if (!isMoved && theFeature->getKind() == SketchPlugin_Arc::ID()) {
378 // Workaround to move arc.
379 // If the arc has not been constrained, we will push it into empty group and apply movement.
380 bool hasEmptyGroup = false;
381 for (aGroupIt = myGroups.begin(); aGroupIt != myGroups.end(); aGroupIt++)
382 if ((*aGroupIt)->isEmpty()) {
383 isMoved = (*aGroupIt)->moveFeature(theFeature) || isMoved;
384 hasEmptyGroup = true;
386 // There is no empty group, create it explicitly
387 if (!hasEmptyGroup) {
388 // find sketch containing the arc
389 CompositeFeaturePtr aWP;
390 const std::set<AttributePtr>& aRefs = theFeature->data()->refsToMe();
391 std::set<AttributePtr>::const_iterator aRefIt = aRefs.begin();
392 for (; aRefIt != aRefs.end(); ++aRefIt) {
393 FeaturePtr anOwner = ModelAPI_Feature::feature((*aRefIt)->owner());
394 if (anOwner && anOwner->getKind() == SketchPlugin_Sketch::ID()) {
395 aWP = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(anOwner);
400 SketchSolver_Group* aGroup = new SketchSolver_Group(aWP);
401 isMoved = aGroup->moveFeature(theFeature) || isMoved;
402 myGroups.push_back(aGroup);
409 // ============================================================================
410 // Function: findGroups
411 // Purpose: search groups of entities interacting with given feature
412 // ============================================================================
413 void SketchSolver_Manager::findGroups(
414 std::shared_ptr<SketchPlugin_Feature> theFeature,
415 std::set<GroupID>& theGroupIDs) const
417 std::shared_ptr<ModelAPI_CompositeFeature> aWP = findWorkplane(theFeature);
419 SketchSolver_Group* anEmptyGroup = 0; // appropriate empty group for specified constraint
420 std::list<SketchSolver_Group*>::const_iterator aGroupIter;
421 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++)
422 if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theFeature)) {
423 if (!(*aGroupIter)->isEmpty())
424 theGroupIDs.insert((*aGroupIter)->getId());
425 else if (!anEmptyGroup)
426 anEmptyGroup = *aGroupIter;
429 // When only empty group is found, use it
430 if (anEmptyGroup && theGroupIDs.empty())
431 theGroupIDs.insert(anEmptyGroup->getId());
434 // ============================================================================
435 // Function: findWorkplane
436 // Purpose: search workplane containing given feature
437 // ============================================================================
438 std::shared_ptr<ModelAPI_CompositeFeature> SketchSolver_Manager
439 ::findWorkplane(std::shared_ptr<SketchPlugin_Feature> theFeature) const
441 // Already verified workplanes
442 std::set<std::shared_ptr<ModelAPI_CompositeFeature> > aVerified;
444 std::list<SketchSolver_Group*>::const_iterator aGroupIter;
445 for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) {
446 std::shared_ptr<ModelAPI_CompositeFeature> aWP = (*aGroupIter)->getWorkplane();
447 if (aVerified.find(aWP) != aVerified.end())
450 DataPtr aData = aWP->data();
451 if (aData->isValid()) {
452 std::shared_ptr<ModelAPI_AttributeRefList> aWPFeatures = std::dynamic_pointer_cast<
453 ModelAPI_AttributeRefList>(aData->attribute(SketchPlugin_Sketch::FEATURES_ID()));
454 std::list<ObjectPtr> aFeaturesList = aWPFeatures->list();
455 std::list<ObjectPtr>::const_iterator anIter;
456 for (anIter = aFeaturesList.begin(); anIter != aFeaturesList.end(); anIter++)
457 if (*anIter == theFeature)
458 return aWP; // workplane is found
460 aVerified.insert(aWP);
463 return std::shared_ptr<ModelAPI_CompositeFeature>();
466 // ============================================================================
467 // Function: resolveConstraints
468 // Purpose: change entities according to available constraints
469 // ============================================================================
470 bool SketchSolver_Manager::resolveConstraints(const std::list<SketchSolver_Group*>& theGroups)
472 bool needToUpdate = false;
473 const std::list<SketchSolver_Group*>& aGroupsToResolve = theGroups.empty() ? myGroups : theGroups;
474 std::list<SketchSolver_Group*>::const_iterator aGroupIter = aGroupsToResolve.begin();
475 for (; aGroupIter != aGroupsToResolve.end(); aGroupIter++)
476 if ((*aGroupIter)->resolveConstraints())
482 // Obtain points and their copies for Mirror, Multi-Rotation and Multi-Translation constraints
483 static void collectPointsAndCopies(FeaturePtr theConstraint, std::list<std::set<AttributePtr> >& thePoints)
485 typedef std::list<std::string> strlist;
486 static strlist aPointAttributes(1, SketchPlugin_Point::COORD_ID());
487 static strlist aLineAttributes;
488 if (aLineAttributes.empty()) {
489 aLineAttributes.push_back(SketchPlugin_Line::START_ID());
490 aLineAttributes.push_back(SketchPlugin_Line::END_ID());
492 static strlist aCircleAttributes(1, SketchPlugin_Circle::CENTER_ID());
493 static strlist anArcAttributes;
494 if (anArcAttributes.empty()) {
495 anArcAttributes.push_back(SketchPlugin_Arc::CENTER_ID());
496 anArcAttributes.push_back(SketchPlugin_Arc::START_ID());
497 anArcAttributes.push_back(SketchPlugin_Arc::END_ID());
500 static std::map<std::string, strlist> aFeatureAttributes;
501 if (aFeatureAttributes.empty()) {
502 aFeatureAttributes[SketchPlugin_Point::ID()] = aPointAttributes;
503 aFeatureAttributes[SketchPlugin_Line::ID()] = aLineAttributes;
504 aFeatureAttributes[SketchPlugin_Circle::ID()] = aCircleAttributes;
505 aFeatureAttributes[SketchPlugin_Arc::ID()] = anArcAttributes;
509 std::set<AttributePtr> aPoints;
510 if (theConstraint->getKind() == SketchPlugin_ConstraintMirror::ID()) {
511 AttributeRefListPtr aBaseRefList = theConstraint->reflist(SketchPlugin_Constraint::ENTITY_B());
512 AttributeRefListPtr aMirrRefList = theConstraint->reflist(SketchPlugin_Constraint::ENTITY_C());
514 std::list<ObjectPtr> aBaseList = aBaseRefList->list();
515 std::list<ObjectPtr> aMirrList = aMirrRefList->list();
516 std::list<ObjectPtr>::const_iterator aBIt, aMIt;
517 for (aBIt = aBaseList.begin(), aMIt = aMirrList.begin();
518 aBIt != aBaseList.end() && aMIt != aMirrList.end(); ++aBIt, ++aMIt) {
519 FeaturePtr aBaseFeature = ModelAPI_Feature::feature(*aBIt);
520 FeaturePtr aMirrFeature = ModelAPI_Feature::feature(*aMIt);
522 strlist anAttrList = aFeatureAttributes[aBaseFeature->getKind()];
523 strlist::iterator anIt = anAttrList.begin();
524 for (; anIt != anAttrList.end(); ++anIt) {
526 aPoints.insert(aBaseFeature->attribute(*anIt));
527 aPoints.insert(aMirrFeature->attribute(*anIt));
528 thePoints.push_back(aPoints);
532 else { // the "Multi" constraints
533 std::string aNbObjName;
534 if (theConstraint->getKind() == SketchPlugin_MultiRotation::ID())
535 aNbObjName = SketchPlugin_MultiRotation::NUMBER_OF_OBJECTS_ID();
537 aNbObjName = SketchPlugin_MultiTranslation::NUMBER_OF_OBJECTS_ID();
538 int aNbCopies = theConstraint->integer(aNbObjName)->value();
540 AttributeRefListPtr aRefList = theConstraint->reflist(SketchPlugin_Constraint::ENTITY_B());
541 std::list<ObjectPtr> aFullList = aRefList->list();
542 std::list<ObjectPtr>::const_iterator anObjIt = aFullList.begin();
543 std::list<ObjectPtr>::const_iterator aCopyIt;
544 while (anObjIt != aFullList.end()) {
545 FeaturePtr aBaseFeature = ModelAPI_Feature::feature(*anObjIt);
546 strlist anAttrList = aFeatureAttributes[aBaseFeature->getKind()];
547 strlist::iterator anIt = anAttrList.begin();
548 for (; anIt != anAttrList.end(); ++anIt) {
551 for (int i = 0; i < aNbCopies && aCopyIt != aFullList.end(); ++i, ++aCopyIt) {
552 FeaturePtr aFeature = ModelAPI_Feature::feature(*aCopyIt);
553 aPoints.insert(aFeature->attribute(*anIt));
555 thePoints.push_back(aPoints);
562 // ============================================================================
563 // Function: degreesOfFreedom
564 // Purpose: calculate DoFs for each sketch
565 // ============================================================================
566 void SketchSolver_Manager::degreesOfFreedom()
568 static std::map<std::string, int> aDoFDelta; // indicates how many DoF adds or decreases a feature
569 static bool isNeedInit = true;
571 aDoFDelta[SketchPlugin_Point::ID()] = 2;
572 aDoFDelta[SketchPlugin_Line::ID()] = 4;
573 aDoFDelta[SketchPlugin_Circle::ID()] = 3;
574 aDoFDelta[SketchPlugin_Arc::ID()] = 5;
576 aDoFDelta[SketchPlugin_ConstraintAngle::ID()] = -1;
577 aDoFDelta[SketchPlugin_ConstraintCollinear::ID()] = -1;
578 aDoFDelta[SketchPlugin_ConstraintDistance::ID()] = -1;
579 aDoFDelta[SketchPlugin_ConstraintEqual::ID()] = -1;
580 aDoFDelta[SketchPlugin_ConstraintHorizontal::ID()] = -1;
581 aDoFDelta[SketchPlugin_ConstraintLength::ID()] = -1;
582 aDoFDelta[SketchPlugin_ConstraintParallel::ID()] = -1;
583 aDoFDelta[SketchPlugin_ConstraintPerpendicular::ID()] = -1;
584 aDoFDelta[SketchPlugin_ConstraintRadius::ID()] = -1;
585 aDoFDelta[SketchPlugin_ConstraintTangent::ID()] = -1;
586 aDoFDelta[SketchPlugin_ConstraintVertical::ID()] = -1;
591 std::map<CompositeFeaturePtr, int> aSketchDoF;
593 std::list<SketchSolver_Group*>::const_iterator aGroupIt = myGroups.begin();
594 for (; aGroupIt != myGroups.end(); ++aGroupIt) {
595 CompositeFeaturePtr aSketch = (*aGroupIt)->getWorkplane();
596 bool isSketchValid = aSketch->data() && aSketch->data()->isValid();
599 std::shared_ptr<GeomDataAPI_Dir> aNormal =
600 std::dynamic_pointer_cast<GeomDataAPI_Dir>(aSketch->data()->attribute(SketchPlugin_Sketch::NORM_ID()));
601 isSketchValid = aNormal && aNormal->isInitialized();
604 if (!isSketchValid) {
605 myDoF.erase(aSketch);
609 // check conflicting constraints in the group
610 if ((*aGroupIt)->isFailed())
611 aSketchDoF[aSketch] = -1;
612 // check the sketch is already processed
613 if (aSketchDoF.find(aSketch) != aSketchDoF.end() || aSketchDoF[aSketch] < 0)
616 std::set<AttributePtr> aCoincidentPoints;
617 std::map<AttributePtr, std::set<FeaturePtr> > aPointOnLine;
618 std::list<std::set<AttributePtr> > aPointsInMultiConstraints;
620 int aNbSubs = aSketch->numberOfSubs();
621 for (int i = 0; i < aNbSubs; ++i) {
622 FeaturePtr aFeature = aSketch->subFeature(i);
623 // check DoF delta for invariant types
624 std::map<std::string, int>::const_iterator aFound = aDoFDelta.find(aFeature->getKind());
625 if (aFound != aDoFDelta.end()) {
626 aDoF += aFound->second;
630 // DoF delta in specific cases
631 if (aFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
632 AttributePtr aCoincPoint[2] = {AttributePtr(), AttributePtr()};
633 FeaturePtr aCoincLine;
634 for (int j = 0; j < 2; ++j) {
635 AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
636 aFeature->attribute(SketchPlugin_Constraint::ATTRIBUTE(j)));
639 if (!aRefAttr->isObject())
640 aCoincPoint[j] = aRefAttr->attr();
642 FeaturePtr anAttr = ModelAPI_Feature::feature(aRefAttr->object());
645 if (anAttr->getKind() == SketchPlugin_Point::ID())
646 aCoincPoint[j] = anAttr->attribute(SketchPlugin_Point::COORD_ID());
647 else if (anAttr->getKind() == SketchPlugin_Line::ID())
651 if (aCoincPoint[0] && aCoincPoint[1]) {
652 // point-point coincidence
653 if (aCoincidentPoints.find(aCoincPoint[0]) == aCoincidentPoints.end() ||
654 aCoincidentPoints.find(aCoincPoint[1]) == aCoincidentPoints.end())
656 // check the coincident point is used in "multi" constraints
657 std::list<std::set<AttributePtr> >::const_iterator
658 aPtIt = aPointsInMultiConstraints.begin();
659 bool isFound[2] = {false, false};
660 for (; aPtIt != aPointsInMultiConstraints.end(); ++aPtIt) {
661 if ((!isFound[0] && (isFound[0] = (aPtIt->find(aCoincPoint[0]) != aPtIt->end())))
662 || (!isFound[1] && (isFound[1] = (aPtIt->find(aCoincPoint[1]) != aPtIt->end()))))
663 aCoincidentPoints.insert(aPtIt->begin(), aPtIt->end());
664 if (isFound[0] && isFound[1])
669 if (aCoincPoint[0] && aCoincLine) {
670 // if the point is already coincident to a line (by middle point constraint), do not decrease DoF
671 std::map<AttributePtr, std::set<FeaturePtr> >::iterator
672 aPtFound = aPointOnLine.find(aCoincPoint[0]);
673 if (aPtFound != aPointOnLine.end() &&
674 aPtFound->second.find(aCoincLine) != aPtFound->second.end())
675 aDoF += 1; // restore value decreased above
677 aPointOnLine[aCoincPoint[0]].insert(aCoincLine);
680 for (int j = 0; j < 2; ++j)
682 aCoincidentPoints.insert(aCoincPoint[j]);
684 else if (aFeature->getKind() == SketchPlugin_ConstraintMiddle::ID()) {
687 for (int j = 0; j < 2; ++j) {
688 AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
689 aFeature->attribute(SketchPlugin_Constraint::ATTRIBUTE(j)));
692 if (aRefAttr->isObject())
693 aLine = ModelAPI_Feature::feature(aRefAttr->object());
695 aPoint = aRefAttr->attr();
697 if (aPoint && aLine) {
698 // if the point is already on the line, decrease 1 DoF, instead decrease 2 DoF
699 std::map<AttributePtr, std::set<FeaturePtr> >::iterator
700 aPtFound = aPointOnLine.find(aPoint);
701 if (aPtFound != aPointOnLine.end() &&
702 aPtFound->second.find(aLine) != aPtFound->second.end())
706 aPointOnLine[aPoint].insert(aLine);
710 else if (aFeature->getKind() == SketchPlugin_ConstraintRigid::ID()) {
711 AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
712 aFeature->attribute(SketchPlugin_Constraint::ENTITY_A()));
714 if (!aRefAttr->isObject())
715 aDoF -= 2; // attribute is a point
717 FeaturePtr anAttr = ModelAPI_Feature::feature(aRefAttr->object());
719 aDoF -= aDoFDelta[anAttr->getKind()];
722 else if (aFeature->getKind() == SketchPlugin_ConstraintMirror::ID() ||
723 aFeature->getKind() == SketchPlugin_MultiRotation::ID() ||
724 aFeature->getKind() == SketchPlugin_MultiTranslation::ID()) {
726 std::string anAttrName;
727 if (aFeature->getKind() == SketchPlugin_ConstraintMirror::ID())
728 anAttrName = SketchPlugin_Constraint::ENTITY_B();
730 if (aFeature->getKind() == SketchPlugin_MultiRotation::ID())
731 aNbCopies = aFeature->integer(SketchPlugin_MultiRotation::NUMBER_OF_OBJECTS_ID())->value() - 1;
732 else if (aFeature->getKind() == SketchPlugin_MultiTranslation::ID())
733 aNbCopies = aFeature->integer(SketchPlugin_MultiTranslation::NUMBER_OF_OBJECTS_ID())->value() - 1;
734 anAttrName = SketchPlugin_Constraint::ENTITY_A();
737 AttributeRefListPtr aRefListOfShapes = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
738 aFeature->attribute(anAttrName));
739 std::list<ObjectPtr> anObjList = aRefListOfShapes->list();
740 std::list<ObjectPtr>::const_iterator anObjIt = anObjList.begin();
741 for (; anObjIt != anObjList.end(); ++anObjIt) {
742 FeaturePtr aSub = ModelAPI_Feature::feature(*anObjIt);
743 aDoF -= aDoFDelta[aSub->getKind()] * aNbCopies;
745 // collect points and their copies for correct calculation of DoF for coincident points
746 collectPointsAndCopies(aFeature, aPointsInMultiConstraints);
750 aSketchDoF[aSketch] = aDoF;
753 // Check the degrees of freedom are changed
754 std::map<CompositeFeaturePtr, int>::const_iterator aDoFIt = aSketchDoF.begin();
755 std::map<CompositeFeaturePtr, int>::iterator aFound;
756 for (; aDoFIt != aSketchDoF.end(); ++aDoFIt) {
757 if (aDoFIt->second < 0)
758 continue; // conflicting constraints on the current sketch
759 aFound = myDoF.find(aDoFIt->first);
760 if (aFound != myDoF.end() && aFound->second == aDoFIt->second)
761 continue; // nothing is changed
762 myDoF[aDoFIt->first] = aDoFIt->second;
763 // change attribute value
764 std::ostringstream aStream;
765 if (aDoFIt->second == 0)
766 aStream << "Sketch fully fixed (DOF = " << aDoFIt->second << ")";
768 aStream << "DOF (degree of freedom) = " << aDoFIt->second;
769 aDoFIt->first->data()->string(SketchPlugin_Sketch::SOLVER_DOF())->setValue(aStream.str());
773 bool SketchSolver_Manager::stopSendUpdate() const
775 // to avoid redisplay of each segment on update by solver one by one in the viewer
776 bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
777 if (isUpdateFlushed) {
778 Events_Loop::loop()->setFlushed(anUpdateEvent, false);
780 return isUpdateFlushed;
783 void SketchSolver_Manager::allowSendUpdate() const
785 Events_Loop::loop()->setFlushed(anUpdateEvent, true);