From ad093343c313ca7883863b06bcad4b220e8910fb Mon Sep 17 00:00:00 2001 From: azv Date: Fri, 24 Jan 2020 16:58:18 +0300 Subject: [PATCH] Python API for periodic B-splines and unit-tests for creation and modification. --- src/SketchAPI/SketchAPI.i | 2 + src/SketchAPI/SketchAPI_BSpline.cpp | 86 +++----- src/SketchAPI/SketchAPI_BSpline.h | 65 +++--- src/SketchAPI/SketchAPI_Projection.cpp | 3 + src/SketchAPI/SketchAPI_Sketch.cpp | 51 ++--- src/SketchAPI/SketchAPI_Sketch.h | 26 +-- src/SketchAPI/SketchAPI_SketchEntity.cpp | 4 + .../SketchPlugin_MacroBSpline.cpp | 18 +- .../Test/TestConstraintCoincidenceBSpline.py | 176 +++++++++++++++ .../Test/TestConstraintTangentBSpline.py | 8 +- src/SketchPlugin/Test/TestCreateBSpline.py | 26 +-- .../Test/TestCreateBSplinePeriodic.py | 184 ++++++++++++++++ src/SketchPlugin/Test/TestMoveBSpline.py | 2 +- .../Test/TestMoveBSplinePeriodic.py | 205 ++++++++++++++++++ src/SketchPlugin/Test/TestPresentation.py | 19 ++ .../Test/TestProjectionBSplinePeriodic.py | 78 +++++++ src/SketchPlugin/Test/TestRemoveBSpline.py | 8 +- .../Test/TestRemoveBSplinePeriodic.py | 108 +++++++++ .../PlaneGCSSolver_AttributeBuilder.cpp | 7 +- 19 files changed, 902 insertions(+), 174 deletions(-) create mode 100644 src/SketchPlugin/Test/TestConstraintCoincidenceBSpline.py create mode 100644 src/SketchPlugin/Test/TestCreateBSplinePeriodic.py create mode 100644 src/SketchPlugin/Test/TestMoveBSplinePeriodic.py create mode 100644 src/SketchPlugin/Test/TestProjectionBSplinePeriodic.py create mode 100644 src/SketchPlugin/Test/TestRemoveBSplinePeriodic.py diff --git a/src/SketchAPI/SketchAPI.i b/src/SketchAPI/SketchAPI.i index e2a43e68d..ad38d9c55 100644 --- a/src/SketchAPI/SketchAPI.i +++ b/src/SketchAPI/SketchAPI.i @@ -49,6 +49,7 @@ %feature("kwargs") SketchAPI_BSpline::controlPolygon; %feature("kwargs") SketchAPI_Ellipse::construction; %feature("kwargs") SketchAPI_EllipticArc::construction; +%feature("kwargs") SketchAPI_Sketch::addSpline; %feature("kwargs") SketchAPI_Sketch::setAngle; // shared pointers @@ -61,6 +62,7 @@ %shared_ptr(SketchAPI_EllipticArc) %shared_ptr(SketchAPI_MacroEllipticArc) %shared_ptr(SketchAPI_BSpline) +%shared_ptr(SketchAPI_BSplinePeriodic) %shared_ptr(SketchAPI_Constraint) %shared_ptr(SketchAPI_ConstraintAngle) %shared_ptr(SketchAPI_IntersectionPoint) diff --git a/src/SketchAPI/SketchAPI_BSpline.cpp b/src/SketchAPI/SketchAPI_BSpline.cpp index 9023d703b..e804d164b 100644 --- a/src/SketchAPI/SketchAPI_BSpline.cpp +++ b/src/SketchAPI/SketchAPI_BSpline.cpp @@ -44,47 +44,11 @@ SketchAPI_BSpline::SketchAPI_BSpline(const std::shared_ptr & t } SketchAPI_BSpline::SketchAPI_BSpline(const std::shared_ptr& theFeature, - const std::list& thePoles, - const std::list& theWeights) + bool theInitialize) : SketchAPI_SketchEntity(theFeature) { - if (initialize()) { - setByDegreePolesAndWeights(ModelHighAPI_Integer(-1), thePoles, theWeights); - } -} - -SketchAPI_BSpline::SketchAPI_BSpline(const std::shared_ptr& theFeature, - const int theDegree, - const std::list& thePoles, - const std::list& theWeights, - const std::list& theKnots, - const std::list& theMults) - : SketchAPI_SketchEntity(theFeature) -{ - if (initialize()) { - if (theKnots.empty() || theMults.empty()) - setByDegreePolesAndWeights(theDegree, thePoles, theWeights); - else - setByParameters(theDegree, thePoles, theWeights, theKnots, theMults); - } -} - -SketchAPI_BSpline::SketchAPI_BSpline(const std::shared_ptr& theFeature, - const ModelHighAPI_Selection& theExternal) - : SketchAPI_SketchEntity(theFeature) -{ - if (initialize()) { - setByExternal(theExternal); - } -} - -SketchAPI_BSpline::SketchAPI_BSpline(const std::shared_ptr& theFeature, - const std::string& theExternalName) - : SketchAPI_SketchEntity(theFeature) -{ - if (initialize()) { - setByExternalName(theExternalName); - } + if (theInitialize) + initialize(); } SketchAPI_BSpline::~SketchAPI_BSpline() @@ -133,7 +97,8 @@ void SketchAPI_BSpline::setByParameters(const ModelHighAPI_Integer& theDegree, fillAttribute(theKnots, knots()); fillAttribute(theMults, multiplicities()); - setStartAndEndPoints(); + if (feature()->getKind() != SketchPlugin_BSplinePeriodic::ID()) + setStartAndEndPoints(); execute(); } @@ -149,12 +114,6 @@ void SketchAPI_BSpline::setByExternal(const ModelHighAPI_Selection & theExternal execute(); } -void SketchAPI_BSpline::setByExternalName(const std::string & theExternalName) -{ - fillAttribute(ModelHighAPI_Selection("EDGE", theExternalName), external()); - execute(); -} - static CompositeFeaturePtr sketchForFeature(FeaturePtr theFeature) { const std::set& aRefs = theFeature->data()->refsToMe(); @@ -212,8 +171,9 @@ static void createSegment(const CompositeFeaturePtr& theSketch, const bool theAuxiliary, std::list& theEntities) { + int aEndPoleIndex = (theStartPoleIndex + 1) % thePoles->size(); GeomPnt2dPtr aStartPoint = thePoles->pnt(theStartPoleIndex); - GeomPnt2dPtr aEndPoint = thePoles->pnt(theStartPoleIndex + 1); + GeomPnt2dPtr aEndPoint = thePoles->pnt(aEndPoleIndex); FeaturePtr aLineFeature = theSketch->addFeature(SketchPlugin_Line::ID()); AttributePoint2DPtr aLineStart = std::dynamic_pointer_cast( @@ -226,14 +186,14 @@ static void createSegment(const CompositeFeaturePtr& theSketch, aLineFeature->execute(); std::ostringstream aName; - aName << theBSpline->name() << "_segment_" << theStartPoleIndex << "_" << theStartPoleIndex + 1; + aName << theBSpline->name() << "_segment_" << theStartPoleIndex << "_" << aEndPoleIndex; aLineFeature->data()->setName(aName.str()); aLineFeature->lastResult()->data()->setName(aName.str()); aLineFeature->boolean(SketchPlugin_Line::AUXILIARY_ID())->setValue(theAuxiliary); createInternalConstraint(theSketch, aLineStart, thePoles, theStartPoleIndex); - createInternalConstraint(theSketch, aLineEnd, thePoles, theStartPoleIndex + 1); + createInternalConstraint(theSketch, aLineEnd, thePoles, aEndPoleIndex); theEntities.push_back(aLineFeature); } @@ -301,10 +261,13 @@ void SketchAPI_BSpline::getDefaultParameters( it != theWeights.end(); ++it) aWeights.push_back(it->value()); + bool isPeriodic = feature()->getKind() == SketchPlugin_BSplinePeriodic::ID(); if (theDegree.intValue() < 0) - aBSplineCurve.reset(new GeomAPI_BSpline2d(thePoles, aWeights)); - else - aBSplineCurve.reset(new GeomAPI_BSpline2d(theDegree.intValue(), thePoles, aWeights)); + aBSplineCurve.reset(new GeomAPI_BSpline2d(thePoles, aWeights, isPeriodic)); + else { + aBSplineCurve.reset(new GeomAPI_BSpline2d(theDegree.intValue(), thePoles, aWeights, + std::list(), std::list(), isPeriodic)); + } } catch (...) { // cannot build a B-spline curve @@ -441,12 +404,14 @@ void SketchAPI_BSpline::dump(ModelHighAPI_Dumper& theDumper) const theDumper << aBase << " = " << aSketchName << ".addSpline("; if (!isDefaultDegree) - theDumper << degree() << ", "; - theDumper << poles(); + theDumper << "degree = " << degree() << ", "; + theDumper << "poles = " << poles(); if (!isDefaultWeights) - theDumper << ", " << weights(); + theDumper << ", weights = " << weights(); if (!isDefaultKnotsMults) - theDumper << ", " << knots() << ", " << multiplicities(); + theDumper << ", knots = " << knots() << ", multiplicities = " << multiplicities(); + if (aBase->getKind() == SketchPlugin_BSplinePeriodic::ID()) + theDumper << ", periodic = True"; theDumper << ")" << std::endl; } // dump "auxiliary" flag if necessary @@ -507,3 +472,12 @@ void SketchAPI_BSpline::dumpControlPolygon( dumpList(theDumper, "auxiliary", anAuxiliary); theDumper << ")" << std::endl; } + + + +// ================================================================================================= +SketchAPI_BSplinePeriodic::SketchAPI_BSplinePeriodic(const FeaturePtr& theFeature) + : SketchAPI_BSpline(theFeature, false) +{ + initialize(); +} diff --git a/src/SketchAPI/SketchAPI_BSpline.h b/src/SketchAPI/SketchAPI_BSpline.h index a72b2690a..377a7b65d 100644 --- a/src/SketchAPI/SketchAPI_BSpline.h +++ b/src/SketchAPI/SketchAPI_BSpline.h @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -44,51 +45,26 @@ public: SKETCHAPI_EXPORT explicit SketchAPI_BSpline(const std::shared_ptr& theFeature); - /// Constructor with values. - SKETCHAPI_EXPORT SketchAPI_BSpline( - const std::shared_ptr& theFeature, - const std::list >& thePoles, - const std::list& theWeights = std::list()); - - /// Constructor with values. - SKETCHAPI_EXPORT SketchAPI_BSpline( - const std::shared_ptr& theFeature, - const int theDegree, - const std::list >& thePoles, - const std::list& theWeights = std::list(), - const std::list& theKnots = std::list(), - const std::list& theMults = std::list()); - - /// Constructor with external. - SKETCHAPI_EXPORT - SketchAPI_BSpline(const std::shared_ptr& theFeature, - const ModelHighAPI_Selection& theExternal); - - /// Constructor with external. - SKETCHAPI_EXPORT - SketchAPI_BSpline(const std::shared_ptr& theFeature, - const std::string& theExternalName); - /// Destructor. SKETCHAPI_EXPORT virtual ~SketchAPI_BSpline(); INTERFACE_8(SketchPlugin_BSpline::ID(), - poles, SketchPlugin_BSpline::POLES_ID(), + poles, SketchPlugin_BSplineBase::POLES_ID(), GeomDataAPI_Point2DArray, /** B-spline poles */, - weights, SketchPlugin_BSpline::WEIGHTS_ID(), + weights, SketchPlugin_BSplineBase::WEIGHTS_ID(), ModelAPI_AttributeDoubleArray, /** B-spline weights */, - knots, SketchPlugin_BSpline::KNOTS_ID(), + knots, SketchPlugin_BSplineBase::KNOTS_ID(), ModelAPI_AttributeDoubleArray, /** B-spline knots */, - multiplicities, SketchPlugin_BSpline::MULTS_ID(), + multiplicities, SketchPlugin_BSplineBase::MULTS_ID(), ModelAPI_AttributeIntArray, /** Knots multiplicities */, - degree, SketchPlugin_BSpline::DEGREE_ID(), + degree, SketchPlugin_BSplineBase::DEGREE_ID(), ModelAPI_AttributeInteger, /** B-spline degree */, startPoint, SketchPlugin_BSpline::START_ID(), GeomDataAPI_Point2D, /** First pole of B-spline */, endPoint, SketchPlugin_BSpline::END_ID(), GeomDataAPI_Point2D, /** Last pole of B-spline */, - external, SketchPlugin_BSpline::EXTERNAL_ID(), + external, SketchPlugin_BSplineBase::EXTERNAL_ID(), ModelAPI_AttributeSelection, /** External */) /// Set by poles and weights. @@ -109,10 +85,6 @@ public: SKETCHAPI_EXPORT void setByExternal(const ModelHighAPI_Selection& theExternal); - /// Set by external name. - SKETCHAPI_EXPORT - void setByExternalName(const std::string& theExternalName); - /// Generate list of construction points coincident with B-spline poles SKETCHAPI_EXPORT std::list > controlPoles( @@ -129,6 +101,9 @@ public: SKETCHAPI_EXPORT virtual void dump(ModelHighAPI_Dumper& theDumper) const; +protected: + SketchAPI_BSpline(const std::shared_ptr& theFeature, bool theInitialize); + private: /// Initialize start and end points of B-spline and apply internal coincidence /// constraint to keep them on the corresponding pole. @@ -156,4 +131,24 @@ private: /// Pointer on B-spline object. typedef std::shared_ptr BSplinePtr; + + +/// \class SketchAPI_BSplinePeriodic +/// \ingroup CPPHighAPI +/// \brief Interface for BSplinePeriodic feature. +class SketchAPI_BSplinePeriodic : public SketchAPI_BSpline +{ +public: + /// Constructor without values. + SKETCHAPI_EXPORT + explicit SketchAPI_BSplinePeriodic(const std::shared_ptr& theFeature); + + /// Destructor. + SKETCHAPI_EXPORT + virtual ~SketchAPI_BSplinePeriodic() {} + + static std::string ID() { return SketchPlugin_BSplinePeriodic::ID(); } + virtual std::string getID() { return SketchPlugin_BSplinePeriodic::ID(); } +}; + #endif // SketchAPI_BSpline_H_ diff --git a/src/SketchAPI/SketchAPI_Projection.cpp b/src/SketchAPI/SketchAPI_Projection.cpp index 3e1a3f789..aa94fa895 100644 --- a/src/SketchAPI/SketchAPI_Projection.cpp +++ b/src/SketchAPI/SketchAPI_Projection.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -112,6 +113,8 @@ std::shared_ptr SketchAPI_Projection::createdFeature() c anEntity.reset(new SketchAPI_EllipticArc(aProjectedFeature)); else if (aProjectedFeature->getKind() == SketchPlugin_BSpline::ID()) anEntity.reset(new SketchAPI_BSpline(aProjectedFeature)); + else if (aProjectedFeature->getKind() == SketchPlugin_BSplinePeriodic::ID()) + anEntity.reset(new SketchAPI_BSplinePeriodic(aProjectedFeature)); else if (aProjectedFeature->getKind() == SketchPlugin_Point::ID()) anEntity.reset(new SketchAPI_Point(aProjectedFeature)); diff --git a/src/SketchAPI/SketchAPI_Sketch.cpp b/src/SketchAPI/SketchAPI_Sketch.cpp index f64f93fc6..a93c26e05 100644 --- a/src/SketchAPI/SketchAPI_Sketch.cpp +++ b/src/SketchAPI/SketchAPI_Sketch.cpp @@ -699,37 +699,29 @@ std::shared_ptr SketchAPI_Sketch::addEllipticArc( } //-------------------------------------------------------------------------------------- -std::shared_ptr SketchAPI_Sketch::addSpline( - const std::list >& thePoles, - const std::list& theWeights) -{ - FeaturePtr aFeature = compositeFeature()->addFeature(SketchPlugin_BSpline::ID()); - return BSplinePtr(new SketchAPI_BSpline(aFeature, thePoles, theWeights)); -} - -std::shared_ptr SketchAPI_Sketch::addSpline( - const int theDegree, - const std::list >& thePoles, - const std::list& theWeights, - const std::list& theKnots, - const std::list& theMults) -{ - FeaturePtr aFeature = compositeFeature()->addFeature(SketchPlugin_BSpline::ID()); - return BSplinePtr(new SketchAPI_BSpline(aFeature, - theDegree, thePoles, theWeights, theKnots, theMults)); -} std::shared_ptr SketchAPI_Sketch::addSpline( - const ModelHighAPI_Selection & theExternal) -{ - FeaturePtr aFeature = compositeFeature()->addFeature(SketchPlugin_BSpline::ID()); - return BSplinePtr(new SketchAPI_BSpline(aFeature, theExternal)); -} + const ModelHighAPI_Selection & external, + const int degree, + const std::list >& poles, + const std::list& weights, + const std::list& knots, + const std::list& multiplicities, + const bool periodic) +{ + FeaturePtr aFeature = compositeFeature()->addFeature( + periodic ? SketchPlugin_BSplinePeriodic::ID() : SketchPlugin_BSpline::ID()); + + BSplinePtr aBSpline(periodic ? new SketchAPI_BSplinePeriodic(aFeature) + : new SketchAPI_BSpline(aFeature)); + if (external.variantType() != ModelHighAPI_Selection::VT_Empty) + aBSpline->setByExternal(external); + else if (knots.empty() || multiplicities.empty()) + aBSpline->setByDegreePolesAndWeights(degree, poles, weights); + else + aBSpline->setByParameters(degree, poles, weights, knots, multiplicities); -std::shared_ptr SketchAPI_Sketch::addSpline(const std::string & theExternalName) -{ - FeaturePtr aFeature = compositeFeature()->addFeature(SketchPlugin_BSpline::ID()); - return BSplinePtr(new SketchAPI_BSpline(aFeature, theExternalName)); + return aBSpline; } //-------------------------------------------------------------------------------------- @@ -1250,7 +1242,8 @@ static std::shared_ptr middlePoint(const ObjectPtr& theObject, aMiddlePoint = pointOnEllipse(aFeature); else if (aFeatureKind == SketchPlugin_EllipticArc::ID()) aMiddlePoint = pointOnEllipse(aFeature, false); - else if (aFeatureKind == SketchPlugin_BSpline::ID()) + else if (aFeatureKind == SketchPlugin_BSpline::ID() || + aFeatureKind == SketchPlugin_BSplinePeriodic::ID()) aMiddlePoint = middlePointOnBSpline(aFeature, theSketch); } return aMiddlePoint; diff --git a/src/SketchAPI/SketchAPI_Sketch.h b/src/SketchAPI/SketchAPI_Sketch.h index 4ea404be6..ddbb18291 100644 --- a/src/SketchAPI/SketchAPI_Sketch.h +++ b/src/SketchAPI/SketchAPI_Sketch.h @@ -30,6 +30,7 @@ #include #include +#include //-------------------------------------------------------------------------------------- class ModelAPI_CompositeFeature; class ModelAPI_Object; @@ -37,7 +38,6 @@ class ModelHighAPI_Double; class ModelHighAPI_Integer; class ModelHighAPI_RefAttr; class ModelHighAPI_Reference; -class ModelHighAPI_Selection; class SketchAPI_Arc; class SketchAPI_MacroArc; class SketchAPI_Circle; @@ -328,22 +328,14 @@ public: /// Add B-spline SKETCHAPI_EXPORT std::shared_ptr addSpline( - const std::list >& thePoles, - const std::list& theWeights = std::list()); - /// Add B-spline - SKETCHAPI_EXPORT - std::shared_ptr addSpline( - const int theDegree, - const std::list >& thePoles, - const std::list& theWeights = std::list(), - const std::list& theKnots = std::list(), - const std::list& theMults = std::list()); - /// Add B-spline - SKETCHAPI_EXPORT - std::shared_ptr addSpline(const ModelHighAPI_Selection & theExternal); - /// Add B-spline - SKETCHAPI_EXPORT - std::shared_ptr addSpline(const std::string & theExternalName); + const ModelHighAPI_Selection & external = ModelHighAPI_Selection(), + const int degree = -1, + const std::list >& poles = + std::list >(), + const std::list& weights = std::list(), + const std::list& knots = std::list(), + const std::list& multiplicities = std::list(), + const bool periodic = false); /// Add projection SKETCHAPI_EXPORT diff --git a/src/SketchAPI/SketchAPI_SketchEntity.cpp b/src/SketchAPI/SketchAPI_SketchEntity.cpp index d169c52de..fa56e6a5c 100644 --- a/src/SketchAPI/SketchAPI_SketchEntity.cpp +++ b/src/SketchAPI/SketchAPI_SketchEntity.cpp @@ -107,6 +107,10 @@ SketchAPI_SketchEntity::wrap(const std::list > aResult.push_back(std::shared_ptr(new SketchAPI_EllipticArc(*anIt))); else if ((*anIt)->getKind() == SketchPlugin_BSpline::ID()) aResult.push_back(std::shared_ptr(new SketchAPI_BSpline(*anIt))); + else if ((*anIt)->getKind() == SketchPlugin_BSplinePeriodic::ID()) { + aResult.push_back( + std::shared_ptr(new SketchAPI_BSplinePeriodic(*anIt))); + } else if ((*anIt)->getKind() == SketchPlugin_Point::ID()) aResult.push_back(std::shared_ptr(new SketchAPI_Point(*anIt))); else if ((*anIt)->getKind() == SketchPlugin_IntersectionPoint::ID()) diff --git a/src/SketchPlugin/SketchPlugin_MacroBSpline.cpp b/src/SketchPlugin/SketchPlugin_MacroBSpline.cpp index 5f325dedf..a7591e3c5 100644 --- a/src/SketchPlugin/SketchPlugin_MacroBSpline.cpp +++ b/src/SketchPlugin/SketchPlugin_MacroBSpline.cpp @@ -154,35 +154,31 @@ FeaturePtr SketchPlugin_MacroBSpline::createBSplineFeature() FeaturePtr aBSpline = sketch()->addFeature( myIsPeriodic ? SketchPlugin_BSplinePeriodic::ID() : SketchPlugin_BSpline::ID()); - aBSpline->integer(myIsPeriodic ? SketchPlugin_BSplinePeriodic::DEGREE_ID() - : SketchPlugin_BSpline::DEGREE_ID())->setValue(myDegree); + aBSpline->integer(SketchPlugin_BSplineBase::DEGREE_ID())->setValue(myDegree); AttributePoint2DArrayPtr aPoles = std::dynamic_pointer_cast( - aBSpline->attribute(myIsPeriodic ? SketchPlugin_BSplinePeriodic::POLES_ID() - : SketchPlugin_BSpline::POLES_ID())); + aBSpline->attribute(SketchPlugin_BSplineBase::POLES_ID())); AttributePoint2DArrayPtr aPolesMacro = std::dynamic_pointer_cast(attribute(POLES_ID())); aPoles->assign(aPolesMacro); - AttributeDoubleArrayPtr aWeights = aBSpline->data()->realArray( - myIsPeriodic ? SketchPlugin_BSplinePeriodic::WEIGHTS_ID() - : SketchPlugin_BSpline::WEIGHTS_ID()); + AttributeDoubleArrayPtr aWeights = + aBSpline->data()->realArray(SketchPlugin_BSplineBase::WEIGHTS_ID()); AttributeDoubleArrayPtr aWeightsMacro = data()->realArray(WEIGHTS_ID()); int aSize = aWeightsMacro->size(); aWeights->setSize(aSize); for (int index = 0; index < aSize; ++index) aWeights->setValue(index, aWeightsMacro->value(index)); - AttributeDoubleArrayPtr aKnots = aBSpline->data()->realArray( - myIsPeriodic ? SketchPlugin_BSplinePeriodic::KNOTS_ID() : SketchPlugin_BSpline::KNOTS_ID()); + AttributeDoubleArrayPtr aKnots = + aBSpline->data()->realArray(SketchPlugin_BSplineBase::KNOTS_ID()); aSize = (int)myKnots.size(); aKnots->setSize(aSize); std::list::iterator aKIt = myKnots.begin(); for (int index = 0; index < aSize; ++index, ++aKIt) aKnots->setValue(index, *aKIt); - AttributeIntArrayPtr aMults = aBSpline->data()->intArray( - myIsPeriodic ? SketchPlugin_BSplinePeriodic::MULTS_ID() : SketchPlugin_BSpline::MULTS_ID()); + AttributeIntArrayPtr aMults = aBSpline->data()->intArray(SketchPlugin_BSplineBase::MULTS_ID()); aSize = (int)myMultiplicities.size(); aMults->setSize(aSize); std::list::iterator aMIt = myMultiplicities.begin(); diff --git a/src/SketchPlugin/Test/TestConstraintCoincidenceBSpline.py b/src/SketchPlugin/Test/TestConstraintCoincidenceBSpline.py new file mode 100644 index 000000000..80ab412e2 --- /dev/null +++ b/src/SketchPlugin/Test/TestConstraintCoincidenceBSpline.py @@ -0,0 +1,176 @@ +# Copyright (C) 2019-2020 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +""" + Test constraint coincidence applied for B-spline curve and its sub-results +""" + +import unittest +import math + +from salome.shaper import model + +from GeomAPI import * +from SketchAPI import * + +__updated__ = "2020-01-21" + +class TestCoincidenceBSpline(unittest.TestCase): + def setUp(self): + model.begin() + self.myDocument = model.moduleDocument() + self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY")) + + self.myPoles = [GeomAPI_Pnt2d(-10, -30), GeomAPI_Pnt2d(20, -15), GeomAPI_Pnt2d(-10, 0), GeomAPI_Pnt2d(20, 15), GeomAPI_Pnt2d(-10, 30)] + self.myWeights = [1, 3, 5, 3, 1] + self.mySpline = self.mySketch.addSpline(poles = self.myPoles, weights = self.myWeights) + self.myControlPoles = self.mySpline.controlPoles(auxiliary = [0, 1, 2, 3, 4]) + self.myControlLines = self.mySpline.controlPolygon(auxiliary = [0, 1, 2, 3]) + + self.myDOF = len(self.myPoles) * 2 + self.myOrigin = self.mySketch.addPoint("Origin") + self.myOX = self.mySketch.addLine("OX") + model.do() + self.myExpectFailure = False + self.myNbPoints = len(self.myPoles) + 1 + self.myNbLines = len(self.myPoles) + self.myNbBSplines = 1 + self.myNbInternalConstraints = len(self.myPoles) * 3 - 2 + self.myNbCoincidences = 1 + + def tearDown(self): + model.end() + if self.myExpectFailure: + assert(self.mySketch.solverError() != ""), "PlaneGCS limitation: if you see this message, then PlaneGCS has solved the set of constraints correctly" + model.undo() + else: + self.checkDOF() + model.testNbSubFeatures(self.mySketch, "SketchPoint", self.myNbPoints) + model.testNbSubFeatures(self.mySketch, "SketchLine", self.myNbLines) + model.testNbSubFeatures(self.mySketch, "SketchBSpline", self.myNbBSplines) + model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidenceInternal", self.myNbInternalConstraints) + model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", self.myNbCoincidences) + + + def checkDOF(self): + self.assertEqual(model.dof(self.mySketch), self.myDOF) + + def setCoincidentWithOrigin(self, thePoint): + self.mySketch.setCoincident(thePoint, self.myOrigin.coordinates()) + self.myDOF -= 2 + model.do() + + def setCoincidentWithOX(self, thePoint): + self.mySketch.setCoincident(thePoint, self.myOX.result()) + self.myDOF -= 1 + model.do() + + def assertPoles(self): + poles = self.mySpline.poles() + assert(poles.size() == len(self.myPoles)) + for index in range(0, len(self.myPoles)): + self.assertPoints(self.myPoles[index], poles.pnt(index)) + + def assertPoints(self, thePoint1, thePoint2): + self.assertAlmostEqual(thePoint1.x(), thePoint2.x()) + self.assertAlmostEqual(thePoint1.y(), thePoint2.y()) + + def assertPointOnLine(self, thePoint, theLineStart, theLineEnd): + vecP = [thePoint.x() - theLineStart.x(), thePoint.y() - theLineStart.y()] + vecL = [theLineEnd.x() - theLineStart.x(), theLineEnd.y() - theLineStart.y()] + dist = math.fabs(vecP[0] * vecL[1] - vecP[1] * vecL[0]) / math.hypot(vecL[0], vecL[1]) + self.assertAlmostEqual(dist, 0.0) + + def assertPointOnSpline(self, thePoint, theSpline): + point = GeomAPI_Pnt(thePoint.x(), thePoint.y(), 0.0) + bspline = GeomAPI_BSpline(GeomAPI_Curve(theSpline.results()[-1].resultSubShapePair()[0].shape())) + proj = bspline.project(point) + self.assertAlmostEqual(point.distance(proj), 0.0) + + + def test_origin_equal_start_point(self): + """ Test 1. Make start point of B-spline coincident with the Origin + """ + self.setCoincidentWithOrigin(self.mySpline.startPoint()) + self.myPoles[0].setX(0) + self.myPoles[0].setY(0) + self.assertPoles() + + def test_origin_equal_end_point(self): + """ Test 2. Make end point of B-spline coincident with the Origin + """ + self.setCoincidentWithOrigin(self.mySpline.endPoint()) + self.myPoles[-1].setX(0) + self.myPoles[-1].setY(0) + self.assertPoles() + + def test_origin_equal_pole(self): + """ Test 3. Make one of B-spline poles coincident with the Origin + """ + self.setCoincidentWithOrigin(SketchAPI_Point(self.myControlPoles[1]).coordinates()) + self.myPoles[1].setX(0) + self.myPoles[1].setY(0) + self.assertPoles() + + def test_origin_on_bspline(self): + """ Test 4. (expected failure) Make Origin lying on the B-spline curve + """ + self.mySketch.setCoincident(self.mySpline.defaultResult(), self.myOrigin.coordinates()) + self.myDOF -= 1 + model.do() + self.myExpectFailure = True + + def test_point_on_bspline(self): + """ Test 5. Place free point on the B-spline curve + """ + point = self.mySketch.addPoint(1, 0) + self.mySketch.setCoincident(self.myOX.defaultResult(), point.coordinates()) + self.mySketch.setCoincident(self.mySpline.defaultResult(), point.coordinates()) + model.do() + self.myNbPoints += 1 + self.myNbCoincidences += 1 + self.assertPointOnSpline(point.coordinates(), self.mySpline) + + + def test_start_point_on_axis(self): + """ Test 6. Make start point of B-spline coincident with the OX + """ + self.setCoincidentWithOX(self.mySpline.startPoint()) + self.myPoles[0].setY(0) + self.assertPoles() + + def test_end_point_on_axis(self): + """ Test 7. Make end point of B-spline coincident with the OX + """ + self.setCoincidentWithOX(self.mySpline.endPoint()) + self.myPoles[-1].setY(0) + self.assertPoles() + + def test_pole_on_axis(self): + """ Test 8. Make one of B-spline poles coincident with the OX + """ + self.setCoincidentWithOX(SketchAPI_Point(self.myControlPoles[1]).coordinates()) + self.myPoles[1].setY(0) + self.assertPoles() + + +if __name__ == "__main__": + test_program = unittest.main(exit=False) + assert test_program.result.wasSuccessful(), "Test failed" + assert model.checkPythonDump() diff --git a/src/SketchPlugin/Test/TestConstraintTangentBSpline.py b/src/SketchPlugin/Test/TestConstraintTangentBSpline.py index 0937fe247..c5effe8c1 100644 --- a/src/SketchPlugin/Test/TestConstraintTangentBSpline.py +++ b/src/SketchPlugin/Test/TestConstraintTangentBSpline.py @@ -39,7 +39,7 @@ class TestTangentBSpline(unittest.TestCase): self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY")) self.myPoles = [GeomAPI_Pnt2d(-10, -30), GeomAPI_Pnt2d(20, -15), GeomAPI_Pnt2d(-10, 0), GeomAPI_Pnt2d(20, 15), GeomAPI_Pnt2d(-10, 30)] self.myWeights = [1, 3, 5, 3, 1] - self.mySpline = self.mySketch.addSpline(self.myPoles, self.myWeights) + self.mySpline = self.mySketch.addSpline(poles = self.myPoles, weights = self.myWeights) self.myControlPoles = self.mySpline.controlPoles(auxiliary = [0, 1, 2, 3, 4]) self.myControlLines = self.mySpline.controlPolygon(auxiliary = [0, 1, 2, 3]) model.do() @@ -188,7 +188,7 @@ class TestTangentBSpline(unittest.TestCase): def test_spline_tangent(self): """ Test 6. Set tangency between two B-spline curves """ - aSpline = self.mySketch.addSpline([(50, -20), (40, 0), (50, 20)]) + aSpline = self.mySketch.addSpline(poles = [(50, -20), (40, 0), (50, 20)]) self.myNbBSplines += 1 self.myDOF += aSpline.poles().size() * 2 model.do() @@ -367,7 +367,7 @@ class TestTangentBSpline(unittest.TestCase): def test_spline_tangent_coincident_by_boundaries(self): """ Test 15. Set tangency between two B-spline curves coincident with B-spline start point """ - aSpline = self.mySketch.addSpline([(50, -20), (40, 0), (50, 20)]) + aSpline = self.mySketch.addSpline(poles = [(50, -20), (40, 0), (50, 20)]) self.myNbBSplines += 1 self.myDOF += aSpline.poles().size() * 2 model.do() @@ -449,7 +449,7 @@ class TestTangentBSpline(unittest.TestCase): def test_spline_tangent_coincident_by_aux(self): """ Test 19. Set tangency between two B-spline curves coincident with B-spline start point """ - aSpline = self.mySketch.addSpline([(50, -20), (40, 0), (50, 20)]) + aSpline = self.mySketch.addSpline(poles = [(50, -20), (40, 0), (50, 20)]) self.myNbBSplines += 1 self.myDOF += aSpline.poles().size() * 2 model.do() diff --git a/src/SketchPlugin/Test/TestCreateBSpline.py b/src/SketchPlugin/Test/TestCreateBSpline.py index 413c84b44..0aa12ee45 100644 --- a/src/SketchPlugin/Test/TestCreateBSpline.py +++ b/src/SketchPlugin/Test/TestCreateBSpline.py @@ -57,7 +57,7 @@ class TestBSpline(unittest.TestCase): def test_bspline_by_coordinates(self): """ Test 1. Create B-spline curve by coordinates of its poles """ - self.mySpline = self.mySketch.addSpline(self.myPolesCoordinates) + self.mySpline = self.mySketch.addSpline(poles = self.myPolesCoordinates) self.myDOF += len(self.myPolesCoordinates) * 2 self.myNbSplines += 1 model.do() @@ -69,7 +69,7 @@ class TestBSpline(unittest.TestCase): def test_bspline_by_poles(self): """ Test 2. Create B-spline curve by poles """ - self.mySpline = self.mySketch.addSpline(self.myPoles) + self.mySpline = self.mySketch.addSpline(poles = self.myPoles) self.myDOF += len(self.myPoles) * 2 self.myNbSplines += 1 model.do() @@ -82,7 +82,7 @@ class TestBSpline(unittest.TestCase): """ Test 3. Create B-spline curve by poles and degree """ self.myDegree = 4 - self.mySpline = self.mySketch.addSpline(self.myDegree, self.myPoles) + self.mySpline = self.mySketch.addSpline(degree = self.myDegree, poles = self.myPoles) self.myDOF += len(self.myPoles) * 2 self.myNbSplines += 1 model.do() @@ -94,7 +94,7 @@ class TestBSpline(unittest.TestCase): def test_bspline_by_poles_and_weights(self): """ Test 4. Create B-spline curve by poles and weights """ - self.mySpline = self.mySketch.addSpline(self.myPoles, [1, 2, 3, 2, 1]) + self.mySpline = self.mySketch.addSpline(poles = self.myPoles, weights = [1, 2, 3, 2, 1]) self.myDOF += len(self.myPoles) * 2 self.myNbSplines += 1 model.do() @@ -122,11 +122,11 @@ class TestBSpline(unittest.TestCase): (42.6953266459619, 76.8712491781612), (54.1042604888826, 93.164895304981) ] - self.mySpline = self.mySketch.addSpline(self.myDegree, - self.myPolesCoordinates, - [1, 1, 1, 1, 1, 1, 0.957903314642061, 0.95790331464206, 1, 1, 1, 1, 1, 1], - [-494.543457494654, 500, 507.372773368102, 1501.91623086297], - [6, 4, 4, 6]) + self.mySpline = self.mySketch.addSpline(degree = self.myDegree, + poles = self.myPolesCoordinates, + weights = [1, 1, 1, 1, 1, 1, 0.957903314642061, 0.95790331464206, 1, 1, 1, 1, 1, 1], + knots = [-494.543457494654, 500, 507.372773368102, 1501.91623086297], + multiplicities = [6, 4, 4, 6]) self.myDOF += len(self.myPolesCoordinates) * 2 self.myNbSplines += 1 model.do() @@ -139,7 +139,7 @@ class TestBSpline(unittest.TestCase): """ Test 6. Create B-spline curve by 2 poles """ self.myDegree = 1 - self.mySpline = self.mySketch.addSpline(self.myPoles[:2]) + self.mySpline = self.mySketch.addSpline(poles = self.myPoles[:2]) self.myDOF += 4 self.myNbSplines += 1 model.do() @@ -152,7 +152,7 @@ class TestBSpline(unittest.TestCase): """ Test 7. Create B-spline curve by 3 poles """ self.myDegree = 2 - self.mySpline = self.mySketch.addSpline(self.myPoles[:3]) + self.mySpline = self.mySketch.addSpline(poles = self.myPoles[:3]) self.myDOF += 6 self.myNbSplines += 1 model.do() @@ -164,7 +164,7 @@ class TestBSpline(unittest.TestCase): def test_bspline_with_poles(self): """ Test 8. Create B-spline curve and points coincident with its poles """ - self.mySpline = self.mySketch.addSpline(self.myPoles) + self.mySpline = self.mySketch.addSpline(poles = self.myPoles) self.mySpline.controlPoles(regular = [0, 2], auxiliary = [1, 3]) self.myDOF += len(self.myPoles) * 2 self.myNbSplines += 1 @@ -178,7 +178,7 @@ class TestBSpline(unittest.TestCase): def test_bspline_with_polygon(self): """ Test 9. Create B-spline curve and its control polygon """ - self.mySpline = self.mySketch.addSpline(self.myPoles) + self.mySpline = self.mySketch.addSpline(poles = self.myPoles) self.mySpline.controlPolygon(regular = [0, 2], auxiliary = [1, 3]) self.myDOF += len(self.myPoles) * 2 self.myNbSplines += 1 diff --git a/src/SketchPlugin/Test/TestCreateBSplinePeriodic.py b/src/SketchPlugin/Test/TestCreateBSplinePeriodic.py new file mode 100644 index 000000000..7cec4b01f --- /dev/null +++ b/src/SketchPlugin/Test/TestCreateBSplinePeriodic.py @@ -0,0 +1,184 @@ +# Copyright (C) 2020 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +""" + Test creation of periodic B-spline curve +""" + +import unittest +from salome.shaper import model + +from GeomAPI import * +from SketchAPI import * + +__updated__ = "2020-01-24" + +class TestBSplinePeriodic(unittest.TestCase): + def setUp(self): + model.begin() + self.myDocument = model.moduleDocument() + self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY")) + self.myPoles = [GeomAPI_Pnt2d(50., 50.), GeomAPI_Pnt2d(70., 70.), GeomAPI_Pnt2d(80., 30.), GeomAPI_Pnt2d(50., 10.), GeomAPI_Pnt2d(10., -30.)] + self.myPolesCoordinates = [(50., 50.), (70., 70.), (80., 30.), (50., 10.), (10., -30.)] + self.myDegree = 3; + self.myDOF = 0 + self.myNbPoints = 0 + self.myNbLines = 0 + self.myNbSplines = 0 + + def tearDown(self): + self.checkDOF() + model.end() + model.testNbSubFeatures(self.mySketch, "SketchPoint", self.myNbPoints) + model.testNbSubFeatures(self.mySketch, "SketchLine", self.myNbLines) + model.testNbSubFeatures(self.mySketch, "SketchBSplinePeriodic", self.myNbSplines) + + + def checkDOF(self): + self.assertEqual(model.dof(self.mySketch), self.myDOF) + + + def test_bspline_by_coordinates(self): + """ Test 1. Create B-spline curve by coordinates of its poles + """ + self.mySpline = self.mySketch.addSpline(poles = self.myPolesCoordinates, periodic = True) + self.myDOF += len(self.myPolesCoordinates) * 2 + self.myNbSplines += 1 + model.do() + + assert(self.mySpline.feature()) + assert(self.mySpline.feature().error() == "") + assert(self.mySpline.degree().value() == self.myDegree) + + def test_bspline_by_poles(self): + """ Test 2. Create B-spline curve by poles + """ + self.mySpline = self.mySketch.addSpline(poles = self.myPoles, periodic = True) + self.myDOF += len(self.myPoles) * 2 + self.myNbSplines += 1 + model.do() + + assert(self.mySpline.feature()) + assert(self.mySpline.feature().error() == "") + assert(self.mySpline.degree().value() == self.myDegree) + + def test_bspline_by_degree_and_poles(self): + """ Test 3. Create B-spline curve by poles and degree + """ + self.myDegree = 4 + self.mySpline = self.mySketch.addSpline(degree = self.myDegree, poles = self.myPoles, periodic = True) + self.myDOF += len(self.myPoles) * 2 + self.myNbSplines += 1 + model.do() + + assert(self.mySpline.feature()) + assert(self.mySpline.feature().error() == "") + assert(self.mySpline.degree().value() == self.myDegree) + + def test_bspline_by_poles_and_weights(self): + """ Test 4. Create B-spline curve by poles and weights + """ + self.mySpline = self.mySketch.addSpline(poles = self.myPoles, weights = [1, 2, 3, 2, 1], periodic = True) + self.myDOF += len(self.myPoles) * 2 + self.myNbSplines += 1 + model.do() + + assert(self.mySpline.feature()) + assert(self.mySpline.feature().error() == "") + assert(self.mySpline.degree().value() == self.myDegree) + + def test_bspline_by_parametric(self): + """ Test 5. Create B-spline curve by whole set of parameters + """ + self.myDegree = 3 + self.myPolesCoordinates = [(-10, 0), (-20, 20), (0, 10), (20, 20), + (10, 0), (20, -20), (0, -10), (-20, -20) + ] + self.mySpline = self.mySketch.addSpline(degree = self.myDegree, + poles = self.myPolesCoordinates, + weights = [1, 1, 1, 1, 1, 1, 1, 1], + knots = [0, 1, 2, 3, 4, 5, 6, 7, 8], + multiplicities = [1, 1, 1, 1, 1, 1, 1, 1, 1], + periodic = True) + self.myDOF += len(self.myPolesCoordinates) * 2 + self.myNbSplines += 1 + model.do() + + assert(self.mySpline.feature()) + assert(self.mySpline.feature().error() == "") + assert(self.mySpline.degree().value() == self.myDegree) + + def test_bspline_linear(self): + """ Test 6. Create B-spline curve by 2 poles + """ + self.myDegree = 1 + self.mySpline = self.mySketch.addSpline(poles = self.myPoles[:2], periodic = True) + self.myDOF += 4 + self.myNbSplines += 1 + model.do() + + assert(self.mySpline.feature()) + assert(self.mySpline.feature().error() == "") + assert(self.mySpline.degree().value() == self.myDegree) + + def test_bspline_parabola(self): + """ Test 7. Create B-spline curve by 3 poles + """ + self.myDegree = 2 + self.mySpline = self.mySketch.addSpline(poles = self.myPoles[:3], periodic = True) + self.myDOF += 6 + self.myNbSplines += 1 + model.do() + + assert(self.mySpline.feature()) + assert(self.mySpline.feature().error() == "") + assert(self.mySpline.degree().value() == self.myDegree) + + def test_bspline_with_poles(self): + """ Test 8. Create B-spline curve and points coincident with its poles + """ + self.mySpline = self.mySketch.addSpline(poles = self.myPoles, periodic = True) + self.mySpline.controlPoles(regular = [0, 2], auxiliary = [1, 3]) + self.myDOF += len(self.myPoles) * 2 + self.myNbSplines += 1 + self.myNbPoints += 4 + model.do() + + assert(self.mySpline.feature()) + assert(self.mySpline.feature().error() == "") + assert(self.mySpline.degree().value() == self.myDegree) + + def test_bspline_with_polygon(self): + """ Test 9. Create B-spline curve and its control polygon + """ + self.mySpline = self.mySketch.addSpline(poles = self.myPoles, periodic = True) + self.mySpline.controlPolygon(regular = [0, 2], auxiliary = [1, 3]) + self.myDOF += len(self.myPoles) * 2 + self.myNbSplines += 1 + self.myNbLines += 4 + model.do() + + assert(self.mySpline.feature()) + assert(self.mySpline.feature().error() == "") + assert(self.mySpline.degree().value() == self.myDegree) + +if __name__ == "__main__": + test_program = unittest.main(exit=False) + assert test_program.result.wasSuccessful(), "Test failed" + assert model.checkPythonDump() diff --git a/src/SketchPlugin/Test/TestMoveBSpline.py b/src/SketchPlugin/Test/TestMoveBSpline.py index eb71bffc8..3e01ac72d 100644 --- a/src/SketchPlugin/Test/TestMoveBSpline.py +++ b/src/SketchPlugin/Test/TestMoveBSpline.py @@ -40,7 +40,7 @@ class TestMoveBSpline(unittest.TestCase): GeomAPI_Pnt2d(80., 30.), GeomAPI_Pnt2d(50., 10.), GeomAPI_Pnt2d(90., -30.)] - self.mySpline = self.mySketch.addSpline(self.myPoles) + self.mySpline = self.mySketch.addSpline(poles = self.myPoles) self.myDOF = len(self.myPoles) * 2 model.do() self.checkDOF() diff --git a/src/SketchPlugin/Test/TestMoveBSplinePeriodic.py b/src/SketchPlugin/Test/TestMoveBSplinePeriodic.py new file mode 100644 index 000000000..d7c702a6f --- /dev/null +++ b/src/SketchPlugin/Test/TestMoveBSplinePeriodic.py @@ -0,0 +1,205 @@ +# Copyright (C) 2019-2020 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +""" + Test movement of the periodic B-spline curve +""" + +import unittest +import math +from GeomAPI import * +from GeomDataAPI import geomDataAPI_Point2DArray +from SketchAPI import * +from salome.shaper import model + +__updated__ = "2020-01-20" + +class TestMoveBSpline(unittest.TestCase): + def setUp(self): + model.begin() + self.myDocument = model.moduleDocument() + self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY")) + self.myPoles = [GeomAPI_Pnt2d(20., 50.), + GeomAPI_Pnt2d(70., 70.), + GeomAPI_Pnt2d(80., 30.), + GeomAPI_Pnt2d(50., -10.), + GeomAPI_Pnt2d(90., -30.)] + self.mySpline = self.mySketch.addSpline(poles = self.myPoles, periodic = True) + self.myDOF = len(self.myPoles) * 2 + model.do() + self.checkDOF() + + def tearDown(self): + self.checkDOF() + model.end() + + def checkDOF(self): + self.assertEqual(model.dof(self.mySketch), self.myDOF) + + def checkPointCoordinates(self, thePoint, theCoordinates): + aCoord = [] + if issubclass(type(theCoordinates), GeomAPI_Pnt2d): + aCoord = [theCoordinates.x(), theCoordinates.y()] + else: + aCoord = theCoordinates + DIGITS = 7 - math.floor(math.log10(math.hypot(aCoord[0], aCoord[1]))) + self.assertAlmostEqual(thePoint.x(), aCoord[0], DIGITS) + self.assertAlmostEqual(thePoint.y(), aCoord[1], DIGITS) + + def checkPoles(self, theBSpline, theCoordinates): + poles = theBSpline.poles() + for index, point in zip(range(0, len(theCoordinates)), theCoordinates): + self.checkPointCoordinates(poles.pnt(index), point) + + def fixPoint(self, thePoint): + self.mySketch.setFixed(thePoint) + self.myDOF -= 2 + model.do() + self.checkDOF() + + + def test_move_free_bspline(self): + """ Test 1. Movement of a free B-spline dragging the edge + """ + oldPosition = GeomAPI_Edge(self.mySpline.defaultResult().shape()).middlePoint() + newPosition = GeomAPI_Pnt2d(120., 90.) + self.mySketch.move(self.mySpline.defaultResult(), newPosition) + model.do() + + # plane is XOY, no need to project oldPosition point + dx = newPosition.x() - oldPosition.x() + dy = newPosition.y() - oldPosition.y() + + newPoles = [] + for pole in self.myPoles: + newPoles.append(GeomAPI_Pnt2d(pole.x() + dx, pole.y() + dy)) + self.checkPoles(self.mySpline, newPoles) + + + def test_move_fixed_bspline(self): + """ Test 2. Movement of a fully fixed B-spline + """ + self.mySketch.setFixed(self.mySpline.defaultResult()) + self.myDOF = 0 + model.do() + + newPosition = GeomAPI_Pnt2d(120., 90.) + self.mySketch.move(self.mySpline.defaultResult(), newPosition) + model.do() + + self.checkPoles(self.mySpline, self.myPoles) + + + def test_move_bspline_with_fixed_pole(self): + """ Test 3. Movement of a B-spline curve with fixed pole + """ + [Point_1, Point_2, Point_3, Point_4, Point_5] = self.mySpline.controlPoles(auxiliary = [0, 1, 2, 3, 4]) + model.do() + + self.fixPoint(Point_2.defaultResult()) + model.do() + + oldPosition = GeomAPI_Edge(self.mySpline.defaultResult().shape()).middlePoint() + newPosition = GeomAPI_Pnt2d(120., 90.) + self.mySketch.move(self.mySpline.defaultResult(), newPosition) + model.do() + + # plane is XOY, no need to project oldPosition point + dx = newPosition.x() - oldPosition.x() + dy = newPosition.y() - oldPosition.y() + + newPoles = [] + for pole in self.myPoles: + newPoles.append(GeomAPI_Pnt2d(pole.x() + dx, pole.y() + dy)) + newPoles[1].setX(newPoles[1].x() - dx) + newPoles[1].setY(newPoles[1].y() - dy) + self.checkPoles(self.mySpline, newPoles) + + + def test_move_bspline_with_fixed_segment(self): + """ Test 4. Movement of a B-spline curve with fixed control segment + """ + [Line_1, Line_2, Line_3, Line_4, Line_5] = self.mySpline.controlPolygon(auxiliary = [0, 1, 2, 3, 4]) + model.do() + + self.mySketch.setFixed(Line_1.defaultResult()) + self.myDOF -= 4 + model.do() + + oldPosition = GeomAPI_Edge(self.mySpline.defaultResult().shape()).middlePoint() + newPosition = GeomAPI_Pnt2d(120., 90.) + self.mySketch.move(self.mySpline.defaultResult(), newPosition) + model.do() + + # plane is XOY, no need to project oldPosition point + dx = newPosition.x() - oldPosition.x() + dy = newPosition.y() - oldPosition.y() + + newPoles = [self.myPoles[0], self.myPoles[1]] + for pole in self.myPoles[2:]: + newPoles.append(GeomAPI_Pnt2d(pole.x() + dx, pole.y() + dy)) + self.checkPoles(self.mySpline, newPoles) + + + def test_move_pole_of_free_bspline(self): + """ Test 5. Movement of a pole of a B-spline curve + """ + [Point_1, Point_2, Point_3, Point_4, Point_5] = self.mySpline.controlPoles(auxiliary = [0, 1, 2, 3, 4]) + [Line_1, Line_2, Line_3, Line_4, Line_5] = self.mySpline.controlPolygon(auxiliary = [0, 1, 2, 3, 4]) + model.do() + + newPoles = self.myPoles + + newPoles[2].setX(newPoles[2].x() + 20.) + newPoles[2].setY(newPoles[2].y() + 20.) + self.mySketch.move(SketchAPI_Point(Point_3).coordinates(), newPoles[2]) + model.do() + + self.checkPoles(self.mySpline, newPoles) + + def test_move_segment_of_free_bspline(self): + """ Test 6. Movement of a control segment of a B-spline curve + """ + [Point_1, Point_2, Point_3, Point_4, Point_5] = self.mySpline.controlPoles(auxiliary = [0, 1, 2, 3, 4]) + [Line_1, Line_2, Line_3, Line_4, Line_5] = self.mySpline.controlPolygon(auxiliary = [0, 1, 2, 3, 4]) + model.do() + + oldPosition = GeomAPI_Pnt2d(0.5 * (self.myPoles[2].x() + self.myPoles[3].x()), + 0.5 * (self.myPoles[2].y() + self.myPoles[3].y())) + newPosition = GeomAPI_Pnt2d(120., 90.) + self.mySketch.move(SketchAPI_Line(Line_3).defaultResult(), newPosition) + model.do() + + dx = newPosition.x() - oldPosition.x() + dy = newPosition.y() - oldPosition.y() + + newPoles = self.myPoles + newPoles[2].setX(newPoles[2].x() + dx) + newPoles[2].setY(newPoles[2].y() + dy) + newPoles[3].setX(newPoles[3].x() + dx) + newPoles[3].setY(newPoles[3].y() + dy) + + self.checkPoles(self.mySpline, newPoles) + + + +if __name__ == "__main__": + test_program = unittest.main(exit=False) + assert test_program.result.wasSuccessful(), "Test failed" + assert model.checkPythonDump() diff --git a/src/SketchPlugin/Test/TestPresentation.py b/src/SketchPlugin/Test/TestPresentation.py index 2b675b4ce..2f09005ef 100644 --- a/src/SketchPlugin/Test/TestPresentation.py +++ b/src/SketchPlugin/Test/TestPresentation.py @@ -175,3 +175,22 @@ aWeights.setValue(3, 1) aBSpline.boolean("need_control_poly").setValue(True) assert(featureToPresentation(aBSpline).getAISObject(None) is not None) aSession.finishOperation() + +# Test presentation for MacroBSplinePeriodic on low-level +aSession.startOperation() +aBSplineP = aSketchFeature.addFeature("SketchMacroBSplinePeriodic") +aPoles = geomDataAPI_Point2DArray(aBSplineP.attribute("poles")) +aPoles.setSize(4) +aPoles.setPnt(0, 0, 0) +aPoles.setPnt(1, 10, 0) +aPoles.setPnt(2, 10, 10) +aPoles.setPnt(3, 0, 10) +aWeights = aBSplineP.data().realArray("weights") +aWeights.setSize(4) +aWeights.setValue(0, 1) +aWeights.setValue(1, 2) +aWeights.setValue(2, 2) +aWeights.setValue(3, 1) +aBSplineP.boolean("need_control_poly").setValue(True) +assert(featureToPresentation(aBSplineP).getAISObject(None) is not None) +aSession.finishOperation() diff --git a/src/SketchPlugin/Test/TestProjectionBSplinePeriodic.py b/src/SketchPlugin/Test/TestProjectionBSplinePeriodic.py new file mode 100644 index 000000000..2ebd6b173 --- /dev/null +++ b/src/SketchPlugin/Test/TestProjectionBSplinePeriodic.py @@ -0,0 +1,78 @@ +# Copyright (C) 2019-2020 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +from salome.shaper import model + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +Cylinder_1 = model.addCylinder(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), 5, 10, 180) +Sketch_1 = model.addSketch(Part_1_doc, model.selection("FACE", "Cylinder_1_1/Face_5")) +SketchCircle_1 = Sketch_1.addCircle(-0.9379111501048892, 5.54816019935757, 2.957303770750356) +model.do() +ExtrusionCut_1 = model.addExtrusionCut(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2r")], model.selection(), [model.selection("SOLID", "Cylinder_1_1")]) +Rotation_1 = model.addRotation(Part_1_doc, [model.selection("SOLID", "ExtrusionCut_1_1")], model.selection("EDGE", "PartSet/OX"), 45) +Edge_1 = model.addEdge(Part_1_doc, [model.selection("EDGE", "[Rotation_1_1/MF:Rotated&Cylinder_1_1/Face_1][Rotation_1_1/MF:Rotated&Sketch_1/SketchCircle_1_2]")], False) + +Sketch_2 = model.addSketch(Part_1_doc, model.standardPlane("XOY")) +SketchProjection_1 = Sketch_2.addProjection(model.selection("EDGE", "Edge_1_1"), True) +SketchBSpline_1 = SketchProjection_1.createdFeature() +model.do() + +Sketch_3 = model.addSketch(Part_1_doc, model.standardPlane("XOZ")) +SketchProjection_2 = Sketch_3.addProjection(model.selection("EDGE", "Edge_1_1"), True) +SketchBSpline_2 = SketchProjection_2.createdFeature() +model.do() + +Sketch_4 = model.addSketch(Part_1_doc, model.standardPlane("YOZ")) +SketchProjection_3 = Sketch_4.addProjection(model.selection("EDGE", "Edge_1_1"), True) +SketchBSpline_3 = SketchProjection_3.createdFeature() +model.do() + +model.end() + +from GeomAPI import * +import math + +TOLERANCE = 1.e-7 + +def checkProjection(theBSpline3D, theBSpline2D, theFlags): + assert(theBSpline2D.isEdge() and theBSpline2D.edge().isBSpline()) + poles2D = GeomAPI_BSpline(GeomAPI_Curve(theBSpline2D)).poles() + poles3D = theBSpline3D.poles() + assert(poles2D.size() == poles3D.size()) + for p2d, p3d in zip(poles2D, poles3D): + assert(math.fabs((p2d.x() - p3d.x()) * theFlags.x()) < TOLERANCE and + math.fabs((p2d.y() - p3d.y()) * theFlags.y()) < TOLERANCE and + math.fabs((p2d.z() - p3d.z()) * theFlags.z()) < TOLERANCE) + + +bspline0 = GeomAPI_BSpline(GeomAPI_Curve(Edge_1.results()[-1].resultSubShapePair()[0].shape())) + +bsplineShape1 = SketchBSpline_1.results()[-1].resultSubShapePair()[0].shape() +checkProjection(bspline0, bsplineShape1, GeomAPI_Pnt(1, 1, 0)) + +bsplineShape2 = SketchBSpline_2.results()[-1].resultSubShapePair()[0].shape() +checkProjection(bspline0, bsplineShape2, GeomAPI_Pnt(1, 0, 1)) + +bsplineShape3 = SketchBSpline_3.results()[-1].resultSubShapePair()[0].shape() +checkProjection(bspline0, bsplineShape3, GeomAPI_Pnt(0, 1, 1)) + +assert(model.checkPythonDump()) diff --git a/src/SketchPlugin/Test/TestRemoveBSpline.py b/src/SketchPlugin/Test/TestRemoveBSpline.py index d9f4461c1..05da29875 100644 --- a/src/SketchPlugin/Test/TestRemoveBSpline.py +++ b/src/SketchPlugin/Test/TestRemoveBSpline.py @@ -24,10 +24,10 @@ from salome.shaper import model from ModelAPI import * -def assertNbSubs(theSketch, theNbPoints, theNbLines, theNbEllipses, theNbInternalConstraints): +def assertNbSubs(theSketch, theNbPoints, theNbLines, theNbSplines, theNbInternalConstraints): model.testNbSubFeatures(theSketch, "SketchPoint", theNbPoints) model.testNbSubFeatures(theSketch, "SketchLine", theNbLines) - model.testNbSubFeatures(theSketch, "SketchBSpline", theNbEllipses) + model.testNbSubFeatures(theSketch, "SketchBSpline", theNbSplines) model.testNbSubFeatures(theSketch, "SketchConstraintCoincidenceInternal", theNbInternalConstraints) model.begin() @@ -36,7 +36,7 @@ Part_1 = model.addPart(partSet) Part_1_doc = Part_1.document() Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) SketchBSpline_1_poles = [(-30, -10), (-15, 20), (0, -10), (15, 20), (30, -10)] -SketchBSpline_1 = Sketch_1.addSpline(SketchBSpline_1_poles) +SketchBSpline_1 = Sketch_1.addSpline(poles = SketchBSpline_1_poles) controlPoles = SketchBSpline_1.controlPoles(auxiliary = [0, 1, 2, 3, 4]) controlLines = SketchBSpline_1.controlPolygon(auxiliary = [0, 1, 2, 3]) model.do() @@ -91,7 +91,7 @@ model.begin() partSet = model.moduleDocument() Sketch_2 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) SketchBSpline_2_poles = [(-30, -10), (-15, -40), (0, -10), (15, -40), (30, -10)] -SketchBSpline_2 = Sketch_2.addSpline(SketchBSpline_2_poles) +SketchBSpline_2 = Sketch_2.addSpline(poles = SketchBSpline_2_poles) controlPoles2 = SketchBSpline_2.controlPoles(auxiliary = [0, 1, 2, 3, 4]) controlLines2 = SketchBSpline_2.controlPolygon(auxiliary = [0, 1, 2, 3]) model.do() diff --git a/src/SketchPlugin/Test/TestRemoveBSplinePeriodic.py b/src/SketchPlugin/Test/TestRemoveBSplinePeriodic.py new file mode 100644 index 000000000..4e9d3eb28 --- /dev/null +++ b/src/SketchPlugin/Test/TestRemoveBSplinePeriodic.py @@ -0,0 +1,108 @@ +# Copyright (C) 2019-2020 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +""" + Test removing peridoc B-spline curve and its construstion elements +""" + +from salome.shaper import model +from ModelAPI import * + +def assertNbSubs(theSketch, theNbPoints, theNbLines, theNbSplines, theNbInternalConstraints): + model.testNbSubFeatures(theSketch, "SketchPoint", theNbPoints) + model.testNbSubFeatures(theSketch, "SketchLine", theNbLines) + model.testNbSubFeatures(theSketch, "SketchBSplinePeriodic", theNbSplines) + model.testNbSubFeatures(theSketch, "SketchConstraintCoincidenceInternal", theNbInternalConstraints) + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchBSpline_1_poles = [(-30, -10), (-15, 20), (0, -10), (15, 20), (30, -10)] +SketchBSpline_1 = Sketch_1.addSpline(poles = SketchBSpline_1_poles, periodic = True) +controlPoles = SketchBSpline_1.controlPoles(auxiliary = [0, 1, 2, 3, 4]) +controlLines = SketchBSpline_1.controlPolygon(auxiliary = [0, 1, 2, 3, 4]) +model.do() +model.end() + +DEFAULT_DOF = len(SketchBSpline_1_poles) * 2 +DEFAULT_POINTS = len(SketchBSpline_1_poles) +DEFAULT_LINES = len(SketchBSpline_1_poles) +DEFAULT_BSPLINES = 1 +DEAFULT_INTERNALS = len(controlPoles) + len(controlLines) * 2 + +assertNbSubs(Sketch_1, DEFAULT_POINTS, DEFAULT_LINES, DEFAULT_BSPLINES, DEAFULT_INTERNALS) +assert(model.dof(Sketch_1) == DEFAULT_DOF) + +# Test 1. Remove auxiliary points one by one. +for pnt in controlPoles: + model.begin() + removeFeaturesAndReferences(FeatureSet([pnt.feature()])) + model.end() + + assertNbSubs(Sketch_1, DEFAULT_POINTS - 1, DEFAULT_LINES, DEFAULT_BSPLINES, DEAFULT_INTERNALS - 1) + assert(model.dof(Sketch_1) == DEFAULT_DOF) + model.undo() + assertNbSubs(Sketch_1, DEFAULT_POINTS, DEFAULT_LINES, DEFAULT_BSPLINES, DEAFULT_INTERNALS) + assert(model.dof(Sketch_1) == DEFAULT_DOF) + +# Test 2. Remove auxiliary lines one by one. +for ln in controlLines: + model.begin() + removeFeaturesAndReferences(FeatureSet([ln.feature()])) + model.end() + + assertNbSubs(Sketch_1, DEFAULT_POINTS, DEFAULT_LINES - 1, DEFAULT_BSPLINES, DEAFULT_INTERNALS - 2) + assert(model.dof(Sketch_1) == DEFAULT_DOF) + model.undo() + assertNbSubs(Sketch_1, DEFAULT_POINTS, DEFAULT_LINES, DEFAULT_BSPLINES, DEAFULT_INTERNALS) + assert(model.dof(Sketch_1) == DEFAULT_DOF) + +# Test 3. Remove the B-spline curve. +model.begin() +removeFeaturesAndReferences(FeatureSet([SketchBSpline_1.feature()])) +model.end() + +assertNbSubs(Sketch_1, 0, 0, 0, 0) +assert(model.dof(Sketch_1) == 0) +model.undo() +assertNbSubs(Sketch_1, DEFAULT_POINTS, DEFAULT_LINES, DEFAULT_BSPLINES, DEAFULT_INTERNALS) +assert(model.dof(Sketch_1) == DEFAULT_DOF) + +# Test 4. Remove some construction elements, make non-auxiliary a couple of the rest and check the dumping. +model.begin() +partSet = model.moduleDocument() +Sketch_2 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchBSpline_2_poles = [(-30, -10), (-15, -40), (0, -10), (15, -40), (30, -10)] +SketchBSpline_2 = Sketch_2.addSpline(poles = SketchBSpline_2_poles, periodic = True) +controlPoles2 = SketchBSpline_2.controlPoles(auxiliary = [0, 1, 2, 3, 4]) +controlLines2 = SketchBSpline_2.controlPolygon(auxiliary = [0, 1, 2, 3, 4]) +model.do() +model.end() + +model.begin() +controlPoles2[1].setAuxiliary(False) +controlLines2[2].setAuxiliary(False) +removeFeaturesAndReferences(FeatureSet([controlPoles2[2].feature(), controlLines2[0].feature()])) +model.end() + +assertNbSubs(Sketch_2, DEFAULT_POINTS - 1, DEFAULT_LINES - 1, DEFAULT_BSPLINES, DEAFULT_INTERNALS - 3) + +assert(model.checkPythonDump()) diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_AttributeBuilder.cpp b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_AttributeBuilder.cpp index 988a3c5a4..3323048d4 100644 --- a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_AttributeBuilder.cpp +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_AttributeBuilder.cpp @@ -89,10 +89,9 @@ static EntityWrapperPtr createScalar(const AttributePtr& theAttribute, (theAttribute->id() == SketchPlugin_MultiRotation::ANGLE_ID() && anOwner->getKind() == SketchPlugin_MultiRotation::ID())) aWrapper = ScalarWrapperPtr(new PlaneGCSSolver_AngleWrapper(createParameter(theStorage))); - else if ((anOwner->getKind() == SketchPlugin_BSpline::ID() && - theAttribute->id() == SketchPlugin_BSpline::DEGREE_ID()) || - (anOwner->getKind() == SketchPlugin_BSplinePeriodic::ID() && - theAttribute->id() == SketchPlugin_BSplinePeriodic::DEGREE_ID())) + else if ((anOwner->getKind() == SketchPlugin_BSpline::ID() || + anOwner->getKind() == SketchPlugin_BSplinePeriodic::ID()) && + theAttribute->id() == SketchPlugin_BSplineBase::DEGREE_ID()) // Degree of B-spline is not processed by the solver aWrapper = ScalarWrapperPtr(new PlaneGCSSolver_ScalarWrapper(createParameter(nullptr))); else -- 2.39.2