From f9fd953bf814fad40ae045f6164c7f80229b0872 Mon Sep 17 00:00:00 2001 From: azv Date: Tue, 11 Aug 2015 15:51:34 +0300 Subject: [PATCH] Update behavior of calculation of Multi-Rotation constraint --- src/SketchSolver/SketchSolver_Constraint.h | 2 +- .../SketchSolver_ConstraintMultiRotation.cpp | 319 ++++++++---------- .../SketchSolver_ConstraintMultiRotation.h | 16 +- .../SketchSolver_ConstraintRigid.cpp | 42 +-- .../SketchSolver_ConstraintRigid.h | 9 - src/SketchSolver/SketchSolver_Group.cpp | 28 +- src/SketchSolver/SketchSolver_Storage.cpp | 277 +++++++++++++++ src/SketchSolver/SketchSolver_Storage.h | 32 ++ 8 files changed, 485 insertions(+), 240 deletions(-) diff --git a/src/SketchSolver/SketchSolver_Constraint.h b/src/SketchSolver/SketchSolver_Constraint.h index 0ceac2812..2a12fc5e9 100644 --- a/src/SketchSolver/SketchSolver_Constraint.h +++ b/src/SketchSolver/SketchSolver_Constraint.h @@ -67,7 +67,7 @@ public: Slvs_hEntity getId(AttributePtr theAttribute) const; /// \brief Adds a feature to constraint and create its analogue in SolveSpace - void addFeature(FeaturePtr theFeature); + virtual void addFeature(FeaturePtr theFeature); /// \brief Shows error message const std::string& error() const diff --git a/src/SketchSolver/SketchSolver_ConstraintMultiRotation.cpp b/src/SketchSolver/SketchSolver_ConstraintMultiRotation.cpp index 0ceaee690..53f7af568 100644 --- a/src/SketchSolver/SketchSolver_ConstraintMultiRotation.cpp +++ b/src/SketchSolver/SketchSolver_ConstraintMultiRotation.cpp @@ -30,8 +30,8 @@ static double squareDistance( void SketchSolver_ConstraintMultiRotation::getAttributes( Slvs_hEntity& theCenter, double& theAngle, - std::vector >& thePoints, - std::vector >& theCircular) + std::vector< std::vector >& thePoints, + std::vector< std::vector >& theEntities) { DataPtr aData = myBaseConstraint->data(); theAngle = std::dynamic_pointer_cast( @@ -65,20 +65,22 @@ void SketchSolver_ConstraintMultiRotation::getAttributes( // Also all circles and arc collected too, because they will be constrained by equal radii. FeaturePtr aFeature; ResultConstructionPtr aRC; - std::vector aPoints[2]; // lists of points of features - std::vector aCircs; // list of circular objects + static const size_t MAX_POINTS = 3; + std::vector aPoints[MAX_POINTS]; // lists of points of features + std::vector anEntities; std::list anObjectList = aRefList->list(); std::list::iterator anObjectIter = anObjectList.begin(); while (anObjectIter != anObjectList.end()) { - aPoints[0].clear(); - aPoints[1].clear(); - aCircs.clear(); + for (size_t i = 0; i < MAX_POINTS; ++i) + aPoints[i].clear(); + anEntities.clear(); for (size_t i = 0; i <= myNumberOfCopies && anObjectIter != anObjectList.end(); i++, anObjectIter++) { aFeature = ModelAPI_Feature::feature(*anObjectIter); if (!aFeature) continue; anEntityID = changeEntity(aFeature, aType); + anEntities.push_back(anEntityID); Slvs_Entity anEntity = myStorage->getEntity(anEntityID); switch (aType) { case SLVS_E_POINT_IN_2D: @@ -91,12 +93,11 @@ void SketchSolver_ConstraintMultiRotation::getAttributes( break; case SLVS_E_CIRCLE: aPoints[0].push_back(anEntity.point[0]); // center of circle - aCircs.push_back(anEntityID); break; case SLVS_E_ARC_OF_CIRCLE: - aPoints[0].push_back(anEntity.point[1]); // start point of arc - aPoints[1].push_back(anEntity.point[2]); // end point of arc - aCircs.push_back(anEntityID); + aPoints[0].push_back(anEntity.point[0]); // center of arc + aPoints[1].push_back(anEntity.point[1]); // start point of arc + aPoints[2].push_back(anEntity.point[2]); // end point of arc break; default: myErrorMsg = SketchSolver_Error::INCORRECT_ATTRIBUTE(); @@ -104,12 +105,11 @@ void SketchSolver_ConstraintMultiRotation::getAttributes( } } - if (!aPoints[0].empty()) - thePoints.push_back(aPoints[0]); - if (!aPoints[1].empty()) - thePoints.push_back(aPoints[1]); - if (!aCircs.empty()) - theCircular.push_back(aCircs); + for (size_t i = 0; i < MAX_POINTS; ++i) + if (!aPoints[i].empty()) + thePoints.push_back(aPoints[i]); + if (!anEntities.empty()) + theEntities.push_back(anEntities); } } @@ -123,67 +123,13 @@ void SketchSolver_ConstraintMultiRotation::process() if (!mySlvsConstraints.empty()) // some data is changed, update constraint update(myBaseConstraint); - Slvs_hEntity aCenter; - std::vector > aPointsAndCopies; - std::vector > aCircsAndCopies; - getAttributes(aCenter, myAngle, aPointsAndCopies, aCircsAndCopies); + std::vector > anEntitiesAndCopies; + getAttributes(myRotationCenter, myAngle, myPointsAndCopies, anEntitiesAndCopies); if (!myErrorMsg.empty()) return; - myAuxLines.clear(); - - // Create lines between neighbor rotated points and make angle between them equal to anAngle. - // Also these lines should have equal lengths. - Slvs_Constraint aConstraint; - myRotationCenter = aCenter; - Slvs_Entity aPrevLine; - std::vector >::iterator aCopyIter = aPointsAndCopies.begin(); - for (; aCopyIter != aPointsAndCopies.end(); aCopyIter++) { - size_t aSize = aCopyIter->size(); - if (aSize <= 1) continue; - - aPrevLine = Slvs_MakeLineSegment(SLVS_E_UNKNOWN, myGroup->getId(), - myGroup->getWorkplaneId(), aCenter, (*aCopyIter)[0]); - aPrevLine.h = myStorage->addEntity(aPrevLine); - std::vector anEqualLines(1, aPrevLine.h); - for (size_t i = 1; i < aSize; i++) { - Slvs_Entity aLine = Slvs_MakeLineSegment(SLVS_E_UNKNOWN, myGroup->getId(), - myGroup->getWorkplaneId(), aCenter, (*aCopyIter)[i]); - aLine.h = myStorage->addEntity(aLine); - // Equal length constraint - aConstraint = Slvs_MakeConstraint(SLVS_E_UNKNOWN, myGroup->getId(), - SLVS_C_EQUAL_LENGTH_LINES, myGroup->getWorkplaneId(), 0.0, - SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, aPrevLine.h, aLine.h); - aConstraint.h = myStorage->addConstraint(aConstraint); - mySlvsConstraints.push_back(aConstraint.h); - // Angle constraint - aConstraint = Slvs_MakeConstraint(SLVS_E_UNKNOWN, myGroup->getId(), - SLVS_C_ANGLE, myGroup->getWorkplaneId(), fabs(myAngle), SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, - aPrevLine.h, aLine.h); - if (myAngle < 0.0) // clockwise rotation - aConstraint.other = true; - aConstraint.h = myStorage->addConstraint(aConstraint); - mySlvsConstraints.push_back(aConstraint.h); - - aPrevLine = aLine; - anEqualLines.push_back(aPrevLine.h); - } - - myAuxLines.push_back(anEqualLines); - } - // Equal radii constraints - for (aCopyIter = aCircsAndCopies.begin(); aCopyIter != aCircsAndCopies.end(); aCopyIter++) { - size_t aSize = aCopyIter->size(); - for (size_t i = 0; i < aSize - 1; i++) { - aConstraint = Slvs_MakeConstraint(SLVS_E_UNKNOWN, myGroup->getId(), - SLVS_C_EQUAL_RADIUS, myGroup->getWorkplaneId(), 0.0, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, - (*aCopyIter)[i], (*aCopyIter)[i+1]); - aConstraint.h = myStorage->addConstraint(aConstraint); - mySlvsConstraints.push_back(aConstraint.h); - } - } - // Set the rotation center unchanged during constraint recalculation + Slvs_Constraint aConstraint; if (!myStorage->isPointFixed(myRotationCenter, aConstraint.h, true)) { aConstraint = Slvs_MakeConstraint( SLVS_E_UNKNOWN, myGroup->getId(), SLVS_C_WHERE_DRAGGED, myGroup->getWorkplaneId(), 0.0, @@ -192,6 +138,28 @@ void SketchSolver_ConstraintMultiRotation::process() mySlvsConstraints.push_back(aConstraint.h); } + // Set all objects unchanged (only initial object may be changed by user) + myCircsAndCopies.clear(); + std::vector >::const_iterator anEntIt = anEntitiesAndCopies.begin(); + std::vector::const_iterator aCpIt; + for (; anEntIt != anEntitiesAndCopies.end(); ++anEntIt) { + std::vector aCircs; + for (aCpIt = anEntIt->begin(); aCpIt != anEntIt->end(); ++aCpIt) { + const Slvs_Entity& anEntity = myStorage->getEntity(*aCpIt); + std::vector aNewConstr; + if (anEntity.type == SLVS_E_CIRCLE) { + aCircs.push_back(anEntity.distance); + // for circles we fix only center + aNewConstr = myStorage->fixEntity(anEntity.point[0]); + } else + aNewConstr = myStorage->fixEntity(*aCpIt); + mySlvsConstraints.insert(mySlvsConstraints.end(), aNewConstr.begin(), aNewConstr.end()); + } + + if (!aCircs.empty()) + myCircsAndCopies.push_back(aCircs); + } + adjustConstraint(); } @@ -252,9 +220,27 @@ bool SketchSolver_ConstraintMultiRotation::remove(ConstraintPtr theConstraint) myFeatureMap[aFIter->first] = anEntity; } + // Clear list of rotated points + myPointsAndCopies.clear(); + return true; } +void SketchSolver_ConstraintMultiRotation::addFeature(FeaturePtr theFeature) +{ + SketchSolver_Constraint::addFeature(theFeature); + + std::map::iterator aFeatIt = myFeatureMap.find(theFeature); + if (aFeatIt == myFeatureMap.end()) + return; + + // store list of points of the feature + const Slvs_Entity& theEntity = myStorage->getEntity(aFeatIt->second); + for (int i = 0; i < 4; i++) + if (theEntity.point[i] != SLVS_E_UNKNOWN) + myPointsJustUpdated.insert(theEntity.point[i]); +} + void SketchSolver_ConstraintMultiRotation::adjustConstraint() { if (abs(myAngle) < tolerance) { @@ -262,63 +248,25 @@ void SketchSolver_ConstraintMultiRotation::adjustConstraint() return; } - // Check the lengths of auxiliary lines are zero. - // If they become zero, remove corresponding Angle constraints. - // It they become non-zero (but were zero recently), add Angle constraint. - std::vector::iterator aConstr = mySlvsConstraints.begin(); - std::map anEqualLines; - bool isFirstRemoved = false; - for (; aConstr != mySlvsConstraints.end(); - isFirstRemoved ? aConstr = mySlvsConstraints.begin() : ++aConstr) { - isFirstRemoved = false; - Slvs_Constraint aConstraint = myStorage->getConstraint(*aConstr); - if (aConstraint.type == SLVS_C_ANGLE || aConstraint.type == SLVS_C_EQUAL_LENGTH_LINES) { - Slvs_Entity aLine = myStorage->getEntity(aConstraint.entityA); - // Line length became zero => remove constraint - if (squareDistance(myStorage, aLine.point[0], aLine.point[1]) < tolerance * tolerance) { - myStorage->removeConstraint(aConstraint.h); - isFirstRemoved = aConstr == mySlvsConstraints.begin(); - std::vector::iterator aTmpIter = aConstr; - if (!isFirstRemoved) - --aConstr; - mySlvsConstraints.erase(aTmpIter); + std::list aCoincident = myStorage->getConstraintsByType(SLVS_C_POINTS_COINCIDENT); + std::list::const_iterator aCoIt; + + // Check overconstrained on rotation center (if it is coincident with other fixed point) + Slvs_hConstraint aFixedCenter; + if (myStorage->isPointFixed(myRotationCenter, aFixedCenter, false)) { + Slvs_hConstraint aFixed; + for (aCoIt = aCoincident.begin(); aCoIt != aCoincident.end(); ++aCoIt) + if ((aCoIt->ptA == myRotationCenter && myStorage->isPointFixed(aCoIt->ptB, aFixed, true)) || + (aCoIt->ptB == myRotationCenter && myStorage->isPointFixed(aCoIt->ptA, aFixed, true))) { + // Un-fix the center + myStorage->removeConstraint(aFixedCenter); + std::vector::iterator aSCIt = mySlvsConstraints.begin(); + for (; aSCIt != mySlvsConstraints.end(); ++aSCIt) + if (*aSCIt == aFixedCenter) { + mySlvsConstraints.erase(aSCIt); + break; + } } - // Store the lines into the map - anEqualLines[aConstraint.entityB] = aConstraint.entityA; - } - } - // Create Angle and Equal constraints for non-degenerated lines - AuxLinesList::iterator anIt = myAuxLines.begin(); - for (; anIt != myAuxLines.end(); ++anIt) { - if (anEqualLines.find(anIt->back()) != anEqualLines.end()) - continue; - - std::vector::iterator anEqLinesIt = anIt->begin(); - Slvs_hEntity aPrevLine = (*anEqLinesIt); - // Check the length of the line - Slvs_Entity aLine = myStorage->getEntity(aPrevLine); - if (squareDistance(myStorage, aLine.point[0], aLine.point[1]) < tolerance * tolerance) - continue; - - for (++anEqLinesIt; anEqLinesIt != anIt->end(); ++anEqLinesIt) { - Slvs_hEntity aLine = (*anEqLinesIt); - // Equal length constraint - Slvs_Constraint aConstraint = Slvs_MakeConstraint(SLVS_E_UNKNOWN, myGroup->getId(), - SLVS_C_EQUAL_LENGTH_LINES, myGroup->getWorkplaneId(), 0.0, - SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, aPrevLine, aLine); - aConstraint.h = myStorage->addConstraint(aConstraint); - mySlvsConstraints.push_back(aConstraint.h); - // Angle constraint - aConstraint = Slvs_MakeConstraint(SLVS_E_UNKNOWN, myGroup->getId(), - SLVS_C_ANGLE, myGroup->getWorkplaneId(), fabs(myAngle), SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, - aPrevLine, aLine); - if (myAngle < 0.0) // clockwise rotation - aConstraint.other = true; - aConstraint.h = myStorage->addConstraint(aConstraint); - mySlvsConstraints.push_back(aConstraint.h); - - aPrevLine = aLine; - } } // Obtain coordinates of rotation center @@ -330,66 +278,67 @@ void SketchSolver_ConstraintMultiRotation::adjustConstraint() double cosA = cos(myAngle * PI / 180.0); double sinA = sin(myAngle * PI / 180.0); + double aVec[2]; // coordinates of vector defining a direction from rotation center to a point + // Update positions of all points to satisfy angles - std::list aConstrAngle = myStorage->getConstraintsByType(SLVS_C_ANGLE); - std::list::iterator anAngIt = aConstrAngle.begin(); - std::vector::iterator aCIt; - Slvs_hConstraint aFixed; // temporary variable - double aVec[2]; // coordinates of vector defining a line - Slvs_Param aTarget[2]; // parameter to be changed - for (; anAngIt != aConstrAngle.end(); anAngIt++) { - for (aCIt = mySlvsConstraints.begin(); aCIt != mySlvsConstraints.end(); aCIt++) - if (anAngIt->h == *aCIt) - break; - if (aCIt == mySlvsConstraints.end()) - continue; - Slvs_Entity aLineA = myStorage->getEntity(anAngIt->entityA); - Slvs_Entity aLineB = myStorage->getEntity(anAngIt->entityB); - if (myStorage->isPointFixed(aLineB.point[1], aFixed)) - continue; - Slvs_Entity aPointA = myStorage->getEntity(aLineA.point[1]); - Slvs_Entity aPointB = myStorage->getEntity(aLineB.point[1]); - for (int i = 0; i < 2; i++) { - aVec[i] = myStorage->getParameter(aPointA.param[i]).val - aCenterXY[i]; - aTarget[i] = myStorage->getParameter(aPointB.param[i]); + std::vector< std::vector >::const_iterator aPointsIter = myPointsAndCopies.begin(); + std::vector::const_iterator aCopyIter; + for (; aPointsIter != myPointsAndCopies.end(); ++aPointsIter) { + aCopyIter = aPointsIter->begin(); + const Slvs_Entity& anInitial = myStorage->getEntity(*aCopyIter); + for (int i = 0; i < 2; i++) + aVec[i] = myStorage->getParameter(anInitial.param[i]).val - aCenterXY[i]; + + // if the point is coincident with another one which is temporary fixed (moved by user), + // we will update its position correspondingly + Slvs_hConstraint aFixed; + for (aCoIt = aCoincident.begin(); aCoIt != aCoincident.end(); ++aCoIt) { + if ((aCoIt->ptA == anInitial.h && myStorage->isPointFixed(aCoIt->ptB, aFixed, true)) || + (aCoIt->ptB == anInitial.h && myStorage->isPointFixed(aCoIt->ptA, aFixed, true))) { + Slvs_hEntity anOtherId = aCoIt->ptA == anInitial.h ? aCoIt->ptB : aCoIt->ptA; + if (!myStorage->isTemporary(aFixed) && + myPointsJustUpdated.find(anOtherId) == myPointsJustUpdated.end()) + continue; // nothing to change + + const Slvs_Entity& anOtherPnt = myStorage->getEntity(anOtherId); + for (int i = 0; i < 2; i++) { + Slvs_Param anInitParam = myStorage->getParameter(anInitial.param[i]); + const Slvs_Param& anOtherParam = myStorage->getParameter(anOtherPnt.param[i]); + anInitParam.val = anOtherParam.val; + myStorage->updateParameter(anInitParam); + aVec[i] = anOtherParam.val - aCenterXY[i]; + } + } } - aTarget[0].val = aCenterXY[0] + aVec[0] * cosA - aVec[1] * sinA; - aTarget[1].val = aCenterXY[1] + aVec[0] * sinA + aVec[1] * cosA; - myStorage->updateParameter(aTarget[0]); - myStorage->updateParameter(aTarget[1]); - anAngIt->valA = myAngle; - myStorage->updateConstraint(*anAngIt); + // update copied points + aCopyIter = aPointsIter->begin(); + for (++aCopyIter; aCopyIter != aPointsIter->end(); ++aCopyIter) { + // rotate direction + double aTemp = aVec[0] * cosA - aVec[1] * sinA; + aVec[1] = aVec[0] * sinA + aVec[1] * cosA; + aVec[0] = aTemp; + + const Slvs_Entity& aTarget = myStorage->getEntity(*aCopyIter); + for (int i = 0; i < 2; i++) { + Slvs_Param aParam = myStorage->getParameter(aTarget.param[i]); + aParam.val = aCenterXY[i] + aVec[i]; + myStorage->updateParameter(aParam); + } + } } - // update positions of centers of arcs for correct radius calculation - std::list aRadii = myStorage->getConstraintsByType(SLVS_C_EQUAL_RADIUS); - std::map::iterator aFeatIt; - for (anAngIt = aRadii.begin(); anAngIt != aRadii.end(); anAngIt++) { - int aNbFound = 0; // number of arcs used in translation - for (aFeatIt = myFeatureMap.begin(); aFeatIt != myFeatureMap.end(); aFeatIt++) - if (aFeatIt->second == anAngIt->entityA || aFeatIt->second == anAngIt->entityB) { - if (aFeatIt->first->getKind() == SketchPlugin_Arc::ID()) - aNbFound++; - else - break; - } - if (aNbFound != 2) - continue; - // two arcs were found, update their centers - Slvs_Entity anArcA = myStorage->getEntity(anAngIt->entityA); - Slvs_Entity anArcB = myStorage->getEntity(anAngIt->entityB); - if (myStorage->isPointFixed(anArcB.point[0], aFixed)) - continue; - Slvs_Entity aCenterA = myStorage->getEntity(anArcA.point[0]); - Slvs_Entity aCenterB = myStorage->getEntity(anArcB.point[0]); - for (int i = 0; i < 2; i++) { - aVec[i] = myStorage->getParameter(aCenterA.param[i]).val - aCenterXY[i]; - aTarget[i] = myStorage->getParameter(aCenterB.param[i]); + for (aPointsIter = myCircsAndCopies.begin(); aPointsIter != myCircsAndCopies.end(); ++aPointsIter) { + aCopyIter = aPointsIter->begin(); + const Slvs_Entity& anInitial = myStorage->getEntity(*aCopyIter); + const Slvs_Param& anInitRad = myStorage->getParameter(anInitial.param[0]); + for (++aCopyIter; aCopyIter != aPointsIter->end(); ++aCopyIter) { + const Slvs_Entity& aCopy = myStorage->getEntity(*aCopyIter); + Slvs_Param aCopyRad = myStorage->getParameter(aCopy.param[0]); + aCopyRad.val = anInitRad.val; + myStorage->updateParameter(aCopyRad); } - aTarget[0].val = aCenterXY[0] + aVec[0] * cosA - aVec[1] * sinA; - aTarget[1].val = aCenterXY[1] + aVec[0] * sinA + aVec[1] * cosA; - myStorage->updateParameter(aTarget[0]); - myStorage->updateParameter(aTarget[1]); } + + myPointsJustUpdated.clear(); } diff --git a/src/SketchSolver/SketchSolver_ConstraintMultiRotation.h b/src/SketchSolver/SketchSolver_ConstraintMultiRotation.h index df01392f5..f3a0f64fc 100644 --- a/src/SketchSolver/SketchSolver_ConstraintMultiRotation.h +++ b/src/SketchSolver/SketchSolver_ConstraintMultiRotation.h @@ -12,8 +12,6 @@ #include -typedef std::vector< std::vector > AuxLinesList; - /** \class SketchSolver_ConstraintMultiRotation * \ingroup Plugins * \brief Convert rotated features to the list of SolveSpace constraints @@ -37,6 +35,9 @@ public: /// \return \c false, if current constraint contains another SketchPlugin constraints (like for multiple coincidence) virtual bool remove(ConstraintPtr theConstraint = ConstraintPtr()); + /// \brief Adds a feature to constraint and create its analogue in SolveSpace + virtual void addFeature(FeaturePtr theFeature); + protected: /// \brief Converts SketchPlugin constraint to a list of SolveSpace constraints virtual void process(); @@ -51,10 +52,10 @@ protected: /// \param[out] theCenter ID of central point of rotation /// \param[out] theAngle rotation angle /// \param[out] thePoints list of IDs of initial points and their rotated copies - /// \param[out] theCircular list of IDs of arcs and circles and their copies + /// \param[out] theEntities list of IDs of entities and their rotated copies void getAttributes(Slvs_hEntity& theCenter, double& theAngle, - std::vector >& thePoints, - std::vector >& theCircular); + std::vector< std::vector >& thePoints, + std::vector< std::vector >& theEntities); /// \brief This method is used in derived objects to check consistence of constraint. virtual void adjustConstraint(); @@ -64,7 +65,10 @@ private: size_t myNumberOfCopies; ///< number of previous copies of initial objects Slvs_hEntity myRotationCenter; ///< ID of center of rotation double myAngle; ///< angle of rotation - AuxLinesList myAuxLines; ///< list of auxiliary lines, created to make clear rotation + std::vector< std::vector > myPointsAndCopies; ///< list of initial points and their rotated copies + std::vector< std::vector > myCircsAndCopies; ///< list of circles and their copies (to change their radii together) + + std::set myPointsJustUpdated; ///< list of points touched by user }; #endif diff --git a/src/SketchSolver/SketchSolver_ConstraintRigid.cpp b/src/SketchSolver/SketchSolver_ConstraintRigid.cpp index c5043d945..d42668633 100644 --- a/src/SketchSolver/SketchSolver_ConstraintRigid.cpp +++ b/src/SketchSolver/SketchSolver_ConstraintRigid.cpp @@ -201,13 +201,13 @@ void SketchSolver_ConstraintRigid::fixPoint(const Slvs_hEntity& thePointID) void SketchSolver_ConstraintRigid::fixLine(const Slvs_Entity& theLine) { Slvs_Constraint anEqual; - if (isAxisParallel(theLine)) { + if (myStorage->isAxisParallel(theLine.h)) { // Fix one point and a line length Slvs_hConstraint aFixed; if (!myStorage->isPointFixed(theLine.point[0], aFixed, true) && !myStorage->isPointFixed(theLine.point[1], aFixed, true)) fixPoint(theLine.point[0]); - if (!isUsedInEqual(theLine, anEqual)) { + if (!myStorage->isUsedInEqual(theLine.h, anEqual)) { // Check the distance is not set yet std::list aDistConstr = myStorage->getConstraintsByType(SLVS_C_PT_PT_DISTANCE); std::list::const_iterator aDIt = aDistConstr.begin(); @@ -235,7 +235,7 @@ void SketchSolver_ConstraintRigid::fixLine(const Slvs_Entity& theLine) } return; } - else if (isUsedInEqual(theLine, anEqual)) { + else if (myStorage->isUsedInEqual(theLine.h, anEqual)) { // Check another entity of Equal is already fixed Slvs_hEntity anOtherEntID = anEqual.entityA == theLine.h ? anEqual.entityB : anEqual.entityA; if (myStorage->isEntityFixed(anOtherEntID, true)) { @@ -280,7 +280,7 @@ void SketchSolver_ConstraintRigid::fixCircle(const Slvs_Entity& theCircle) bool isFixRadius = true; // Verify the arc is under Equal constraint Slvs_Constraint anEqual; - if (isUsedInEqual(theCircle, anEqual)) { + if (myStorage->isUsedInEqual(theCircle.h, anEqual)) { // Check another entity of Equal is already fixed Slvs_hEntity anOtherEntID = anEqual.entityA == theCircle.h ? anEqual.entityB : anEqual.entityA; if (myStorage->isEntityFixed(anOtherEntID, true)) @@ -317,7 +317,7 @@ void SketchSolver_ConstraintRigid::fixArc(const Slvs_Entity& theArc) // Verify the arc is under Equal constraint Slvs_Constraint anEqual; - if (isUsedInEqual(theArc, anEqual)) { + if (myStorage->isUsedInEqual(theArc.h, anEqual)) { // Check another entity of Equal is already fixed Slvs_hEntity anOtherEntID = anEqual.entityA == theArc.h ? anEqual.entityB : anEqual.entityA; if (myStorage->isEntityFixed(anOtherEntID, true)) { @@ -383,35 +383,3 @@ void SketchSolver_ConstraintRigid::fixArc(const Slvs_Entity& theArc) } } } - -bool SketchSolver_ConstraintRigid::isUsedInEqual( - const Slvs_Entity& theEntity, Slvs_Constraint& theEqual) const -{ - // Check the entity is used in Equal constraint - std::list anEqualConstr = myStorage->getConstraintsByType(SLVS_C_EQUAL_LENGTH_LINES); - std::list anAddList = myStorage->getConstraintsByType(SLVS_C_EQUAL_LINE_ARC_LEN); - anEqualConstr.insert(anEqualConstr.end(), anAddList.begin(), anAddList.end()); - anAddList = myStorage->getConstraintsByType(SLVS_C_EQUAL_RADIUS); - anEqualConstr.insert(anEqualConstr.end(), anAddList.begin(), anAddList.end()); - - std::list::const_iterator anEqIter = anEqualConstr.begin(); - for (; anEqIter != anEqualConstr.end(); anEqIter++) - if (anEqIter->entityA == theEntity.h || anEqIter->entityB == theEntity.h) { - theEqual = *anEqIter; - return true; - } - return false; -} - -bool SketchSolver_ConstraintRigid::isAxisParallel(const Slvs_Entity& theEntity) const -{ - std::list aConstr = myStorage->getConstraintsByType(SLVS_C_HORIZONTAL); - std::list aVert = myStorage->getConstraintsByType(SLVS_C_VERTICAL); - aConstr.insert(aConstr.end(), aVert.begin(), aVert.end()); - - std::list::const_iterator anIter = aConstr.begin(); - for (; anIter != aConstr.end(); anIter++) - if (anIter->entityA == theEntity.h) - return true; - return false; -} diff --git a/src/SketchSolver/SketchSolver_ConstraintRigid.h b/src/SketchSolver/SketchSolver_ConstraintRigid.h index 78b47fedd..22f4c6dd5 100644 --- a/src/SketchSolver/SketchSolver_ConstraintRigid.h +++ b/src/SketchSolver/SketchSolver_ConstraintRigid.h @@ -64,15 +64,6 @@ private: /// There will be fixed start and end points and the radius of the arc. void fixArc(const Slvs_Entity& theArc); - /// \brief Verifies the entity is used in any equal constraint - /// \param[in] theEntity entity to be found - /// \param[out] theEqual constraint, which uses the entity - /// \return \c true, if the Equal constrait is found - bool isUsedInEqual(const Slvs_Entity& theEntity, Slvs_Constraint& theEqual) const; - - /// \brief Check the entity is horizontal of vertical - bool isAxisParallel(const Slvs_Entity& theEntity) const; - protected: FeaturePtr myBaseFeature; ///< fixed feature (when it is set, myBaseConstraint should be NULL) }; diff --git a/src/SketchSolver/SketchSolver_Group.cpp b/src/SketchSolver/SketchSolver_Group.cpp index 11f334f51..3ee780ee0 100644 --- a/src/SketchSolver/SketchSolver_Group.cpp +++ b/src/SketchSolver/SketchSolver_Group.cpp @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include @@ -269,15 +271,29 @@ bool SketchSolver_Group::updateFeature(std::shared_ptr the if (aConstraints.empty()) return false; std::set::iterator aCIter = aConstraints.begin(); + std::set aPostponed; // postponed constraints Multi-Rotation and Multi-Translation for (; aCIter != aConstraints.end(); aCIter++) { ConstraintConstraintMap::iterator aSolConIter = myConstraints.find(*aCIter); if (aSolConIter == myConstraints.end() || !aSolConIter->first->data() || !aSolConIter->first->data()->isValid()) continue; myFeatureStorage->changeFeature(theFeature, aSolConIter->first); + + if (aSolConIter->first->getKind() == SketchPlugin_MultiRotation::ID() || + aSolConIter->first->getKind() == SketchPlugin_MultiTranslation::ID()) { + aPostponed.insert(aSolConIter->second); + continue; + } aSolConIter->second->addFeature(theFeature); aSolConIter->second->update(); } + + // Update postponed constraints + std::set::iterator aSCIter = aPostponed.begin(); + for (; aSCIter != aPostponed.end(); ++aSCIter) { + (*aSCIter)->addFeature(theFeature); + (*aSCIter)->update(); + } return true; } @@ -426,12 +442,20 @@ bool SketchSolver_Group::resolveConstraints() else { // To avoid overconstraint situation, we will remove temporary constraints one-by-one // and try to find the case without overconstraint + bool isLastChance = false; int aNbTemp = myStorage->numberTemporary(); while (true) { aResult = myConstrSolver.solve(); - if (aResult == SLVS_RESULT_OKAY || aNbTemp <= 0) + if (aResult == SLVS_RESULT_OKAY || isLastChance) break; - aNbTemp = myStorage->deleteTemporaryConstraint(); + if (aNbTemp <= 0) { + // try to update parameters and resolve once again + ConstraintConstraintMap::iterator aConstrIt = myConstraints.begin(); + for (; aConstrIt != myConstraints.end(); ++aConstrIt) + aConstrIt->second->update(); + isLastChance = true; + } else + aNbTemp = myStorage->deleteTemporaryConstraint(); myStorage->initializeSolver(myConstrSolver); } } diff --git a/src/SketchSolver/SketchSolver_Storage.cpp b/src/SketchSolver/SketchSolver_Storage.cpp index dade8f43f..2f2700fd2 100644 --- a/src/SketchSolver/SketchSolver_Storage.cpp +++ b/src/SketchSolver/SketchSolver_Storage.cpp @@ -6,6 +6,8 @@ #include +#include +#include #include /** \brief Search the entity/parameter with specified ID in the list of elements @@ -753,6 +755,281 @@ bool SketchSolver_Storage::isCoincident( } +std::vector SketchSolver_Storage::fixEntity(const Slvs_hEntity& theEntity) +{ + std::vector aNewConstraints; + + int aPos = Search(theEntity, myEntities); + if (aPos >= 0 && aPos < (int)myEntities.size()) { + switch (myEntities[aPos].type) { + case SLVS_E_POINT_IN_2D: + case SLVS_E_POINT_IN_3D: + fixPoint(myEntities[aPos], aNewConstraints); + break; + case SLVS_E_LINE_SEGMENT: + fixLine(myEntities[aPos], aNewConstraints); + break; + case SLVS_E_CIRCLE: + fixCircle(myEntities[aPos], aNewConstraints); + break; + case SLVS_E_ARC_OF_CIRCLE: + fixArc(myEntities[aPos], aNewConstraints); + break; + default: + break; + } + } + + return aNewConstraints; +} + +void SketchSolver_Storage::fixPoint(const Slvs_Entity& thePoint, + std::vector& theCreated) +{ + Slvs_Constraint aConstraint; + Slvs_hConstraint aConstrID = SLVS_E_UNKNOWN; + bool isFixed = isPointFixed(thePoint.h, aConstrID, true); + bool isForceUpdate = (isFixed && isTemporary(aConstrID)); + if (!isForceUpdate) { // create new constraint + if (isFixed) return; + aConstraint = Slvs_MakeConstraint(SLVS_E_UNKNOWN, thePoint.group, SLVS_C_WHERE_DRAGGED, thePoint.wrkpl, + 0.0, thePoint.h, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN); + aConstraint.h = addConstraint(aConstraint); + theCreated.push_back(aConstraint.h); + } else { // update already existent constraint + if (!isFixed || aConstrID == SLVS_E_UNKNOWN) + return; + int aPos = Search(aConstrID, myConstraints); + if (aPos >= 0 && aPos < (int)myConstraints.size()) + myConstraints[aPos].ptA = thePoint.h; + } +} + +void SketchSolver_Storage::fixLine(const Slvs_Entity& theLine, + std::vector& theCreated) +{ + Slvs_Entity aPoint[2] = { + getEntity(theLine.point[0]), + getEntity(theLine.point[1]) + }; + + Slvs_Constraint anEqual; + if (isAxisParallel(theLine.h)) { + // Fix one point and a line length + Slvs_hConstraint aFixed; + if (!isPointFixed(theLine.point[0], aFixed, true) && + !isPointFixed(theLine.point[1], aFixed, true)) + fixPoint(aPoint[0], theCreated); + if (!isUsedInEqual(theLine.h, anEqual)) { + // Check the distance is not set yet + std::vector::const_iterator aDistIt = myConstraints.begin(); + for (; aDistIt != myConstraints.end(); ++aDistIt) + if ((aDistIt->type == SLVS_C_PT_PT_DISTANCE) && + ((aDistIt->ptA == theLine.point[0] && aDistIt->ptB == theLine.point[1]) || + (aDistIt->ptA == theLine.point[1] && aDistIt->ptB == theLine.point[0]))) + return; + // Calculate distance between points on the line + double aCoords[4]; + for (int i = 0; i < 2; i++) + for (int j = 0; j < 2; j++) { + Slvs_Param aParam = getParameter(aPoint[i].param[j]); + aCoords[2*i+j] = aParam.val; + } + + double aLength = sqrt((aCoords[2] - aCoords[0]) * (aCoords[2] - aCoords[0]) + + (aCoords[3] - aCoords[1]) * (aCoords[3] - aCoords[1])); + // fix line length + Slvs_Constraint aDistance = Slvs_MakeConstraint(SLVS_E_UNKNOWN, theLine.group, + SLVS_C_PT_PT_DISTANCE, theLine.wrkpl, aLength, + theLine.point[0], theLine.point[1], SLVS_E_UNKNOWN, SLVS_E_UNKNOWN); + aDistance.h = addConstraint(aDistance); + theCreated.push_back(aDistance.h); + } + return; + } + else if (isUsedInEqual(theLine.h, anEqual)) { + // Check another entity of Equal is already fixed + Slvs_hEntity anOtherEntID = anEqual.entityA == theLine.h ? anEqual.entityB : anEqual.entityA; + if (isEntityFixed(anOtherEntID, true)) { + // Fix start point of the line (if end point is not fixed yet) ... + Slvs_hConstraint anEndFixedID = SLVS_E_UNKNOWN; + bool isFixed = isPointFixed(theLine.point[1], anEndFixedID, true); + if (isFixed == SLVS_E_UNKNOWN) + fixPoint(aPoint[0], theCreated); + // ... and create fixed point lying on this line + Slvs_hEntity aPointToCopy = anEndFixedID == SLVS_E_UNKNOWN ? theLine.point[1] : theLine.point[0]; + // Firstly, search already fixed point on line + bool isPonLineFixed = false; + Slvs_hEntity aFixedPoint; + std::vector::const_iterator aPLIter = myConstraints.begin(); + for (; aPLIter != myConstraints.end() && !isPonLineFixed; ++aPLIter) + if (aPLIter->type == SLVS_C_PT_ON_LINE && aPLIter->entityA == theLine.h) { + isPonLineFixed = isPointFixed(aPLIter->ptA, anEndFixedID); + aFixedPoint = aPLIter->ptA; + } + + if (isPonLineFixed) { // update existent constraint + copyEntity(aPointToCopy, aFixedPoint); + } else { // create new constraint + Slvs_hEntity aCopied = copyEntity(aPointToCopy); + Slvs_Constraint aPonLine = Slvs_MakeConstraint(SLVS_E_UNKNOWN, theLine.group, SLVS_C_PT_ON_LINE, + theLine.wrkpl, 0.0, aCopied, SLVS_E_UNKNOWN, theLine.h, SLVS_E_UNKNOWN); + aPonLine.h = addConstraint(aPonLine); + theCreated.push_back(aPonLine.h); + fixPoint(getEntity(aCopied), theCreated); + } + return; + } + } + + // Fix both points + for (int i = 0; i < 2; i++) + fixPoint(aPoint[i], theCreated); +} + +void SketchSolver_Storage::fixCircle(const Slvs_Entity& theCircle, + std::vector& theCreated) +{ + bool isFixRadius = true; + // Verify the arc is under Equal constraint + Slvs_Constraint anEqual; + if (isUsedInEqual(theCircle.h, anEqual)) { + // Check another entity of Equal is already fixed + Slvs_hEntity anOtherEntID = anEqual.entityA == theCircle.h ? anEqual.entityB : anEqual.entityA; + if (isEntityFixed(anOtherEntID, true)) + isFixRadius = false; + } + + fixPoint(getEntity(theCircle.point[0]), theCreated); + + if (isFixRadius) { + // Search the radius is already fixed + std::vector::const_iterator aDiamIter = myConstraints.begin(); + for (; aDiamIter != myConstraints.end(); ++aDiamIter) + if (aDiamIter->type == SLVS_C_DIAMETER && aDiamIter->entityA == theCircle.h) + return; + + // Fix radius of a circle + const Slvs_Entity& aRadEnt = getEntity(theCircle.distance); + double aRadius = getParameter(aRadEnt.param[0]).val; + Slvs_Constraint aFixedR = Slvs_MakeConstraint(SLVS_E_UNKNOWN, theCircle.group, SLVS_C_DIAMETER, + theCircle.wrkpl, aRadius * 2.0, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, theCircle.h, SLVS_E_UNKNOWN); + aFixedR.h = addConstraint(aFixedR); + theCreated.push_back(aFixedR.h); + } +} + +void SketchSolver_Storage::fixArc(const Slvs_Entity& theArc, + std::vector& theCreated) +{ + Slvs_Entity aPoint[3] = { + getEntity(theArc.point[0]), + getEntity(theArc.point[1]), + getEntity(theArc.point[2]) + }; + + bool isFixRadius = true; + std::list aPointsToFix; + aPointsToFix.push_back(aPoint[1]); + aPointsToFix.push_back(aPoint[2]); + + // Verify the arc is under Equal constraint + Slvs_Constraint anEqual; + if (isUsedInEqual(theArc.h, anEqual)) { + // Check another entity of Equal is already fixed + Slvs_hEntity anOtherEntID = anEqual.entityA == theArc.h ? anEqual.entityB : anEqual.entityA; + if (isEntityFixed(anOtherEntID, true)) { + isFixRadius = false; + Slvs_Entity anOtherEntity = getEntity(anOtherEntID); + if (anOtherEntity.type == SLVS_E_LINE_SEGMENT) { + aPointsToFix.pop_back(); + aPointsToFix.push_back(aPoint[0]); + } + } + } + + Slvs_hConstraint aConstrID; + int aNbPointsToFix = 2; // number of fixed points for the arc + if (isPointFixed(theArc.point[0], aConstrID, true)) + aNbPointsToFix--; + + double anArcPoints[3][2]; + for (int i = 0; i < 3; i++) { + const Slvs_Entity& aPointOnArc = getEntity(theArc.point[i]); + for (int j = 0; j < 2; j++) + anArcPoints[i][j] = getParameter(aPointOnArc.param[j]).val; + } + + // Radius of the arc + std::shared_ptr aCenter(new GeomAPI_Pnt2d(anArcPoints[0][0], anArcPoints[0][1])); + std::shared_ptr aStart(new GeomAPI_Pnt2d(anArcPoints[1][0], anArcPoints[1][1])); + double aRadius = aCenter->distance(aStart); + + // Update end point of the arc to be on a curve + std::shared_ptr anEnd(new GeomAPI_Pnt2d(anArcPoints[2][0], anArcPoints[2][1])); + double aDistance = anEnd->distance(aCenter); + std::shared_ptr aDir = anEnd->xy()->decreased(aCenter->xy()); + if (aDistance < tolerance) + aDir = aStart->xy()->decreased(aCenter->xy())->multiplied(-1.0); + else + aDir = aDir->multiplied(aRadius / aDistance); + double xy[2] = {aCenter->x() + aDir->x(), aCenter->y() + aDir->y()}; + const Slvs_Entity& aEndPoint = getEntity(theArc.point[2]); + for (int i = 0; i < 2; i++) { + Slvs_Param aParam = getParameter(aEndPoint.param[i]); + aParam.val = xy[i]; + updateParameter(aParam); + } + + std::list::iterator aPtIt = aPointsToFix.begin(); + for (; aNbPointsToFix > 0; aPtIt++, aNbPointsToFix--) + fixPoint(*aPtIt, theCreated); + + if (isFixRadius) { + // Fix radius of the arc + bool isExists = false; + std::vector::iterator anIt = myConstraints.begin(); + for (; anIt != myConstraints.end() && !isExists; ++anIt) + if (anIt->type == SLVS_C_DIAMETER && anIt->entityA == theArc.h) + isExists = true; + if (!isExists) { + Slvs_Constraint aFixedR = Slvs_MakeConstraint(SLVS_E_UNKNOWN, theArc.group, SLVS_C_DIAMETER, + theArc.wrkpl, aRadius * 2.0, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, theArc.h, SLVS_E_UNKNOWN); + aFixedR.h = addConstraint(aFixedR); + theCreated.push_back(aFixedR.h); + } + } +} + + +bool SketchSolver_Storage::isAxisParallel(const Slvs_hEntity& theEntity) const +{ + std::vector::const_iterator anIter = myConstraints.begin(); + for (; anIter != myConstraints.end(); anIter++) + if ((anIter->type == SLVS_C_HORIZONTAL || anIter->type == SLVS_C_VERTICAL) && + anIter->entityA == theEntity) + return true; + return false; +} + +bool SketchSolver_Storage::isUsedInEqual( + const Slvs_hEntity& theEntity, Slvs_Constraint& theEqual) const +{ + // Check the entity is used in Equal constraint + std::vector::const_iterator anEqIter = myConstraints.begin(); + for (; anEqIter != myConstraints.end(); anEqIter++) + if ((anEqIter->type == SLVS_C_EQUAL_LENGTH_LINES || + anEqIter->type == SLVS_C_EQUAL_LINE_ARC_LEN || + anEqIter->type == SLVS_C_EQUAL_RADIUS) && + (anEqIter->entityA == theEntity || anEqIter->entityB == theEntity)) { + theEqual = *anEqIter; + return true; + } + return false; +} + + + // ======================================================== diff --git a/src/SketchSolver/SketchSolver_Storage.h b/src/SketchSolver/SketchSolver_Storage.h index 3f965fca2..346440996 100644 --- a/src/SketchSolver/SketchSolver_Storage.h +++ b/src/SketchSolver/SketchSolver_Storage.h @@ -150,6 +150,38 @@ public: /// \brief Check two points are coincident bool isCoincident(const Slvs_hEntity& thePoint1, const Slvs_hEntity& thePoint2) const; + /// \brief Check the entity is horizontal of vertical + bool isAxisParallel(const Slvs_hEntity& theEntity) const; + + /// \brief Verifies the entity is used in any equal constraint + /// \param[in] theEntity entity to be found + /// \param[out] theEqual constraint, which uses the entity + /// \return \c true, if the Equal constrait is found + bool isUsedInEqual(const Slvs_hEntity& theEntity, Slvs_Constraint& theEqual) const; + + /// \brief Fixes specified entity + /// \param theEntity ID of the entity to be fixed + /// \return List of created constraints + std::vector fixEntity(const Slvs_hEntity& theEntity); + +private: + /// \brief Fixes specified point + /// \param [in] thePoint point to be fixed + /// \param [out] theCreated list of the Fixed constraints created + void fixPoint(const Slvs_Entity& thePoint, std::vector& theCreated); + /// \brief Fixes specified line + /// \param [in] theLine line to be fixed + /// \param [out] theCreated list of the Fixed constraints created + void fixLine(const Slvs_Entity& theLine, std::vector& theCreated); + /// \brief Fixes specified circle + /// \param [in] theCircle circle to be fixed + /// \param [out] theCreated list of the Fixed constraints created + void fixCircle(const Slvs_Entity& theCircle, std::vector& theCreated); + /// \brief Fixes specified arc + /// \param [in] theArc arc to be fixed + /// \param [out] theCreated list of the Fixed constraints created + void fixArc(const Slvs_Entity& theArc, std::vector& theCreated); + private: Slvs_hParam myParamMaxID; ///< current parameter index (may differs with the number of parameters) std::vector myParameters; ///< list of parameters used in the current group of constraints (sorted by the identifier) -- 2.39.2