From 1ec0dcac22713723af319242e9508079eb2ace46 Mon Sep 17 00:00:00 2001 From: azv Date: Fri, 4 Oct 2019 12:53:38 +0300 Subject: [PATCH] Task 2.12. New entities: ellipses and arcs of ellipses (issue #3003) Unit-tests for elliptic arcs: * Middle point * Perpendicular * Tangent * Projection * Trim * Split --- src/GeomAPI/GeomAPI_Ellipse2d.cpp | 49 +- src/SketchAPI/SketchAPI_Projection.cpp | 4 + src/SketchPlugin/CMakeLists.txt | 6 + .../TestConstraintMiddlePointOnEllipticArc.py | 288 +++++++++++ .../TestConstraintPerpendicularEllipseLine.py | 76 +++ .../Test/TestConstraintTangentEllipticArc.py | 458 ++++++++++++++++++ src/SketchPlugin/Test/TestPresentation.py | 30 +- .../Test/TestProjectionEllipticArc.py | 61 +++ src/SketchPlugin/Test/TestSplitEllipse.py | 135 ++++++ src/SketchPlugin/Test/TestTrimEllipse.py | 117 +++++ .../PlaneGCSSolver/PlaneGCSSolver_Storage.cpp | 2 +- 11 files changed, 1214 insertions(+), 12 deletions(-) create mode 100644 src/SketchPlugin/Test/TestConstraintMiddlePointOnEllipticArc.py create mode 100644 src/SketchPlugin/Test/TestConstraintPerpendicularEllipseLine.py create mode 100644 src/SketchPlugin/Test/TestConstraintTangentEllipticArc.py create mode 100644 src/SketchPlugin/Test/TestProjectionEllipticArc.py create mode 100644 src/SketchPlugin/Test/TestSplitEllipse.py create mode 100644 src/SketchPlugin/Test/TestTrimEllipse.py diff --git a/src/GeomAPI/GeomAPI_Ellipse2d.cpp b/src/GeomAPI/GeomAPI_Ellipse2d.cpp index 7db3ef8a4..92c16886f 100644 --- a/src/GeomAPI/GeomAPI_Ellipse2d.cpp +++ b/src/GeomAPI/GeomAPI_Ellipse2d.cpp @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include #define MY_ELLIPSE implPtr() @@ -121,20 +123,42 @@ double GeomAPI_Ellipse2d::majorRadius() const return MY_ELLIPSE->MajorRadius(); } +// theArrangePoint is used to select the nearest solution point if intersection is detected template -static double extrema(EXTREMAPTR theAlgo, +static double extrema(IntAna2d_AnaIntersection* theIntersectionAlgo, + EXTREMAPTR theExtremaAlgo, + GeomPnt2dPtr theArrangePoint, GeomPnt2dPtr& thePoint1, GeomPnt2dPtr& thePoint2) { double aDistance = Precision::Infinite(); - if (theAlgo->IsDone() && theAlgo->NbExt() > 0) { - Extrema_POnCurv2d aP1, aP2; - for (int i = 1; i <= theAlgo->NbExt(); ++i) - if (theAlgo->SquareDistance(i) < aDistance) { - aDistance = Sqrt(theAlgo->SquareDistance(i)); - theAlgo->Points(i, aP1, aP2); + if (theIntersectionAlgo->IsDone() && theIntersectionAlgo->NbPoints() > 0) { + gp_Pnt2d anArrangePoint(theArrangePoint->x(), theArrangePoint->y()); + gp_Pnt2d anInterPnt = theIntersectionAlgo->Point(1).Value(); + aDistance = anArrangePoint.SquareDistance(anInterPnt); + // get solution nearest to theArrangePoint + for (int i = 2; i <= theIntersectionAlgo->NbPoints(); ++i) { + const IntAna2d_IntPoint& aPnt = theIntersectionAlgo->Point(i); + double aSqDist = aPnt.Value().SquareDistance(anArrangePoint); + if (aSqDist < aDistance) { + aDistance = aSqDist; + anInterPnt = aPnt.Value(); } + } + aDistance = 0.0; // curves are intersected + thePoint1 = GeomPnt2dPtr(new GeomAPI_Pnt2d(anInterPnt.X(), anInterPnt.Y())); + thePoint2 = GeomPnt2dPtr(new GeomAPI_Pnt2d(anInterPnt.X(), anInterPnt.Y())); + } + else if (theExtremaAlgo->IsDone() && theExtremaAlgo->NbExt() > 0) { + Extrema_POnCurv2d aP1, aP2; + for (int i = 1; i <= theExtremaAlgo->NbExt(); ++i) { + double aSqDist = theExtremaAlgo->SquareDistance(i); + if (aSqDist < aDistance) { + aDistance = aSqDist; + theExtremaAlgo->Points(i, aP1, aP2); + } + } aDistance = Sqrt(aDistance); thePoint1 = GeomPnt2dPtr(new GeomAPI_Pnt2d(aP1.Value().X(), aP1.Value().Y())); thePoint2 = GeomPnt2dPtr(new GeomAPI_Pnt2d(aP2.Value().X(), aP2.Value().Y())); @@ -146,16 +170,20 @@ double GeomAPI_Ellipse2d::distance(const std::shared_ptr& theLine std::shared_ptr& thePointOnMe, std::shared_ptr& thePointOnLine) { + IntAna2d_AnaIntersection anInter(theLine->impl(), IntAna2d_Conic(*MY_ELLIPSE)); Extrema_ExtElC2d anExtema(theLine->impl(), *MY_ELLIPSE); - return extrema(&anExtema, thePointOnLine, thePointOnMe); + return extrema(&anInter, &anExtema, theLine->location(), thePointOnLine, thePointOnMe); } double GeomAPI_Ellipse2d::distance(const std::shared_ptr& theCircle, std::shared_ptr& thePointOnMe, std::shared_ptr& thePointOnCircle) { + IntAna2d_AnaIntersection anInter(theCircle->impl(), IntAna2d_Conic(*MY_ELLIPSE)); Extrema_ExtElC2d anExtema(theCircle->impl(), *MY_ELLIPSE); - return extrema(&anExtema, thePointOnCircle, thePointOnMe); + GeomPnt2dPtr aCircleStart; + theCircle->D0(0.0, aCircleStart); + return extrema(&anInter, &anExtema, aCircleStart, thePointOnCircle, thePointOnMe); } double GeomAPI_Ellipse2d::distance(const std::shared_ptr& theEllipse, @@ -165,9 +193,10 @@ double GeomAPI_Ellipse2d::distance(const std::shared_ptr& the Handle(Geom2d_Ellipse) anEllipse1 = new Geom2d_Ellipse(theEllipse->impl()); Handle(Geom2d_Ellipse) anEllipse2 = new Geom2d_Ellipse(*MY_ELLIPSE); + IntAna2d_AnaIntersection anInter(theEllipse->impl(), IntAna2d_Conic(*MY_ELLIPSE)); Extrema_ExtCC2d* anExtema = new Extrema_ExtCC2d(Geom2dAdaptor_Curve(anEllipse1), Geom2dAdaptor_Curve(anEllipse2)); - double aDistance = extrema(anExtema, thePointOnEllipse, thePointOnMe); + double aDistance = extrema(&anInter, anExtema, theEllipse->firstFocus(), thePointOnEllipse, thePointOnMe); delete anExtema; return aDistance; } diff --git a/src/SketchAPI/SketchAPI_Projection.cpp b/src/SketchAPI/SketchAPI_Projection.cpp index 493991519..4baf94150 100644 --- a/src/SketchAPI/SketchAPI_Projection.cpp +++ b/src/SketchAPI/SketchAPI_Projection.cpp @@ -22,11 +22,13 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -104,6 +106,8 @@ std::shared_ptr SketchAPI_Projection::createdFeature() c anEntity.reset(new SketchAPI_Arc(aProjectedFeature)); else if (aProjectedFeature->getKind() == SketchPlugin_Ellipse::ID()) anEntity.reset(new SketchAPI_Ellipse(aProjectedFeature)); + else if (aProjectedFeature->getKind() == SketchPlugin_EllipticArc::ID()) + anEntity.reset(new SketchAPI_EllipticArc(aProjectedFeature)); else if (aProjectedFeature->getKind() == SketchPlugin_Point::ID()) anEntity.reset(new SketchAPI_Point(aProjectedFeature)); diff --git a/src/SketchPlugin/CMakeLists.txt b/src/SketchPlugin/CMakeLists.txt index 357172d64..1edde96b7 100644 --- a/src/SketchPlugin/CMakeLists.txt +++ b/src/SketchPlugin/CMakeLists.txt @@ -228,13 +228,16 @@ ADD_UNIT_TESTS( TestConstraintLength.py TestConstraintMiddlePoint.py TestConstraintMiddlePointOnArc.py + TestConstraintMiddlePointOnEllipticArc.py TestConstraintParallel.py TestConstraintPerpendicular.py TestConstraintPerpendicularArcLine.py + TestConstraintPerpendicularEllipseLine.py TestConstraintRadius.py TestConstraintRadiusFailure.py TestConstraintTangent.py TestConstraintTangentEllipse.py + TestConstraintTangentEllipticArc.py TestConstraintVertical.py TestCreateArcByCenterStartEnd.py TestCreateArcByTangentEdge.py @@ -277,6 +280,7 @@ ADD_UNIT_TESTS( TestPresentation.py TestProjection.py TestProjectionEllipse.py + TestProjectionEllipticArc.py TestProjectionIntoResult.py TestProjectionUpdate.py TestRectangle.py @@ -289,6 +293,7 @@ ADD_UNIT_TESTS( TestSketchPointLine.py TestSnowflake.py TestSplit.py + TestSplitEllipse.py TestSplitLine.py TestSplitPreview.py TestTrimArc01.py @@ -305,6 +310,7 @@ ADD_UNIT_TESTS( TestTrimCircle04.py TestTrimCircle05.py TestTrimCircleAndArc01.py + TestTrimEllipse.py TestTrimLine01.py TestTrimLine02.py TestTrimLine03.py diff --git a/src/SketchPlugin/Test/TestConstraintMiddlePointOnEllipticArc.py b/src/SketchPlugin/Test/TestConstraintMiddlePointOnEllipticArc.py new file mode 100644 index 000000000..e9f4d35d4 --- /dev/null +++ b/src/SketchPlugin/Test/TestConstraintMiddlePointOnEllipticArc.py @@ -0,0 +1,288 @@ +# 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 middle point on an elliptic arc +""" + +import unittest +import math + +from salome.shaper import model +from GeomAPI import * +from SketchAPI import * + +__updated__ = "2019-10-02" + +class TestMiddlePointOnEllipticArc(unittest.TestCase): + def setUp(self): + model.begin() + self.myTestPassed = True + self.myDocument = model.moduleDocument() + self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY")) + self.myArc = self.mySketch.addEllipticArc(30, 20, 50, 30, 45, 40, 5, 6.11485435, False) + self.myLine = self.mySketch.addLine(10, 40, 40, 80) + self.myDOF = 11 + model.do() + self.checkDOF() + + def tearDown(self): + if self.myTestPassed: + self.assertArc(self.myArc) + self.checkMiddlePoint(self.myLine.startPoint(), self.myArc) + self.checkDOF() + model.end() + + def checkDOF(self): + self.assertEqual(model.dof(self.mySketch), self.myDOF) + + def toPeriod(self, theValue): + while theValue < -math.pi: + theValue += 2.0 * math.pi + while theValue >= math.pi: + theValue -= 2.0 * math.pi + return theValue + + def checkMiddlePoint(self, thePoint, theArc): + self.myTestPassed = False + anEllipse = theArc.defaultResult().shape().edge().ellipse() + # check point on ellipse + self.checkPointOnEllipse(thePoint, anEllipse) + # check angles + TOLERANCE = 1.e-5 + startAngle = 0; startPoint = GeomAPI_Pnt(theArc.startPoint().x(), theArc.startPoint().y(), 0) + startAngle = anEllipse.parameter(startPoint, TOLERANCE, startAngle) + endAngle = 0; endPoint = GeomAPI_Pnt(theArc.endPoint().x(), theArc.endPoint().y(), 0) + endAngle = anEllipse.parameter(endPoint, TOLERANCE, endAngle) + midAngle = 0; midPoint = GeomAPI_Pnt(thePoint.x(), thePoint.y(), 0) + midAngle = anEllipse.parameter(midPoint, TOLERANCE, midAngle) + diffMS = self.toPeriod(midAngle - startAngle) + diffEM = self.toPeriod(endAngle - midAngle) + self.assertAlmostEqual(diffMS, diffEM) + self.assertEqual(diffMS < 0, theArc.reversed().value()) + self.myTestPassed = True + + def checkPointOnEllipse(self, theCoordinates, theEllipse): + point = GeomAPI_Pnt2d(theCoordinates.x(), theCoordinates.y()) + firstFocus2d = GeomAPI_Pnt2d(theEllipse.firstFocus().x(), theEllipse.firstFocus().y()) + distPF1 = model.distancePointPoint(firstFocus2d, point) + secondFocus2d = GeomAPI_Pnt2d(theEllipse.secondFocus().x(), theEllipse.secondFocus().y()) + distPF2 = model.distancePointPoint(secondFocus2d, point) + if issubclass(type(theEllipse), SketchAPI_Ellipse): + majorRad = theEllipse.majorRadius().value() + else: + majorRad = theEllipse.majorRadius() + NB_DIGITS = 7 - math.floor(math.log10(majorRad)) + self.assertAlmostEqual(distPF1 + distPF2, 2.0 * majorRad, NB_DIGITS) + + def assertArc(self, theArc): + anEllipse = theArc.defaultResult().shape().edge().ellipse() + self.checkPointOnEllipse(theArc.startPoint(), anEllipse) + self.checkPointOnEllipse(theArc.endPoint(), anEllipse) + + def rotatePoint(self, thePoint, theCenter, theAngle): + dirX = thePoint.x() - theCenter.x() + dirY = thePoint.y() - theCenter.y() + newX = theCenter.x() + dirX * math.cos(theAngle) - dirY * math.sin(theAngle) + newY = theCenter.y() + dirX * math.sin(theAngle) + dirY * math.cos(theAngle) + self.mySketch.move(thePoint, newX, newY) + + def moveArc(self): + ANGLE_STEP = math.pi * 5.0 / 180.0 + ANGLE_THRESHOLD = math.pi / 2.0 + # move start point of the arc clockwise + fullAngle = 0.0 + while fullAngle < ANGLE_THRESHOLD: + self.rotatePoint(self.myArc.startPoint(), self.myArc.center(), -ANGLE_STEP) + model.do() + self.checkMiddlePoint(self.myLine.startPoint(), self.myArc) + fullAngle += ANGLE_STEP + # move start point of the arc conterclockwise + fullAngle = 0.0 + while fullAngle < ANGLE_THRESHOLD: + self.rotatePoint(self.myArc.startPoint(), self.myArc.center(), ANGLE_STEP) + model.do() + self.checkMiddlePoint(self.myLine.startPoint(), self.myArc) + fullAngle += ANGLE_STEP + + # move end point of the arc clockwise + fullAngle = 0.0 + while fullAngle < ANGLE_THRESHOLD: + self.rotatePoint(self.myArc.endPoint(), self.myArc.center(), -ANGLE_STEP) + model.do() + self.checkMiddlePoint(self.myLine.startPoint(), self.myArc) + fullAngle += ANGLE_STEP + # move end point of the arc conterclockwise + fullAngle = 0.0 + while fullAngle < ANGLE_THRESHOLD: + self.rotatePoint(self.myArc.endPoint(), self.myArc.center(), ANGLE_STEP) + model.do() + self.checkMiddlePoint(self.myLine.startPoint(), self.myArc) + fullAngle += ANGLE_STEP + + # move center of the arc + DELTA = [0.1, 0.1] + for i in range(0, 40): + if i == 10 or i == 30: + DELTA = [-DELTA[0], -DELTA[1]] + self.mySketch.move(self.myArc.center(), self.myArc.center().x() + DELTA[0], self.myArc.center().y() + DELTA[1]) + model.do() + self.checkMiddlePoint(self.myLine.startPoint(), self.myArc) + DELTA = [-0.1, 0.1] + for i in range(0, 40): + if i == 10 or i == 30: + DELTA = [-DELTA[0], -DELTA[1]] + self.mySketch.move(self.myArc.center(), self.myArc.center().x() + DELTA[0], self.myArc.center().y() + DELTA[1]) + model.do() + self.checkMiddlePoint(self.myLine.startPoint(), self.myArc) + + def moveLine(self): + DELTA = [0.1, 0.0] + for i in range(0, 40): + if i == 10 or i == 30: + DELTA = [-DELTA[0], -DELTA[1]] + self.mySketch.move(self.myLine.startPoint(), self.myLine.startPoint().x() + DELTA[0], self.myLine.startPoint().y() + DELTA[1]) + model.do() + self.checkMiddlePoint(self.myLine.startPoint(), self.myArc) + DELTA = [0.0, 0.1] + for i in range(0, 40): + if i == 10 or i == 30: + DELTA = [-DELTA[0], -DELTA[1]] + self.mySketch.move(self.myLine.startPoint(), self.myLine.startPoint().x() + DELTA[0], self.myLine.startPoint().y() + DELTA[1]) + model.do() + self.checkMiddlePoint(self.myLine.startPoint(), self.myArc) + + + def test_middle_point_PA(self): + """ Test 1. Set middle point constraint (point is the first argument) + """ + self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[-1]) + self.myDOF -= 2 + model.do() + + def test_middle_point_AP(self): + """ Test 2. Set middle point constraint (point is the second argument) + """ + self.mySketch.setMiddlePoint(self.myArc.results()[-1], self.myLine.startPoint()) + self.myDOF -= 2 + model.do() + + def test_coincident_middle_point(self): + """ Test 3. Set middle point constraint for the point already coincident with the arc + """ + self.mySketch.setCoincident(self.myLine.startPoint(), self.myArc.results()[-1]) + model.do() + self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[-1]) + self.myDOF -= 2 + model.do() + + def test_middle_point_coincident(self): + """ Test 4. Set concidence of the point and the arc which are already constrained with middle point + """ + self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[-1]) + model.do() + self.mySketch.setCoincident(self.myLine.startPoint(), self.myArc.results()[-1]) + self.myDOF -= 2 + model.do() + + @unittest.expectedFailure + def test_middle_point_limitation(self): + """ Test 5. Check middle point fails if the point's coordinates are equal to the arc start point + """ + self.myLine.startPoint().setValue(self.myArc.startPoint().pnt()) + model.do() + coincidence = self.mySketch.setCoincident(self.myLine.startPoint(), self.myArc.results()[-1]) + self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[-1]) + self.myDOF -= 2 + model.do() + # this check will fail due to the limitation of PlanGCS + self.checkMiddlePoint(self.myLine.startPoint(), self.myArc) + + def test_middle_point_equal_start(self): + """ Test 6. Check middle point does not fail if the point's coordinates are equal to the arc end point + """ + self.myLine.startPoint().setValue(self.myArc.endPoint().pnt()) + model.do() + coincidence = self.mySketch.setCoincident(self.myLine.startPoint(), self.myArc.results()[-1]) + self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[-1]) + self.myDOF -= 2 + model.do() + + def test_middle_point_move_arc(self): + """ Test 7. Set middle point constraint and move arc + """ + self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[-1]) + self.myDOF -= 2 + model.do() + self.checkMiddlePoint(self.myLine.startPoint(), self.myArc) + self.moveArc() + + def test_middle_point_coincidence_move_arc(self): + """ Test 8. Set coincidence and middle point constraint and move arc + """ + self.mySketch.setCoincident(self.myLine.startPoint(), self.myArc.results()[-1]) + model.do() + self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[-1]) + self.myDOF -= 2 + model.do() + self.checkMiddlePoint(self.myLine.startPoint(), self.myArc) + self.moveArc() + + def test_middle_point_move_line(self): + """ Test 9. Set middle point constraint and move line + """ + self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[-1]) + self.myDOF -= 2 + model.do() + self.checkMiddlePoint(self.myLine.startPoint(), self.myArc) + self.moveLine() + + def test_middle_point_coincidence_move_line(self): + """ Test 10. Set coincidence and middle point constraint and move line + """ + self.mySketch.setCoincident(self.myLine.startPoint(), self.myArc.results()[-1]) + model.do() + self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[-1]) + self.myDOF -= 2 + model.do() + self.checkMiddlePoint(self.myLine.startPoint(), self.myArc) + self.moveLine() + + def test_remove_middle_point(self): + """ Test 11. Set and then remove middle point constraint + """ + mp = self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[-1]) + self.myDOF -= 2 + model.do() + self.assertArc(self.myArc) + self.checkMiddlePoint(self.myLine.startPoint(), self.myArc) + self.checkDOF() + # remove middle point + self.myDocument.removeFeature(mp.feature()) + self.myDOF += 2 + model.do() + self.checkDOF() + # set flag False to avoid checking middle point constraint in tearDown() method + self.myTestPassed = False + + +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/TestConstraintPerpendicularEllipseLine.py b/src/SketchPlugin/Test/TestConstraintPerpendicularEllipseLine.py new file mode 100644 index 000000000..4a9678b47 --- /dev/null +++ b/src/SketchPlugin/Test/TestConstraintPerpendicularEllipseLine.py @@ -0,0 +1,76 @@ +# 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 +# + +from salome.shaper import model +import math +from GeomAPI import * + +TOLERANCE = 1.e-7 + +def checkPerpendicular(theLine, theEllipse): + focus1 = theEllipse.firstFocus() + focus2 = theEllipse.secondFocus() + pf1 = theLine.project(focus1) + pf2 = theLine.project(focus2) + dist1 = focus1.distance(pf1) + dist2 = focus2.distance(pf2) + if math.fabs(dist1 - dist2) < TOLERANCE: + # no need further check, the line is equal to one of the ellipses axis + return + x = (dist2 * pf1.x() - dist1 * pf2.x()) / (dist2 - dist1) + y = (dist2 * pf1.y() - dist1 * pf2.y()) / (dist2 - dist1) + pnt = GeomAPI_Pnt(x, y, 0) + # check point on ellipse + majorRad = theEllipse.majorRadius() + assert(math.fabs(pnt.distance(focus1) + pnt.distance(focus2) - 2.0 * majorRad) < TOLERANCE * majorRad) + + +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")) +SketchLine_1 = Sketch_1.addLine(-40, 10, -20, 30) +SketchArc_1 = Sketch_1.addArc(25, -30, 42, -34, 13, -17.31142245955048, False) +SketchEllipse_1 = Sketch_1.addEllipse(-50, 0, -30, -5, 15) +[SketchPoint_1, SketchPoint_2, SketchPoint_3, SketchPoint_4, SketchPoint_5, SketchPoint_6, SketchPoint_7, SketchLine_2, SketchLine_3] = SketchEllipse_1.construction(center = "aux", firstFocus = "aux", secondFocus = "aux", majorAxisStart = "aux", majorAxisEnd = "aux", minorAxisStart = "aux", minorAxisEnd = "aux", majorAxis = "aux", minorAxis = "aux") +SketchEllipticArc_1 = Sketch_1.addEllipticArc(5, 30, -5, 45, -7, 47, 15, 33.68010611, False) +[SketchPoint_8, SketchPoint_9, SketchPoint_10, SketchPoint_11, SketchPoint_12, SketchPoint_13, SketchPoint_14, SketchLine_4, SketchLine_5] = SketchEllipticArc_1.construction(center = "aux", firstFocus = "aux", secondFocus = "aux", majorAxisStart = "aux", majorAxisEnd = "aux", minorAxisStart = "aux", minorAxisEnd = "aux", majorAxis = "aux", minorAxis = "aux") +model.do() + +# check error on perpendicularity of arc and elliptic arc +SketchConstraintPerpendicular_1 = Sketch_1.setPerpendicular(SketchArc_1.results()[1], SketchEllipticArc_1.result()) +model.do() +assert(SketchConstraintPerpendicular_1.feature().error() != "") + +# avoid the failure +Part_1_doc.removeFeature(SketchConstraintPerpendicular_1.feature()) +model.do() +assert(Sketch_1.feature().error() == "") + +# set correct constraints +SketchConstraintPerpendicular_2 = Sketch_1.setPerpendicular(SketchEllipticArc_1.result(), SketchLine_1.result()) +SketchConstraintPerpendicular_3 = Sketch_1.setPerpendicular(SketchLine_1.result(), SketchEllipse_1.result()) +model.do() + +checkPerpendicular(SketchLine_1.defaultResult().shape().edge().line(), SketchEllipse_1.defaultResult().shape().edge().ellipse()) +checkPerpendicular(SketchLine_1.defaultResult().shape().edge().line(), SketchEllipticArc_1.defaultResult().shape().edge().ellipse()) +model.end() + +assert(model.checkPythonDump()) diff --git a/src/SketchPlugin/Test/TestConstraintTangentEllipticArc.py b/src/SketchPlugin/Test/TestConstraintTangentEllipticArc.py new file mode 100644 index 000000000..77f0fd853 --- /dev/null +++ b/src/SketchPlugin/Test/TestConstraintTangentEllipticArc.py @@ -0,0 +1,458 @@ +# 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 "Tangent" applied to elliptic arc and another entity +""" + +import unittest +import math + +from salome.shaper import model + +from GeomAPI import * +from SketchAPI import * + +__updated__ = "2019-10-04" + +class TestTangentEllipticArc(unittest.TestCase): + def setUp(self): + center = GeomAPI_Pnt2d(30., 20.) + axisEnd = GeomAPI_Pnt2d(50., 30.) + startPoint = GeomAPI_Pnt2d(45, 40) + endPoint = GeomAPI_Pnt2d(5, 6.11485435) + + model.begin() + self.myDocument = model.moduleDocument() + self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY")) + macroEllipticArc = self.mySketch.addEllipticArc(center, axisEnd, startPoint, endPoint, False) + model.do() + self.myEllipticArc = SketchAPI_EllipticArc(model.lastSubFeature(self.mySketch, "SketchEllipticArc")) + self.myCenter = macroEllipticArc.center() + self.myFocus1 = macroEllipticArc.focus1() + self.myFocus2 = macroEllipticArc.focus2() + self.myMajorAxis = macroEllipticArc.majorAxis() + self.myMajorStart = macroEllipticArc.majorAxisStart() + self.myMajorEnd = macroEllipticArc.majorAxisEnd() + self.myMinorAxis = macroEllipticArc.minorAxis() + self.myMinorStart = macroEllipticArc.minorAxisStart() + self.myMinorEnd = macroEllipticArc.minorAxisEnd() + + self.myDOF = 7 + self.myNbPoints = 7 + self.myNbLines = 2 + self.myNbArcs = 0 + self.myNbCircles = 0 + self.myNbEllipses = 0 + self.myNbEllipticArcs = 1 + self.myNbInternals = 11 + self.myNbCoincidence = 0 + self.myNbTangency = 0 + + def tearDown(self): + model.end() + self.checkDOF() + self.assertPoints(self.myCenter.coordinates(), self.myEllipticArc.center()) + self.assertPoints(self.myFocus1.coordinates(), self.myEllipticArc.firstFocus()) + self.assertPoints(self.myFocus2.coordinates(), self.myEllipticArc.secondFocus()) + self.assertPoints(self.myMajorStart.coordinates(), self.myEllipticArc.majorAxisNegative()) + self.assertPoints(self.myMajorEnd.coordinates(), self.myEllipticArc.majorAxisPositive()) + self.assertPoints(self.myMajorAxis.startPoint(), self.myEllipticArc.majorAxisNegative()) + self.assertPoints(self.myMajorAxis.endPoint(), self.myEllipticArc.majorAxisPositive()) + self.assertPoints(self.myMinorStart.coordinates(), self.myEllipticArc.minorAxisNegative()) + self.assertPoints(self.myMinorEnd.coordinates(), self.myEllipticArc.minorAxisPositive()) + self.assertPoints(self.myMinorAxis.startPoint(), self.myEllipticArc.minorAxisNegative()) + self.assertPoints(self.myMinorAxis.endPoint(), self.myEllipticArc.minorAxisPositive()) + model.testNbSubFeatures(self.mySketch, "SketchPoint", self.myNbPoints) + model.testNbSubFeatures(self.mySketch, "SketchLine", self.myNbLines) + model.testNbSubFeatures(self.mySketch, "SketchArc", self.myNbArcs) + model.testNbSubFeatures(self.mySketch, "SketchCircle", self.myNbCircles) + model.testNbSubFeatures(self.mySketch, "SketchEllipse", self.myNbEllipses) + model.testNbSubFeatures(self.mySketch, "SketchEllipticArc", self.myNbEllipticArcs) + model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidenceInternal", self.myNbInternals) + model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", self.myNbCoincidence) + model.testNbSubFeatures(self.mySketch, "SketchConstraintTangent", self.myNbTangency) + + + def checkDOF(self): + self.assertEqual(model.dof(self.mySketch), self.myDOF) + + def assertTangentLineEllipse(self, theLine, theEllipticArc): + aLine = GeomAPI_Lin2d(theLine.startPoint().pnt(), theLine.endPoint().pnt()) + projF1 = aLine.project(theEllipticArc.firstFocus().pnt()) + projF2 = aLine.project(theEllipticArc.secondFocus().pnt()) + + distF1P1 = model.distancePointPoint(theEllipticArc.firstFocus(), projF1) + distF2P2 = model.distancePointPoint(theEllipticArc.secondFocus(), projF2) + + tgPoint = GeomAPI_Pnt2d((projF1.x() * distF2P2 + projF2.x() * distF1P1) / (distF1P1 + distF2P2), (projF1.y() * distF2P2 + projF2.y() * distF1P1) / (distF1P1 + distF2P2)) + distF1T = model.distancePointPoint(theEllipticArc.firstFocus(), tgPoint) + distF2T = model.distancePointPoint(theEllipticArc.secondFocus(), tgPoint) + NB_DIGITS = 7 - math.floor(math.log10(theEllipticArc.majorRadius().value())) + self.assertAlmostEqual(distF1T + distF2T, 2 * theEllipticArc.majorRadius().value(), NB_DIGITS) + + def assertTangentCircleEllipse(self, theCircle, theEllipticArc): + axis = GeomAPI_Dir2d(theEllipticArc.firstFocus().x() - theEllipticArc.center().x(), theEllipticArc.firstFocus().y() - theEllipticArc.center().y()) + anEllipticArc = GeomAPI_Ellipse2d(theEllipticArc.center().pnt(), axis, theEllipticArc.majorRadius().value(), theEllipticArc.minorRadius().value()) + aCircle = GeomAPI_Circ2d(theCircle.center().pnt(), GeomAPI_Dir2d(1, 0), theCircle.radius().value()) + + pOnE = GeomAPI_Pnt2d(0, 0) + pOnC = GeomAPI_Pnt2d(0, 0) + anEllipticArc.distance(aCircle, pOnE, pOnC) + self.assertAlmostEqual(model.distancePointPoint(pOnE, theCircle.center()), theCircle.radius().value()) + + dist1 = model.distancePointPoint(pOnC, theEllipticArc.firstFocus()) + dist2 = model.distancePointPoint(pOnC, theEllipticArc.secondFocus()) + NB_DIGITS = 7 - math.floor(math.log10(theEllipticArc.majorRadius().value())) + self.assertAlmostEqual(dist1 + dist2, 2 * theEllipticArc.majorRadius().value(), NB_DIGITS) + + def assertTangentEllipses(self, theEllipticArc1, theEllipticArc2): + axis1 = GeomAPI_Dir2d(theEllipticArc1.firstFocus().x() - theEllipticArc1.center().x(), theEllipticArc1.firstFocus().y() - theEllipticArc1.center().y()) + anEllipticArc1 = GeomAPI_Ellipse2d(theEllipticArc1.center().pnt(), axis1, theEllipticArc1.majorRadius().value(), theEllipticArc1.minorRadius().value()) + axis2 = GeomAPI_Dir2d(theEllipticArc2.firstFocus().x() - theEllipticArc2.center().x(), theEllipticArc2.firstFocus().y() - theEllipticArc2.center().y()) + anEllipticArc2 = GeomAPI_Ellipse2d(theEllipticArc2.center().pnt(), axis2, theEllipticArc2.majorRadius().value(), theEllipticArc2.minorRadius().value()) + + p1 = GeomAPI_Pnt2d(0, 0) + p2 = GeomAPI_Pnt2d(0, 0) + anEllipticArc1.distance(anEllipticArc2, p1, p2) + + dist1 = model.distancePointPoint(p2, theEllipticArc1.firstFocus()) + dist2 = model.distancePointPoint(p2, theEllipticArc1.secondFocus()) + NB_DIGITS = 7 - math.floor(math.log10(theEllipticArc1.majorRadius().value())) + self.assertAlmostEqual(dist1 + dist2, 2 * theEllipticArc1.majorRadius().value()) + + dist1 = model.distancePointPoint(p1, theEllipticArc2.firstFocus()) + dist2 = model.distancePointPoint(p1, theEllipticArc2.secondFocus()) + NB_DIGITS = 7 - math.floor(math.log10(theEllipticArc2.majorRadius().value())) + self.assertAlmostEqual(dist1 + dist2, 2 * theEllipticArc2.majorRadius().value(), NB_DIGITS) + + def assertPoints(self, thePoint1, thePoint2): + self.assertAlmostEqual(thePoint1.x(), thePoint2.x(), 6) + self.assertAlmostEqual(thePoint1.y(), thePoint2.y(), 6) + + + def test_line_tangent(self): + """ Test 1. Set tangency between elliptic arc and a line + """ + aLine = self.mySketch.addLine(10, -10, 90, 40) + self.myNbLines += 1 + self.myDOF += 4 + model.do() + + self.mySketch.setTangent(self.myEllipticArc.result(), aLine.result()) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.assertTangentLineEllipse(aLine, self.myEllipticArc) + + + def test_line_coincident_then_tangent(self): + """ Test 2. Set tangency between elliptic arc and a line, if the extremity of the line is coincident with the elliptic arc + """ + aLine = self.mySketch.addLine(10, -10, 90, 40) + self.mySketch.setCoincident(aLine.endPoint(), self.myEllipticArc.result()) + self.myNbLines += 1 + self.myNbCoincidence += 1 + self.myDOF += 3 + model.do() + + self.mySketch.setTangent(self.myEllipticArc.result(), aLine.result()) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.assertTangentLineEllipse(aLine, self.myEllipticArc) + + + def test_line_tangent_then_coincident(self): + """ Test 3. Set tangency between elliptic arc and a line, after that apply coincidence of extremity of the line and the elliptic arc's curve + """ + aLine = self.mySketch.addLine(10, -10, 90, 40) + self.myNbLines += 1 + self.myDOF += 4 + model.do() + + self.mySketch.setTangent(self.myEllipticArc.result(), aLine.result()) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.mySketch.setCoincident(aLine.startPoint(), self.myEllipticArc.result()) + self.myNbCoincidence += 1 + self.myDOF -= 1 + model.do() + + self.assertTangentLineEllipse(aLine, self.myEllipticArc) + + + def test_line_tangent_then_remove_coincidence(self): + """ Test 4. Set tangency between elliptic arc and a line, which have a coincident point, then remove this coincidence + """ + aLine = self.mySketch.addLine(10, -10, 90, 40) + aCoincidence = self.mySketch.setCoincident(aLine.endPoint(), self.myEllipticArc.result()) + self.myNbLines += 1 + self.myNbCoincidence += 1 + self.myDOF += 3 + model.do() + + self.mySketch.setTangent(self.myEllipticArc.result(), aLine.result()) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.myDocument.removeFeature(aCoincidence.feature()) + self.myNbCoincidence -= 1 + self.myDOF += 1 + model.do() + + self.assertTangentLineEllipse(aLine, self.myEllipticArc) + + + def test_line_coincident_point_then_tangent(self): + """ Test 5. Set tangency between elliptic arc and a line, if the extremity of the line is coincident with the start point of elliptic arc + """ + aLine = self.mySketch.addLine(10, -10, 90, 40) + self.mySketch.setCoincident(aLine.endPoint(), self.myEllipticArc.startPoint()) + self.myNbLines += 1 + self.myNbCoincidence += 1 + self.myDOF += 2 + model.do() + + self.mySketch.setTangent(self.myEllipticArc.result(), aLine.result()) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.assertTangentLineEllipse(aLine, self.myEllipticArc) + + + def test_line_tangent_then_coincident_point(self): + """ Test 6. Set tangency between elliptic arc and a line, after that apply coincidence of extremities of the line and the elliptic arc + """ + aLine = self.mySketch.addLine(10, -10, 90, 40) + self.myNbLines += 1 + self.myDOF += 4 + model.do() + + self.mySketch.setTangent(self.myEllipticArc.result(), aLine.result()) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.assertTangentLineEllipse(aLine, self.myEllipticArc) + + self.mySketch.setCoincident(aLine.startPoint(), self.myEllipticArc.startPoint()) + self.myNbCoincidence += 1 + self.myDOF -= 2 + model.do() + + self.assertTangentLineEllipse(aLine, self.myEllipticArc) + + + def test_line_tangent_then_remove_coincidence_on_extremity(self): + """ Test 7. Set tangency between elliptic arc and a line, which have a coincident boundary point, then remove this coincidence + """ + aLine = self.mySketch.addLine(10, -10, 90, 40) + aCoincidence = self.mySketch.setCoincident(aLine.endPoint(), self.myEllipticArc.endPoint()) + self.myNbLines += 1 + self.myNbCoincidence += 1 + self.myDOF += 2 + model.do() + + self.mySketch.setTangent(self.myEllipticArc.result(), aLine.result()) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.myDocument.removeFeature(aCoincidence.feature()) + self.myNbCoincidence -= 1 + self.myDOF += 2 + model.do() + + self.assertTangentLineEllipse(aLine, self.myEllipticArc) + + + def test_circle_tangent(self): + """ Test 8. Set tangency between elliptic arc and a circle + """ + aCircle = self.mySketch.addCircle(30, 10, 20) + self.myNbCircles += 1 + self.myDOF += 3 + model.do() + + self.mySketch.setTangent(self.myEllipticArc.result(), aCircle.defaultResult()) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.assertTangentCircleEllipse(aCircle, self.myEllipticArc) + + + def test_circle_coincident_then_tangent(self): + """ Test 9. Set tangency between elliptic arc and a circle, if the circle is coincident with start point of elliptic arc's minor axis + """ + aCircle = self.mySketch.addCircle(30, 10, 20) + self.mySketch.setCoincident(self.myEllipticArc.startPoint(), aCircle.defaultResult()) + self.myNbCircles += 1 + self.myNbCoincidence += 1 + self.myDOF += 2 + model.do() + + self.mySketch.setTangent(self.myEllipticArc.result(), aCircle.defaultResult()) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.assertTangentCircleEllipse(aCircle, self.myEllipticArc) + self.assertAlmostEqual(model.distancePointPoint(aCircle.center(), self.myEllipticArc.startPoint()), aCircle.radius().value()) + + + def test_arc_tangent(self): + """ Test 10. Set tangency between elliptic arc and a circular arc + """ + anArc = self.mySketch.addArc(30, -10, 40, -10, 20, -10, False) + self.myNbArcs += 1 + self.myDOF += 5 + model.do() + + self.mySketch.setTangent(self.myEllipticArc.result(), anArc.results()[-1]) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.assertTangentCircleEllipse(anArc, self.myEllipticArc) + + + def test_arc_coincident_then_tangent(self): + """ Test 11. Set tangency between elliptic arc and an arc, if the extremities of the arcs are coincident + """ + anArc = self.mySketch.addArc(30, -10, 40, -10, 20, -10, False) + self.mySketch.setCoincident(anArc.endPoint(), self.myEllipticArc.startPoint()) + self.myNbArcs += 1 + self.myNbCoincidence += 1 + self.myDOF += 3 + model.do() + + self.mySketch.setTangent(self.myEllipticArc.result(), anArc.results()[-1]) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.assertTangentCircleEllipse(anArc, self.myEllipticArc) + + + def test_arc_tangent_then_coincident(self): + """ Test 12. Set tangency between elliptic arc and an arc, after that apply coincidence of extremities of the arcs + """ + anArc = self.mySketch.addArc(30, -10, 40, -10, 20, -10, False) + self.myNbArcs += 1 + self.myDOF += 5 + model.do() + + self.mySketch.setTangent(self.myEllipticArc.result(), anArc.results()[-1]) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.mySketch.setCoincident(anArc.startPoint(), self.myEllipticArc.startPoint()) + self.myNbCoincidence += 1 + self.myDOF -= 2 + model.do() + + self.assertTangentCircleEllipse(anArc, self.myEllipticArc) + + + def test_arc_tangent_then_remove_coincidence(self): + """ Test 13. Set tangency between elliptic arc and an arc, which have a coincident point, then remove this coincidence + """ + anArc = self.mySketch.addArc(30, -10, 40, -10, 20, -10, False) + aCoincidence = self.mySketch.setCoincident(anArc.endPoint(), self.myEllipticArc.endPoint()) + self.myNbArcs += 1 + self.myNbCoincidence += 1 + self.myDOF += 4 + model.do() + + self.mySketch.setTangent(self.myEllipticArc.result(), anArc.results()[-1]) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.myDocument.removeFeature(aCoincidence.feature()) + self.myNbCoincidence -= 1 + self.myDOF += 1 + model.do() + + self.assertTangentCircleEllipse(anArc, self.myEllipticArc) + + + def test_ellipse_tangent(self): + """ Test 14. Set tangency between ellipse and elliptic arc + """ + anEllipse = self.mySketch.addEllipse(-30, 10, -10, 0, 20) + self.myNbEllipses += 1 + self.myDOF += 5 + model.do() + + self.mySketch.setTangent(self.myEllipticArc.result(), anEllipse.result()) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.assertTangentEllipses(anEllipse, self.myEllipticArc) + + + def test_elliptic_arcs_tangent(self): + """ Test 15. Set tangency between two elliptic arcs + """ + anEllipticArc = self.mySketch.addEllipticArc(35, 20, 60, 30, 40, 40, 20, -0.4890968089561491, True) + self.myNbEllipticArcs += 1 + self.myDOF += 7 + model.do() + + self.mySketch.setTangent(self.myEllipticArc.result(), anEllipticArc.result()) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.assertTangentEllipses(anEllipticArc, self.myEllipticArc) + + + def test_elliptic_arcs_coincident_then_tangent(self): + """ Test 16. Set tangency between two elliptic arcs, if their extremities are coincident + """ + anEllipticArc = self.mySketch.addEllipticArc(35, 20, 60, 30, 40, 40, 20, -0.4890968089561491, True) + self.mySketch.setCoincident(anEllipticArc.startPoint(), self.myEllipticArc.endPoint()) + self.myNbEllipticArcs += 1 + self.myNbCoincidence += 1 + self.myDOF += 5 + model.do() + + self.mySketch.setTangent(self.myEllipticArc.result(), anEllipticArc.result()) + self.myNbTangency += 1 + self.myDOF -= 1 + model.do() + + self.assertTangentEllipses(anEllipticArc, self.myEllipticArc) + + + +if __name__ == "__main__": + test_program = unittest.main(exit=False) + assert test_program.result.wasSuccessful(), "Test failed" diff --git a/src/SketchPlugin/Test/TestPresentation.py b/src/SketchPlugin/Test/TestPresentation.py index 0265fd406..9f251831d 100644 --- a/src/SketchPlugin/Test/TestPresentation.py +++ b/src/SketchPlugin/Test/TestPresentation.py @@ -89,6 +89,8 @@ assert(featureToPresentation(SketchConstraintMirror_1.feature()).getAISObject(No assert(featureToPresentation(SketchMultiTranslation_1.feature()).getAISObject(None) is not None) assert(featureToPresentation(SketchMultiRotation_1.feature()).getAISObject(None) is not None) +model.end() + # Test presentation for Fillet on low-level aSession = ModelAPI_Session.get() aSketchFeature = featureToCompositeFeature(Sketch_1.feature()) @@ -126,4 +128,30 @@ anArcPnt3.setValue(0, 5) assert(featureToPresentation(anArc).getAISObject(None) is not None) aSession.finishOperation() -model.end() +# Test presentation for MacroEllipse on low-level +aSession.startOperation() +anEllipse = aSketchFeature.addFeature("SketchMacroEllipse") +anEllipsePnt1 = geomDataAPI_Point2D(anEllipse.attribute("first_point")) +anEllipsePnt2 = geomDataAPI_Point2D(anEllipse.attribute("second_point")) +anEllipsePnt3 = geomDataAPI_Point2D(anEllipse.attribute("passed_point")) +anEllipseType = anEllipse.string("ellipse_type") +anEllipseType.setValue("by_center_axis_point") +anEllipsePnt1.setValue(10, 0) +anEllipsePnt2.setValue(-10, 0) +anEllipsePnt3.setValue(0, 5) +assert(featureToPresentation(anEllipse).getAISObject(None) is not None) +aSession.finishOperation() + +# Test presentation for MacroEllipticArc on low-level +aSession.startOperation() +anEllipticArc = aSketchFeature.addFeature("SketchMacroEllipticArc") +anEllipticArcPnt1 = geomDataAPI_Point2D(anEllipticArc.attribute("center")) +anEllipticArcPnt2 = geomDataAPI_Point2D(anEllipticArc.attribute("major_axis_point")) +anEllipticArcPnt3 = geomDataAPI_Point2D(anEllipticArc.attribute("start_point")) +anEllipticArcPnt4 = geomDataAPI_Point2D(anEllipticArc.attribute("end_point")) +anEllipticArcPnt1.setValue(0, 0) +anEllipticArcPnt2.setValue(10, 0) +anEllipticArcPnt3.setValue(0, 5) +anEllipticArcPnt4.setValue(-10, 0) +assert(featureToPresentation(anEllipticArc).getAISObject(None) is not None) +aSession.finishOperation() diff --git a/src/SketchPlugin/Test/TestProjectionEllipticArc.py b/src/SketchPlugin/Test/TestProjectionEllipticArc.py new file mode 100644 index 000000000..7d8fc93c3 --- /dev/null +++ b/src/SketchPlugin/Test/TestProjectionEllipticArc.py @@ -0,0 +1,61 @@ +# 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 +# + +from SketchAPI import * + +from salome.shaper import model + +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")) +SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False) +SketchLine_1 = SketchProjection_1.createdFeature() +SketchLine_2 = Sketch_1.addLine(-17.32050807568878, -110, 0, -100) +SketchLine_3 = Sketch_1.addLine(0, -100, 10, -117.3205080756888) +SketchArc_1 = Sketch_1.addArc(0, -100, 10, -117.3205080756888, -17.32050807568878, -110, False) +SketchConstraintRadius_1 = Sketch_1.setRadius(SketchArc_1.results()[1], 20) +SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchArc_1.center(), SketchLine_1.result()) +SketchConstraintDistance_1 = Sketch_1.setDistance(SketchArc_1.center(), SketchAPI_Line(SketchLine_1).startPoint(), 100, True) +SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchArc_1.center(), SketchLine_2.endPoint()) +SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchArc_1.center(), SketchLine_3.startPoint()) +SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_2.startPoint(), SketchArc_1.endPoint()) +SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchArc_1.startPoint()) +SketchConstraintPerpendicular_1 = Sketch_1.setPerpendicular(SketchLine_2.result(), SketchLine_3.result()) +SketchConstraintAngle_1 = Sketch_1.setAngle(SketchLine_3.result(), SketchLine_1.result(), 150) +model.do() +Plane_4 = model.addPlane(Part_1_doc, model.selection("FACE", "PartSet/XOZ"), model.selection("EDGE", "PartSet/OX"), 30) +Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("COMPOUND", "Sketch_1")], model.selection(), model.selection("FACE", "Plane_1"), 0, model.selection(), 0) +Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Plane_1")) +SketchProjection_2 = Sketch_2.addProjection(model.selection("EDGE", "[Extrusion_1_1/To_Face][Extrusion_1_1/Generated_Face&Sketch_1/SketchArc_1_2]"), True) +SketchEllipticArc_1 = SketchProjection_2.createdFeature() +SketchProjection_3 = Sketch_2.addProjection(model.selection("EDGE", "[Extrusion_1_1/From_Face][Extrusion_1_1/Generated_Face&Sketch_1/SketchArc_1_2]"), True) +SketchEllipticArc_2 = SketchProjection_3.createdFeature() +model.do() +model.end() + +from GeomAPI import * + +ellipse1 = SketchEllipticArc_1.results()[-1].resultSubShapePair()[0].shape() +assert(ellipse1.isEdge() and ellipse1.edge().isEllipse()) +ellipse2 = SketchEllipticArc_2.results()[-1].resultSubShapePair()[0].shape() +assert(ellipse2.isEdge() and ellipse2.edge().isEllipse()) + +assert(model.checkPythonDump()) diff --git a/src/SketchPlugin/Test/TestSplitEllipse.py b/src/SketchPlugin/Test/TestSplitEllipse.py new file mode 100644 index 000000000..6425ca4d0 --- /dev/null +++ b/src/SketchPlugin/Test/TestSplitEllipse.py @@ -0,0 +1,135 @@ +# 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 +# + +from salome.shaper import model +from salome.shaper import geom +import math + +from ModelAPI import * +from SketchAPI import * + +CENTER = geom.Pnt2d(10, 10) +MAJOR_RADIUS = 50 +MINOR_RADIUS = 30 + +DOF = 10 +NB_LINES = 3 +NB_ELLIPSES = 1 +NB_ELLIPTIC_ARCS = 0 +NB_COINCIDENCES = 6 + +TOLERANCE = 1.e-6 + +def checkFeaturesQuantity(theSketch): + model.testNbSubFeatures(theSketch, "SketchLine", NB_LINES) + model.testNbSubFeatures(theSketch, "SketchEllipse", NB_ELLIPSES) + model.testNbSubFeatures(theSketch, "SketchEllipticArc", NB_ELLIPTIC_ARCS) + model.testNbSubFeatures(theSketch, "SketchConstraintCoincidence", NB_COINCIDENCES) + assert(model.dof(theSketch) == DOF) + +def checkEllipticArcs(theSketch): + for aSub in theSketch.features().list(): + aFeature = ModelAPI_Feature.feature(aSub) + if aFeature is not None and aFeature.getKind() == "SketchEllipticArc": + assertEllipticArc(SketchAPI_EllipticArc(aFeature)) + +def assertEllipticArc(theArc): + assertPoints(theArc.center(), CENTER) + assertPoints(theArc.majorAxisPositive(), geom.Pnt2d(CENTER.x() + MAJOR_RADIUS, CENTER.y())) + assertPoints(theArc.minorAxisPositive(), geom.Pnt2d(CENTER.x(), CENTER.y() + MINOR_RADIUS)) + +def assertPoints(thePoint1, thePoint2): + assert(math.fabs(thePoint1.x() - thePoint2.x()) < TOLERANCE), "{} != {}".format(thePoint1.x(), thePoint2.x()) + assert(math.fabs(thePoint1.y() - thePoint2.y()) < TOLERANCE), "{} != {}".format(thePoint1.y(), thePoint2.y()) + + +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")) +SketchEllipse_1 = Sketch_1.addEllipse(CENTER.x(), CENTER.y(), CENTER.x() + math.sqrt(MAJOR_RADIUS**2 - MINOR_RADIUS**2), CENTER.y(), MINOR_RADIUS) +SketchLine_1 = Sketch_1.addLine(-16.74176451428603, -15.34869012470842, -16.85909682653373, 35.30399198463829) +SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchEllipse_1.result()) +SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchEllipse_1.result()) +SketchLine_2 = Sketch_1.addLine(-16.85909682653373, 35.30399198463829, 20.9032928583277, -19.27802168426675) +SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint()) +SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchEllipse_1.result()) +SketchLine_3 = Sketch_1.addLine(34.69765676551338, 36.08465583643841, 35.0422024535432, -15.96612629290852) +SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_3.startPoint(), SketchEllipse_1.result()) +SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchEllipse_1.result()) +model.do() + +checkFeaturesQuantity(Sketch_1) +checkEllipticArcs(Sketch_1) + +# split the ellipse +SketchSplit = Sketch_1.addSplit(SketchEllipse_1, geom.Pnt2d(CENTER.x() + MAJOR_RADIUS, CENTER.y())) +model.do() +NB_ELLIPSES -= 1 +NB_ELLIPTIC_ARCS += 2 +NB_COINCIDENCES += 4 +DOF += 3 + +checkFeaturesQuantity(Sketch_1) +checkEllipticArcs(Sketch_1) + +# split the middle arc of ellipse +EllipticArc = SketchAPI_EllipticArc(model.lastSubFeature(Sketch_1, "SketchEllipticArc")) +ANGLE = -math.pi/2 - math.pi/10 +SketchSplit = Sketch_1.addSplit(EllipticArc, geom.Pnt2d(CENTER.x() + MAJOR_RADIUS * math.cos(ANGLE), CENTER.y() + MINOR_RADIUS * math.sin(ANGLE))) +model.do() +NB_ELLIPTIC_ARCS += 2 +NB_COINCIDENCES += 4 +DOF += 8 + +checkFeaturesQuantity(Sketch_1) +checkEllipticArcs(Sketch_1) + +# try to split the boundary arc of ellipse, +# it shoult fail, because there is no coincident points +EllipticArc = SketchAPI_EllipticArc(model.lastSubFeature(Sketch_1, "SketchEllipticArc")) +SketchSplit = Sketch_1.addSplit(EllipticArc, geom.Pnt2d(CENTER.x() - MAJOR_RADIUS, CENTER.y())) +model.end() +aValidators = ModelAPI_Session.get().validators() +assert(not aValidators.validate(SketchSplit.feature())) + +# remove previous split and add coincidence +model.undo() +model.begin() +Part_1_doc.removeFeature(SketchSplit.feature()) +model.do() +Sketch_1.setCoincident(SketchLine_2.startPoint(), EllipticArc.result()) +model.do() +NB_COINCIDENCES += 1 +DOF -= 1 + +# split the boundary arc of ellipse +SketchSplit = Sketch_1.addSplit(EllipticArc, geom.Pnt2d(CENTER.x() - MAJOR_RADIUS, CENTER.y())) +model.do() +NB_ELLIPTIC_ARCS += 1 +NB_COINCIDENCES += 2 +DOF += 4 + +checkFeaturesQuantity(Sketch_1) +checkEllipticArcs(Sketch_1) + +model.end() + +assert(model.checkPythonDump()) diff --git a/src/SketchPlugin/Test/TestTrimEllipse.py b/src/SketchPlugin/Test/TestTrimEllipse.py new file mode 100644 index 000000000..f621c2c8c --- /dev/null +++ b/src/SketchPlugin/Test/TestTrimEllipse.py @@ -0,0 +1,117 @@ +# 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 +# + +from salome.shaper import model +from salome.shaper import geom +import math + +from ModelAPI import * +from SketchAPI import * + +CENTER = geom.Pnt2d(10, 10) +MAJOR_RADIUS = 50 +MINOR_RADIUS = 30 + +DOF = 11 +NB_LINES = 3 +NB_ELLIPSES = 1 +NB_ELLIPTIC_ARCS = 0 +NB_COINCIDENCES = 5 +NB_EQUALS = 0 + +TOLERANCE = 1.e-6 + +def checkFeaturesQuantity(theSketch): + model.testNbSubFeatures(theSketch, "SketchLine", NB_LINES) + model.testNbSubFeatures(theSketch, "SketchEllipse", NB_ELLIPSES) + model.testNbSubFeatures(theSketch, "SketchEllipticArc", NB_ELLIPTIC_ARCS) + model.testNbSubFeatures(theSketch, "SketchConstraintCoincidence", NB_COINCIDENCES) + model.testNbSubFeatures(theSketch, "SketchConstraintEqual", NB_EQUALS) + assert(model.dof(theSketch) == DOF) + +def checkEllipticArcs(theSketch): + for aSub in theSketch.features().list(): + aFeature = ModelAPI_Feature.feature(aSub) + if aFeature is not None and aFeature.getKind() == "SketchEllipticArc": + assertEllipticArc(SketchAPI_EllipticArc(aFeature)) + +def assertEllipticArc(theArc): + assertPoints(theArc.center(), CENTER) + assertPoints(theArc.majorAxisPositive(), geom.Pnt2d(CENTER.x() + MAJOR_RADIUS, CENTER.y())) + assertPoints(theArc.minorAxisPositive(), geom.Pnt2d(CENTER.x(), CENTER.y() + MINOR_RADIUS)) + +def assertPoints(thePoint1, thePoint2): + assert(math.fabs(thePoint1.x() - thePoint2.x()) < TOLERANCE), "{} != {}".format(thePoint1.x(), thePoint2.x()) + assert(math.fabs(thePoint1.y() - thePoint2.y()) < TOLERANCE), "{} != {}".format(thePoint1.y(), thePoint2.y()) + + +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")) +SketchEllipse_1 = Sketch_1.addEllipse(CENTER.x(), CENTER.y(), CENTER.x() + math.sqrt(MAJOR_RADIUS**2 - MINOR_RADIUS**2), CENTER.y(), MINOR_RADIUS) +SketchLine_1 = Sketch_1.addLine(-16.74176451428603, -15.34869012470842, -16.85909682653373, 35.30399198463829) +SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchEllipse_1.result()) +SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchEllipse_1.result()) +SketchLine_2 = Sketch_1.addLine(-16.85909682653373, 35.30399198463829, 20.9032928583277, -19.27802168426675) +SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint()) +SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchEllipse_1.result()) +SketchLine_3 = Sketch_1.addLine(34.69765676551338, 36.08465583643841, 35.0422024535432, -17.96612629290852) +SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_3.startPoint(), SketchEllipse_1.result()) +model.do() + +checkFeaturesQuantity(Sketch_1) +checkEllipticArcs(Sketch_1) + +# trim the ellipse +SketchSplit = Sketch_1.addTrim(SketchEllipse_1, geom.Pnt2d(CENTER.x() + MAJOR_RADIUS, CENTER.y())) +model.do() +NB_ELLIPSES -= 1 +NB_ELLIPTIC_ARCS += 1 +NB_COINCIDENCES += 1 + +checkFeaturesQuantity(Sketch_1) +checkEllipticArcs(Sketch_1) + +# trim the middle arc of ellipse +EllipticArc = SketchAPI_EllipticArc(model.lastSubFeature(Sketch_1, "SketchEllipticArc")) +ANGLE = -math.pi/2 - math.pi/10 +SketchSplit = Sketch_1.addTrim(EllipticArc, geom.Pnt2d(CENTER.x() + MAJOR_RADIUS * math.cos(ANGLE), CENTER.y() + MINOR_RADIUS * math.sin(ANGLE))) +model.do() +NB_ELLIPTIC_ARCS += 1 +NB_COINCIDENCES += 1 +NB_EQUALS += 1 +DOF += 1 + +checkFeaturesQuantity(Sketch_1) +checkEllipticArcs(Sketch_1) + +# trim the boundary arc of ellipse +SketchSplit = Sketch_1.addTrim(EllipticArc, geom.Pnt2d(CENTER.x() - MAJOR_RADIUS, CENTER.y())) +model.do() +NB_COINCIDENCES -= 1 +DOF += 1 + +checkFeaturesQuantity(Sketch_1) +checkEllipticArcs(Sketch_1) + +model.end() + +assert(model.checkPythonDump()) diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.cpp b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.cpp index 4b89a59dc..4f546aacc 100644 --- a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.cpp +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.cpp @@ -105,7 +105,7 @@ EntityWrapperPtr PlaneGCSSolver_Storage::createAttribute( /// \brief Update value static bool updateValue(const double& theSource, double& theDest) { - static const double aTol = 1000. * tolerance; + static const double aTol = 1.e4 * tolerance; bool isUpdated = fabs(theSource - theDest) > aTol; if (isUpdated) theDest = theSource; -- 2.39.2