]> SALOME platform Git repositories - modules/shaper.git/commitdiff
Salome HOME
Task 2.12. New entities: ellipses and arcs of ellipses (issue #3003)
authorazv <azv@opencascade.com>
Fri, 4 Oct 2019 09:53:38 +0000 (12:53 +0300)
committerazv <azv@opencascade.com>
Fri, 4 Oct 2019 12:31:16 +0000 (15:31 +0300)
Unit-tests for elliptic arcs:
* Middle point
* Perpendicular
* Tangent
* Projection
* Trim
* Split

src/GeomAPI/GeomAPI_Ellipse2d.cpp
src/SketchAPI/SketchAPI_Projection.cpp
src/SketchPlugin/CMakeLists.txt
src/SketchPlugin/Test/TestConstraintMiddlePointOnEllipticArc.py [new file with mode: 0644]
src/SketchPlugin/Test/TestConstraintPerpendicularEllipseLine.py [new file with mode: 0644]
src/SketchPlugin/Test/TestConstraintTangentEllipticArc.py [new file with mode: 0644]
src/SketchPlugin/Test/TestPresentation.py
src/SketchPlugin/Test/TestProjectionEllipticArc.py [new file with mode: 0644]
src/SketchPlugin/Test/TestSplitEllipse.py [new file with mode: 0644]
src/SketchPlugin/Test/TestTrimEllipse.py [new file with mode: 0644]
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.cpp

index 7db3ef8a4461f0329cc9004e8ee002d34331efce..92c16886f728d03d4c047c7c38fd8f8c9ae2817d 100644 (file)
@@ -35,6 +35,8 @@
 #include <GeomLib_Tool.hxx>
 #include <gp_Ax22d.hxx>
 #include <gp_Elips2d.hxx>
+#include <IntAna2d_AnaIntersection.hxx>
+#include <IntAna2d_Conic.hxx>
 #include <Precision.hxx>
 
 #define MY_ELLIPSE implPtr<gp_Elips2d>()
@@ -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 <typename EXTREMAPTR>
-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<GeomAPI_Lin2d>& theLine
                                    std::shared_ptr<GeomAPI_Pnt2d>& thePointOnMe,
                                    std::shared_ptr<GeomAPI_Pnt2d>& thePointOnLine)
 {
+  IntAna2d_AnaIntersection anInter(theLine->impl<gp_Lin2d>(), IntAna2d_Conic(*MY_ELLIPSE));
   Extrema_ExtElC2d anExtema(theLine->impl<gp_Lin2d>(), *MY_ELLIPSE);
-  return extrema(&anExtema, thePointOnLine, thePointOnMe);
+  return extrema(&anInter, &anExtema, theLine->location(), thePointOnLine, thePointOnMe);
 }
 
 double GeomAPI_Ellipse2d::distance(const std::shared_ptr<GeomAPI_Circ2d>& theCircle,
                                    std::shared_ptr<GeomAPI_Pnt2d>& thePointOnMe,
                                    std::shared_ptr<GeomAPI_Pnt2d>& thePointOnCircle)
 {
+  IntAna2d_AnaIntersection anInter(theCircle->impl<gp_Circ2d>(), IntAna2d_Conic(*MY_ELLIPSE));
   Extrema_ExtElC2d anExtema(theCircle->impl<gp_Circ2d>(), *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<GeomAPI_Ellipse2d>& theEllipse,
@@ -165,9 +193,10 @@ double GeomAPI_Ellipse2d::distance(const std::shared_ptr<GeomAPI_Ellipse2d>& the
   Handle(Geom2d_Ellipse) anEllipse1 = new Geom2d_Ellipse(theEllipse->impl<gp_Elips2d>());
   Handle(Geom2d_Ellipse) anEllipse2 = new Geom2d_Ellipse(*MY_ELLIPSE);
 
+  IntAna2d_AnaIntersection anInter(theEllipse->impl<gp_Elips2d>(), 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;
 }
index 493991519f467748531f2181d9747fc1daef88a2..4baf94150d033d352ab0027122a39bbae3822ffd 100644 (file)
 #include <SketchPlugin_Line.h>
 #include <SketchPlugin_Circle.h>
 #include <SketchPlugin_Ellipse.h>
+#include <SketchPlugin_EllipticArc.h>
 #include <SketchPlugin_Point.h>
 
 #include <SketchAPI_Arc.h>
 #include <SketchAPI_Circle.h>
 #include <SketchAPI_Ellipse.h>
+#include <SketchAPI_EllipticArc.h>
 #include <SketchAPI_Line.h>
 #include <SketchAPI_Point.h>
 
@@ -104,6 +106,8 @@ std::shared_ptr<SketchAPI_SketchEntity> 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));
 
index 357172d648fd1fe312beca523255450bd855e5bc..1edde96b787f1727fe859a269003b38860465146 100644 (file)
@@ -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 (file)
index 0000000..e9f4d35
--- /dev/null
@@ -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 (file)
index 0000000..4a9678b
--- /dev/null
@@ -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 (file)
index 0000000..77f0fd8
--- /dev/null
@@ -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"
index 0265fd406cba0e6d43fff3ecbd32fb67527f90a8..9f251831d5b2354dad301854c7c6f96d5af0922d 100644 (file)
@@ -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 (file)
index 0000000..7d8fc93
--- /dev/null
@@ -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 (file)
index 0000000..6425ca4
--- /dev/null
@@ -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 (file)
index 0000000..f621c2c
--- /dev/null
@@ -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())
index 4b89a59dccfde47d154cff9b13e1d84279503a7e..4f546aacce57e886154834202d6e9f626bccaec8 100644 (file)
@@ -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;