X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2FSketchSolver%2FSketchSolver_Manager.cpp;h=80f1f98ef7349e26fcc46d12c48a5fec78a01b5e;hb=9dc1fc2ff6f4143369cdb2042a5e01e8e737c635;hp=354a3d9d8a43bd794bcd050d36e2a908145a3645;hpb=0b5a982a51a44fd23b53772325922fd93ebb126e;p=modules%2Fshaper.git diff --git a/src/SketchSolver/SketchSolver_Manager.cpp b/src/SketchSolver/SketchSolver_Manager.cpp index 354a3d9d8..80f1f98ef 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; @@ -52,9 +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() @@ -72,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 @@ -79,42 +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(); - // 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); 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; @@ -122,15 +178,28 @@ 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(); + + myIsComputed = false; + + // 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); 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 || @@ -138,26 +207,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 + 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); + } + myIsComputed = false; + } + + if (hasProperFeature) + degreesOfFreedom(); +} + +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; + } + } + } } } - myIsComputed = false; } // ============================================================================ @@ -169,7 +289,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 +305,7 @@ bool SketchSolver_Manager::changeWorkplane(CompositeFeaturePtr theSketch) } myGroups.push_back(aNewGroup); } - return aResult; + return aResult || isUpdated; } // ============================================================================ @@ -198,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 @@ -218,7 +338,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) @@ -226,11 +346,12 @@ 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 - std::vector::iterator aFirstGroupIter; + std::list::iterator aFirstGroupIter; for (aFirstGroupIter = myGroups.begin(); aFirstGroupIter != myGroups.end(); aFirstGroupIter++) if ((*aFirstGroupIter)->getId() == *aGroupsIter) break; @@ -238,23 +359,22 @@ 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) @@ -273,12 +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) { - 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; } // ============================================================================ @@ -292,7 +444,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()) @@ -316,7 +468,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()) @@ -342,30 +494,390 @@ 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; + } + } +} + + +// 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 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); }