From 02690400946d61ec39d168542fd1f13c5e3a9fb2 Mon Sep 17 00:00:00 2001 From: azv Date: Fri, 24 Mar 2017 15:24:38 +0300 Subject: [PATCH] Issue #2024: Redesign of circle and arc of circle Implement validators for MacroCircle feature --- src/SketchPlugin/SketchPlugin_Plugin.cpp | 6 + src/SketchPlugin/SketchPlugin_Tools.cpp | 67 ++++- src/SketchPlugin/SketchPlugin_Tools.h | 7 + src/SketchPlugin/SketchPlugin_Validators.cpp | 278 ++++++++++++++++++- src/SketchPlugin/SketchPlugin_Validators.h | 66 +++++ src/SketchPlugin/plugin-Sketch.xml | 15 +- 6 files changed, 424 insertions(+), 15 deletions(-) diff --git a/src/SketchPlugin/SketchPlugin_Plugin.cpp b/src/SketchPlugin/SketchPlugin_Plugin.cpp index 586176f31..72c04cd8f 100644 --- a/src/SketchPlugin/SketchPlugin_Plugin.cpp +++ b/src/SketchPlugin/SketchPlugin_Plugin.cpp @@ -88,6 +88,12 @@ SketchPlugin_Plugin::SketchPlugin_Plugin() new SketchPlugin_IntersectionValidator); aFactory->registerValidator("SketchPlugin_ProjectionValidator", new SketchPlugin_ProjectionValidator); + aFactory->registerValidator("SketchPlugin_DifferentReference", + new SketchPlugin_DifferentReferenceValidator); + aFactory->registerValidator("SketchPlugin_CirclePassedPointValidator", + new SketchPlugin_CirclePassedPointValidator); + aFactory->registerValidator("SketchPlugin_ThirdPointValidator", + new SketchPlugin_ThirdPointValidator); // register this plugin ModelAPI_Session::get()->registerPlugin(this); diff --git a/src/SketchPlugin/SketchPlugin_Tools.cpp b/src/SketchPlugin/SketchPlugin_Tools.cpp index 9a4d82f84..5758a6b7d 100644 --- a/src/SketchPlugin/SketchPlugin_Tools.cpp +++ b/src/SketchPlugin/SketchPlugin_Tools.cpp @@ -75,6 +75,19 @@ std::shared_ptr getCoincidencePoint(const FeaturePtr theStartCoin return aPnt; } +std::set findCoincidentConstraints(const FeaturePtr& theFeature) +{ + std::set aCoincident; + const std::set& aRefsList = theFeature->data()->refsToMe(); + std::set::const_iterator aIt; + for (aIt = aRefsList.cbegin(); aIt != aRefsList.cend(); ++aIt) { + FeaturePtr aConstrFeature = std::dynamic_pointer_cast((*aIt)->owner()); + if (aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) + aCoincident.insert(aConstrFeature); + } + return aCoincident; +} + void findCoincidences(const FeaturePtr theStartCoin, const std::string& theAttr, std::set& theList) @@ -90,22 +103,54 @@ void findCoincidences(const FeaturePtr theStartCoin, return; } theList.insert(aObj); - const std::set& aRefsList = aObj->data()->refsToMe(); - std::set::const_iterator aIt; - for(aIt = aRefsList.cbegin(); aIt != aRefsList.cend(); ++aIt) { - std::shared_ptr aAttr = (*aIt); - FeaturePtr aConstrFeature = std::dynamic_pointer_cast(aAttr->owner()); - if(aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) { - std::shared_ptr aPnt = getCoincidencePoint(aConstrFeature); - if(aPnt.get() && aOrig->isEqual(aPnt)) { - findCoincidences(aConstrFeature, SketchPlugin_ConstraintCoincidence::ENTITY_A(), theList); - findCoincidences(aConstrFeature, SketchPlugin_ConstraintCoincidence::ENTITY_B(), theList); - } + std::set aCoincidences = findCoincidentConstraints(aObj); + std::set::const_iterator aCIt = aCoincidences.begin(); + for (; aCIt != aCoincidences.end(); ++aCIt) { + FeaturePtr aConstrFeature = *aCIt; + std::shared_ptr aPnt = getCoincidencePoint(aConstrFeature); + if(aPnt.get() && aOrig->isEqual(aPnt)) { + findCoincidences(aConstrFeature, SketchPlugin_ConstraintCoincidence::ENTITY_A(), theList); + findCoincidences(aConstrFeature, SketchPlugin_ConstraintCoincidence::ENTITY_B(), theList); } } } } +std::set findFeaturesCoincidentToPoint(const AttributePoint2DPtr& thePoint) +{ + std::set aCoincidentFeatures; + + FeaturePtr anOwner = ModelAPI_Feature::feature(thePoint->owner()); + aCoincidentFeatures.insert(anOwner); + + std::set aCoincidences = findCoincidentConstraints(anOwner); + std::set::const_iterator aCIt = aCoincidences.begin(); + for (; aCIt != aCoincidences.end(); ++aCIt) { + bool isPointUsedInCoincidence = false; + AttributeRefAttrPtr anOtherCoincidentAttr; + for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) { + AttributeRefAttrPtr aRefAttr = (*aCIt)->refattr(SketchPlugin_Constraint::ATTRIBUTE(i)); + if (!aRefAttr) + continue; + if (!aRefAttr->isObject() && aRefAttr->attr() == thePoint) + isPointUsedInCoincidence = true; + else + anOtherCoincidentAttr = aRefAttr; + } + + if (isPointUsedInCoincidence) { + ObjectPtr anObj; + if (anOtherCoincidentAttr->isObject()) + anObj = anOtherCoincidentAttr->object(); + else + anObj = anOtherCoincidentAttr->attr()->owner(); + aCoincidentFeatures.insert(ModelAPI_Feature::feature(anObj)); + } + } + + return aCoincidentFeatures; +} + void resetAttribute(SketchPlugin_Feature* theFeature, const std::string& theId) { diff --git a/src/SketchPlugin/SketchPlugin_Tools.h b/src/SketchPlugin/SketchPlugin_Tools.h index 6d588b5d3..622b63d68 100644 --- a/src/SketchPlugin/SketchPlugin_Tools.h +++ b/src/SketchPlugin/SketchPlugin_Tools.h @@ -11,6 +11,7 @@ #include #include +#include class SketchPlugin_Feature; @@ -23,6 +24,9 @@ void clearExpressions(FeaturePtr theFeature); /// \param[in] theStartCoin coincidence feature std::shared_ptr getCoincidencePoint(const FeaturePtr theStartCoin); +/// Find all Coincident constraints referred to the feature or its attribute +std::set findCoincidentConstraints(const FeaturePtr& theFeature); + /// Finds lines coincident at point /// \param[in] theStartCoin coincidence feature /// \param[in] theAttr attribute name @@ -31,6 +35,9 @@ void findCoincidences(const FeaturePtr theStartCoin, const std::string& theAttr, std::set& theList); +/// Find all features the point is coincident to. +std::set findFeaturesCoincidentToPoint(const AttributePoint2DPtr& thePoint); + void resetAttribute(SketchPlugin_Feature* theFeature, const std::string& theId); /// Creates coincidence or tangent constraint. diff --git a/src/SketchPlugin/SketchPlugin_Validators.cpp b/src/SketchPlugin/SketchPlugin_Validators.cpp index d42c78360..f224f99eb 100755 --- a/src/SketchPlugin/SketchPlugin_Validators.cpp +++ b/src/SketchPlugin/SketchPlugin_Validators.cpp @@ -10,10 +10,11 @@ #include "SketchPlugin_Circle.h" #include "SketchPlugin_ConstraintCoincidence.h" #include "SketchPlugin_ConstraintDistance.h" -#include "SketchPlugin_Fillet.h" #include "SketchPlugin_ConstraintRigid.h" #include "SketchPlugin_ConstraintTangent.h" +#include "SketchPlugin_Fillet.h" #include "SketchPlugin_Line.h" +#include "SketchPlugin_MacroCircle.h" #include "SketchPlugin_Point.h" #include "SketchPlugin_Sketch.h" #include "SketchPlugin_Tools.h" @@ -38,6 +39,7 @@ #include #include +#include #include #include #include @@ -939,3 +941,277 @@ bool SketchPlugin_ProjectionValidator::isValid(const AttributePtr& theAttribute, theError = "Error: Selected object is not line, circle or arc."; return false; } + + +static std::set common(const std::set& theSet1, + const std::set& theSet2) +{ + std::set aCommon; + if (theSet1.empty() || theSet2.empty()) + return aCommon; + + std::set::const_iterator anIt2 = theSet2.begin(); + for (; anIt2 != theSet2.end(); ++anIt2) + if (theSet1.find(*anIt2) != theSet1.end()) + aCommon.insert(*anIt2); + return aCommon; +} + +bool SketchPlugin_DifferentReferenceValidator::isValid( + const AttributePtr& theAttribute, + const std::list& theArguments, + Events_InfoMessage& theError) const +{ + FeaturePtr anOwner = ModelAPI_Feature::feature(theAttribute->owner()); + + int aNbFeaturesReferred = 0; + int aNbAttributesReferred = 0; + std::set aCommonReferredFeatures; + + // find all features referred by attributes listed in theArguments + std::list::const_iterator anArgIt = theArguments.begin(); + for (; anArgIt != theArguments.end(); ++anArgIt) { + AttributeRefAttrPtr aRefAttr = anOwner->refattr(*anArgIt); + if (!aRefAttr) + continue; + + std::set aCoincidentFeatures; + if (aRefAttr->isObject()) { + FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttr->object()); + if (aFeature) { + aCoincidentFeatures.insert(aFeature); + aNbFeaturesReferred += 1; + } + } else { + AttributePoint2DPtr aPoint = + std::dynamic_pointer_cast(aRefAttr->attr()); + if (aPoint) { + aCoincidentFeatures = SketchPlugin_Tools::findFeaturesCoincidentToPoint(aPoint); + aNbAttributesReferred += 1; + } + } + + if (aCommonReferredFeatures.empty()) + aCommonReferredFeatures = aCoincidentFeatures; + else + aCommonReferredFeatures = common(aCommonReferredFeatures, aCoincidentFeatures); + + if (aCommonReferredFeatures.empty()) + return true; + } + + bool isOk = aNbFeaturesReferred < 1; + if (aNbFeaturesReferred == 1) { + if (aCommonReferredFeatures.size() == 1) { + FeaturePtr aFeature = *aCommonReferredFeatures.begin(); + isOk = aNbAttributesReferred <= 1 || + aFeature->getKind() == SketchPlugin_Circle::ID() || + aFeature->getKind() == SketchPlugin_Arc::ID(); + } else + isOk = false; + } + + if (!isOk) + theError = "Attributes are referred to the same feature"; + return isOk; +} + +bool SketchPlugin_CirclePassedPointValidator::isValid( + const AttributePtr& theAttribute, + const std::list&, + Events_InfoMessage& theError) const +{ + static const std::string aErrorMessage( + "Passed point refers to the same feature as a center point"); + + FeaturePtr anOwner = ModelAPI_Feature::feature(theAttribute->owner()); + + AttributeRefAttrPtr aCenterRef = + anOwner->refattr(SketchPlugin_MacroCircle::CENTER_POINT_REF_ID()); + AttributeRefAttrPtr aPassedRef = + anOwner->refattr(SketchPlugin_MacroCircle::PASSED_POINT_REF_ID()); + + if (!aPassedRef->isObject()) + return true; + + FeaturePtr aPassedFeature = ModelAPI_Feature::feature(aPassedRef->object()); + if (!aPassedFeature) + return true; + + if (aCenterRef->isObject()) { + FeaturePtr aCenterFeature = ModelAPI_Feature::feature(aCenterRef->object()); + if (aCenterFeature == aPassedFeature) { + theError = aErrorMessage; + return false; + } + } else { + AttributePoint2DPtr aCenterPoint = + std::dynamic_pointer_cast(aCenterRef->attr()); + if (aCenterPoint) { + std::set aCoincidentFeatures = + SketchPlugin_Tools::findFeaturesCoincidentToPoint(aCenterPoint); + // check one of coincident features is a feature referred by passed point + std::set::const_iterator anIt = aCoincidentFeatures.begin(); + for(; anIt != aCoincidentFeatures.end(); ++anIt) + if (*anIt == aPassedFeature) { + theError = aErrorMessage; + return false; + } + } + } + return true; +} + +bool SketchPlugin_ThirdPointValidator::isValid( + const AttributePtr& theAttribute, + const std::list&, + Events_InfoMessage& theError) const +{ + FeaturePtr anOwner = ModelAPI_Feature::feature(theAttribute->owner()); + return arePointsNotOnLine(anOwner, theError) && arePointsNotSeparated(anOwner, theError); +} + +static std::shared_ptr toPoint(const FeaturePtr& theMacroCircle, + const std::string& thePointAttrName, + const std::string& theRefPointAttrName) +{ + AttributePoint2DPtr aPointAttr = std::dynamic_pointer_cast( + theMacroCircle->attribute(thePointAttrName)); + AttributeRefAttrPtr aRefAttr = theMacroCircle->refattr(theRefPointAttrName); + + std::shared_ptr aPoint = aPointAttr->pnt(); + if (aRefAttr) { + if (aRefAttr->isObject()) { + // project a point onto selected feature + std::shared_ptr aFeature = + std::dynamic_pointer_cast( + ModelAPI_Feature::feature(aRefAttr->object())); + if (aFeature) { + SketchPlugin_Sketch* aSketch = aFeature->sketch(); + std::shared_ptr anEdge = + std::dynamic_pointer_cast(aFeature->lastResult()->shape()); + if (anEdge) { + std::shared_ptr aPoint3D = aSketch->to3D(aPoint->x(), aPoint->y()); + if (anEdge->isLine()) + aPoint3D = anEdge->line()->project(aPoint3D); + else if (anEdge->isCircle()) + aPoint3D = anEdge->circle()->project(aPoint3D); + aPoint = aSketch->to2D(aPoint3D); + } + } + } else { + AttributePoint2DPtr anOtherPoint = + std::dynamic_pointer_cast(aRefAttr->attr()); + if (anOtherPoint) + aPoint = anOtherPoint->pnt(); // the reference point is much more precise, use it + } + } + + return aPoint; +} + +static bool isPointsOnLine(const std::shared_ptr& thePoint1, + const std::shared_ptr& thePoint2, + const std::shared_ptr& thePoint3) +{ + static const double aTolerance = 1.e-7; + if (thePoint1->distance(thePoint2) < aTolerance || + thePoint1->distance(thePoint3) < aTolerance) + return true; + + std::shared_ptr aDirP1P2(new GeomAPI_Dir2d(thePoint2->x() - thePoint1->x(), + thePoint2->y() - thePoint1->y())); + std::shared_ptr aDirP1P3(new GeomAPI_Dir2d(thePoint3->x() - thePoint1->x(), + thePoint3->y() - thePoint1->y())); + return fabs(aDirP1P2->cross(aDirP1P3)) < aTolerance; +} + +static bool isOnSameSide(const std::shared_ptr& theLine, + const std::shared_ptr& thePoint1, + const std::shared_ptr& thePoint2) +{ + static const double aTolerance = 1.e-7; + std::shared_ptr aLineDir = theLine->direction(); + std::shared_ptr aLineLoc = theLine->location()->xyz(); + std::shared_ptr aDirP1L(new GeomAPI_Dir(thePoint1->xyz()->decreased(aLineLoc))); + std::shared_ptr aDirP2L(new GeomAPI_Dir(thePoint2->xyz()->decreased(aLineLoc))); + return aLineDir->cross(aDirP1L)->dot(aLineDir->cross(aDirP2L)) > -aTolerance; +} + +static bool isOnSameSide(const std::shared_ptr& theCircle, + const std::shared_ptr& thePoint1, + const std::shared_ptr& thePoint2) +{ + static const double aTolerance = 1.e-7; + std::shared_ptr aCenter = theCircle->center(); + double aDistP1C = thePoint1->distance(aCenter); + double aDistP2C = thePoint2->distance(aCenter); + return (aDistP1C - theCircle->radius()) * (aDistP2C - theCircle->radius()) > -aTolerance; +} + +bool SketchPlugin_ThirdPointValidator::arePointsNotOnLine( + const FeaturePtr& theMacroCircle, + Events_InfoMessage& theError) const +{ + static const std::string aErrorPointsOnLine( + "Selected points are on the same line"); + + std::shared_ptr aFirstPoint = toPoint(theMacroCircle, + SketchPlugin_MacroCircle::FIRST_POINT_ID(), + SketchPlugin_MacroCircle::FIRST_POINT_REF_ID()); + std::shared_ptr aSecondPoint = toPoint(theMacroCircle, + SketchPlugin_MacroCircle::SECOND_POINT_ID(), + SketchPlugin_MacroCircle::SECOND_POINT_REF_ID()); + std::shared_ptr aThirdPoint = toPoint(theMacroCircle, + SketchPlugin_MacroCircle::THIRD_POINT_ID(), + SketchPlugin_MacroCircle::THIRD_POINT_REF_ID()); + + if (isPointsOnLine(aFirstPoint, aSecondPoint, aThirdPoint)) { + theError = aErrorPointsOnLine; + return false; + } + return true; +} + +bool SketchPlugin_ThirdPointValidator::arePointsNotSeparated( + const FeaturePtr& theMacroCircle, + Events_InfoMessage& theError) const +{ + static const std::string aErrorPointsApart( + "Selected entity is lying between first two points"); + + AttributeRefAttrPtr aThirdPointRef = + theMacroCircle->refattr(SketchPlugin_MacroCircle::THIRD_POINT_REF_ID()); + FeaturePtr aRefByThird; + if (aThirdPointRef->isObject()) + aRefByThird = ModelAPI_Feature::feature(aThirdPointRef->object()); + if (!aRefByThird) + return true; + + std::shared_ptr aFirstPoint = toPoint(theMacroCircle, + SketchPlugin_MacroCircle::FIRST_POINT_ID(), + SketchPlugin_MacroCircle::FIRST_POINT_REF_ID()); + std::shared_ptr aSecondPoint = toPoint(theMacroCircle, + SketchPlugin_MacroCircle::SECOND_POINT_ID(), + SketchPlugin_MacroCircle::SECOND_POINT_REF_ID()); + + std::shared_ptr aThirdShape = + std::dynamic_pointer_cast(aRefByThird->lastResult()->shape()); + if (!aThirdShape) + return true; + + SketchPlugin_Sketch* aSketch = + std::dynamic_pointer_cast(theMacroCircle)->sketch(); + std::shared_ptr aFirstPnt3D = aSketch->to3D(aFirstPoint->x(), aFirstPoint->y()); + std::shared_ptr aSecondPnt3D = aSketch->to3D(aSecondPoint->x(), aSecondPoint->y()); + + bool isOk = true; + if (aThirdShape->isLine()) + isOk = isOnSameSide(aThirdShape->line(), aFirstPnt3D, aSecondPnt3D); + else if (aThirdShape->isCircle() || aThirdShape->isArc()) + isOk = isOnSameSide(aThirdShape->circle(), aFirstPnt3D, aSecondPnt3D); + + if (!isOk) + theError = aErrorPointsApart; + return isOk; +} diff --git a/src/SketchPlugin/SketchPlugin_Validators.h b/src/SketchPlugin/SketchPlugin_Validators.h index 70120d614..dc686af90 100644 --- a/src/SketchPlugin/SketchPlugin_Validators.h +++ b/src/SketchPlugin/SketchPlugin_Validators.h @@ -271,4 +271,70 @@ class SketchPlugin_ProjectionValidator : public ModelAPI_AttributeValidator Events_InfoMessage& theError) const; }; +/**\class SketchPlugin_DifferentReferenceValidator + * \ingroup Validators + * \brief Validator for attributes of a sketch feature. + * + * It checks that at least one of specified attributes + * refers to another feature in respect to each other. + */ +class SketchPlugin_DifferentReferenceValidator : public ModelAPI_AttributeValidator +{ + public: + //! returns true if attribute is valid + //! \param theAttribute the checked attribute + //! \param theArguments arguments of the attribute + //! \param theError error message + virtual bool isValid(const AttributePtr& theAttribute, + const std::list& theArguments, + Events_InfoMessage& theError) const; +}; + +/**\class SketchPlugin_CirclePassedPointValidator + * \ingroup Validators + * \brief Validator for passed point of MacroCircle feature. + * + * Checks that passed point does not refer to the feature, the center is coincident to. + */ +class SketchPlugin_CirclePassedPointValidator : public ModelAPI_AttributeValidator +{ + public: + //! returns true if attribute is valid + //! \param theAttribute the checked attribute + //! \param theArguments arguments of the attribute + //! \param theError error message + virtual bool isValid(const AttributePtr& theAttribute, + const std::list&, + Events_InfoMessage& theError) const; +}; + +/**\class SketchPlugin_ThirdPointValidator + * \ingroup Validators + * \brief Validator for the third point of MacroCircle feature. + * + * Checks that third point does not lie on a line passed through the first two points. + * Checks that third point does not refer to feature lying between the first two points. + */ +class SketchPlugin_ThirdPointValidator : public ModelAPI_AttributeValidator +{ + public: + //! returns true if attribute is valid + //! \param theAttribute the checked attribute + //! \param theArguments arguments of the attribute + //! \param theError error message + virtual bool isValid(const AttributePtr& theAttribute, + const std::list&, + Events_InfoMessage& theError) const; + +private: + //! returns true if three points have not been placed on the same line + bool arePointsNotOnLine(const FeaturePtr& theMacroCircle, + Events_InfoMessage& theError) const; + + //! returns true if the first two points have not been separated + //! by a feature referred by thrid point + bool arePointsNotSeparated(const FeaturePtr& theMacroCircle, + Events_InfoMessage& theError) const; +}; + #endif diff --git a/src/SketchPlugin/plugin-Sketch.xml b/src/SketchPlugin/plugin-Sketch.xml index 0667afb9b..c2f644b56 100644 --- a/src/SketchPlugin/plugin-Sketch.xml +++ b/src/SketchPlugin/plugin-Sketch.xml @@ -91,7 +91,10 @@ title="Passed point" tooltip="Passed point coordinates" accept_expressions="0" - enable_value="enable_by_preferences"/> + enable_value="enable_by_preferences"> + + + + enable_value="enable_by_preferences"> + + + enable_value="enable_by_preferences"> + + + +