From: azv Date: Thu, 19 Sep 2019 08:53:30 +0000 (+0300) Subject: Task 2.12. New entities: ellipses and arcs of ellipses (issue #3003) X-Git-Tag: V9_4_0a2~4^2~76 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=c30e18c73237bc82890b00fe48352f88e054ed5b;p=modules%2Fshaper.git Task 2.12. New entities: ellipses and arcs of ellipses (issue #3003) Unit tests for constraints Angle, Coincidence, Collinear and Distance applied to ellipse. --- diff --git a/src/ModelHighAPI/ModelHighAPI_Dumper.cpp b/src/ModelHighAPI/ModelHighAPI_Dumper.cpp index c8e37dc1c..836f33c93 100644 --- a/src/ModelHighAPI/ModelHighAPI_Dumper.cpp +++ b/src/ModelHighAPI/ModelHighAPI_Dumper.cpp @@ -1227,7 +1227,8 @@ ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const AttributePtr& theAttr // Check the attribute belongs to copied (in multi-translation or multi-rotation) feature. // In this case we need to cast explicitly feature to appropriate type. AttributeBooleanPtr isCopy = anOwner->boolean("Copy"); - if (isCopy.get() && isCopy->value()) { + AttributeReferencePtr hasParent = anOwner->reference("ParentFeature"); + if ((isCopy.get() && isCopy->value()) || (hasParent && hasParent->value())) { aWrapperPrefix = featureWrapper(anOwner) + "("; aWrapperSuffix = ")"; importModule("SketchAPI"); diff --git a/src/SketchAPI/SketchAPI_MacroEllipse.cpp b/src/SketchAPI/SketchAPI_MacroEllipse.cpp index ded0f75ba..c3ed48080 100644 --- a/src/SketchAPI/SketchAPI_MacroEllipse.cpp +++ b/src/SketchAPI/SketchAPI_MacroEllipse.cpp @@ -18,12 +18,44 @@ // #include "SketchAPI_MacroEllipse.h" +#include "SketchAPI_Line.h" +#include "SketchAPI_Point.h" #include #include #include +#include + +#define MAJOR_AXIS_NEGATIVE (std::dynamic_pointer_cast( \ + feature()->attribute(SketchPlugin_MacroEllipse::FIRST_POINT_ID()))) +#define MAJOR_AXIS_POSITIVE (std::dynamic_pointer_cast( \ + feature()->attribute(SketchPlugin_MacroEllipse::SECOND_POINT_ID()))) +#define PASSED_POINT (std::dynamic_pointer_cast( \ + feature()->attribute(SketchPlugin_MacroEllipse::PASSED_POINT_ID()))) + +#define MAJOR_AXIS_NEGATIVE_REF (feature()->refattr( \ + SketchPlugin_MacroEllipse::FIRST_POINT_REF_ID())) +#define MAJOR_AXIS_POSITIVE_REF (feature()->refattr( \ + SketchPlugin_MacroEllipse::SECOND_POINT_REF_ID())) +#define PASSED_POINT_REF (feature()->refattr(SketchPlugin_MacroEllipse::PASSED_POINT_REF_ID())) + + +// find a parent sketch +static CompositeFeaturePtr sketch(FeaturePtr theFeature) +{ + CompositeFeaturePtr aSketch; + const std::set& aRefs = theFeature->data()->refsToMe(); + for (std::set::const_iterator anIt = aRefs.begin(); anIt != aRefs.end(); ++anIt) + if ((*anIt)->id() == SketchPlugin_Sketch::FEATURES_ID()) { + aSketch = std::dynamic_pointer_cast((*anIt)->owner()); + break; + } + return aSketch; +} + + SketchAPI_MacroEllipse::SketchAPI_MacroEllipse(const std::shared_ptr& theFeature) : SketchAPI_SketchEntity(theFeature) { @@ -102,10 +134,11 @@ void SketchAPI_MacroEllipse::initializePoints(double theX1, double theY1, double theX2, double theY2, double theX3, double theY3) { - fillAttribute(majorAxisNegativePoint(), theX1, theY1); - fillAttribute(majorAxisPositivePoint(), theX2, theY2); - fillAttribute(passedPoint(), theX3, theY3); + fillAttribute(MAJOR_AXIS_NEGATIVE, theX1, theY1); + fillAttribute(MAJOR_AXIS_POSITIVE, theX2, theY2); + fillAttribute(PASSED_POINT, theX3, theY3); + mySketch = sketch(feature()); execute(); } @@ -113,10 +146,11 @@ void SketchAPI_MacroEllipse::initializePoints(const std::shared_ptr& thePoint2, const std::shared_ptr& thePoint3) { - fillAttribute(thePoint1, mymajorAxisNegativePoint); - fillAttribute(thePoint2, mymajorAxisPositivePoint); - fillAttribute(thePoint3, mypassedPoint); + fillAttribute(thePoint1, MAJOR_AXIS_NEGATIVE); + fillAttribute(thePoint2, MAJOR_AXIS_POSITIVE); + fillAttribute(thePoint3, PASSED_POINT); + mySketch = sketch(feature()); execute(); } @@ -145,10 +179,94 @@ void SketchAPI_MacroEllipse::initializePoints( const ModelHighAPI_RefAttr& thePassedPointRef) { fillAttribute(theMajorAxisPoint1, theMajorAxisPoint1Ref, - mymajorAxisNegativePoint, mymajorAxisNegativePointRef); + MAJOR_AXIS_NEGATIVE, MAJOR_AXIS_NEGATIVE_REF); fillAttribute(theMajorAxisPoint2, theMajorAxisPoint2Ref, - mymajorAxisPositivePoint, mymajorAxisPositivePointRef); - fillAttribute(thePassedPoint, thePassedPointRef, mypassedPoint, mypassedPointRef); + MAJOR_AXIS_POSITIVE, MAJOR_AXIS_POSITIVE_REF); + fillAttribute(thePassedPoint, thePassedPointRef, + PASSED_POINT, PASSED_POINT_REF); + mySketch = sketch(feature()); execute(); } + +std::shared_ptr SketchAPI_MacroEllipse::center() +{ + if (!myCenter) + collectAuxiliary(); + return myCenter; +} + +std::shared_ptr SketchAPI_MacroEllipse::focus1() +{ + if (!myFocus1) + collectAuxiliary(); + return myFocus1; +} + +std::shared_ptr SketchAPI_MacroEllipse::focus2() +{ + if (!myFocus2) + collectAuxiliary(); + return myFocus2; +} + +std::shared_ptr SketchAPI_MacroEllipse::majorAxisStart() +{ + if (!myMajorAxisStart) + collectAuxiliary(); + return myMajorAxisStart; +} + +std::shared_ptr SketchAPI_MacroEllipse::majorAxisEnd() +{ + if (!myMajorAxisEnd) + collectAuxiliary(); + return myMajorAxisEnd; +} + +std::shared_ptr SketchAPI_MacroEllipse::minorAxisStart() +{ + if (!myMinorAxisStart) + collectAuxiliary(); + return myMinorAxisStart; +} + +std::shared_ptr SketchAPI_MacroEllipse::minorAxisEnd() +{ + if (!myMinorAxisEnd) + collectAuxiliary(); + return myMinorAxisEnd; +} + +std::shared_ptr SketchAPI_MacroEllipse::majorAxis() +{ + if (!myMajorAxis) + collectAuxiliary(); + return myMajorAxis; +} + +std::shared_ptr SketchAPI_MacroEllipse::minorAxis() +{ + if (!myMinorAxis) + collectAuxiliary(); + return myMinorAxis; +} + +void SketchAPI_MacroEllipse::collectAuxiliary() +{ + // collect auxiliary features + int aNbSubs = mySketch->numberOfSubs(); + std::shared_ptr* anAuxPoint[] = { + &myCenter, &myFocus1, &myFocus2, + &myMajorAxisStart, &myMajorAxisEnd, + &myMinorAxisStart, &myMinorAxisEnd + }; + std::shared_ptr* anAuxLine[] = {&myMajorAxis, &myMinorAxis}; + for (int aPtInd = 7, aLinInd = 2; (aPtInd > 0 || aLinInd > 0) && aNbSubs >= 0; ) { + FeaturePtr aCurFeature = mySketch->subFeature(--aNbSubs); + if (aPtInd > 0 && aCurFeature->getKind() == SketchPlugin_Point::ID()) + anAuxPoint[--aPtInd]->reset(new SketchAPI_Point(aCurFeature)); + else if (aLinInd > 0 && aCurFeature->getKind() == SketchPlugin_Line::ID()) + anAuxLine[--aLinInd]->reset(new SketchAPI_Line(aCurFeature)); + } +} diff --git a/src/SketchAPI/SketchAPI_MacroEllipse.h b/src/SketchAPI/SketchAPI_MacroEllipse.h index 6f74bbc98..596a417e6 100644 --- a/src/SketchAPI/SketchAPI_MacroEllipse.h +++ b/src/SketchAPI/SketchAPI_MacroEllipse.h @@ -26,6 +26,8 @@ #include class ModelHighAPI_RefAttr; +class SketchAPI_Point; +class SketchAPI_Line; /// \class SketchAPI_MacroEllipse /// \ingroup CPPHighAPI @@ -68,25 +70,28 @@ public: SKETCHAPI_EXPORT virtual ~SketchAPI_MacroEllipse(); - INTERFACE_9(SketchPlugin_MacroEllipse::ID(), + INTERFACE_1(SketchPlugin_MacroEllipse::ID(), ellipseType, SketchPlugin_MacroEllipse::ELLIPSE_TYPE(), - ModelAPI_AttributeString, /** Ellipse type */, - centerPoint, SketchPlugin_MacroEllipse::FIRST_POINT_ID(), - GeomDataAPI_Point2D, /** Center point */, - centerPointRef, SketchPlugin_MacroEllipse::FIRST_POINT_REF_ID(), - ModelAPI_AttributeRefAttr, /** Reference to a center point */, - majorAxisNegativePoint, SketchPlugin_MacroEllipse::FIRST_POINT_ID(), - GeomDataAPI_Point2D, /** Major axis negative point point */, - majorAxisNegativePointRef, SketchPlugin_MacroEllipse::FIRST_POINT_REF_ID(), - ModelAPI_AttributeRefAttr, /** Reference to the negative point on a major axis */, - majorAxisPositivePoint, SketchPlugin_MacroEllipse::SECOND_POINT_ID(), - GeomDataAPI_Point2D, /** Major axis positive point point */, - majorAxisPositivePointRef, SketchPlugin_MacroEllipse::SECOND_POINT_REF_ID(), - ModelAPI_AttributeRefAttr, /** Reference to the positive point on a major axis */, - passedPoint, SketchPlugin_MacroEllipse::PASSED_POINT_ID(), - GeomDataAPI_Point2D, /** Passed point */, - passedPointRef, SketchPlugin_MacroEllipse::PASSED_POINT_REF_ID(), - ModelAPI_AttributeRefAttr, /** Reference to a passed point */) + ModelAPI_AttributeString, /** Ellipse type */) + + /// Return created auxiliary center point + SKETCHAPI_EXPORT std::shared_ptr center(); + /// Return created auxiliary focus in the positive direction of major axis + SKETCHAPI_EXPORT std::shared_ptr focus1(); + /// Return created auxiliary focus in the negative direction of major axis + SKETCHAPI_EXPORT std::shared_ptr focus2(); + /// Return created auxiliary point - start of major axis + SKETCHAPI_EXPORT std::shared_ptr majorAxisStart(); + /// Return created auxiliary point - end of major axis + SKETCHAPI_EXPORT std::shared_ptr majorAxisEnd(); + /// Return created auxiliary point - start of minor axis + SKETCHAPI_EXPORT std::shared_ptr minorAxisStart(); + /// Return created auxiliary point - end of minor axis + SKETCHAPI_EXPORT std::shared_ptr minorAxisEnd(); + /// Return created auxiliary major axis + SKETCHAPI_EXPORT std::shared_ptr majorAxis(); + /// Return created auxiliary minor axis + SKETCHAPI_EXPORT std::shared_ptr minorAxis(); private: /// Set flag of creation by center, major semi-axis and passed point. @@ -111,6 +116,21 @@ private: const ModelHighAPI_RefAttr& theMajorAxisPoint2Ref, const std::shared_ptr& thePassedPoint, const ModelHighAPI_RefAttr& thePassedPointRef); + + /// Collect auxiliary features + void collectAuxiliary(); + +private: + CompositeFeaturePtr mySketch; + std::shared_ptr myCenter; + std::shared_ptr myFocus1; + std::shared_ptr myFocus2; + std::shared_ptr myMajorAxisStart; + std::shared_ptr myMajorAxisEnd; + std::shared_ptr myMinorAxisStart; + std::shared_ptr myMinorAxisEnd; + std::shared_ptr myMajorAxis; + std::shared_ptr myMinorAxis; }; /// Pointer on Circle object. diff --git a/src/SketchPlugin/CMakeLists.txt b/src/SketchPlugin/CMakeLists.txt index 523709878..3476cd275 100644 --- a/src/SketchPlugin/CMakeLists.txt +++ b/src/SketchPlugin/CMakeLists.txt @@ -203,9 +203,13 @@ ADD_UNIT_TESTS( TestChangeSketchPlane2.py TestChangeSketchPlane3.py TestConstraintAngle.py + TestConstraintAngleEllipse.py TestConstraintCoincidence.py + TestConstraintCoincidenceEllipse.py TestConstraintCollinear.py + TestConstraintCollinearEllipse.py TestConstraintDistance.py + TestConstraintDistanceEllipse.py TestConstraintDistanceBehavior.py TestConstraintDistanceHorizontal.py TestConstraintDistanceVertical.py diff --git a/src/SketchPlugin/Test/TestConstraintAngleEllipse.py b/src/SketchPlugin/Test/TestConstraintAngleEllipse.py new file mode 100644 index 000000000..f795677f9 --- /dev/null +++ b/src/SketchPlugin/Test/TestConstraintAngleEllipse.py @@ -0,0 +1,125 @@ +# Copyright (C) 2019 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 "Angle" applied for ellipse's axes +""" + +import unittest +import math + +from salome.shaper import model + +from GeomAPI import * +from SketchAPI import * + +__updated__ = "2019-09-12" + +class TestAngleEllipse(unittest.TestCase): + def setUp(self): + axisStart = GeomAPI_Pnt2d(30., 60.) + axisEnd = GeomAPI_Pnt2d(80., 50.) + passedPoint = GeomAPI_Pnt2d(60., 60.) + self.myAngle = 60 + + model.begin() + self.myDocument = model.moduleDocument() + self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY")) + macroEllipse = self.mySketch.addEllipse(axisStart, axisEnd, passedPoint, False) + self.myDOF = 5 + self.myOX = self.mySketch.addLine("OX") + model.do() + self.myEllipse = SketchAPI_Ellipse(model.lastSubFeature(self.mySketch, "SketchEllipse")) + self.myCenter = macroEllipse.center() + self.myFocus1 = macroEllipse.focus1() + self.myFocus2 = macroEllipse.focus2() + self.myMajorAxis = macroEllipse.majorAxis() + self.myMajorStart = macroEllipse.majorAxisStart() + self.myMajorEnd = macroEllipse.majorAxisEnd() + self.myMinorAxis = macroEllipse.minorAxis() + self.myMinorStart = macroEllipse.minorAxisStart() + self.myMinorEnd = macroEllipse.minorAxisEnd() + + + def tearDown(self): + model.end() + self.checkDOF() + self.assertPoints(self.myCenter.coordinates(), self.myEllipse.center()) + self.assertPoints(self.myFocus1.coordinates(), self.myEllipse.firstFocus()) + self.assertPoints(self.myFocus2.coordinates(), self.myEllipse.secondFocus()) + self.assertPoints(self.myMajorStart.coordinates(), self.myEllipse.majorAxisNegative()) + self.assertPoints(self.myMajorEnd.coordinates(), self.myEllipse.majorAxisPositive()) + self.assertPoints(self.myMajorAxis.startPoint(), self.myEllipse.majorAxisNegative()) + self.assertPoints(self.myMajorAxis.endPoint(), self.myEllipse.majorAxisPositive()) + self.assertPoints(self.myMinorStart.coordinates(), self.myEllipse.minorAxisNegative()) + self.assertPoints(self.myMinorEnd.coordinates(), self.myEllipse.minorAxisPositive()) + self.assertPoints(self.myMinorAxis.startPoint(), self.myEllipse.minorAxisNegative()) + self.assertPoints(self.myMinorAxis.endPoint(), self.myEllipse.minorAxisPositive()) + model.testNbSubFeatures(self.mySketch, "SketchPoint", 7) + model.testNbSubFeatures(self.mySketch, "SketchLine", 3) + model.testNbSubFeatures(self.mySketch, "SketchEllipse", 1) + model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidenceInternal", 11) + + + def checkDOF(self): + self.assertEqual(model.dof(self.mySketch), self.myDOF) + + def assertPoints(self, thePoint1, thePoint2): + self.assertAlmostEqual(thePoint1.x(), thePoint2.x()) + self.assertAlmostEqual(thePoint1.y(), thePoint2.y()) + + def assertAngle(self, theStart, theEnd, theAngle): + dirX = (theEnd.x() - theStart.x()) / math.hypot(theEnd.x() - theStart.x(), theEnd.y() - theStart.y()) + self.assertAlmostEqual(math.fabs(dirX), math.cos(theAngle * math.pi / 180)) + + + def test_angle_major_axis(self): + """ Test 1. Make angle between major axis of ellipse and OX + """ + self.mySketch.setAngle(self.myOX.result(), self.myMajorAxis.result(), self.myAngle) + self.myDOF -= 1 + model.do() + self.assertAngle(self.myMajorAxis.startPoint(), self.myMajorAxis.endPoint(), self.myAngle) + self.assertAngle(self.myMajorStart.coordinates(), self.myMajorEnd.coordinates(), self.myAngle) + model.testNbSubFeatures(self.mySketch, "SketchConstraintAngle", 1) + + def test_angle_minor_axis(self): + """ Test 2. Make angle between minor axis of ellipse and OX + """ + self.mySketch.setAngle(self.myOX.result(), self.myMinorAxis.result(), self.myAngle) + self.myDOF -= 1 + model.do() + self.assertAngle(self.myMinorAxis.startPoint(), self.myMinorAxis.endPoint(), self.myAngle) + self.assertAngle(self.myMinorStart.coordinates(), self.myMinorEnd.coordinates(), self.myAngle) + model.testNbSubFeatures(self.mySketch, "SketchConstraintAngle", 1) + + def test_angle_ellipse_axes(self): + """ Test 3. Set angle between axes of an ellipse. Check conflicting constraints. + """ + self.mySketch.setAngle(self.myMajorAxis.result(), self.myMinorAxis.result(), self.myAngle) + model.end() + self.assertNotEqual(self.mySketch.solverError(), "") + model.undo() + model.begin() + + +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/TestConstraintCoincidenceEllipse.py b/src/SketchPlugin/Test/TestConstraintCoincidenceEllipse.py new file mode 100644 index 000000000..1b5944d08 --- /dev/null +++ b/src/SketchPlugin/Test/TestConstraintCoincidenceEllipse.py @@ -0,0 +1,241 @@ +# Copyright (C) 2019 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 ellipse and its sub-results +""" + +import unittest +import math + +from salome.shaper import model + +from GeomAPI import * +from SketchAPI import * + +__updated__ = "2019-09-12" + +class TestCoincidenceEllipse(unittest.TestCase): + def setUp(self): + axisStart = GeomAPI_Pnt2d(30., 60.) + axisEnd = GeomAPI_Pnt2d(80., 50.) + passedPoint = GeomAPI_Pnt2d(60., 60.) + + model.begin() + self.myDocument = model.moduleDocument() + self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY")) + macroEllipse = self.mySketch.addEllipse(axisStart, axisEnd, passedPoint, False) + self.myDOF = 5 + self.myOrigin = self.mySketch.addPoint("Origin") + self.myOX = self.mySketch.addLine("OX") + model.do() + self.myEllipse = SketchAPI_Ellipse(model.lastSubFeature(self.mySketch, "SketchEllipse")) + self.myCenter = macroEllipse.center() + self.myFocus1 = macroEllipse.focus1() + self.myFocus2 = macroEllipse.focus2() + self.myMajorAxis = macroEllipse.majorAxis() + self.myMajorStart = macroEllipse.majorAxisStart() + self.myMajorEnd = macroEllipse.majorAxisEnd() + self.myMinorAxis = macroEllipse.minorAxis() + self.myMinorStart = macroEllipse.minorAxisStart() + self.myMinorEnd = macroEllipse.minorAxisEnd() + self.myExpectFailure = False + + 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() + self.assertPoints(self.myCenter.coordinates(), self.myEllipse.center()) + self.assertPoints(self.myFocus1.coordinates(), self.myEllipse.firstFocus()) + self.assertPoints(self.myFocus2.coordinates(), self.myEllipse.secondFocus()) + self.assertPoints(self.myMajorStart.coordinates(), self.myEllipse.majorAxisNegative()) + self.assertPoints(self.myMajorEnd.coordinates(), self.myEllipse.majorAxisPositive()) + self.assertPoints(self.myMajorAxis.startPoint(), self.myEllipse.majorAxisNegative()) + self.assertPoints(self.myMajorAxis.endPoint(), self.myEllipse.majorAxisPositive()) + self.assertPoints(self.myMinorStart.coordinates(), self.myEllipse.minorAxisNegative()) + self.assertPoints(self.myMinorEnd.coordinates(), self.myEllipse.minorAxisPositive()) + self.assertPoints(self.myMinorAxis.startPoint(), self.myEllipse.minorAxisNegative()) + self.assertPoints(self.myMinorAxis.endPoint(), self.myEllipse.minorAxisPositive()) + model.testNbSubFeatures(self.mySketch, "SketchPoint", 8) + model.testNbSubFeatures(self.mySketch, "SketchLine", 3) + model.testNbSubFeatures(self.mySketch, "SketchEllipse", 1) + model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidenceInternal", 11) + model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", 1) + + + def checkDOF(self): + self.assertEqual(model.dof(self.mySketch), self.myDOF) + + def checkPointFixing(self, thePoint): + self.mySketch.setCoincident(thePoint, self.myOrigin.coordinates()) + self.myDOF -= 2 + model.do() + if not self.myExpectFailure: + self.assertPoints(thePoint, self.myOrigin.coordinates()) + self.assertGreater(self.myEllipse.majorRadius().value(), 0.0) + self.assertGreater(self.myEllipse.minorRadius().value(), 0.0) + + def assertPoints(self, thePoint1, thePoint2): + self.assertAlmostEqual(thePoint1.x(), thePoint2.x()) + self.assertAlmostEqual(thePoint1.y(), thePoint2.y()) + + def checkPointOnAxis(self, thePoint): + self.mySketch.setCoincident(thePoint, self.myOX.result()) + self.myDOF -= 1 + model.do() + if not self.myExpectFailure: + self.assertAlmostEqual(thePoint.y(), 0.0) + self.assertGreater(self.myEllipse.majorRadius().value(), 0.0) + self.assertGreater(self.myEllipse.minorRadius().value(), 0.0) + + def checkPointOnLine(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) + self.assertGreater(self.myEllipse.majorRadius().value(), 0.0) + self.assertGreater(self.myEllipse.minorRadius().value(), 0.0) + + def checkPointOnEllipse(self, thePoint, theEllipse): + firstFocus2d = GeomAPI_Pnt2d(theEllipse.firstFocus().x(), theEllipse.firstFocus().y()) + distPF1 = model.distancePointPoint(firstFocus2d, thePoint) + secondFocus2d = GeomAPI_Pnt2d(theEllipse.secondFocus().x(), theEllipse.secondFocus().y()) + distPF2 = model.distancePointPoint(secondFocus2d, thePoint) + self.assertAlmostEqual(distPF1 + distPF2, 2.0 * theEllipse.majorRadius().value(), 7) + + + def test_concident_center(self): + """ Test 1. Make center of ellipse coincident with the Origin + """ + self.checkPointFixing(self.myCenter.coordinates()) + + def test_coincident_first_focus(self): + """ Test 2. Make first focus of ellipse coincident with the Origin + """ + self.checkPointFixing(self.myFocus1.coordinates()) + + def test_coincident_second_focus(self): + """ Test 3. Make second focus of ellipse coincident with the Origin + """ + self.checkPointFixing(self.myFocus2.coordinates()) + + def test_coincident_major_axis_start(self): + """ Test 4. Make start point on the major axis of ellipse coincident with the Origin + """ + self.checkPointFixing(self.myMajorStart.coordinates()) + + def test_coincident_major_axis_end(self): + """ Test 5. Make end point on the major axis of ellipse coincident with the Origin + """ + self.checkPointFixing(self.myMajorEnd.coordinates()) + + def test_coincident_minor_axis_start(self): + """ Test 6. Make start point on the minor axis of ellipse coincident with the Origin + """ + self.checkPointFixing(self.myMinorStart.coordinates()) + + def test_coincident_minor_axis_end(self): + """ Test 7. Make end point on the minor axis of ellipse coincident with the Origin. + Check solver is failed to compute the coincidence. + """ + self.myExpectFailure = True + self.checkPointFixing(self.myMinorEnd.coordinates()) + + + def test_center_on_line(self): + """ Test 8. Make center of ellipse coincident with the OX + """ + self.checkPointOnAxis(self.myCenter.coordinates()) + + def test_first_focus_on_line(self): + """ Test 9. Make first focus of ellipse coincident with the OX + """ + self.checkPointOnAxis(self.myFocus1.coordinates()) + + def test_second_focus_on_line(self): + """ Test 10. Make second focus of ellipse coincident with the OX + """ + self.checkPointOnAxis(self.myFocus2.coordinates()) + + def test_major_axis_start_on_line(self): + """ Test 11. Make start point on the major axis of ellipse coincident with the OX + """ + self.checkPointOnAxis(self.myMajorStart.coordinates()) + + def test_major_axis_end_on_line(self): + """ Test 12. Make end point on the major axis of ellipse coincident with the OX + """ + self.checkPointOnAxis(self.myMajorEnd.coordinates()) + + def test_minor_axis_start_on_line(self): + """ Test 13. Make start point on the minor axis of ellipse coincident with the OX + """ + self.checkPointOnAxis(self.myMinorStart.coordinates()) + + def test_minor_axis_end_on_line(self): + """ Test 14. Make end point on the minor axis of ellipse coincident with the OX + """ + self.myExpectFailure = True + self.checkPointOnAxis(self.myMinorEnd.coordinates()) + + + def test_origin_on_major_axis(self): + """ Test 15. Make origin coincident with the major axis of the ellipse + """ + self.mySketch.setCoincident(self.myMajorAxis.result(), self.myOrigin.coordinates()) + self.myDOF -= 1 + model.do() + self.checkPointOnLine(self.myOrigin.coordinates(), self.myMajorStart.coordinates(), self.myMajorEnd.coordinates()) + + def test_origin_on_minor_axis(self): + """ Test 16. Make origin coincident with the minor axis of the ellipse + """ + self.mySketch.setCoincident(self.myMinorAxis.result(), self.myOrigin.coordinates()) + self.myDOF -= 1 + model.end() + # solver shows wrong result + assert(self.mySketch.solverError() != ""), "PlaneGCS limitation: if you see this message, then PlaneGCS has solved the set of constraints correctly" + model.undo() + + # move ellipse and set coincidence once again + model.begin() + self.mySketch.move(self.myCenter, 20, 10) + model.do() + self.mySketch.setCoincident(self.myMinorAxis.results()[-1], self.myOrigin.coordinates()) + model.do() + self.checkPointOnLine(self.myOrigin.coordinates(), self.myMinorStart.coordinates(), self.myMinorEnd.coordinates()) + + + def test_origin_on_ellipse(self): + """ Test 17. Make origin coincident with the ellipse + """ + self.mySketch.setCoincident(self.myEllipse.results()[-1], self.myOrigin.coordinates()) + self.myDOF -= 1 + model.do() + self.checkPointOnEllipse(self.myOrigin.coordinates(), self.myEllipse) + + +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/TestConstraintCollinearEllipse.py b/src/SketchPlugin/Test/TestConstraintCollinearEllipse.py new file mode 100644 index 000000000..38fff8975 --- /dev/null +++ b/src/SketchPlugin/Test/TestConstraintCollinearEllipse.py @@ -0,0 +1,134 @@ +# Copyright (C) 2019 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 "Collinear" applied for ellipse's axes +""" + +import unittest +import math + +from salome.shaper import model + +from GeomAPI import * +from SketchAPI import * + +__updated__ = "2019-09-18" + +class TestCollinearEllipse(unittest.TestCase): + def setUp(self): + axisStart = GeomAPI_Pnt2d(30., 60.) + axisEnd = GeomAPI_Pnt2d(80., 50.) + passedPoint = GeomAPI_Pnt2d(60., 60.) + + model.begin() + self.myDocument = model.moduleDocument() + self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY")) + macroEllipse = self.mySketch.addEllipse(axisStart, axisEnd, passedPoint, False) + self.myDOF = 5 + self.myOX = self.mySketch.addLine("OX") + model.do() + self.myEllipse = SketchAPI_Ellipse(model.lastSubFeature(self.mySketch, "SketchEllipse")) + self.myCenter = macroEllipse.center() + self.myFocus1 = macroEllipse.focus1() + self.myFocus2 = macroEllipse.focus2() + self.myMajorAxis = macroEllipse.majorAxis() + self.myMajorStart = macroEllipse.majorAxisStart() + self.myMajorEnd = macroEllipse.majorAxisEnd() + self.myMinorAxis = macroEllipse.minorAxis() + self.myMinorStart = macroEllipse.minorAxisStart() + self.myMinorEnd = macroEllipse.minorAxisEnd() + + + def tearDown(self): + model.end() + self.checkDOF() + self.assertPoints(self.myCenter.coordinates(), self.myEllipse.center()) + self.assertPoints(self.myFocus1.coordinates(), self.myEllipse.firstFocus()) + self.assertPoints(self.myFocus2.coordinates(), self.myEllipse.secondFocus()) + self.assertPoints(self.myMajorStart.coordinates(), self.myEllipse.majorAxisNegative()) + self.assertPoints(self.myMajorEnd.coordinates(), self.myEllipse.majorAxisPositive()) + self.assertPoints(self.myMajorAxis.startPoint(), self.myEllipse.majorAxisNegative()) + self.assertPoints(self.myMajorAxis.endPoint(), self.myEllipse.majorAxisPositive()) + self.assertPoints(self.myMinorStart.coordinates(), self.myEllipse.minorAxisNegative()) + self.assertPoints(self.myMinorEnd.coordinates(), self.myEllipse.minorAxisPositive()) + self.assertPoints(self.myMinorAxis.startPoint(), self.myEllipse.minorAxisNegative()) + self.assertPoints(self.myMinorAxis.endPoint(), self.myEllipse.minorAxisPositive()) + model.testNbSubFeatures(self.mySketch, "SketchPoint", 7) + model.testNbSubFeatures(self.mySketch, "SketchLine", 3) + model.testNbSubFeatures(self.mySketch, "SketchEllipse", 1) + model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidenceInternal", 11) + + + def checkDOF(self): + self.assertEqual(model.dof(self.mySketch), self.myDOF) + + def assertPoints(self, thePoint1, thePoint2): + self.assertAlmostEqual(thePoint1.x(), thePoint2.x()) + self.assertAlmostEqual(thePoint1.y(), thePoint2.y()) + + + def test_collinear_major_axis(self): + """ Test 1. Make major axis of ellipse collinear with OX + """ + self.mySketch.setCollinear(self.myOX.result(), self.myMajorAxis.result()) + self.myDOF -= 2 + model.do() + self.assertAlmostEqual(self.myMajorAxis.startPoint().y(), 0) + self.assertAlmostEqual(self.myMajorAxis.endPoint().y(), 0) + self.assertAlmostEqual(self.myMajorStart.coordinates().y(), 0) + self.assertAlmostEqual(self.myMajorEnd.coordinates().y(), 0) + model.testNbSubFeatures(self.mySketch, "SketchConstraintCollinear", 1) + + def test_collinear_minor_axis(self): + """ Test 2. Make minor axis of ellipse collinear with OX + """ + self.mySketch.setCollinear(self.myOX.result(), self.myMinorAxis.result()) + self.myDOF -= 2 + model.end() + # solver shows wrong result + assert(self.mySketch.solverError() != ""), "PlaneGCS limitation: if you see this message, then PlaneGCS has solved the set of constraints correctly" + model.undo() + + model.begin() + self.mySketch.move(self.myMinorStart.coordinates(), 0, 70) + self.mySketch.move(self.myMinorEnd.coordinates(), 50, 70) + model.do() + self.mySketch.setCollinear(self.myOX.result(), self.myMinorAxis.result()) + model.do() + self.assertAlmostEqual(self.myMinorAxis.startPoint().y(), 0) + self.assertAlmostEqual(self.myMinorAxis.endPoint().y(), 0) + self.assertAlmostEqual(self.myMinorStart.coordinates().y(), 0) + self.assertAlmostEqual(self.myMinorEnd.coordinates().y(), 0) + model.testNbSubFeatures(self.mySketch, "SketchConstraintCollinear", 1) + + def test_collinear_ellipse_axes(self): + """ Test 3. Set collinear axes of an ellipse. Check conflicting constraints. + """ + self.mySketch.setCollinear(self.myEllipse.majorAxis(), self.myEllipse.minorAxis()) + model.end() + self.assertNotEqual(self.mySketch.solverError(), "") + model.undo() + model.begin() + + +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/TestConstraintDistanceEllipse.py b/src/SketchPlugin/Test/TestConstraintDistanceEllipse.py new file mode 100644 index 000000000..6ec2980ca --- /dev/null +++ b/src/SketchPlugin/Test/TestConstraintDistanceEllipse.py @@ -0,0 +1,199 @@ +# Copyright (C) 2019 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 "Distance" applied sub-elements of an ellipse +""" + +import unittest +import math + +from salome.shaper import model + +from GeomAPI import * +from SketchAPI import * + +__updated__ = "2019-09-12" + +class TestDistanceEllipse(unittest.TestCase): + def setUp(self): + axisStart = GeomAPI_Pnt2d(20., 60.) + axisEnd = GeomAPI_Pnt2d(80., 50.) + passedPoint = GeomAPI_Pnt2d(60., 70.) + + model.begin() + self.myDocument = model.moduleDocument() + self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY")) + macroEllipse = self.mySketch.addEllipse(axisStart, axisEnd, passedPoint, False) + self.myDOF = 5 + self.myOrigin = self.mySketch.addPoint("Origin") + self.myOX = self.mySketch.addLine("OX") + model.do() + self.myEllipse = SketchAPI_Ellipse(model.lastSubFeature(self.mySketch, "SketchEllipse")) + self.myCenter = macroEllipse.center() + self.myFocus1 = macroEllipse.focus1() + self.myFocus2 = macroEllipse.focus2() + self.myMajorAxis = macroEllipse.majorAxis() + self.myMajorStart = macroEllipse.majorAxisStart() + self.myMajorEnd = macroEllipse.majorAxisEnd() + self.myMinorAxis = macroEllipse.minorAxis() + self.myMinorStart = macroEllipse.minorAxisStart() + self.myMinorEnd = macroEllipse.minorAxisEnd() + self.myExpectFailure = False + self.myDistance = 20 + + 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() + self.assertPoints(self.myCenter.coordinates(), self.myEllipse.center()) + self.assertPoints(self.myFocus1.coordinates(), self.myEllipse.firstFocus()) + self.assertPoints(self.myFocus2.coordinates(), self.myEllipse.secondFocus()) + self.assertPoints(self.myMajorStart.coordinates(), self.myEllipse.majorAxisNegative()) + self.assertPoints(self.myMajorEnd.coordinates(), self.myEllipse.majorAxisPositive()) + self.assertPoints(self.myMajorAxis.startPoint(), self.myEllipse.majorAxisNegative()) + self.assertPoints(self.myMajorAxis.endPoint(), self.myEllipse.majorAxisPositive()) + self.assertPoints(self.myMinorStart.coordinates(), self.myEllipse.minorAxisNegative()) + self.assertPoints(self.myMinorEnd.coordinates(), self.myEllipse.minorAxisPositive()) + self.assertPoints(self.myMinorAxis.startPoint(), self.myEllipse.minorAxisNegative()) + self.assertPoints(self.myMinorAxis.endPoint(), self.myEllipse.minorAxisPositive()) + model.testNbSubFeatures(self.mySketch, "SketchPoint", 8) + model.testNbSubFeatures(self.mySketch, "SketchLine", 3) + model.testNbSubFeatures(self.mySketch, "SketchEllipse", 1) + model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidenceInternal", 11) + model.testNbSubFeatures(self.mySketch, "SketchConstraintDistance", 1) + + + def checkDOF(self): + self.assertEqual(model.dof(self.mySketch), self.myDOF) + + def checkPointPointDistance(self, thePoint1, thePoint2): + self.mySketch.setDistance(thePoint1, thePoint2, self.myDistance) + self.myDOF -= 1 + model.do() + if not self.myExpectFailure: + self.assertAlmostEqual(model.distancePointPoint(thePoint1, thePoint2), self.myDistance) + self.assertGreater(self.myEllipse.majorRadius().value(), 0.0) + self.assertGreater(self.myEllipse.minorRadius().value(), 0.0) + + def checkPointLineDistance(self, thePoint, theLine): + self.mySketch.setDistance(thePoint, theLine.result(), self.myDistance) + self.myDOF -= 1 + model.do() + self.assertAlmostEqual(model.distancePointLine(thePoint, theLine), self.myDistance) + self.assertGreater(self.myEllipse.majorRadius().value(), 0.0) + self.assertGreater(self.myEllipse.minorRadius().value(), 0.0) + + def assertPoints(self, thePoint1, thePoint2): + self.assertAlmostEqual(thePoint1.x(), thePoint2.x()) + self.assertAlmostEqual(thePoint1.y(), thePoint2.y()) + + + def test_distance_center(self): + """ Test 1. Set distance to the Origin from the center of ellipse + """ + self.checkPointPointDistance(self.myCenter.coordinates(), self.myOrigin.coordinates()) + + def test_distance_first_focus(self): + """ Test 2. Set distance to the Origin from the first focus of ellipse + """ + self.checkPointPointDistance(self.myFocus1.coordinates(), self.myOrigin.coordinates()) + + def test_distance_second_focus(self): + """ Test 3. Set distance to the Origin from the second focus of ellipse + """ + self.checkPointPointDistance(self.myFocus2.coordinates(), self.myOrigin.coordinates()) + + def test_distance_major_axis_start(self): + """ Test 4. Set distance to the Origin from the start point on the major axis of ellipse + """ + self.checkPointPointDistance(self.myMajorStart.coordinates(), self.myOrigin.coordinates()) + + def test_distance_major_axis_end(self): + """ Test 5. Set distance to the Origin from the end point on the major axis of ellipse + """ + self.checkPointPointDistance(self.myMajorEnd.coordinates(), self.myOrigin.coordinates()) + + def test_distance_minor_axis_start(self): + """ Test 6. Set distance to the Origin from the start point on the minor axis of ellipse + """ + self.checkPointPointDistance(self.myMinorStart.coordinates(), self.myOrigin.coordinates()) + + def test_distance_minor_axis_end(self): + """ Test 7. Set distance to the Origin from the end point on the minor axis of ellipse + """ + self.myExpectFailure = True + self.checkPointPointDistance(self.myMinorEnd.coordinates(), self.myOrigin.coordinates()) + + + def test_distance_center_to_line(self): + """ Test 8. Set distance from theOX to the center of ellipse + """ + self.checkPointLineDistance(self.myCenter.coordinates(), self.myOX) + + def test_distance_first_focus_to_line(self): + """ Test 9. Set distance from theOX to the first focus of ellipse + """ + self.checkPointLineDistance(self.myFocus1.coordinates(), self.myOX) + + def test_distance_second_focus_to_line(self): + """ Test 10. Set distance from theOX to the second focus of ellipse + """ + self.checkPointLineDistance(self.myFocus2.coordinates(), self.myOX) + + def test_distance_major_axis_start_to_line(self): + """ Test 11. Set distance from theOX to the start point on the major axis of ellipse + """ + self.checkPointLineDistance(self.myMajorStart.coordinates(), self.myOX) + + def test_distance_major_axis_end_to_line(self): + """ Test 12. Set distance from theOX to the end point on the major axis of ellipse + """ + self.checkPointLineDistance(self.myMajorEnd.coordinates(), self.myOX) + + def test_distance_minor_axis_start_to_line(self): + """ Test 13. Set distance from theOX to the start point on the minor axis of ellipse + """ + self.checkPointLineDistance(self.myMinorStart.coordinates(), self.myOX) + + def test_distance_minor_axis_end_to_line(self): + """ Test 14. Set distance from theOX to the end point on the minor axis of ellipse + """ + self.myDistance = 150 + self.checkPointLineDistance(self.myMinorEnd.coordinates(), self.myOX) + + + def test_distance_origin_to_major_axis(self): + """ Test 15. Set distance from the Origin to the major axis of the ellipse + """ + self.checkPointLineDistance(self.myOrigin.coordinates(), self.myMajorAxis) + + def test_distance_origin_to_minor_axis(self): + """ Test 16. Set distance from the Origin to the minor axis of the ellipse + """ + self.checkPointLineDistance(self.myOrigin.coordinates(), self.myMinorAxis) + + +if __name__ == "__main__": + test_program = unittest.main(exit=False) + assert test_program.result.wasSuccessful(), "Test failed" + assert model.checkPythonDump()