From b49300c581e67523203dc497c967676d42fb0728 Mon Sep 17 00:00:00 2001 From: azv Date: Wed, 3 May 2017 08:25:22 +0300 Subject: [PATCH] Task 2.7. Horizontal and Vertical Distance constraint 1. Implemented SketchPlugin entities related to Horizontal and Vertical Distance constraints. 2. Add processing of new constraints in the solver. 3. Special Python commands for new constraints in SketchAPI 4. Unit tests --- src/SketchAPI/SketchAPI_Constraint.cpp | 10 + src/SketchAPI/SketchAPI_Rectangle.cpp | 9 + src/SketchAPI/SketchAPI_Rectangle.h | 3 + src/SketchAPI/SketchAPI_Sketch.cpp | 30 +++ src/SketchAPI/SketchAPI_Sketch.h | 14 ++ src/SketchPlugin/CMakeLists.txt | 7 + .../SketchPlugin_ConstraintDistance.cpp | 26 +++ .../SketchPlugin_ConstraintDistance.h | 5 +- ...tchPlugin_ConstraintDistanceHorizontal.cpp | 149 +++++++++++++ ...ketchPlugin_ConstraintDistanceHorizontal.h | 66 ++++++ ...ketchPlugin_ConstraintDistanceVertical.cpp | 149 +++++++++++++ .../SketchPlugin_ConstraintDistanceVertical.h | 66 ++++++ src/SketchPlugin/SketchPlugin_Plugin.cpp | 9 + .../Test/TestConstraintDistanceBehavior.py | 82 +++++++ .../Test/TestConstraintDistanceHorizontal.py | 208 ++++++++++++++++++ .../Test/TestConstraintDistanceVertical.py | 208 ++++++++++++++++++ src/SketchPlugin/icons/distance_h.png | Bin 0 -> 428 bytes src/SketchPlugin/icons/distance_v.png | Bin 0 -> 481 bytes src/SketchPlugin/plugin-Sketch.xml | 68 +++++- .../PlaneGCSSolver/PlaneGCSSolver_Defs.h | 2 + .../PlaneGCSSolver/PlaneGCSSolver_Tools.cpp | 41 +++- src/SketchSolver/SketchSolver_Constraint.cpp | 4 + .../SketchSolver_ConstraintDistance.cpp | 14 +- src/SketchSolver/SketchSolver_Error.h | 6 + .../SketcherPrs_LengthDimension.cpp | 6 +- 25 files changed, 1174 insertions(+), 8 deletions(-) create mode 100644 src/SketchPlugin/SketchPlugin_ConstraintDistanceHorizontal.cpp create mode 100644 src/SketchPlugin/SketchPlugin_ConstraintDistanceHorizontal.h create mode 100644 src/SketchPlugin/SketchPlugin_ConstraintDistanceVertical.cpp create mode 100644 src/SketchPlugin/SketchPlugin_ConstraintDistanceVertical.h create mode 100644 src/SketchPlugin/Test/TestConstraintDistanceBehavior.py create mode 100644 src/SketchPlugin/Test/TestConstraintDistanceHorizontal.py create mode 100644 src/SketchPlugin/Test/TestConstraintDistanceVertical.py create mode 100644 src/SketchPlugin/icons/distance_h.png create mode 100644 src/SketchPlugin/icons/distance_v.png diff --git a/src/SketchAPI/SketchAPI_Constraint.cpp b/src/SketchAPI/SketchAPI_Constraint.cpp index 2bf24e028..e7409f5c2 100644 --- a/src/SketchAPI/SketchAPI_Constraint.cpp +++ b/src/SketchAPI/SketchAPI_Constraint.cpp @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include #include #include @@ -69,6 +71,14 @@ static const std::string& constraintTypeToSetter(const std::string& theType) static const std::string DISTANCE_SETTER("setDistance"); return DISTANCE_SETTER; } + if (theType == SketchPlugin_ConstraintDistanceHorizontal::ID()) { + static const std::string DISTANCE_SETTER("setHorizontalDistance"); + return DISTANCE_SETTER; + } + if (theType == SketchPlugin_ConstraintDistanceVertical::ID()) { + static const std::string DISTANCE_SETTER("setVerticalDistance"); + return DISTANCE_SETTER; + } if (theType == SketchPlugin_ConstraintEqual::ID()) { static const std::string EQUAL_SETTER("setEqual"); return EQUAL_SETTER; diff --git a/src/SketchAPI/SketchAPI_Rectangle.cpp b/src/SketchAPI/SketchAPI_Rectangle.cpp index ce60335d0..7337d3b84 100644 --- a/src/SketchAPI/SketchAPI_Rectangle.cpp +++ b/src/SketchAPI/SketchAPI_Rectangle.cpp @@ -67,3 +67,12 @@ void SketchAPI_Rectangle::setByPoints( //-------------------------------------------------------------------------------------- +std::list > SketchAPI_Rectangle::lines() const +{ + std::list aFeatures; + std::list aList = linesList()->list(); + std::list::const_iterator anIt = aList.begin(); + for (; anIt != aList.end(); ++anIt) + aFeatures.push_back(ModelAPI_Feature::feature(*anIt)); + return SketchAPI_SketchEntity::wrap(aFeatures); +} diff --git a/src/SketchAPI/SketchAPI_Rectangle.h b/src/SketchAPI/SketchAPI_Rectangle.h index 098d92ff3..9853cb34f 100644 --- a/src/SketchAPI/SketchAPI_Rectangle.h +++ b/src/SketchAPI/SketchAPI_Rectangle.h @@ -52,6 +52,9 @@ public: SKETCHAPI_EXPORT void setByPoints(const std::shared_ptr & theStartPoint, const std::shared_ptr & theEndPoint); + + /// List of lines composing rectangle + SKETCHAPI_EXPORT std::list > lines() const; }; //! Pointer on Rectangle object diff --git a/src/SketchAPI/SketchAPI_Sketch.cpp b/src/SketchAPI/SketchAPI_Sketch.cpp index cc0ec8169..57704a146 100644 --- a/src/SketchAPI/SketchAPI_Sketch.cpp +++ b/src/SketchAPI/SketchAPI_Sketch.cpp @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include #include @@ -616,6 +618,34 @@ std::shared_ptr SketchAPI_Sketch::setDistance( return InterfacePtr(new ModelHighAPI_Interface(aFeature)); } +std::shared_ptr SketchAPI_Sketch::setHorizontalDistance( + const ModelHighAPI_RefAttr & thePoint1, + const ModelHighAPI_RefAttr & thePoint2, + const ModelHighAPI_Double & theValue) +{ + std::shared_ptr aFeature = + compositeFeature()->addFeature(SketchPlugin_ConstraintDistanceHorizontal::ID()); + fillAttribute(thePoint1, aFeature->refattr(SketchPlugin_Constraint::ENTITY_A())); + fillAttribute(thePoint2, aFeature->refattr(SketchPlugin_Constraint::ENTITY_B())); + fillAttribute(theValue, aFeature->real(SketchPlugin_Constraint::VALUE())); + aFeature->execute(); + return InterfacePtr(new ModelHighAPI_Interface(aFeature)); +} + +std::shared_ptr SketchAPI_Sketch::setVerticalDistance( + const ModelHighAPI_RefAttr & thePoint1, + const ModelHighAPI_RefAttr & thePoint2, + const ModelHighAPI_Double & theValue) +{ + std::shared_ptr aFeature = + compositeFeature()->addFeature(SketchPlugin_ConstraintDistanceVertical::ID()); + fillAttribute(thePoint1, aFeature->refattr(SketchPlugin_Constraint::ENTITY_A())); + fillAttribute(thePoint2, aFeature->refattr(SketchPlugin_Constraint::ENTITY_B())); + fillAttribute(theValue, aFeature->real(SketchPlugin_Constraint::VALUE())); + aFeature->execute(); + return InterfacePtr(new ModelHighAPI_Interface(aFeature)); +} + std::shared_ptr SketchAPI_Sketch::setEqual( const ModelHighAPI_RefAttr & theObject1, const ModelHighAPI_RefAttr & theObject2) diff --git a/src/SketchAPI/SketchAPI_Sketch.h b/src/SketchAPI/SketchAPI_Sketch.h index 15450976f..e7282dea2 100644 --- a/src/SketchAPI/SketchAPI_Sketch.h +++ b/src/SketchAPI/SketchAPI_Sketch.h @@ -321,6 +321,20 @@ public: const ModelHighAPI_RefAttr & thePointOrLine, const ModelHighAPI_Double & theValue); + /// Set horizontal distance + SKETCHAPI_EXPORT + std::shared_ptr setHorizontalDistance( + const ModelHighAPI_RefAttr & thePoint1, + const ModelHighAPI_RefAttr & thePoint2, + const ModelHighAPI_Double & theValue); + + /// Set vertical distance + SKETCHAPI_EXPORT + std::shared_ptr setVerticalDistance( + const ModelHighAPI_RefAttr & thePoint1, + const ModelHighAPI_RefAttr & thePoint2, + const ModelHighAPI_Double & theValue); + /// Set equal SKETCHAPI_EXPORT std::shared_ptr setEqual( diff --git a/src/SketchPlugin/CMakeLists.txt b/src/SketchPlugin/CMakeLists.txt index 010ee47f9..e13581812 100644 --- a/src/SketchPlugin/CMakeLists.txt +++ b/src/SketchPlugin/CMakeLists.txt @@ -13,6 +13,8 @@ SET(PROJECT_HEADERS SketchPlugin_ConstraintCoincidence.h SketchPlugin_ConstraintCollinear.h SketchPlugin_ConstraintDistance.h + SketchPlugin_ConstraintDistanceHorizontal.h + SketchPlugin_ConstraintDistanceVertical.h SketchPlugin_ConstraintEqual.h SketchPlugin_ConstraintHorizontal.h SketchPlugin_ConstraintLength.h @@ -56,6 +58,8 @@ SET(PROJECT_SOURCES SketchPlugin_ConstraintCoincidence.cpp SketchPlugin_ConstraintCollinear.cpp SketchPlugin_ConstraintDistance.cpp + SketchPlugin_ConstraintDistanceHorizontal.cpp + SketchPlugin_ConstraintDistanceVertical.cpp SketchPlugin_ConstraintEqual.cpp SketchPlugin_ConstraintHorizontal.cpp SketchPlugin_ConstraintLength.cpp @@ -141,6 +145,9 @@ ADD_UNIT_TESTS(TestSketchPointLine.py TestConstraintCollinear.py TestConstraintLength.py TestConstraintDistance.py + TestConstraintDistanceHorizontal.py + TestConstraintDistanceVertical.py + TestConstraintDistanceBehavior.py TestConstraintParallel.py TestConstraintPerpendicular.py TestConstraintRadius.py diff --git a/src/SketchPlugin/SketchPlugin_ConstraintDistance.cpp b/src/SketchPlugin/SketchPlugin_ConstraintDistance.cpp index eef61d0a9..6f7627c45 100644 --- a/src/SketchPlugin/SketchPlugin_ConstraintDistance.cpp +++ b/src/SketchPlugin/SketchPlugin_ConstraintDistance.cpp @@ -164,6 +164,32 @@ double SketchPlugin_ConstraintDistance::calculateCurrentDistance() return aDistance; } +bool SketchPlugin_ConstraintDistance::areAttributesInitialized() +{ + std::shared_ptr aData = data(); + std::shared_ptr aPlane = SketchPlugin_Sketch::plane(sketch()); + std::shared_ptr aPointA = + SketcherPrs_Tools::getFeaturePoint(aData, SketchPlugin_Constraint::ENTITY_A(), aPlane); + std::shared_ptr aPointB = + SketcherPrs_Tools::getFeaturePoint(aData, SketchPlugin_Constraint::ENTITY_B(), aPlane); + + if (!aPointA && !aPointB) + return false; + else if (aPointA || aPointB) { + FeaturePtr aLine; + if (!aPointA) + aLine = SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_A()); + else if (!aPointB) + aLine = SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_B()); + else // both points are initialized + return true; + + if (!aLine || aLine->getKind() != SketchPlugin_Line::ID()) + return false; + } + return true; +} + void SketchPlugin_ConstraintDistance::attributeChanged(const std::string& theID) { if (theID == SketchPlugin_Constraint::ENTITY_A() || diff --git a/src/SketchPlugin/SketchPlugin_ConstraintDistance.h b/src/SketchPlugin/SketchPlugin_ConstraintDistance.h index ef9e3eb1a..37cbe6f72 100644 --- a/src/SketchPlugin/SketchPlugin_ConstraintDistance.h +++ b/src/SketchPlugin/SketchPlugin_ConstraintDistance.h @@ -71,7 +71,10 @@ class SketchPlugin_ConstraintDistance : public SketchPlugin_ConstraintBase protected: /// Returns the current distance between the feature attributes - double calculateCurrentDistance(); + virtual double calculateCurrentDistance(); + + /// Check the attributes related to distanced points/features are initialized + bool areAttributesInitialized(); private: bool myFlyoutUpdate; ///< to avoid cyclic dependencies on automatic updates of flyout point diff --git a/src/SketchPlugin/SketchPlugin_ConstraintDistanceHorizontal.cpp b/src/SketchPlugin/SketchPlugin_ConstraintDistanceHorizontal.cpp new file mode 100644 index 000000000..29c8ce222 --- /dev/null +++ b/src/SketchPlugin/SketchPlugin_ConstraintDistanceHorizontal.cpp @@ -0,0 +1,149 @@ +// Copyright (C) 2014-20xx CEA/DEN, EDF R&D --> + +// File: SketchPlugin_ConstraintDistanceHorizontal.cpp +// Created: 2 May 2017 +// Author: Artem ZHIDKOV + +#include + +#include +#include + +#include +#include +#include + +#include + +const double tolerance = 1e-7; + + +SketchPlugin_ConstraintDistanceHorizontal::SketchPlugin_ConstraintDistanceHorizontal() + : SketchPlugin_ConstraintDistance() +{ +} + +//************************************************************************************* +void SketchPlugin_ConstraintDistanceHorizontal::initAttributes() +{ + data()->addAttribute(SketchPlugin_Constraint::VALUE(), ModelAPI_AttributeDouble::typeId()); + data()->addAttribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT(), GeomDataAPI_Point2D::typeId()); + data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefAttr::typeId()); + data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefAttr::typeId()); +} + +//************************************************************************************* +void SketchPlugin_ConstraintDistanceHorizontal::execute() +{ + AttributeDoublePtr anAttrValue = real(SketchPlugin_Constraint::VALUE()); + if (anAttrValue->isInitialized() || !areAttributesInitialized()) + return; + + double aDistance = calculateCurrentDistance(); + anAttrValue->setValue(aDistance); +} + +//************************************************************************************* +AISObjectPtr SketchPlugin_ConstraintDistanceHorizontal::getAISObject(AISObjectPtr thePrevious) +{ + if (!sketch()) + return thePrevious; + + AISObjectPtr anAIS = SketcherPrs_Factory::lengthDimensionConstraint(this, + sketch()->coordinatePlane(), + thePrevious); + return anAIS; +} + +//************************************************************************************* +void SketchPlugin_ConstraintDistanceHorizontal::move(double theDeltaX, double theDeltaY) +{ + std::shared_ptr aData = data(); + if (!aData->isValid()) + return; + + // Recalculate a shift of flyout point in terms of local coordinates + std::shared_ptr aDir(new GeomAPI_XY(theDeltaX, theDeltaY)); + std::shared_ptr aPlane = SketchPlugin_Sketch::plane(sketch()); + std::shared_ptr aPointA = SketcherPrs_Tools::getFeaturePoint( + data(), SketchPlugin_Constraint::ENTITY_A(), aPlane); + std::shared_ptr aPointB = SketcherPrs_Tools::getFeaturePoint( + data(), SketchPlugin_Constraint::ENTITY_B(), aPlane); + + if (!aPointA || !aPointB) + return; + + std::shared_ptr aStartPnt = aPointA->pnt()->xy(); + std::shared_ptr aEndPnt = aPointB->pnt()->xy(); + + std::shared_ptr aLineDir(new GeomAPI_Dir2d(aEndPnt->decreased(aStartPnt))); + double dX = aDir->dot(aLineDir->xy()); + double dY = -aDir->cross(aLineDir->xy()); + + std::shared_ptr aPoint = std::dynamic_pointer_cast( + aData->attribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT())); + myFlyoutUpdate = true; + if (aPoint->isInitialized()) { + aPoint->setValue(aPoint->x() + dX, aPoint->y() + dY); + } else { + aPoint->setValue(dX, dY); + } + myFlyoutUpdate = false; +} + +double SketchPlugin_ConstraintDistanceHorizontal::calculateCurrentDistance() +{ + std::shared_ptr aData = data(); + std::shared_ptr aPlane = SketchPlugin_Sketch::plane(sketch()); + std::shared_ptr aPointA = + SketcherPrs_Tools::getFeaturePoint(aData, SketchPlugin_Constraint::ENTITY_A(), aPlane); + std::shared_ptr aPointB = + SketcherPrs_Tools::getFeaturePoint(aData, SketchPlugin_Constraint::ENTITY_B(), aPlane); + + return aPointB->x() - aPointA->x(); +} + +void SketchPlugin_ConstraintDistanceHorizontal::attributeChanged(const std::string& theID) +{ + if (theID == SketchPlugin_Constraint::ENTITY_A() || + theID == SketchPlugin_Constraint::ENTITY_B()) + { + AttributeDoublePtr aValueAttr = real(SketchPlugin_Constraint::VALUE()); + if (!aValueAttr->isInitialized() && areAttributesInitialized()) { + // only if it is not initialized, try to compute the current value + double aDistance = calculateCurrentDistance(); + aValueAttr->setValue(aDistance); + } + } else if (theID == SketchPlugin_Constraint::FLYOUT_VALUE_PNT() && !myFlyoutUpdate) { + myFlyoutUpdate = true; + // Recalculate flyout point in local coordinates of the distance constraint: + // the X coordinate is a length of projection of the flyout point on the + // line binding two distanced points + // or a line of projection of the distanced point onto the distanced segment + // the Y coordinate is a distance from the flyout point to the line + std::shared_ptr aFlyoutAttr = + std::dynamic_pointer_cast( + attribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT())); + std::shared_ptr aFlyoutPnt = aFlyoutAttr->pnt(); + + std::shared_ptr aPlane = SketchPlugin_Sketch::plane(sketch()); + std::shared_ptr aPointA = SketcherPrs_Tools::getFeaturePoint( + data(), SketchPlugin_Constraint::ENTITY_A(), aPlane); + std::shared_ptr aPointB = SketcherPrs_Tools::getFeaturePoint( + data(), SketchPlugin_Constraint::ENTITY_B(), aPlane); + + std::shared_ptr aStartPnt = aPointA->pnt()->xy(); + std::shared_ptr aEndPnt = aPointB->pnt()->xy(); + + if (aEndPnt->distance(aStartPnt) < tolerance) + return; + + std::shared_ptr aLineDir(new GeomAPI_Dir2d(aEndPnt->decreased(aStartPnt))); + std::shared_ptr aFlyoutDir = aFlyoutPnt->xy()->decreased(aStartPnt); + + double X = aFlyoutDir->dot(aLineDir->xy()); + double Y = -aFlyoutDir->cross(aLineDir->xy()); + aFlyoutAttr->setValue(X, Y); + myFlyoutUpdate = false; + } +} diff --git a/src/SketchPlugin/SketchPlugin_ConstraintDistanceHorizontal.h b/src/SketchPlugin/SketchPlugin_ConstraintDistanceHorizontal.h new file mode 100644 index 000000000..948fe13e2 --- /dev/null +++ b/src/SketchPlugin/SketchPlugin_ConstraintDistanceHorizontal.h @@ -0,0 +1,66 @@ +// Copyright (C) 2014-20xx CEA/DEN, EDF R&D --> + +// File: SketchPlugin_ConstraintDistanceHorizontal.h +// Created: 2 May 2017 +// Author: Artem ZHIDKOV + +#ifndef SketchPlugin_ConstraintDistanceHorizontal_H_ +#define SketchPlugin_ConstraintDistanceHorizontal_H_ + +#include +#include + +/** \class SketchPlugin_ConstraintDistanceHorizontal + * \ingroup Plugins + * \brief Feature for creation of a new constraint which defines a horizontal distance between two points. + * + * This constraint has three attributes: + * SketchPlugin_Constraint::VALUE(), SketchPlugin_Constraint::ENTITY_A() and SketchPlugin_Constraint::ENTITY_B() + */ +class SketchPlugin_ConstraintDistanceHorizontal : public SketchPlugin_ConstraintDistance +{ +public: + /// Distance constraint kind + inline static const std::string& ID() + { + static const std::string MY_CONSTRAINT_DISTANCE_ID("SketchConstraintDistanceHorizontal"); + return MY_CONSTRAINT_DISTANCE_ID; + } + + /// \brief Returns the kind of a feature + SKETCHPLUGIN_EXPORT virtual const std::string& getKind() + { + static std::string MY_KIND = SketchPlugin_ConstraintDistanceHorizontal::ID(); + return MY_KIND; + } + + /// \brief Creates a new part document if needed + SKETCHPLUGIN_EXPORT virtual void execute(); + + /// \brief Request for initialization of data model of the feature: adding all attributes + SKETCHPLUGIN_EXPORT virtual void initAttributes(); + + /// Returns the AIS preview + SKETCHPLUGIN_EXPORT virtual AISObjectPtr getAISObject(AISObjectPtr thePrevious); + + /// Moves the feature + /// \param theDeltaX the delta for X coordinate is moved + /// \param theDeltaY the delta for Y coordinate is moved + SKETCHPLUGIN_EXPORT virtual void move(const double theDeltaX, const double theDeltaY); + + /// Called on change of any argument-attribute of this object + /// \param theID identifier of changed attribute + SKETCHPLUGIN_EXPORT virtual void attributeChanged(const std::string& theID); + + /// \brief Use plugin manager for features creation + SketchPlugin_ConstraintDistanceHorizontal(); + +protected: + /// Returns the current distance between the feature attributes + virtual double calculateCurrentDistance(); + +private: + bool myFlyoutUpdate; ///< to avoid cyclic dependencies on automatic updates of flyout point +}; + +#endif diff --git a/src/SketchPlugin/SketchPlugin_ConstraintDistanceVertical.cpp b/src/SketchPlugin/SketchPlugin_ConstraintDistanceVertical.cpp new file mode 100644 index 000000000..49db91f59 --- /dev/null +++ b/src/SketchPlugin/SketchPlugin_ConstraintDistanceVertical.cpp @@ -0,0 +1,149 @@ +// Copyright (C) 2014-20xx CEA/DEN, EDF R&D --> + +// File: SketchPlugin_ConstraintDistanceVertical.cpp +// Created: 10 May 2017 +// Author: Artem ZHIDKOV + +#include + +#include +#include + +#include +#include +#include + +#include + +const double tolerance = 1e-7; + + +SketchPlugin_ConstraintDistanceVertical::SketchPlugin_ConstraintDistanceVertical() + : SketchPlugin_ConstraintDistance() +{ +} + +//************************************************************************************* +void SketchPlugin_ConstraintDistanceVertical::initAttributes() +{ + data()->addAttribute(SketchPlugin_Constraint::VALUE(), ModelAPI_AttributeDouble::typeId()); + data()->addAttribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT(), GeomDataAPI_Point2D::typeId()); + data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefAttr::typeId()); + data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefAttr::typeId()); +} + +//************************************************************************************* +void SketchPlugin_ConstraintDistanceVertical::execute() +{ + AttributeDoublePtr anAttrValue = real(SketchPlugin_Constraint::VALUE()); + if (anAttrValue->isInitialized() || !areAttributesInitialized()) + return; + + double aDistance = calculateCurrentDistance(); + anAttrValue->setValue(aDistance); +} + +//************************************************************************************* +AISObjectPtr SketchPlugin_ConstraintDistanceVertical::getAISObject(AISObjectPtr thePrevious) +{ + if (!sketch()) + return thePrevious; + + AISObjectPtr anAIS = SketcherPrs_Factory::lengthDimensionConstraint(this, + sketch()->coordinatePlane(), + thePrevious); + return anAIS; +} + +//************************************************************************************* +void SketchPlugin_ConstraintDistanceVertical::move(double theDeltaX, double theDeltaY) +{ + std::shared_ptr aData = data(); + if (!aData->isValid()) + return; + + // Recalculate a shift of flyout point in terms of local coordinates + std::shared_ptr aDir(new GeomAPI_XY(theDeltaX, theDeltaY)); + std::shared_ptr aPlane = SketchPlugin_Sketch::plane(sketch()); + std::shared_ptr aPointA = SketcherPrs_Tools::getFeaturePoint( + data(), SketchPlugin_Constraint::ENTITY_A(), aPlane); + std::shared_ptr aPointB = SketcherPrs_Tools::getFeaturePoint( + data(), SketchPlugin_Constraint::ENTITY_B(), aPlane); + + if (!aPointA || !aPointB) + return; + + std::shared_ptr aStartPnt = aPointA->pnt()->xy(); + std::shared_ptr aEndPnt = aPointB->pnt()->xy(); + + std::shared_ptr aLineDir(new GeomAPI_Dir2d(aEndPnt->decreased(aStartPnt))); + double dX = aDir->dot(aLineDir->xy()); + double dY = -aDir->cross(aLineDir->xy()); + + std::shared_ptr aPoint = std::dynamic_pointer_cast( + aData->attribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT())); + myFlyoutUpdate = true; + if (aPoint->isInitialized()) { + aPoint->setValue(aPoint->x() + dX, aPoint->y() + dY); + } else { + aPoint->setValue(dX, dY); + } + myFlyoutUpdate = false; +} + +double SketchPlugin_ConstraintDistanceVertical::calculateCurrentDistance() +{ + std::shared_ptr aData = data(); + std::shared_ptr aPlane = SketchPlugin_Sketch::plane(sketch()); + std::shared_ptr aPointA = + SketcherPrs_Tools::getFeaturePoint(aData, SketchPlugin_Constraint::ENTITY_A(), aPlane); + std::shared_ptr aPointB = + SketcherPrs_Tools::getFeaturePoint(aData, SketchPlugin_Constraint::ENTITY_B(), aPlane); + + return aPointB->y() - aPointA->y(); +} + +void SketchPlugin_ConstraintDistanceVertical::attributeChanged(const std::string& theID) +{ + if (theID == SketchPlugin_Constraint::ENTITY_A() || + theID == SketchPlugin_Constraint::ENTITY_B()) + { + AttributeDoublePtr aValueAttr = real(SketchPlugin_Constraint::VALUE()); + if (!aValueAttr->isInitialized() && areAttributesInitialized()) { + // only if it is not initialized, try to compute the current value + double aDistance = calculateCurrentDistance(); + aValueAttr->setValue(aDistance); + } + } else if (theID == SketchPlugin_Constraint::FLYOUT_VALUE_PNT() && !myFlyoutUpdate) { + myFlyoutUpdate = true; + // Recalculate flyout point in local coordinates of the distance constraint: + // the X coordinate is a length of projection of the flyout point on the + // line binding two distanced points + // or a line of projection of the distanced point onto the distanced segment + // the Y coordinate is a distance from the flyout point to the line + std::shared_ptr aFlyoutAttr = + std::dynamic_pointer_cast( + attribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT())); + std::shared_ptr aFlyoutPnt = aFlyoutAttr->pnt(); + + std::shared_ptr aPlane = SketchPlugin_Sketch::plane(sketch()); + std::shared_ptr aPointA = SketcherPrs_Tools::getFeaturePoint( + data(), SketchPlugin_Constraint::ENTITY_A(), aPlane); + std::shared_ptr aPointB = SketcherPrs_Tools::getFeaturePoint( + data(), SketchPlugin_Constraint::ENTITY_B(), aPlane); + + std::shared_ptr aStartPnt = aPointA->pnt()->xy(); + std::shared_ptr aEndPnt = aPointB->pnt()->xy(); + + if (aEndPnt->distance(aStartPnt) < tolerance) + return; + + std::shared_ptr aLineDir(new GeomAPI_Dir2d(aEndPnt->decreased(aStartPnt))); + std::shared_ptr aFlyoutDir = aFlyoutPnt->xy()->decreased(aStartPnt); + + double X = aFlyoutDir->dot(aLineDir->xy()); + double Y = -aFlyoutDir->cross(aLineDir->xy()); + aFlyoutAttr->setValue(X, Y); + myFlyoutUpdate = false; + } +} diff --git a/src/SketchPlugin/SketchPlugin_ConstraintDistanceVertical.h b/src/SketchPlugin/SketchPlugin_ConstraintDistanceVertical.h new file mode 100644 index 000000000..e5f14d83e --- /dev/null +++ b/src/SketchPlugin/SketchPlugin_ConstraintDistanceVertical.h @@ -0,0 +1,66 @@ +// Copyright (C) 2014-20xx CEA/DEN, EDF R&D --> + +// File: SketchPlugin_ConstraintDistanceVertical.h +// Created: 10 May 2017 +// Author: Artem ZHIDKOV + +#ifndef SketchPlugin_ConstraintDistanceVertical_H_ +#define SketchPlugin_ConstraintDistanceVertical_H_ + +#include +#include + +/** \class SketchPlugin_ConstraintDistanceVertical + * \ingroup Plugins + * \brief Feature for creation of a new constraint which defines a vertical distance between two points. + * + * This constraint has three attributes: + * SketchPlugin_Constraint::VALUE(), SketchPlugin_Constraint::ENTITY_A() and SketchPlugin_Constraint::ENTITY_B() + */ +class SketchPlugin_ConstraintDistanceVertical : public SketchPlugin_ConstraintDistance +{ +public: + /// Distance constraint kind + inline static const std::string& ID() + { + static const std::string MY_CONSTRAINT_DISTANCE_ID("SketchConstraintDistanceVertical"); + return MY_CONSTRAINT_DISTANCE_ID; + } + + /// \brief Returns the kind of a feature + SKETCHPLUGIN_EXPORT virtual const std::string& getKind() + { + static std::string MY_KIND = SketchPlugin_ConstraintDistanceVertical::ID(); + return MY_KIND; + } + + /// \brief Creates a new part document if needed + SKETCHPLUGIN_EXPORT virtual void execute(); + + /// \brief Request for initialization of data model of the feature: adding all attributes + SKETCHPLUGIN_EXPORT virtual void initAttributes(); + + /// Returns the AIS preview + SKETCHPLUGIN_EXPORT virtual AISObjectPtr getAISObject(AISObjectPtr thePrevious); + + /// Moves the feature + /// \param theDeltaX the delta for X coordinate is moved + /// \param theDeltaY the delta for Y coordinate is moved + SKETCHPLUGIN_EXPORT virtual void move(const double theDeltaX, const double theDeltaY); + + /// Called on change of any argument-attribute of this object + /// \param theID identifier of changed attribute + SKETCHPLUGIN_EXPORT virtual void attributeChanged(const std::string& theID); + + /// \brief Use plugin manager for features creation + SketchPlugin_ConstraintDistanceVertical(); + +protected: + /// Returns the current distance between the feature attributes + virtual double calculateCurrentDistance(); + +private: + bool myFlyoutUpdate; ///< to avoid cyclic dependencies on automatic updates of flyout point +}; + +#endif diff --git a/src/SketchPlugin/SketchPlugin_Plugin.cpp b/src/SketchPlugin/SketchPlugin_Plugin.cpp index 9999ea08c..4776e5e3a 100644 --- a/src/SketchPlugin/SketchPlugin_Plugin.cpp +++ b/src/SketchPlugin/SketchPlugin_Plugin.cpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include #include @@ -172,6 +174,10 @@ FeaturePtr SketchPlugin_Plugin::createFeature(std::string theFeatureID) return FeaturePtr(new SketchPlugin_ConstraintCollinear); } else if (theFeatureID == SketchPlugin_ConstraintDistance::ID()) { return FeaturePtr(new SketchPlugin_ConstraintDistance); + } else if (theFeatureID == SketchPlugin_ConstraintDistanceHorizontal::ID()) { + return FeaturePtr(new SketchPlugin_ConstraintDistanceHorizontal); + } else if (theFeatureID == SketchPlugin_ConstraintDistanceVertical::ID()) { + return FeaturePtr(new SketchPlugin_ConstraintDistanceVertical); } else if (theFeatureID == SketchPlugin_ConstraintLength::ID()) { return FeaturePtr(new SketchPlugin_ConstraintLength); } else if (theFeatureID == SketchPlugin_ConstraintParallel::ID()) { @@ -256,6 +262,7 @@ std::shared_ptr SketchPlugin_Plugin aMsg->setState(SketchPlugin_Line::ID(), aHasSketchPlane); aMsg->setState(SketchPlugin_Circle::ID(), aHasSketchPlane); aMsg->setState(SketchPlugin_Arc::ID(), aHasSketchPlane); + aMsg->setState(SketchPlugin_Ellipse::ID(), aHasSketchPlane); aMsg->setState(SketchPlugin_Projection::ID(), aHasSketchPlane); aMsg->setState(SketchPlugin_ConstraintCoincidence::ID(), aHasSketchPlane); aMsg->setState(SketchPlugin_ConstraintCollinear::ID(), aHasSketchPlane); @@ -279,6 +286,8 @@ std::shared_ptr SketchPlugin_Plugin aMsg->setState(SketchPlugin_Trim::ID(), aHasSketchPlane); aMsg->setState(SketchPlugin_MacroArc::ID(), aHasSketchPlane); aMsg->setState(SketchPlugin_MacroCircle::ID(), aHasSketchPlane); + aMsg->setState(SketchPlugin_ConstraintDistanceHorizontal::ID(), aHasSketchPlane); + aMsg->setState(SketchPlugin_ConstraintDistanceVertical::ID(), aHasSketchPlane); // SketchRectangle is a python feature, so its ID is passed just as a string aMsg->setState("SketchRectangle", aHasSketchPlane); } diff --git a/src/SketchPlugin/Test/TestConstraintDistanceBehavior.py b/src/SketchPlugin/Test/TestConstraintDistanceBehavior.py new file mode 100644 index 000000000..37c0458e4 --- /dev/null +++ b/src/SketchPlugin/Test/TestConstraintDistanceBehavior.py @@ -0,0 +1,82 @@ +from salome.shaper import model +from SketchAPI import * +import math + +TOLERANCE = 1.e-7 + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +DistanceParam = model.addParameter(Part_1_doc, "distance", "10.") +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchRectangle_1 = Sketch_1.addRectangle(20., 20., 70., 50.) +[SketchLine_1, SketchLine_2, SketchLine_3, SketchLine_4] = SketchRectangle_1.lines() +firstPoint = SketchAPI_Line(SketchLine_2).startPoint() +secondPoint = SketchAPI_Line(SketchLine_3).endPoint() +model.do() + +# ============================================================================= +# Test 1. +# ============================================================================= +# horizontal distance constraint +SketchConstraintDistanceHorizontal_1 = Sketch_1.setHorizontalDistance(firstPoint, secondPoint, "distance") +model.do() + +# changing the parameter +for param in range(-30, 31, 2): + DistanceParam.setValue(param) + model.do() + dist = secondPoint.x() - firstPoint.x() + assert math.fabs(dist - param) < TOLERANCE, "Incorrect horizontal distance {}, expected {}".format(dist, param) + +model.testNbSubFeatures(Sketch_1, "SketchLine", 4) +model.testNbSubFeatures(Sketch_1, "SketchConstraintDistanceHorizontal", 1) + +# remove horizontal distance constraint +Part_1_doc.removeFeature(SketchConstraintDistanceHorizontal_1.feature()) +model.do() + +# ============================================================================= +# Test 2. +# ============================================================================= +# Vertical distance constraint +SketchConstraintDistanceVertical_1 = Sketch_1.setVerticalDistance(firstPoint, secondPoint, "distance") +model.do() + +# changing the parameter +for param in range(-30, 31, 2): + DistanceParam.setValue(param) + model.do() + dist = secondPoint.y() - firstPoint.y() + assert math.fabs(dist - param) < TOLERANCE, "Incorrect vertical distance {}, expected {}".format(dist, param) + +model.testNbSubFeatures(Sketch_1, "SketchLine", 4) +model.testNbSubFeatures(Sketch_1, "SketchConstraintDistanceVertical", 1) + +# remove verticel distance constraint +Part_1_doc.removeFeature(SketchConstraintDistanceVertical_1.feature()) +model.do() + +# ============================================================================= +# Test 3. +# ============================================================================= +# distance constraint +SketchConstraintDistance_1 = Sketch_1.setDistance(firstPoint, secondPoint, "distance") +model.do() + +# changing the parameter +for param in range(-30, 31, 2): + DistanceParam.setValue(param) + model.do() + if param <= 0: + assert SketchConstraintDistance_1.feature().error() != '', "ERROR: Sketch should not be valid due to negative distance value" + else: + dist = model.distancePointPoint(firstPoint, secondPoint) + assert math.fabs(dist - math.fabs(param)) < TOLERANCE, "Incorrect distance {}, expected {}".format(dist, math.fabs(param)) + +model.testNbSubFeatures(Sketch_1, "SketchLine", 4) +model.testNbSubFeatures(Sketch_1, "SketchConstraintDistance", 1) +# leave distance constraint alive + +model.end() diff --git a/src/SketchPlugin/Test/TestConstraintDistanceHorizontal.py b/src/SketchPlugin/Test/TestConstraintDistanceHorizontal.py new file mode 100644 index 000000000..dedb8d685 --- /dev/null +++ b/src/SketchPlugin/Test/TestConstraintDistanceHorizontal.py @@ -0,0 +1,208 @@ +""" + TestConstraintDistanceHorizontal.py + Unit test of SketchPlugin_ConstraintDistanceHorizontal class + + SketchPlugin_ConstraintDistanceHorizontal + static const std::string MY_CONSTRAINT_DISTANCE_ID("SketchConstraintDistance"); + data()->addAttribute(SketchPlugin_Constraint::VALUE(), ModelAPI_AttributeDouble::typeId()); + data()->addAttribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT(), GeomDataAPI_Point2D::typeId()); + data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefAttr::typeId()); + data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefAttr::typeId()); +""" + +from GeomDataAPI import * +from ModelAPI import * +import math +from salome.shaper import model + +#========================================================================= +# Initialization of the test +#========================================================================= + +__updated__ = "2017-05-10" + + +def horizontalDistance(point1, point2): + """ + subroutine to calculate signed distance between two points + """ + return point2.x() - point1.x() + +aSession = ModelAPI_Session.get() +aDocument = aSession.moduleDocument() +#========================================================================= +# Creation of a sketch +#========================================================================= +aSession.startOperation() +aSketchCommonFeature = aDocument.addFeature("Sketch") +aSketchFeature = featureToCompositeFeature(aSketchCommonFeature) +origin = geomDataAPI_Point(aSketchFeature.attribute("Origin")) +origin.setValue(0, 0, 0) +dirx = geomDataAPI_Dir(aSketchFeature.attribute("DirX")) +dirx.setValue(1, 0, 0) +norm = geomDataAPI_Dir(aSketchFeature.attribute("Norm")) +norm.setValue(0, 0, 1) +aSession.finishOperation() +#========================================================================= +# Create two movable and one fixed point +#========================================================================= +aSession.startOperation() +aPoint1 = aSketchFeature.addFeature("SketchPoint") +aPoint1Coords = geomDataAPI_Point2D(aPoint1.attribute("PointCoordinates")) +aPoint1Coords.setValue(50., 50.) +aSession.finishOperation() +aSession.startOperation() +aPoint2 = aSketchFeature.addFeature("SketchPoint") +aPoint2Coords = geomDataAPI_Point2D(aPoint2.attribute("PointCoordinates")) +aPoint2Coords.setValue(70., 70.) +aSession.finishOperation() +aSession.startOperation() +anOriginResult = modelAPI_Result(aDocument.objectByName("Construction", "Origin")) +anOriginShape = anOriginResult.shape() +anExtPoint = aSketchFeature.addFeature("SketchPoint") +anExtCoords = geomDataAPI_Point2D(anExtPoint.attribute("PointCoordinates")) +anExtCoords.setValue(0., 0.) +anExtPoint.selection("External").setValue(anOriginResult, anOriginShape) +aSession.finishOperation() +assert (model.dof(aSketchFeature) == 4) + +#========================================================================= +# Create a constraint to keep horizontal distance between movable points +#========================================================================= +DISTANCE1 = 25. +aSession.startOperation() +aHDist1 = aSketchFeature.addFeature("SketchConstraintDistanceHorizontal") +aDistance = aHDist1.real("ConstraintValue") +refattrA = aHDist1.refattr("ConstraintEntityA") +refattrB = aHDist1.refattr("ConstraintEntityB") +assert (not aDistance.isInitialized()) +assert (not refattrA.isInitialized()) +assert (not refattrB.isInitialized()) +refattrA.setObject(aPoint1.lastResult()) +refattrB.setObject(aPoint2.lastResult()) +aSession.finishOperation() +assert (model.dof(aSketchFeature) == 3) +# set flyout point then abort operation, after that check the Distance is correct +aSession.startOperation() +aFlyoutPoint = geomDataAPI_Point2D(aHDist1.attribute("ConstraintFlyoutValuePnt")) +aFlyoutPoint.setValue(50.0, 100.0) +aSession.abortOperation() +assert (refattrA.isInitialized()) +assert (refattrB.isInitialized()) +assert (aDistance.isInitialized()) +aSession.startOperation() +aDistance.setValue(DISTANCE1) +aSession.finishOperation() +assert math.fabs(horizontalDistance(aPoint1Coords, aPoint2Coords) - DISTANCE1) < 1.e-5, "Distance values are different: {0} != {1}".format(horizontalDistance(aPoint1Coords, aPoint2Coords), DISTANCE1) +assert (model.dof(aSketchFeature) == 3) +#========================================================================= +# Change a distance value +#========================================================================= +d = DISTANCE1 + 20. +dStep = -5. +while d >= -30.: + aSession.startOperation() + DISTANCE1 = d + aDistance.setValue(DISTANCE1) + aSession.finishOperation() + assert math.fabs(horizontalDistance(aPoint1Coords, aPoint2Coords) - DISTANCE1) < 1.e-5, "Distance values are different: {0} != {1}".format(horizontalDistance(aPoint1Coords, aPoint2Coords), DISTANCE1) + d += dStep +assert (model.dof(aSketchFeature) == 3) + +#========================================================================= +# Create a constraint to keep horizontal distance between fixed and movable points +#========================================================================= +DISTANCE2 = 50. +aSession.startOperation() +aHDist2 = aSketchFeature.addFeature("SketchConstraintDistanceHorizontal") +aDistance = aHDist2.real("ConstraintValue") +refattrA = aHDist2.refattr("ConstraintEntityA") +refattrB = aHDist2.refattr("ConstraintEntityB") +assert (not aDistance.isInitialized()) +assert (not refattrA.isInitialized()) +assert (not refattrB.isInitialized()) +refattrA.setObject(anExtPoint.lastResult()) +refattrB.setAttr(aPoint1Coords) +aDistance.setValue(DISTANCE2) +aSession.finishOperation() +assert math.fabs(horizontalDistance(anExtCoords, aPoint1Coords) - DISTANCE2) < 1.e-5, "Distance values are different: {0} != {1}".format(horizontalDistance(anExtCoords, aPoint1Coords), DISTANCE2) +assert math.fabs(aPoint1Coords.x() - DISTANCE2) < 1.e-5, "Wrong point coordinates ({}, {}), expected x = {}".format(aPoint1Coords.x(), aPoint1Coords.y(), DISTANCE2) +assert (model.dof(aSketchFeature) == 2) +#========================================================================= +# Change a distance value (check previous constraint is applied too) +#========================================================================= +d = DISTANCE2 +dStep = -5. +while d >= -50.: + aSession.startOperation() + DISTANCE2 = d + aDistance.setValue(DISTANCE2) + aSession.finishOperation() + assert math.fabs(horizontalDistance(anExtCoords, aPoint1Coords) - DISTANCE2) < 1.e-5, "Distance values are different: {0} != {1}".format(horizontalDistance(anExtCoords, aPoint1Coords), DISTANCE2) + assert math.fabs(aPoint1Coords.x() - DISTANCE2) < 1.e-5, "Wrong point coordinates ({}, {}), expected x = {}".format(aPoint1Coords.x(), aPoint1Coords.y(), DISTANCE2) + assert math.fabs(horizontalDistance(aPoint1Coords, aPoint2Coords) - DISTANCE1) < 1.e-5, "Distance values are different: {0} != {1}".format(horizontalDistance(aPoint1Coords, aPoint2Coords), DISTANCE1) + d += dStep +assert (model.dof(aSketchFeature) == 2) + +#========================================================================= +# Remove first distance +#========================================================================= +aStoredCoords = [aPoint2Coords.x(), aPoint2Coords.y()] +aSession.startOperation() +aDocument.removeFeature(aHDist1) +aSession.finishOperation() +assert (model.dof(aSketchFeature) == 3) +aSession.startOperation() +DISTANCE2 = 20. +aDistance.setValue(DISTANCE2) +aSession.finishOperation() +assert math.fabs(horizontalDistance(anExtCoords, aPoint1Coords) - DISTANCE2) < 1.e-5, "Distance values are different: {0} != {1}".format(horizontalDistance(anExtCoords, aPoint1Coords), DISTANCE2) +assert math.fabs(aPoint1Coords.x() - DISTANCE2) < 1.e-5, "Wrong point coordinates ({}, {}), expected x = {}".format(aPoint1Coords.x(), aPoint1Coords.y(), DISTANCE2) +assert aPoint2Coords.x() == aStoredCoords[0] and aPoint2Coords.y() == aStoredCoords[1] + +#========================================================================= +# Create line and set horizontal distance between line boundaries +#========================================================================= +aSession.startOperation() +aLine = aSketchFeature.addFeature("SketchLine") +aStartPoint = geomDataAPI_Point2D(aLine.attribute("StartPoint")) +aEndPoint = geomDataAPI_Point2D(aLine.attribute("EndPoint")) +aStartPoint.setValue(50., 0.) +aEndPoint.setValue(100., 20.) +aSession.finishOperation() +assert (model.dof(aSketchFeature) == 7) + +DISTANCE3 = 50. +aSession.startOperation() +aHDist3 = aSketchFeature.addFeature("SketchConstraintDistanceHorizontal") +aDistance = aHDist3.real("ConstraintValue") +refattrA = aHDist3.refattr("ConstraintEntityA") +refattrB = aHDist3.refattr("ConstraintEntityB") +assert (not aDistance.isInitialized()) +assert (not refattrA.isInitialized()) +assert (not refattrB.isInitialized()) +refattrA.setAttr(aStartPoint) +refattrB.setAttr(aEndPoint) +aDistance.setValue(DISTANCE3) +aSession.finishOperation() +assert math.fabs(horizontalDistance(aStartPoint, aEndPoint) - DISTANCE3) < 1.e-5, "Distance values are different: {0} != {1}".format(horizontalDistance(aStartPoint, aEndPoint), DISTANCE3) +assert (model.dof(aSketchFeature) == 6) +#========================================================================= +# Change a distance value +#========================================================================= +d = DISTANCE3 +dStep = -5. +while d >= -50.: + aSession.startOperation() + DISTANCE3 = d + aDistance.setValue(DISTANCE3) + aSession.finishOperation() + assert math.fabs(horizontalDistance(aStartPoint, aEndPoint) - DISTANCE3) < 1.e-5, "Distance values are different: {0} != {1}".format(horizontalDistance(aStartPoint, aEndPoint), DISTANCE3) + d += dStep +assert (model.dof(aSketchFeature) == 6) + +#========================================================================= +# End of test +#========================================================================= + +assert(model.checkPythonDump()) diff --git a/src/SketchPlugin/Test/TestConstraintDistanceVertical.py b/src/SketchPlugin/Test/TestConstraintDistanceVertical.py new file mode 100644 index 000000000..09ba21142 --- /dev/null +++ b/src/SketchPlugin/Test/TestConstraintDistanceVertical.py @@ -0,0 +1,208 @@ +""" + TestConstraintDistanceVertical.py + Unit test of SketchPlugin_ConstraintDistanceVertical class + + SketchPlugin_ConstraintDistanceVertical + static const std::string MY_CONSTRAINT_DISTANCE_ID("SketchConstraintDistance"); + data()->addAttribute(SketchPlugin_Constraint::VALUE(), ModelAPI_AttributeDouble::typeId()); + data()->addAttribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT(), GeomDataAPI_Point2D::typeId()); + data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefAttr::typeId()); + data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefAttr::typeId()); +""" + +from GeomDataAPI import * +from ModelAPI import * +import math +from salome.shaper import model + +#========================================================================= +# Initialization of the test +#========================================================================= + +__updated__ = "2017-05-10" + + +def verticalDistance(point1, point2): + """ + subroutine to calculate signed distance between two points + """ + return point2.y() - point1.y() + +aSession = ModelAPI_Session.get() +aDocument = aSession.moduleDocument() +#========================================================================= +# Creation of a sketch +#========================================================================= +aSession.startOperation() +aSketchCommonFeature = aDocument.addFeature("Sketch") +aSketchFeature = featureToCompositeFeature(aSketchCommonFeature) +origin = geomDataAPI_Point(aSketchFeature.attribute("Origin")) +origin.setValue(0, 0, 0) +dirx = geomDataAPI_Dir(aSketchFeature.attribute("DirX")) +dirx.setValue(1, 0, 0) +norm = geomDataAPI_Dir(aSketchFeature.attribute("Norm")) +norm.setValue(0, 0, 1) +aSession.finishOperation() +#========================================================================= +# Create two movable and one fixed point +#========================================================================= +aSession.startOperation() +aPoint1 = aSketchFeature.addFeature("SketchPoint") +aPoint1Coords = geomDataAPI_Point2D(aPoint1.attribute("PointCoordinates")) +aPoint1Coords.setValue(50., 50.) +aSession.finishOperation() +aSession.startOperation() +aPoint2 = aSketchFeature.addFeature("SketchPoint") +aPoint2Coords = geomDataAPI_Point2D(aPoint2.attribute("PointCoordinates")) +aPoint2Coords.setValue(70., 70.) +aSession.finishOperation() +aSession.startOperation() +anOriginResult = modelAPI_Result(aDocument.objectByName("Construction", "Origin")) +anOriginShape = anOriginResult.shape() +anExtPoint = aSketchFeature.addFeature("SketchPoint") +anExtCoords = geomDataAPI_Point2D(anExtPoint.attribute("PointCoordinates")) +anExtCoords.setValue(0., 0.) +anExtPoint.selection("External").setValue(anOriginResult, anOriginShape) +aSession.finishOperation() +assert (model.dof(aSketchFeature) == 4) + +#========================================================================= +# Create a constraint to keep vertical distance between movable points +#========================================================================= +DISTANCE1 = 25. +aSession.startOperation() +aVDist1 = aSketchFeature.addFeature("SketchConstraintDistanceVertical") +aDistance = aVDist1.real("ConstraintValue") +refattrA = aVDist1.refattr("ConstraintEntityA") +refattrB = aVDist1.refattr("ConstraintEntityB") +assert (not aDistance.isInitialized()) +assert (not refattrA.isInitialized()) +assert (not refattrB.isInitialized()) +refattrA.setObject(aPoint1.lastResult()) +refattrB.setObject(aPoint2.lastResult()) +aSession.finishOperation() +assert (model.dof(aSketchFeature) == 3) +# set flyout point then abort operation, after that check the Distance is correct +aSession.startOperation() +aFlyoutPoint = geomDataAPI_Point2D(aVDist1.attribute("ConstraintFlyoutValuePnt")) +aFlyoutPoint.setValue(50.0, 100.0) +aSession.abortOperation() +assert (refattrA.isInitialized()) +assert (refattrB.isInitialized()) +assert (aDistance.isInitialized()) +aSession.startOperation() +aDistance.setValue(DISTANCE1) +aSession.finishOperation() +assert math.fabs(verticalDistance(aPoint1Coords, aPoint2Coords) - DISTANCE1) < 1.e-5, "Distance values are different: {0} != {1}".format(verticalDistance(aPoint1Coords, aPoint2Coords), DISTANCE1) +assert (model.dof(aSketchFeature) == 3) +#========================================================================= +# Change a distance value +#========================================================================= +d = DISTANCE1 + 20. +dStep = -5. +while d >= -30.: + aSession.startOperation() + DISTANCE1 = d + aDistance.setValue(DISTANCE1) + aSession.finishOperation() + assert math.fabs(verticalDistance(aPoint1Coords, aPoint2Coords) - DISTANCE1) < 1.e-5, "Distance values are different: {0} != {1}".format(verticalDistance(aPoint1Coords, aPoint2Coords), DISTANCE1) + d += dStep +assert (model.dof(aSketchFeature) == 3) + +#========================================================================= +# Create a constraint to keep vertical distance between fixed and movable points +#========================================================================= +DISTANCE2 = 50. +aSession.startOperation() +aVDist2 = aSketchFeature.addFeature("SketchConstraintDistanceVertical") +aDistance = aVDist2.real("ConstraintValue") +refattrA = aVDist2.refattr("ConstraintEntityA") +refattrB = aVDist2.refattr("ConstraintEntityB") +assert (not aDistance.isInitialized()) +assert (not refattrA.isInitialized()) +assert (not refattrB.isInitialized()) +refattrA.setObject(anExtPoint.lastResult()) +refattrB.setAttr(aPoint1Coords) +aDistance.setValue(DISTANCE2) +aSession.finishOperation() +assert math.fabs(verticalDistance(anExtCoords, aPoint1Coords) - DISTANCE2) < 1.e-5, "Distance values are different: {0} != {1}".format(verticalDistance(anExtCoords, aPoint1Coords), DISTANCE2) +assert math.fabs(aPoint1Coords.y() - DISTANCE2) < 1.e-5, "Wrong point coordinates ({}, {}), expected y = {}".format(aPoint1Coords.x(), aPoint1Coords.y(), DISTANCE2) +assert (model.dof(aSketchFeature) == 2) +#========================================================================= +# Change a distance value (check previous constraint is applied too) +#========================================================================= +d = DISTANCE2 +dStep = -5. +while d >= -50.: + aSession.startOperation() + DISTANCE2 = d + aDistance.setValue(DISTANCE2) + aSession.finishOperation() + assert math.fabs(verticalDistance(anExtCoords, aPoint1Coords) - DISTANCE2) < 1.e-5, "Distance values are different: {0} != {1}".format(verticalDistance(anExtCoords, aPoint1Coords), DISTANCE2) + assert math.fabs(aPoint1Coords.y() - DISTANCE2) < 1.e-5, "Wrong point coordinates ({}, {}), expected y = {}".format(aPoint1Coords.x(), aPoint1Coords.y(), DISTANCE2) + assert math.fabs(verticalDistance(aPoint1Coords, aPoint2Coords) - DISTANCE1) < 1.e-5, "Distance values are different: {0} != {1}".format(verticalDistance(aPoint1Coords, aPoint2Coords), DISTANCE1) + d += dStep +assert (model.dof(aSketchFeature) == 2) + +#========================================================================= +# Remove first distance +#========================================================================= +aStoredCoords = [aPoint2Coords.x(), aPoint2Coords.y()] +aSession.startOperation() +aDocument.removeFeature(aVDist1) +aSession.finishOperation() +assert (model.dof(aSketchFeature) == 3) +aSession.startOperation() +DISTANCE2 = 20. +aDistance.setValue(DISTANCE2) +aSession.finishOperation() +assert math.fabs(verticalDistance(anExtCoords, aPoint1Coords) - DISTANCE2) < 1.e-5, "Distance values are different: {0} != {1}".format(verticalDistance(anExtCoords, aPoint1Coords), DISTANCE2) +assert math.fabs(aPoint1Coords.y() - DISTANCE2) < 1.e-5, "Wrong point coordinates ({}, {}), expected y = {}".format(aPoint1Coords.x(), aPoint1Coords.y(), DISTANCE2) +assert aPoint2Coords.x() == aStoredCoords[0] and aPoint2Coords.y() == aStoredCoords[1] + +#========================================================================= +# Create line and set vertical distance between line boundaries +#========================================================================= +aSession.startOperation() +aLine = aSketchFeature.addFeature("SketchLine") +aStartPoint = geomDataAPI_Point2D(aLine.attribute("StartPoint")) +aEndPoint = geomDataAPI_Point2D(aLine.attribute("EndPoint")) +aStartPoint.setValue(50., 0.) +aEndPoint.setValue(100., 20.) +aSession.finishOperation() +assert (model.dof(aSketchFeature) == 7) + +DISTANCE3 = 50. +aSession.startOperation() +aVDist3 = aSketchFeature.addFeature("SketchConstraintDistanceVertical") +aDistance = aVDist3.real("ConstraintValue") +refattrA = aVDist3.refattr("ConstraintEntityA") +refattrB = aVDist3.refattr("ConstraintEntityB") +assert (not aDistance.isInitialized()) +assert (not refattrA.isInitialized()) +assert (not refattrB.isInitialized()) +refattrA.setAttr(aStartPoint) +refattrB.setAttr(aEndPoint) +aDistance.setValue(DISTANCE3) +aSession.finishOperation() +assert math.fabs(verticalDistance(aStartPoint, aEndPoint) - DISTANCE3) < 1.e-5, "Distance values are different: {0} != {1}".format(verticalDistance(aStartPoint, aEndPoint), DISTANCE3) +assert (model.dof(aSketchFeature) == 6) +#========================================================================= +# Change a distance value +#========================================================================= +d = DISTANCE3 +dStep = -5. +while d >= -50.: + aSession.startOperation() + DISTANCE3 = d + aDistance.setValue(DISTANCE3) + aSession.finishOperation() + assert math.fabs(verticalDistance(aStartPoint, aEndPoint) - DISTANCE3) < 1.e-5, "Distance values are different: {0} != {1}".format(verticalDistance(aStartPoint, aEndPoint), DISTANCE3) + d += dStep +assert (model.dof(aSketchFeature) == 6) + +#========================================================================= +# End of test +#========================================================================= + +assert(model.checkPythonDump()) diff --git a/src/SketchPlugin/icons/distance_h.png b/src/SketchPlugin/icons/distance_h.png new file mode 100644 index 0000000000000000000000000000000000000000..b88db2102a89f985a8ddfbe0a219025c626d15bb GIT binary patch literal 428 zcmV;d0aN~oP)xAtJS4%zwvpI@W%dvI&CYLT0T9>{C{w@$cFe`SfRoO3&ZUYm{{XOz^RmDp zaGevlCvYS%n{BA<1;8cX0Q#fiqx5WuP>LWr1UXzJN1f+zNCB7PAz_X|R-| z!TN6vMgRq>Fe2i=Y-)gA^n!Lh1@VU0f4-?Y)tXtxHI?|kA+5brP}{(jz0ht~_MhJF zdbPPtZI!xF;1AMB07QX5MnP3iItZ;yS`_*X-SSlOD9mP)DEtBG=57Pa>JZDh(oOFI z#VYU+g}wtY?|L&poQf<4u#^IAlFz4Y7#^Uk4zZkM5gr3SHd@!Tuhf46@4(BnRpc9~ W#d5}Z$9}N@0000Px#1ZP1_K>z@;j|==^1poj532;bRa{vGh*8l(w*8xH(n|J^K00v@9M??Vs0RI60 zpuMM)00007bV*G`2jK+@1|}w>&v*C$00B`+L_t(IPnDBBO9Md=hG$O{KMED}&{DKg z@S_nVpoj)+lq>B0CpLor!NSH`K}?re2x1{A1O=_EHAjeAgrv!x&+aW@Pjhm<@a*i& z-OkSL-6%o+*50CscP1)+Q(kMoRxUSnJdeC&^bSPHl8D+V)SI`l$a_vUlLVdkg0Tiq z)$y?->~aR{@Cd8czk{^=VlZTCw5W}$zrR8r2=1zKvMYGU=FQ|jB!pddh<`^VPFc`);T>JU0({!ouA9K8sdSm9scxo5H`rzHZ>01M Xi#Bu@Vy%X700000NkvXXu0mjfqK(kK literal 0 HcmV?d00001 diff --git a/src/SketchPlugin/plugin-Sketch.xml b/src/SketchPlugin/plugin-Sketch.xml index e33f3dede..106733478 100644 --- a/src/SketchPlugin/plugin-Sketch.xml +++ b/src/SketchPlugin/plugin-Sketch.xml @@ -10,7 +10,7 @@ SketchEllipse SketchMacroEllipse SketchRectangle SketchProjection - SketchConstraintLength SketchConstraintRadius SketchConstraintDistance + SketchConstraintLength SketchConstraintRadius SketchConstraintDistance SketchConstraintDistanceHorizontal SketchConstraintDistanceVertical SketchConstraintParallel SketchConstraintPerpendicular SketchConstraintRigid SketchConstraintHorizontal SketchConstraintVertical SketchConstraintEqual SketchConstraintTangent @@ -636,7 +636,71 @@ - + + + + + + + + +