X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FSketchSolver%2FSketchSolver_Manager.cpp;h=80f1f98ef7349e26fcc46d12c48a5fec78a01b5e;hb=9dc1fc2ff6f4143369cdb2042a5e01e8e737c635;hp=453fbf5fc15b4eb6e5d29bed13a888fcf3666a35;hpb=37e97ce820e95060b6a448b1fcc29df3ebccea87;p=modules%2Fshaper.git diff --git a/src/SketchSolver/SketchSolver_Manager.cpp b/src/SketchSolver/SketchSolver_Manager.cpp index 453fbf5fc..80f1f98ef 100644 --- a/src/SketchSolver/SketchSolver_Manager.cpp +++ b/src/SketchSolver/SketchSolver_Manager.cpp @@ -8,6 +8,9 @@ #include "SketchSolver_Error.h" #include + +#include + #include #include #include @@ -15,9 +18,27 @@ #include #include #include +#include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -26,9 +47,11 @@ #include #include +#include #include #include #include +#include static const Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED); @@ -56,12 +79,13 @@ SketchSolver_Manager::SketchSolver_Manager() // Register in event loop Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED)); - Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED)); + Events_Loop::loop()->registerListener(this, anUpdateEvent); Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED)); Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_MOVED)); Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_SOLVER_FAILED)); Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_SOLVER_REPAIRED)); + Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_SKETCH_PREPARED)); } SketchSolver_Manager::~SketchSolver_Manager() @@ -79,6 +103,11 @@ BuilderPtr SketchSolver_Manager::builder() return myBuilder; } +bool SketchSolver_Manager::groupMessages() +{ + return true; +} + // ============================================================================ // Function: processEvent // Purpose: listen the event loop and process the message @@ -86,44 +115,62 @@ BuilderPtr SketchSolver_Manager::builder() void SketchSolver_Manager::processEvent( const std::shared_ptr& theMessage) { + static const Events_ID aSketchPreparedEvent = Events_Loop::eventByName(EVENT_SKETCH_PREPARED); + // sketch is prepared for resolve: all the needed events + // are collected and must be processed by the solver + if (theMessage->eventID() == aSketchPreparedEvent) { + flushGrouped(anUpdateEvent); + return; + } + checkConflictingConstraints(theMessage); + if (myIsComputed) return; myIsComputed = true; + + // Shows that the message has at least one feature applicable for solver + bool hasProperFeature = false; + if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED) - || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED) + || theMessage->eventID() == anUpdateEvent || theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED)) { std::shared_ptr anUpdateMsg = std::dynamic_pointer_cast(theMessage); std::set aFeatures = anUpdateMsg->objects(); bool isUpdateFlushed = stopSendUpdate(); - // Shows the message has at least one feature applicable for solver - bool hasProperFeature = false; bool isMovedEvt = theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED); if (isMovedEvt) { std::set::iterator aFeatIter; for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) { - std::shared_ptr aSFeature = + std::shared_ptr aSFeature = std::dynamic_pointer_cast(*aFeatIter); - if (aSFeature) { - moveEntity(aSFeature); + if (aSFeature && moveEntity(aSFeature)) { + // Want to avoid recalculation of DoF too frequently. + // So, set the flag when the feature is really moved. hasProperFeature = true; } } + if (!hasProperFeature) + // in this iteration it will compute nothing, so, no problem with recursion + // it is important that solver flushes signal updated after processing move signal as there + // is optimization that relies on this update, might be found by key "optimization" + myIsComputed = false; } else { - std::list aSketchFeatures = SketchSolver_Group::selectApplicableFeatures(aFeatures); + std::list aSketchFeatures = + SketchSolver_Group::selectApplicableFeatures(aFeatures); std::list::iterator aFeatIter = aSketchFeatures.begin(); for (; aFeatIter != aSketchFeatures.end(); ++aFeatIter) { if ((*aFeatIter)->getKind() == SketchPlugin_Sketch::ID()) { - std::shared_ptr aSketch = + std::shared_ptr aSketch = std::dynamic_pointer_cast(*aFeatIter); hasProperFeature = changeWorkplane(aSketch) || hasProperFeature; continue; } - std::shared_ptr aFeature = + std::shared_ptr aFeature = std::dynamic_pointer_cast(*aFeatIter); if (!aFeature) continue; @@ -139,6 +186,9 @@ void SketchSolver_Manager::processEvent( // Features may be updated => now send events, but for all changed at once if (isUpdateFlushed) allowSendUpdate(); + + myIsComputed = false; + // send update for movement in any case if (needToUpdate || isMovedEvt) Events_Loop::loop()->flush(anUpdateEvent); @@ -148,7 +198,8 @@ void SketchSolver_Manager::processEvent( std::dynamic_pointer_cast(theMessage); const std::set& aFeatureGroups = aDeleteMsg->groups(); - // Find SketchPlugin_Sketch::ID() in groups. The constraint groups should be updated when an object removed from Sketch + // Find SketchPlugin_Sketch::ID() in groups. + // The constraint groups should be updated when an object removed from Sketch std::set::const_iterator aFGrIter; for (aFGrIter = aFeatureGroups.begin(); aFGrIter != aFeatureGroups.end(); aFGrIter++) if (aFGrIter->compare(ModelAPI_ResultConstruction::group()) == 0 || @@ -156,6 +207,7 @@ void SketchSolver_Manager::processEvent( break; if (aFGrIter != aFeatureGroups.end()) { + hasProperFeature = true; std::list aGroupsToResolve; std::list::iterator aGroupIter = myGroups.begin(); std::list aSeparatedGroups; @@ -166,10 +218,12 @@ void SketchSolver_Manager::processEvent( myGroups.erase(aRemoveIt); continue; } - if (!(*aGroupIter)->isConsistent()) { // some constraints were removed, try to split the group + if (!(*aGroupIter)->isConsistent()) { + // some constraints were removed, try to split the group (*aGroupIter)->splitGroup(aSeparatedGroups); - //if (!(*aGroupIter)->getWorkplane()->string( - // SketchPlugin_Sketch::SOLVER_ERROR())->value().empty()) + if (!(*aGroupIter)->getWorkplane()->string( + SketchPlugin_Sketch::SOLVER_ERROR())->value().empty() || + (*aGroupIter)->isFailed()) aGroupsToResolve.push_back(*aGroupIter); } aGroupIter++; @@ -183,11 +237,15 @@ void SketchSolver_Manager::processEvent( if (!aGroupsToResolve.empty()) resolveConstraints(aGroupsToResolve); } + myIsComputed = false; } - myIsComputed = false; + + if (hasProperFeature) + degreesOfFreedom(); } -void SketchSolver_Manager::checkConflictingConstraints(const std::shared_ptr& theMessage) +void SketchSolver_Manager:: + checkConflictingConstraints(const std::shared_ptr& theMessage) { if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_SOLVER_REPAIRED)) { std::shared_ptr aMessage = @@ -247,7 +305,7 @@ bool SketchSolver_Manager::changeWorkplane(CompositeFeaturePtr theSketch) } myGroups.push_back(aNewGroup); } - return aResult; + return aResult || isUpdated; } // ============================================================================ @@ -260,7 +318,7 @@ bool SketchSolver_Manager::changeFeature(std::shared_ptr t std::set aGroups; findGroups(theFeature, aGroups); - std::shared_ptr aConstraint = + std::shared_ptr aConstraint = std::dynamic_pointer_cast(theFeature); // Process the groups list @@ -288,7 +346,8 @@ bool SketchSolver_Manager::changeFeature(std::shared_ptr t return (*aGroupIter)->updateFeature(theFeature); return (*aGroupIter)->changeConstraint(aConstraint); } - } else if (aGroups.size() > 1) { // Several groups applicable for this feature => need to merge them + } else if (aGroups.size() > 1) { + // Several groups applicable for this feature => need to merge them std::set::const_iterator aGroupsIter = aGroups.begin(); // Search first group @@ -334,23 +393,44 @@ bool SketchSolver_Manager::changeFeature(std::shared_ptr t // Function: moveEntity // Purpose: update element moved on the sketch, which is used by constraints // ============================================================================ -void SketchSolver_Manager::moveEntity(std::shared_ptr theFeature) +bool SketchSolver_Manager::moveEntity(std::shared_ptr theFeature) { bool isMoved = false; std::list::iterator aGroupIt = myGroups.begin(); for (; aGroupIt != myGroups.end(); aGroupIt++) - if (!(*aGroupIt)->isEmpty() && (*aGroupIt)->isInteract(theFeature)) { - (*aGroupIt)->moveFeature(theFeature); - isMoved = true; - } + if (!(*aGroupIt)->isEmpty() && (*aGroupIt)->isInteract(theFeature)) + isMoved = (*aGroupIt)->moveFeature(theFeature) || isMoved; if (!isMoved && theFeature->getKind() == SketchPlugin_Arc::ID()) { // Workaround to move arc. // If the arc has not been constrained, we will push it into empty group and apply movement. + bool hasEmptyGroup = false; for (aGroupIt = myGroups.begin(); aGroupIt != myGroups.end(); aGroupIt++) - if ((*aGroupIt)->isEmpty()) - (*aGroupIt)->moveFeature(theFeature); + if ((*aGroupIt)->isEmpty()) { + isMoved = (*aGroupIt)->moveFeature(theFeature) || isMoved; + hasEmptyGroup = true; + } + // There is no empty group, create it explicitly + if (!hasEmptyGroup) { + // find sketch containing the arc + CompositeFeaturePtr aWP; + const std::set& aRefs = theFeature->data()->refsToMe(); + std::set::const_iterator aRefIt = aRefs.begin(); + for (; aRefIt != aRefs.end(); ++aRefIt) { + FeaturePtr anOwner = ModelAPI_Feature::feature((*aRefIt)->owner()); + if (anOwner && anOwner->getKind() == SketchPlugin_Sketch::ID()) { + aWP = std::dynamic_pointer_cast(anOwner); + break; + } + } + if (aWP) { + SketchSolver_Group* aGroup = new SketchSolver_Group(aWP); + isMoved = aGroup->moveFeature(theFeature) || isMoved; + myGroups.push_back(aGroup); + } + } } + return isMoved; } // ============================================================================ @@ -417,7 +497,8 @@ std::shared_ptr SketchSolver_Manager bool SketchSolver_Manager::resolveConstraints(const std::list& theGroups) { bool needToUpdate = false; - const std::list& aGroupsToResolve = theGroups.empty() ? myGroups : theGroups; + const std::list& aGroupsToResolve = theGroups.empty() ? + myGroups : theGroups; std::list::const_iterator aGroupIter = aGroupsToResolve.begin(); for (; aGroupIter != aGroupsToResolve.end(); aGroupIter++) if ((*aGroupIter)->resolveConstraints()) @@ -425,6 +506,367 @@ bool SketchSolver_Manager::resolveConstraints(const std::list >& thePoints) +{ + typedef std::list strlist; + static strlist aPointAttributes(1, SketchPlugin_Point::COORD_ID()); + static strlist aLineAttributes; + if (aLineAttributes.empty()) { + aLineAttributes.push_back(SketchPlugin_Line::START_ID()); + aLineAttributes.push_back(SketchPlugin_Line::END_ID()); + }; + static strlist aCircleAttributes(1, SketchPlugin_Circle::CENTER_ID()); + static strlist anArcAttributes; + if (anArcAttributes.empty()) { + anArcAttributes.push_back(SketchPlugin_Arc::CENTER_ID()); + anArcAttributes.push_back(SketchPlugin_Arc::START_ID()); + anArcAttributes.push_back(SketchPlugin_Arc::END_ID()); + }; + + static std::map aFeatureAttributes; + if (aFeatureAttributes.empty()) { + aFeatureAttributes[SketchPlugin_Point::ID()] = aPointAttributes; + aFeatureAttributes[SketchPlugin_Line::ID()] = aLineAttributes; + aFeatureAttributes[SketchPlugin_Circle::ID()] = aCircleAttributes; + aFeatureAttributes[SketchPlugin_Arc::ID()] = anArcAttributes; + } + + + std::set aPoints; + if (theConstraint->getKind() == SketchPlugin_ConstraintMirror::ID()) { + AttributeRefListPtr aBaseRefList = theConstraint->reflist(SketchPlugin_Constraint::ENTITY_B()); + AttributeRefListPtr aMirrRefList = theConstraint->reflist(SketchPlugin_Constraint::ENTITY_C()); + + std::list aBaseList = aBaseRefList->list(); + std::list aMirrList = aMirrRefList->list(); + std::list::const_iterator aBIt, aMIt; + for (aBIt = aBaseList.begin(), aMIt = aMirrList.begin(); + aBIt != aBaseList.end() && aMIt != aMirrList.end(); ++aBIt, ++aMIt) { + FeaturePtr aBaseFeature = ModelAPI_Feature::feature(*aBIt); + FeaturePtr aMirrFeature = ModelAPI_Feature::feature(*aMIt); + + strlist anAttrList = aFeatureAttributes[aBaseFeature->getKind()]; + strlist::iterator anIt = anAttrList.begin(); + for (; anIt != anAttrList.end(); ++anIt) { + aPoints.clear(); + aPoints.insert(aBaseFeature->attribute(*anIt)); + aPoints.insert(aMirrFeature->attribute(*anIt)); + thePoints.push_back(aPoints); + } + } + } + else { // the "Multi" constraints + std::string aNbObjName; + if (theConstraint->getKind() == SketchPlugin_MultiRotation::ID()) + aNbObjName = SketchPlugin_MultiRotation::NUMBER_OF_OBJECTS_ID(); + else + aNbObjName = SketchPlugin_MultiTranslation::NUMBER_OF_OBJECTS_ID(); + int aNbCopies = theConstraint->integer(aNbObjName)->value(); + + AttributeRefListPtr aRefList = theConstraint->reflist(SketchPlugin_Constraint::ENTITY_B()); + std::list aFullList = aRefList->list(); + std::list::const_iterator anObjIt = aFullList.begin(); + std::list::const_iterator aCopyIt; + while (anObjIt != aFullList.end()) { + FeaturePtr aBaseFeature = ModelAPI_Feature::feature(*anObjIt); + strlist anAttrList = aFeatureAttributes[aBaseFeature->getKind()]; + strlist::iterator anIt = anAttrList.begin(); + for (; anIt != anAttrList.end(); ++anIt) { + aPoints.clear(); + aCopyIt = anObjIt; + for (int i = 0; i < aNbCopies && aCopyIt != aFullList.end(); ++i, ++aCopyIt) { + FeaturePtr aFeature = ModelAPI_Feature::feature(*aCopyIt); + aPoints.insert(aFeature->attribute(*anIt)); + } + thePoints.push_back(aPoints); + } + anObjIt = aCopyIt; + } + } +} + + +// returns true if the feature is external +static bool isExternal(const FeaturePtr& theFeature) +{ + AttributeSelectionPtr anAttr = theFeature->selection(SketchPlugin_SketchEntity::EXTERNAL_ID()); + return anAttr && anAttr->context() && !anAttr->isInvalid(); +} + +// ============================================================================ +// Function: degreesOfFreedom +// Purpose: calculate DoFs for each sketch +// ============================================================================ +void SketchSolver_Manager::degreesOfFreedom() +{ + static std::map aDoFDelta; // indicates how many DoF adds or decreases a feature + static bool isNeedInit = true; + if (isNeedInit) { + aDoFDelta[SketchPlugin_Point::ID()] = 2; + aDoFDelta[SketchPlugin_Line::ID()] = 4; + aDoFDelta[SketchPlugin_Circle::ID()] = 3; + aDoFDelta[SketchPlugin_Arc::ID()] = 5; + + aDoFDelta[SketchPlugin_ConstraintAngle::ID()] = -1; + aDoFDelta[SketchPlugin_ConstraintCollinear::ID()] = -1; + aDoFDelta[SketchPlugin_ConstraintDistance::ID()] = -1; + aDoFDelta[SketchPlugin_ConstraintEqual::ID()] = -1; + aDoFDelta[SketchPlugin_ConstraintHorizontal::ID()] = -1; + aDoFDelta[SketchPlugin_ConstraintLength::ID()] = -1; + aDoFDelta[SketchPlugin_ConstraintParallel::ID()] = -1; + aDoFDelta[SketchPlugin_ConstraintPerpendicular::ID()] = -1; + aDoFDelta[SketchPlugin_ConstraintRadius::ID()] = -1; + aDoFDelta[SketchPlugin_ConstraintTangent::ID()] = -1; + aDoFDelta[SketchPlugin_ConstraintVertical::ID()] = -1; + + isNeedInit = false; + } + + std::map aSketchDoF; + + std::list::const_iterator aGroupIt = myGroups.begin(); + for (; aGroupIt != myGroups.end(); ++aGroupIt) { + CompositeFeaturePtr aSketch = (*aGroupIt)->getWorkplane(); + bool isSketchValid = aSketch->data() && aSketch->data()->isValid(); + + if (isSketchValid) { + std::shared_ptr aNormal = + std::dynamic_pointer_cast( + aSketch->data()->attribute(SketchPlugin_Sketch::NORM_ID())); + isSketchValid = aNormal && aNormal->isInitialized(); + } + + if (!isSketchValid) { + myDoF.erase(aSketch); + continue; + } + + // check conflicting constraints in the group + if ((*aGroupIt)->isFailed()) + aSketchDoF[aSketch] = -1; + // check the sketch is already processed + if (aSketchDoF.find(aSketch) != aSketchDoF.end() || aSketchDoF[aSketch] < 0) + continue; + + std::set aCoincidentPoints; + std::set aFixedPoints; + std::map > aPointOnLine; + std::list > aPointsInMultiConstraints; + int aDoF = 0; + int aNbSubs = aSketch->numberOfSubs(); + for (int i = 0; i < aNbSubs; ++i) { + FeaturePtr aFeature = aSketch->subFeature(i); + // do not change DoF for external feature + if (isExternal(aFeature)) + continue; + // check DoF delta for invariant types + std::map::const_iterator aFound = aDoFDelta.find(aFeature->getKind()); + if (aFound != aDoFDelta.end()) { + aDoF += aFound->second; + continue; + } + + // DoF delta in specific cases + if (aFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) { + AttributePtr aCoincPoint[2] = {AttributePtr(), AttributePtr()}; + FeaturePtr aCoincLine; + for (int j = 0; j < 2; ++j) { + AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast( + aFeature->attribute(SketchPlugin_Constraint::ATTRIBUTE(j))); + if (!aRefAttr) + continue; + if (!aRefAttr->isObject()) + aCoincPoint[j] = aRefAttr->attr(); + else { + FeaturePtr anAttr = ModelAPI_Feature::feature(aRefAttr->object()); + if (!anAttr) + continue; + if (anAttr->getKind() == SketchPlugin_Point::ID()) + aCoincPoint[j] = anAttr->attribute(SketchPlugin_Point::COORD_ID()); + else if (anAttr->getKind() == SketchPlugin_Line::ID()) + aCoincLine = anAttr; + } + } + if (aCoincPoint[0] && aCoincPoint[1]) { + bool isDoFDecreased = false; + // point-point coincidence + if (aCoincidentPoints.find(aCoincPoint[0]) == aCoincidentPoints.end() || + aCoincidentPoints.find(aCoincPoint[1]) == aCoincidentPoints.end()) { + aDoF -= 2; + isDoFDecreased = true; + } + // check the coincident point is used in "multi" constraints + std::list >::const_iterator + aPtIt = aPointsInMultiConstraints.begin(); + bool isFound[2] = {false, false}; + for (; aPtIt != aPointsInMultiConstraints.end(); ++aPtIt) { + if ((!isFound[0] && (isFound[0] = (aPtIt->find(aCoincPoint[0]) != aPtIt->end()))) + || (!isFound[1] && (isFound[1] = (aPtIt->find(aCoincPoint[1]) != aPtIt->end())))) + aCoincidentPoints.insert(aPtIt->begin(), aPtIt->end()); + if (isFound[0] && isFound[1]) + break; + } + // check both points are fixed => not need to decrease DoF + bool isFixed[2] = { aFixedPoints.find(aCoincPoint[0]) != aFixedPoints.end(), + aFixedPoints.find(aCoincPoint[1]) != aFixedPoints.end() }; + if (isFixed[0] && isFixed[1] && isDoFDecreased) + aDoF += 2; // revert decrease of DoF + else if (isFixed[0] && !isFixed[1]) + aFixedPoints.insert(aCoincPoint[1]); + else if (!isFixed[0] && isFixed[1]) + aFixedPoints.insert(aCoincPoint[0]); + } else { + aDoF -= 1; + if (aCoincPoint[0] && aCoincLine) { + // if the point is already coincident to a line + // (by middle point constraint), do not decrease DoF + std::map >::iterator + aPtFound = aPointOnLine.find(aCoincPoint[0]); + if (aPtFound != aPointOnLine.end() && + aPtFound->second.find(aCoincLine) != aPtFound->second.end()) + aDoF += 1; // restore value decreased above + else + aPointOnLine[aCoincPoint[0]].insert(aCoincLine); + } + } + for (int j = 0; j < 2; ++j) + if (aCoincPoint[j]) + aCoincidentPoints.insert(aCoincPoint[j]); + } + else if (aFeature->getKind() == SketchPlugin_ConstraintMiddle::ID()) { + AttributePtr aPoint; + FeaturePtr aLine; + for (int j = 0; j < 2; ++j) { + AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast( + aFeature->attribute(SketchPlugin_Constraint::ATTRIBUTE(j))); + if (!aRefAttr) + continue; + if (aRefAttr->isObject()) + aLine = ModelAPI_Feature::feature(aRefAttr->object()); + else + aPoint = aRefAttr->attr(); + } + if (aPoint && aLine) { + // if the point is already on the line, decrease 1 DoF, instead decrease 2 DoF + std::map >::iterator + aPtFound = aPointOnLine.find(aPoint); + if (aPtFound != aPointOnLine.end() && + aPtFound->second.find(aLine) != aPtFound->second.end()) + aDoF -= 1; + else { + aDoF -= 2; + aPointOnLine[aPoint].insert(aLine); + } + } + } + else if (aFeature->getKind() == SketchPlugin_ConstraintRigid::ID()) { + AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast( + aFeature->attribute(SketchPlugin_Constraint::ENTITY_A())); + assert(aRefAttr); + std::set aPoints; + if (!aRefAttr->isObject()) { + aDoF -= 2; // attribute is a point + aPoints.insert(aRefAttr->attr()); + } else { + FeaturePtr anAttr = ModelAPI_Feature::feature(aRefAttr->object()); + if (anAttr) { + if (isExternal(anAttr)) + continue; // feature is already fixed since it is external + aDoF -= aDoFDelta[anAttr->getKind()]; + std::list aPtAttrs = + anAttr->data()->attributes(GeomDataAPI_Point2D::typeId()); + aPoints.insert(aPtAttrs.begin(), aPtAttrs.end()); + } + } + + // Check whether feature's points are already coincident with fixed points. + // In this case we need to revert decrease of DoF for these points. + // If the coordinates of fixed points are different, it will be processed by solver. + for (int k = 0; k < i; ++k) { + FeaturePtr aFeature = aSketch->subFeature(k); + if (aFeature->getKind() != SketchPlugin_ConstraintCoincidence::ID()) + continue; + AttributePtr aCoincPoint[2] = {AttributePtr(), AttributePtr()}; + for (int j = 0; j < 2; ++j) { + AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast( + aFeature->attribute(SketchPlugin_Constraint::ATTRIBUTE(j))); + if (!aRefAttr) + continue; + if (!aRefAttr->isObject()) + aCoincPoint[j] = aRefAttr->attr(); + else { + FeaturePtr anAttr = ModelAPI_Feature::feature(aRefAttr->object()); + if (anAttr && anAttr->getKind() == SketchPlugin_Point::ID()) + aCoincPoint[j] = anAttr->attribute(SketchPlugin_Point::COORD_ID()); + } + } + if (aCoincPoint[0] && aCoincPoint[1]) { + if ((aFixedPoints.find(aCoincPoint[0]) != aFixedPoints.end() && + aPoints.find(aCoincPoint[1]) != aPoints.end()) || + (aFixedPoints.find(aCoincPoint[1]) != aFixedPoints.end() && + aPoints.find(aCoincPoint[0]) != aPoints.end())) + aDoF += 2; // point already fixed + } + } + // store fixed points + aFixedPoints.insert(aPoints.begin(), aPoints.end()); + } + else if (aFeature->getKind() == SketchPlugin_ConstraintMirror::ID() || + aFeature->getKind() == SketchPlugin_MultiRotation::ID() || + aFeature->getKind() == SketchPlugin_MultiTranslation::ID()) { + int aNbCopies = 1; + std::string anAttrName; + if (aFeature->getKind() == SketchPlugin_ConstraintMirror::ID()) + anAttrName = SketchPlugin_Constraint::ENTITY_B(); + else { + if (aFeature->getKind() == SketchPlugin_MultiRotation::ID()) + aNbCopies = + aFeature->integer(SketchPlugin_MultiRotation::NUMBER_OF_OBJECTS_ID())->value() - 1; + else if (aFeature->getKind() == SketchPlugin_MultiTranslation::ID()) + aNbCopies = + aFeature->integer(SketchPlugin_MultiTranslation::NUMBER_OF_OBJECTS_ID())->value() - 1; + anAttrName = SketchPlugin_Constraint::ENTITY_A(); + } + + AttributeRefListPtr aRefListOfShapes = std::dynamic_pointer_cast( + aFeature->attribute(anAttrName)); + std::list anObjList = aRefListOfShapes->list(); + std::list::const_iterator anObjIt = anObjList.begin(); + for (; anObjIt != anObjList.end(); ++anObjIt) { + FeaturePtr aSub = ModelAPI_Feature::feature(*anObjIt); + aDoF -= aDoFDelta[aSub->getKind()] * aNbCopies; + } + // collect points and their copies for correct calculation of DoF for coincident points + collectPointsAndCopies(aFeature, aPointsInMultiConstraints); + } + } + + aSketchDoF[aSketch] = aDoF; + } + + // Check the degrees of freedom are changed + std::map::const_iterator aDoFIt = aSketchDoF.begin(); + std::map::iterator aFound; + for (; aDoFIt != aSketchDoF.end(); ++aDoFIt) { + if (aDoFIt->second < 0) + continue; // conflicting constraints on the current sketch + aFound = myDoF.find(aDoFIt->first); + if (aFound != myDoF.end() && aFound->second == aDoFIt->second) + continue; // nothing is changed + myDoF[aDoFIt->first] = aDoFIt->second; + // change attribute value + std::ostringstream aStream; + if (aDoFIt->second == 0) + aStream << "Sketch fully fixed (DOF = " << aDoFIt->second << ")"; + else + aStream << "DOF (degree of freedom) = " << aDoFIt->second; + aDoFIt->first->data()->string(SketchPlugin_Sketch::SOLVER_DOF())->setValue(aStream.str()); + } +} + bool SketchSolver_Manager::stopSendUpdate() const { // to avoid redisplay of each segment on update by solver one by one in the viewer