X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2FSketchSolver%2FSketchSolver_Manager.cpp;h=3a8f8ea86b9c0fafd2fb13b64f5c93c6bfae9677;hb=801131b41013eeb97fe917311b47bff78fb86d86;hp=34b2ea0405d432afa86608c2ee8a5c0fbf683b0e;hpb=c5c4a4d92186640c86102e4bf04a4d9556578d07;p=modules%2Fshaper.git diff --git a/src/SketchSolver/SketchSolver_Manager.cpp b/src/SketchSolver/SketchSolver_Manager.cpp index 34b2ea040..3a8f8ea86 100644 --- a/src/SketchSolver/SketchSolver_Manager.cpp +++ b/src/SketchSolver/SketchSolver_Manager.cpp @@ -5,8 +5,12 @@ // Author: Artem ZHIDKOV #include "SketchSolver_Manager.h" +#include "SketchSolver_Error.h" #include + +#include + #include #include #include @@ -14,8 +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 @@ -24,9 +47,13 @@ #include #include +#include #include #include #include +#include + +static const Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED); // Initialization of constraint manager self pointer SketchSolver_Manager* SketchSolver_Manager::mySelf = 0; @@ -55,6 +82,9 @@ SketchSolver_Manager::SketchSolver_Manager() Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED)); 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)); } SketchSolver_Manager::~SketchSolver_Manager() @@ -79,9 +109,14 @@ BuilderPtr SketchSolver_Manager::builder() void SketchSolver_Manager::processEvent( const std::shared_ptr& theMessage) { + 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() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED)) { @@ -89,8 +124,7 @@ void SketchSolver_Manager::processEvent( std::dynamic_pointer_cast(theMessage); std::set aFeatures = anUpdateMsg->objects(); - // Shows the message has at least one feature applicable for solver - bool hasProperFeature = false; + bool isUpdateFlushed = stopSendUpdate(); bool isMovedEvt = theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_MOVED); @@ -99,11 +133,16 @@ void SketchSolver_Manager::processEvent( for (aFeatIter = aFeatures.begin(); aFeatIter != aFeatures.end(); aFeatIter++) { 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::iterator aFeatIter = aSketchFeatures.begin(); @@ -122,9 +161,18 @@ void SketchSolver_Manager::processEvent( } } + bool needToUpdate = false; // Solve the set of constraints if (hasProperFeature) - resolveConstraints(isMovedEvt); // send update for movement in any case + needToUpdate = resolveConstraints(); + + // Features may be updated => now send events, but for all changed at once + if (isUpdateFlushed) + allowSendUpdate(); + // send update for movement in any case + if (needToUpdate || isMovedEvt) + Events_Loop::loop()->flush(anUpdateEvent); + } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) { std::shared_ptr aDeleteMsg = std::dynamic_pointer_cast(theMessage); @@ -138,28 +186,77 @@ void SketchSolver_Manager::processEvent( break; if (aFGrIter != aFeatureGroups.end()) { - std::vector::iterator aGroupIter = myGroups.begin(); + hasProperFeature = true; + std::list aGroupsToResolve; + std::list::iterator aGroupIter = myGroups.begin(); std::list aSeparatedGroups; while (aGroupIter != myGroups.end()) { if (!(*aGroupIter)->isWorkplaneValid()) { // the group should be removed delete *aGroupIter; - int aShift = aGroupIter - myGroups.begin(); - myGroups.erase(aGroupIter); - aGroupIter = myGroups.begin() + aShift; + std::list::iterator aRemoveIt = aGroupIter++; + myGroups.erase(aRemoveIt); continue; } 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() || + (*aGroupIter)->isFailed()) + aGroupsToResolve.push_back(*aGroupIter); } aGroupIter++; } - if (aSeparatedGroups.size() > 0) + if (aSeparatedGroups.size() > 0) { myGroups.insert(myGroups.end(), aSeparatedGroups.begin(), aSeparatedGroups.end()); + aGroupsToResolve.insert(aGroupsToResolve.end(), + aSeparatedGroups.begin(), aSeparatedGroups.end()); + } + + if (!aGroupsToResolve.empty()) + resolveConstraints(aGroupsToResolve); } } + + if (hasProperFeature) + degreesOfFreedom(); myIsComputed = false; } +void SketchSolver_Manager::checkConflictingConstraints(const std::shared_ptr& theMessage) +{ + if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_SOLVER_REPAIRED)) { + std::shared_ptr aMessage = + std::dynamic_pointer_cast(theMessage); + std::set aSentObjs = aMessage->objects(); + if (!aSentObjs.empty()) { + // Obtain sketch where the constraints are placed. + // It is enough to check only one constraint. + CompositeFeaturePtr aSketch; + FeaturePtr aConstraint = ModelAPI_Feature::feature(*aSentObjs.begin()); + std::list::const_iterator aGrIt = myGroups.begin(); + for (; aGrIt != myGroups.end(); ++aGrIt) + if ((*aGrIt)->isInteract(aConstraint)) { + aSketch = (*aGrIt)->getWorkplane(); + break; + } + + // Search failed groups built on the same sketch + if (aSketch) { + for (aGrIt = myGroups.begin(); aGrIt != myGroups.end(); ++aGrIt) { + SketchSolver_Group* aGroup = *aGrIt; + if (aGroup->isBaseWorkplane(aSketch) && aGroup->isFailed() && + !aGroup->isInteract(aConstraint)) { + // reset error message on the sketch + aGroup->getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue( + SketchSolver_Error::CONSTRAINTS()); + break; + } + } + } + } + } +} + // ============================================================================ // Function: changeWorkplane // Purpose: update workplane by given parameters of the sketch @@ -169,7 +266,7 @@ bool SketchSolver_Manager::changeWorkplane(CompositeFeaturePtr theSketch) bool aResult = true; // changed when a workplane wrongly updated bool isUpdated = false; // Try to update specified workplane in all groups - std::vector::iterator aGroupIter; + std::list::iterator aGroupIter; for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) if ((*aGroupIter)->isBaseWorkplane(theSketch)) { isUpdated = true; @@ -185,7 +282,7 @@ bool SketchSolver_Manager::changeWorkplane(CompositeFeaturePtr theSketch) } myGroups.push_back(aNewGroup); } - return aResult; + return aResult || isUpdated; } // ============================================================================ @@ -218,7 +315,7 @@ bool SketchSolver_Manager::changeFeature(std::shared_ptr t return true; } else if (aGroups.size() == 1) { // Only one group => add feature into it GroupID aGroupId = *(aGroups.begin()); - std::vector::iterator aGroupIter; + std::list::iterator aGroupIter; for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) if ((*aGroupIter)->getId() == aGroupId) { // If the group is empty, the feature is not added (the constraint only) @@ -230,7 +327,7 @@ bool SketchSolver_Manager::changeFeature(std::shared_ptr t std::set::const_iterator aGroupsIter = aGroups.begin(); // Search first group - std::vector::iterator aFirstGroupIter; + std::list::iterator aFirstGroupIter; for (aFirstGroupIter = myGroups.begin(); aFirstGroupIter != myGroups.end(); aFirstGroupIter++) if ((*aFirstGroupIter)->getId() == *aGroupsIter) break; @@ -238,28 +335,30 @@ bool SketchSolver_Manager::changeFeature(std::shared_ptr t return false; // Append other groups to the first one - std::vector::iterator anOtherGroupIter = aFirstGroupIter + 1; + std::list::iterator anOtherGroupIter = aFirstGroupIter; + ++anOtherGroupIter; for (aGroupsIter++; aGroupsIter != aGroups.end(); aGroupsIter++) { for (; anOtherGroupIter != myGroups.end(); anOtherGroupIter++) if ((*anOtherGroupIter)->getId() == *aGroupsIter) break; if (anOtherGroupIter == myGroups.end()) { // Group disappears - anOtherGroupIter = aFirstGroupIter + 1; + anOtherGroupIter = aFirstGroupIter; + ++anOtherGroupIter; continue; } (*aFirstGroupIter)->mergeGroups(**anOtherGroupIter); - int aShiftFirst = aFirstGroupIter - myGroups.begin(); - int aShiftOther = anOtherGroupIter - myGroups.begin(); - delete *anOtherGroupIter; - myGroups.erase(anOtherGroupIter); - aFirstGroupIter = myGroups.begin() + aShiftFirst; - anOtherGroupIter = myGroups.begin() + aShiftOther; + std::list::iterator aRemoveIt = anOtherGroupIter++; + delete *aRemoveIt; + myGroups.erase(aRemoveIt); } if (aConstraint) - return (*aFirstGroupIter)->changeConstraint(aConstraint); - return (*aFirstGroupIter)->updateFeature(theFeature); + (*aFirstGroupIter)->changeConstraint(aConstraint); + else + (*aFirstGroupIter)->updateFeature(theFeature); + // groups are merged => need to resolve them + return true; } // Something goes wrong @@ -270,12 +369,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) { - std::vector::iterator aGroupIt = myGroups.begin(); + bool isMoved = false; + std::list::iterator aGroupIt = myGroups.begin(); for (; aGroupIt != myGroups.end(); aGroupIt++) if (!(*aGroupIt)->isEmpty() && (*aGroupIt)->isInteract(theFeature)) - (*aGroupIt)->moveFeature(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()) { + 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; } // ============================================================================ @@ -289,7 +420,7 @@ void SketchSolver_Manager::findGroups( std::shared_ptr aWP = findWorkplane(theFeature); SketchSolver_Group* anEmptyGroup = 0; // appropriate empty group for specified constraint - std::vector::const_iterator aGroupIter; + std::list::const_iterator aGroupIter; for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) if (aWP == (*aGroupIter)->getWorkplane() && (*aGroupIter)->isInteract(theFeature)) { if (!(*aGroupIter)->isEmpty()) @@ -313,7 +444,7 @@ std::shared_ptr SketchSolver_Manager // Already verified workplanes std::set > aVerified; - std::vector::const_iterator aGroupIter; + std::list::const_iterator aGroupIter; for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) { std::shared_ptr aWP = (*aGroupIter)->getWorkplane(); if (aVerified.find(aWP) != aVerified.end()) @@ -339,30 +470,370 @@ std::shared_ptr SketchSolver_Manager // Function: resolveConstraints // Purpose: change entities according to available constraints // ============================================================================ -void SketchSolver_Manager::resolveConstraints(const bool theForceUpdate) +bool SketchSolver_Manager::resolveConstraints(const std::list& theGroups) { - //myIsComputed = true; bool needToUpdate = false; - static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED); + const std::list& aGroupsToResolve = theGroups.empty() ? myGroups : theGroups; + std::list::const_iterator aGroupIter = aGroupsToResolve.begin(); + for (; aGroupIter != aGroupsToResolve.end(); aGroupIter++) + if ((*aGroupIter)->resolveConstraints()) + needToUpdate = true; + return needToUpdate; +} + + +// Obtain points and their copies for Mirror, Multi-Rotation and Multi-Translation constraints +static void collectPointsAndCopies(FeaturePtr theConstraint, 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; + } + } +} + +// ============================================================================ +// 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); + // 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) { + 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 bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent); if (isUpdateFlushed) { Events_Loop::loop()->setFlushed(anUpdateEvent, false); } + return isUpdateFlushed; +} - std::vector::iterator aGroupIter; - for (aGroupIter = myGroups.begin(); aGroupIter != myGroups.end(); aGroupIter++) - if ((*aGroupIter)->resolveConstraints()) - needToUpdate = true; - - // Features may be updated => now send events, but for all changed at once - if (isUpdateFlushed) { - Events_Loop::loop()->setFlushed(anUpdateEvent, true); - } - // Must be before flush because on "Updated" flush the results may be produced - // and the creation event is appeared with many new objects. If myIsComputed these - // events are missed in processEvents and some elements are not added. - //myIsComputed = false; - if (needToUpdate || theForceUpdate) - Events_Loop::loop()->flush(anUpdateEvent); +void SketchSolver_Manager::allowSendUpdate() const +{ + Events_Loop::loop()->setFlushed(anUpdateEvent, true); }