]> SALOME platform Git repositories - modules/shaper.git/commitdiff
Salome HOME
Issue 2556: Functionality of inspection “WhatIs”
authorazv <azv@opencascade.com>
Mon, 3 Sep 2018 10:19:51 +0000 (13:19 +0300)
committerazv <azv@opencascade.com>
Mon, 3 Sep 2018 10:21:08 +0000 (13:21 +0300)
Unit tests for the inspection functionality.

16 files changed:
src/GeomAPI/CMakeLists.txt
src/GeomAPI/GeomAPI.i
src/GeomAPI/GeomAPI_Cone.cpp
src/GeomAPI/GeomAPI_Face.cpp
src/GeomAPI/GeomAPI_Pnt.cpp
src/GeomAPI/GeomAPI_Pnt.h
src/GeomAPI/GeomAPI_Shell.cpp
src/GeomAPI/GeomAPI_Solid.cpp
src/GeomAPI/GeomAPI_Wire.cpp
src/GeomAPI/GeomAPI_swig.h
src/GeomAPI/Test/TestBox.py [new file with mode: 0644]
src/GeomAPI/Test/TestCone.py [new file with mode: 0644]
src/GeomAPI/Test/TestCylinder.py [new file with mode: 0644]
src/GeomAPI/Test/TestPolygon.py [new file with mode: 0644]
src/GeomAPI/Test/TestSphere.py [new file with mode: 0644]
src/GeomAPI/Test/TestTorus.py [new file with mode: 0644]

index 82b062a0c41cb57ae0f8f092911f6a4e73f6359d..4959dde28860961bc060f21b1b58f7acfcf5fe7e 100644 (file)
@@ -22,6 +22,7 @@ FIND_PACKAGE(SWIG REQUIRED)
 
 INCLUDE(${SWIG_USE_FILE})
 INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
+INCLUDE(UnitTest)
 
 SET(PROJECT_HEADERS
     GeomAPI.h
@@ -157,3 +158,12 @@ ENDIF(WIN32)
 INSTALL(TARGETS _GeomAPI DESTINATION ${SHAPER_INSTALL_SWIG})
 INSTALL(TARGETS GeomAPI DESTINATION ${SHAPER_INSTALL_BIN})
 INSTALL(FILES ${SWIG_SCRIPTS} DESTINATION ${SHAPER_INSTALL_SWIG})
+
+ADD_UNIT_TESTS(
+  TestBox.py
+  TestCone.py
+  TestCylinder.py
+  TestPolygon.py
+  TestSphere.py
+  TestTorus.py
+)
index 82e29210883fd3a0b4e3e77cbdbf8a1372db6526..aa26482458d7cac892cda2ac8a9a319fe1a1ba03 100644 (file)
 
 // standard definitions
 %include "typemaps.i"
+%include "std_list.i"
 %include "std_string.i"
 %include "std_shared_ptr.i"
 
 // shared pointers
 %shared_ptr(GeomAPI_AISObject)
+%shared_ptr(GeomAPI_Angle)
+%shared_ptr(GeomAPI_Angle2d)
 %shared_ptr(GeomAPI_Ax1)
 %shared_ptr(GeomAPI_Ax2)
 %shared_ptr(GeomAPI_Ax3)
+%shared_ptr(GeomAPI_Box)
 %shared_ptr(GeomAPI_Circ)
 %shared_ptr(GeomAPI_Circ2d)
+%shared_ptr(GeomAPI_Cone)
 %shared_ptr(GeomAPI_Curve)
+%shared_ptr(GeomAPI_Cylinder)
 %shared_ptr(GeomAPI_DataMapOfShapeMapOfShapes)
 %shared_ptr(GeomAPI_DataMapOfShapeShape)
 %shared_ptr(GeomAPI_Dir)
 %shared_ptr(GeomAPI_Dir2d)
 %shared_ptr(GeomAPI_Edge)
+%shared_ptr(GeomAPI_Ellipse)
+%shared_ptr(GeomAPI_Ellipse2d)
 %shared_ptr(GeomAPI_Face)
 %shared_ptr(GeomAPI_ICustomPrs)
 %shared_ptr(GeomAPI_Interface)
 %shared_ptr(GeomAPI_Shape)
 %shared_ptr(GeomAPI_ShapeExplorer)
 %shared_ptr(GeomAPI_ShapeIterator)
+%shared_ptr(GeomAPI_Shell)
+%shared_ptr(GeomAPI_Solid)
+%shared_ptr(GeomAPI_Sphere)
+%shared_ptr(GeomAPI_Torus)
+%shared_ptr(GeomAPI_Trsf)
 %shared_ptr(GeomAPI_Vertex)
+%shared_ptr(GeomAPI_Wire)
 %shared_ptr(GeomAPI_XY)
 %shared_ptr(GeomAPI_XYZ)
-%shared_ptr(GeomAPI_Trsf)
-%shared_ptr(GeomAPI_Wire)
-%shared_ptr(GeomAPI_Shell)
-%shared_ptr(GeomAPI_Solid)
-%shared_ptr(GeomAPI_Box)
+
+
+%typemap(in) std::list<std::shared_ptr<GeomAPI_Pnt> > &thePoints (std::list<std::shared_ptr<GeomAPI_Pnt> > temp) {
+  std::shared_ptr<GeomAPI_Pnt> * temp_pnt;
+  int newmem = 0;
+  if (PySequence_Check($input)) {
+    for (Py_ssize_t i = 0; i < PySequence_Size($input); ++i) {
+      PyObject * item = PySequence_GetItem($input, i);
+      if ((SWIG_ConvertPtrAndOwn(item, (void **)&temp_pnt, $descriptor(std::shared_ptr<GeomAPI_Pnt> *), SWIG_POINTER_EXCEPTION, &newmem)) == 0) {
+        if (!temp_pnt) {
+          PyErr_SetString(PyExc_TypeError, "argument must be list of GeomAPI_Pnt.");
+          return NULL;
+       }
+        temp.push_back(*temp_pnt);
+        if (newmem & SWIG_CAST_NEW_MEMORY) {
+          delete temp_pnt;
+        }
+      }
+      Py_DECREF(item);
+    }
+    $1 = &temp;
+  } else {
+    PyErr_SetString(PyExc_ValueError, "argument must be list of GeomAPI_Pnt.");
+    return NULL;
+  }
+}
 
 
 // all supported interfaces
 %include "GeomAPI_Interface.h"
 %include "GeomAPI_Shape.h"
 %include "GeomAPI_AISObject.h"
+%include "GeomAPI_Angle.h"
+%include "GeomAPI_Angle2d.h"
 %include "GeomAPI_Ax1.h"
 %include "GeomAPI_Ax2.h"
 %include "GeomAPI_Ax3.h"
+%include "GeomAPI_Box.h"
 %include "GeomAPI_Circ.h"
 %include "GeomAPI_Circ2d.h"
+%include "GeomAPI_Cone.h"
 %include "GeomAPI_Curve.h"
+%include "GeomAPI_Cylinder.h"
 %include "GeomAPI_DataMapOfShapeMapOfShapes.h"
 %include "GeomAPI_DataMapOfShapeShape.h"
 %include "GeomAPI_Dir.h"
 %include "GeomAPI_Dir2d.h"
 %include "GeomAPI_Edge.h"
+%include "GeomAPI_Ellipse.h"
+%include "GeomAPI_Ellipse2d.h"
 %include "GeomAPI_Face.h"
 %include "GeomAPI_ICustomPrs.h"
 %include "GeomAPI_IPresentable.h"
 %include "GeomAPI_Pnt2d.h"
 %include "GeomAPI_ShapeExplorer.h"
 %include "GeomAPI_ShapeIterator.h"
+%include "GeomAPI_Shell.h"
+%include "GeomAPI_Solid.h"
+%include "GeomAPI_Sphere.h"
+%include "GeomAPI_Torus.h"
+%include "GeomAPI_Trsf.h"
 %include "GeomAPI_Vertex.h"
+%include "GeomAPI_Wire.h"
 %include "GeomAPI_XY.h"
 %include "GeomAPI_XYZ.h"
-%include "GeomAPI_Trsf.h"
-%include "GeomAPI_Wire.h"
-%include "GeomAPI_Shell.h"
-%include "GeomAPI_Solid.h"
-%include "GeomAPI_Box.h"
+
+// std::list -> []
+%template(PointList) std::list<std::shared_ptr<GeomAPI_Pnt> >;
+%template(ShapeList) std::list<std::shared_ptr<GeomAPI_Shape> >;
index e18d4d333e6cb87f05f8ed034a083e00ec945e41..c8344c3acf960bc4a0465d57bb69f87863d8fb0a 100644 (file)
@@ -61,11 +61,19 @@ GeomAPI_Cone::GeomAPI_Cone(const std::shared_ptr<GeomAPI_Pnt>& theLocation,
                            const double theSemiAngle,
                            const double theRadius1,
                            const double theRadius2)
-  : GeomAPI_Interface(
-      newCone(theLocation->impl<gp_Pnt>(), theAxis->impl<gp_Dir>(), theSemiAngle, theRadius1)),
-    myRadius1(theRadius1),
+  : myRadius1(theRadius1),
     myRadius2(theRadius2)
 {
+  gp_Pnt aLoc = theLocation->impl<gp_Pnt>();
+  gp_Dir aDir = theAxis->impl<gp_Dir>();
+  if (theRadius1 > theRadius2) {
+    aLoc.ChangeCoord() += aDir.XYZ() * (theRadius1 - theRadius2) / Tan(theSemiAngle);
+    aDir.Reverse();
+    myRadius1 = theRadius2;
+    myRadius2 = theRadius1;
+  }
+
+  setImpl(newCone(aLoc, aDir, theSemiAngle, myRadius1));
 }
 
 //=================================================================================================
index ff5dbba27f32b2260d9445ac14b01070c39f5efc..8e8a036552229f714fd34183216f69bdfc34d72c 100644 (file)
@@ -196,15 +196,17 @@ std::shared_ptr<GeomAPI_Cone> GeomAPI_Face::getCone() const
   Handle(Geom_Surface) aSurf = BRep_Tool::Surface(aFace);
   if (aSurf->IsKind(STANDARD_TYPE(Geom_ConicalSurface))) {
     gp_Cone aCon = Handle(Geom_ConicalSurface)::DownCast(aSurf)->Cone();
-    const gp_Pnt& aLoc = aCon.Location();
-    const gp_Dir& aDir = aCon.Position().Direction();
-    double aRadius1 = aCon.RefRadius();
+    gp_Pnt aLoc = aCon.Location();
+    gp_Dir aDir = aCon.Position().Direction();
 
     double aUMin, aUMax, aVMin, aVMax;
     BRepTools::UVBounds(aFace, aUMin, aUMax, aVMin, aVMax);
 
     double aSemiAngle = Abs(aCon.SemiAngle());
-    double aRadius2 = aRadius1 - (aVMax - aVMin) * Sin(aSemiAngle);
+    double aRadius1 = aCon.RefRadius() + aVMin * Sin(aCon.SemiAngle());
+    double aRadius2 = aCon.RefRadius() + aVMax * Sin(aCon.SemiAngle());
+
+    aLoc.ChangeCoord() += aDir.XYZ() * aVMin * Cos(aCon.SemiAngle());
 
     GeomPointPtr aLocation(new GeomAPI_Pnt(aLoc.X(), aLoc.Y(), aLoc.Z()));
     GeomDirPtr aDirection(new GeomAPI_Dir(aDir.X(), aDir.Y(), aDir.Z()));
index 976d3949f3733bf96dddfe5502494f05f39f8b44..97d66fc6e28ccefb821e4df20a37adb7048e015e 100644 (file)
@@ -85,6 +85,20 @@ bool GeomAPI_Pnt::isEqual(const std::shared_ptr<GeomAPI_Pnt>& theOther) const
   return distance(theOther) < Precision::Confusion();
 }
 
+bool GeomAPI_Pnt::isLess(const std::shared_ptr<GeomAPI_Pnt>& theOther) const
+{
+  if (MY_PNT->X() + Precision::Confusion() < theOther->x())
+    return true;
+  else if (MY_PNT->X() < theOther->x() + Precision::Confusion()) {
+    if (MY_PNT->Y() + Precision::Confusion() < theOther->y())
+      return true;
+    else if (MY_PNT->Y() < theOther->y() + Precision::Confusion() &&
+             MY_PNT->Z() + Precision::Confusion() < theOther->z())
+      return true;
+  }
+  return false;
+}
+
 std::shared_ptr<GeomAPI_Pnt2d> GeomAPI_Pnt::to2D(const std::shared_ptr<GeomAPI_Pnt>& theOrigin,
   const std::shared_ptr<GeomAPI_Dir>& theDirX, const std::shared_ptr<GeomAPI_Dir>& theDirY)
 {
index 333a2350acf1dfe8c710e469fd0a1015c80a6dde..d06d5d9fb1be8a0a210c697c611be2d8be89ce1a 100644 (file)
@@ -76,6 +76,13 @@ class GeomAPI_Pnt : public GeomAPI_Interface
   GEOMAPI_EXPORT
   bool isEqual(const std::shared_ptr<GeomAPI_Pnt>& theOther) const;
 
+  /// Returns \c true, if the current point is less than theOther.
+  /// The point is less than other, if X coordinate is less.
+  /// In case of X's are equal, if Y is less than other.
+  /// If Y's are equal too, compare Z's.
+  GEOMAPI_EXPORT
+  bool isLess(const std::shared_ptr<GeomAPI_Pnt>& theOther) const;
+
   /// Projects a point to the plane defined by the origin and 2 axes vectors in this plane
   GEOMAPI_EXPORT
   std::shared_ptr<GeomAPI_Pnt2d> to2D(const std::shared_ptr<GeomAPI_Pnt>& theOrigin,
index baab4a1a9fd9cfb12db13622f5dbc75803655aaa..ae5e12ae972a347398aa852ad71fefd8414fd931 100644 (file)
@@ -176,7 +176,7 @@ std::shared_ptr<GeomAPI_Cone> GeomAPI_Shell::getCone() const
 
   GeomPointPtr anApex;
   GeomDirPtr anAxis;
-  double aSemiAngle, aCosSemiAngle;
+  double aSemiAngle, aTanSemiAngle;
   double aHeight1, aHeight2;
 
   for (TopExp_Explorer anExp(impl<TopoDS_Shape>(), TopAbs_FACE); anExp.More(); anExp.Next()) {
@@ -193,9 +193,9 @@ std::shared_ptr<GeomAPI_Cone> GeomAPI_Shell::getCone() const
       anApex = aCurCone->apex();
       anAxis = aCurCone->axis();
       aSemiAngle = aCurCone->semiAngle();
-      aCosSemiAngle = Cos(aSemiAngle);
-      aHeight1 = aCurCone->radius1() * aCosSemiAngle;
-      aHeight2 = aCurCone->radius2() * aCosSemiAngle;
+      aTanSemiAngle = Tan(aSemiAngle);
+      aHeight1 = aCurCone->radius1() / aTanSemiAngle;
+      aHeight2 = aCurCone->radius2() / aTanSemiAngle;
       isFirstFace = false;
     }
     else {
@@ -211,15 +211,15 @@ std::shared_ptr<GeomAPI_Cone> GeomAPI_Shell::getCone() const
 
       double aSign = anAxis->dot(aCurCone->axis());
       double aCurSemiAngle = aCurCone->semiAngle();
-      double aCosCurSemiAngle = Cos(aSemiAngle);
+      double aTanCurSemiAngle = Tan(aSemiAngle);
 
-      double aH = aCurCone->radius1() * aCosCurSemiAngle * aSign;
+      double aH = aCurCone->radius1() / aTanCurSemiAngle * aSign;
       if (aH < aHeight1)
         aHeight1 = aH;
       else if (aH > aHeight2)
         aHeight2 = aH;
 
-      aH = aCurCone->radius2() * aCosCurSemiAngle * aSign;
+      aH = aCurCone->radius2() / aTanCurSemiAngle * aSign;
       if (aH < aHeight1)
         aHeight1 = aH;
       else if (aH > aHeight2)
@@ -231,8 +231,8 @@ std::shared_ptr<GeomAPI_Cone> GeomAPI_Shell::getCone() const
   if (isCone) {
     GeomPointPtr aLocation(new GeomAPI_Pnt(
       anApex->xyz()->added(anAxis->xyz()->multiplied(aHeight1))));
-    double aRadius1 = aHeight1 * Tan(aSemiAngle);
-    double aRadius2 = aHeight2 * Tan(aSemiAngle);
+    double aRadius1 = aHeight1 * aTanSemiAngle;
+    double aRadius2 = aHeight2 * aTanSemiAngle;
 
     aCone = GeomConePtr(new GeomAPI_Cone(aLocation, anAxis, aSemiAngle, aRadius1, aRadius2));
   }
@@ -298,11 +298,25 @@ std::shared_ptr<GeomAPI_Box> GeomAPI_Shell::getParallelepiped() const
     std::list<GeomPointPtr> aCorners;
     if (aWire->isRectangle(aCorners)) {
       // convert rectangle to plane with dimensions
-      GeomPointPtr anOrigin = aCorners.front();
-      aCorners.pop_front();
 
-      GeomPointPtr aFront = aCorners.front();
-      GeomPointPtr aBack = aCorners.back();
+      // find corner with the smallest coordinates
+      std::list<GeomPointPtr>::const_iterator aPrev = --aCorners.end();
+      std::list<GeomPointPtr>::const_iterator aCur = aPrev--;
+      std::list<GeomPointPtr>::const_iterator aNext = aCorners.begin();
+      GeomPointPtr anOrigin = *aCur;
+      GeomPointPtr aFront = *aNext;
+      GeomPointPtr aBack = *aPrev;
+      aPrev = aCur;
+      aCur = aNext++;
+      while (aNext != aCorners.end()) {
+        if ((*aCur)->isLess(anOrigin)) {
+          anOrigin = *aCur;
+          aFront = *aNext;
+          aBack = *aPrev;
+        }
+        aPrev = aCur;
+        aCur = aNext++;
+      }
 
       aPlanes[aNbPlanes].myWidth = aBack->distance(anOrigin);
       aPlanes[aNbPlanes].myDepth = aFront->distance(anOrigin);
index f128d3d65cf894dbe57aa0097fd7a958062b7891..8ac3a38624f339617430b649a155d4dfc220d174 100644 (file)
@@ -273,8 +273,8 @@ std::shared_ptr<GeomAPI_Cone> GeomAPI_Solid::getCone() const
         anApex->z() + aParam0 * anAxis->z()));
 
     // calculate radii of caps
-    aParam0 /= Cos(aSemiAngle);
-    aParam1 /= Cos(aSemiAngle);
+    aParam0 *= Tan(aSemiAngle);
+    aParam1 *= Tan(aSemiAngle);
 
     aCone = GeomConePtr(new GeomAPI_Cone(aLocation, anAxis, aSemiAngle, aParam0, aParam1));
   }
index bcbaf3e946be37bff95143533d3635145f0dd2c8..65729cbf7bed5f9d49b16703852d3c74f73a366e 100644 (file)
@@ -81,6 +81,8 @@ bool GeomAPI_Wire::isPolygon(std::list<GeomPointPtr>& thePoints) const
 //==================================================================================================
 bool GeomAPI_Wire::isRectangle(std::list<GeomPointPtr>& thePoints) const
 {
+  thePoints.clear();
+
   const TopoDS_Wire& aWire = TopoDS::Wire(impl<TopoDS_Shape>());
   const Handle(Standard_Type)& aLineType = STANDARD_TYPE(Geom_Line);
 
index a7712b0ed867c6b11ed7ed438e63d9624fa3a43e..e92af5fba156fa400f1086bf9a820a9fffdfd0bf 100644 (file)
@@ -23,6 +23,8 @@
 
   #include "GeomAPI.h"
   #include "GeomAPI_AISObject.h"
+  #include "GeomAPI_Angle.h"
+  #include "GeomAPI_Angle2d.h"
   #include "GeomAPI_Ax1.h"
   #include "GeomAPI_Ax2.h"
   #include "GeomAPI_Ax3.h"
@@ -37,6 +39,8 @@
   #include "GeomAPI_Dir.h"
   #include "GeomAPI_Dir2d.h"
   #include "GeomAPI_Edge.h"
+  #include "GeomAPI_Ellipse.h"
+  #include "GeomAPI_Ellipse2d.h"
   #include "GeomAPI_Face.h"
   #include "GeomAPI_ICustomPrs.h"
   #include "GeomAPI_Interface.h"
   #include "GeomAPI_Solid.h"
   #include "GeomAPI_Sphere.h"
   #include "GeomAPI_Torus.h"
+  #include "GeomAPI_Trsf.h"
   #include "GeomAPI_Vertex.h"
+  #include "GeomAPI_Wire.h"
   #include "GeomAPI_XY.h"
   #include "GeomAPI_XYZ.h"
-  #include "GeomAPI_Trsf.h"
-  #include "GeomAPI_Wire.h"
 
   #include <memory>
   #include <string>
diff --git a/src/GeomAPI/Test/TestBox.py b/src/GeomAPI/Test/TestBox.py
new file mode 100644 (file)
index 0000000..a8412a4
--- /dev/null
@@ -0,0 +1,165 @@
+## Copyright (C) 2018-20xx  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<mailto:webmaster.salome@opencascade.com>
+##
+
+from GeomAPI import *
+from SketchAPI import *
+
+from salome.shaper import model
+
+import math
+
+TOLERANCE = 1.e-7
+
+def checkBox(theFeature, theCorner, theWidth, theDepth, theHeight):
+    aShape = theFeature.result().resultSubShapePair()[0].shape()
+    assert(aShape.isSolid())
+    aBox = aShape.solid().getParallelepiped()
+    assert(aBox is not None)
+    assert(aBox.isAxesAligned())
+    aCorner = aBox.corner()
+    assert(aCorner.distance(theCorner) < TOLERANCE), "({}, {}, {}) != expected ({}, {}, {})".format(aCorner.x(), aCorner.y(), aCorner.z(), theCorner.x(), theCorner.y(), theCorner.z())
+    assert(math.fabs(aBox.width() - theWidth) < TOLERANCE), "Width {} != {}".format(aBox.width(), theWidth)
+    assert(math.fabs(aBox.depth() - theDepth) < TOLERANCE), "Depth {} != {}".format(aBox.depth(), theDepth)
+    assert(math.fabs(aBox.height() - theHeight) < TOLERANCE), "Height {} != {}".format(aBox.height(), theHeight)
+
+def assertParallelepiped(theBox, theCorner1, theCorner2):
+    assert(theBox is not None)
+    assert(theBox.isAxesAligned() == False)
+    aCorner1 = theBox.corner()
+    axes = theBox.axes()
+    dirX = axes.dirX()
+    dirY = axes.dirY()
+    dirZ = axes.normal()
+    aCorner2 = GeomAPI.GeomAPI_Pnt(aCorner1.x() + dirX.x() * theBox.width() + dirY.x() * theBox.depth() + dirZ.x() * theBox.height(),
+                                   aCorner1.y() + dirX.y() * theBox.width() + dirY.y() * theBox.depth() + dirZ.y() * theBox.height(),
+                                   aCorner1.z() + dirX.z() * theBox.width() + dirY.z() * theBox.depth() + dirZ.z() * theBox.height())
+    assert(aCorner1.distance(theCorner1) < TOLERANCE), "({}, {}, {}) != expected ({}, {}, {})".format(aCorner1.x(), aCorner1.y(), aCorner1.z(), theCorner1.x(), theCorner1.y(), theCorner1.z())
+    assert(aCorner2.distance(theCorner2) < TOLERANCE), "({}, {}, {}) != expected ({}, {}, {})".format(aCorner2.x(), aCorner2.y(), aCorner2.z(), theCorner2.x(), theCorner2.y(), theCorner2.z())
+
+def checkRotatedBox(theFeature, theCorner1, theCorner2):
+    aShape = theFeature.result().resultSubShapePair()[0].shape()
+    assert(aShape.isSolid())
+    aBox = aShape.solid().getParallelepiped()
+    assertParallelepiped(aBox, theCorner1, theCorner2)
+
+def checkShellRotatedBox(theDocument, theFaceNames, theCorner1, theCorner2):
+    aSelection = []
+    for name in theFaceNames:
+        aSelection.append(model.selection("FACE", name))
+    aShell = model.addShell(theDocument, aSelection)
+    aShape = aShell.result().resultSubShapePair()[0].shape()
+    assert(aShape.isShell())
+    assertParallelepiped(aShape.shell().getParallelepiped(), theCorner1, theCorner2)
+    theDocument.removeFeature(aShell.feature())
+
+def checkShellNotBox(theDocument, theFaceNames):
+    aSelection = []
+    for name in theFaceNames:
+        aSelection.append(model.selection("FACE", name))
+    aShell = model.addShell(theDocument, aSelection)
+    aShape = aShell.result().resultSubShapePair()[0].shape()
+    assert(aShape.isShell())
+    assert(aShape.shell().getParallelepiped() is None)
+    theDocument.removeFeature(aShell.feature())
+
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+ParamSize = model.addParameter(Part_1_doc, "BoxSize", "10")
+ParamWidth = model.addParameter(Part_1_doc, "Width", "20")
+ParamDepth = model.addParameter(Part_1_doc, "Depth", "10")
+ParamHeight = model.addParameter(Part_1_doc, "Height", "25")
+ParamAngle = model.addParameter(Part_1_doc, "Angle", "30")
+Box_1 = model.addBox(Part_1_doc, "BoxSize", "BoxSize", "BoxSize")
+Sketch_1 = model.addSketch(Part_1_doc, model.selection("FACE", "Box_1_1/Top"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "Box_1_1/Front&Box_1_1/Right&Box_1_1/Top"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchLine_1 = Sketch_1.addLine(30, 10, 10, 10)
+SketchLine_2 = Sketch_1.addLine(10, 10, 10, 20)
+SketchLine_3 = Sketch_1.addLine(10, 20, 30, 20)
+SketchLine_4 = Sketch_1.addLine(30, 20, 30, 10)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchPoint_1.result())
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_1.startPoint())
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_2.result())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_3.result())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_4.result())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_1.result(), "Width")
+SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_2.result(), "Depth")
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_1r-SketchLine_2r-SketchLine_3r-SketchLine_4r")], model.selection(), "Height", 0)
+model.do()
+
+# Test 1. Check boxes
+aCornerBox = GeomAPI.GeomAPI_Pnt(0, 0, 0)
+checkBox(Box_1, aCornerBox, ParamSize.value(), ParamSize.value(), ParamSize.value())
+aCornerPara = GeomAPI.GeomAPI_Pnt(ParamSize.value(), ParamSize.value(), ParamSize.value())
+checkBox(Extrusion_1, aCornerPara, ParamWidth.value(), ParamDepth.value(), ParamHeight.value())
+
+# Test 2. Rotate box to keep it still axes-aligned
+Rotation_1 = model.addRotation(Part_1_doc, [model.selection("SOLID", "Extrusion_1_1")], model.selection("EDGE", "Extrusion_1_1/Generated_Face_4&Extrusion_1_1/Generated_Face_1"), 90)
+aCornerPara.setX(aCornerPara.x() + ParamWidth.value() - ParamDepth.value())
+aCornerPara.setY(aCornerPara.y() - ParamWidth.value())
+checkBox(Rotation_1, aCornerPara, ParamDepth.value(), ParamWidth.value(), ParamHeight.value())
+
+# Test 3. Rotate boxes
+Axis_4 = model.addAxis(Part_1_doc, model.selection("VERTEX", "Box_1_1/Back&Box_1_1/Left&Box_1_1/Bottom"), model.selection("VERTEX", "Box_1_1/Front&Box_1_1/Right&Box_1_1/Top"))
+Rotation_2 = model.addRotation(Part_1_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "Axis_1"), "Angle")
+Rotation_3 = model.addRotation(Part_1_doc, [model.selection("SOLID", "Rotation_1_1")], model.selection("EDGE", "Axis_1"), "Angle")
+
+aRotDir = GeomAPI.GeomAPI_Dir(1, 1, 1)
+anAngle = ParamAngle.value() * math.pi / 180.0
+aCosAngle = math.cos(anAngle)
+aSinAngle = math.sin(anAngle)
+
+aCornerBox = GeomAPI.GeomAPI_Pnt(0, ParamSize.value(), 0)
+aCornerBox1 = GeomAPI.GeomAPI_Pnt(ParamSize.value(), 0, ParamSize.value())
+aCornerBox = GeomAPI.GeomAPI_Pnt(aCornerBox.x() * (aCosAngle + (1 - aCosAngle) * aRotDir.x()**2) + aCornerBox.y() * ((1 - aCosAngle) * aRotDir.x() * aRotDir.y() - aSinAngle * aRotDir.z()) + aCornerBox.z() * ((1 - aCosAngle) * aRotDir.x() * aRotDir.z() + aSinAngle * aRotDir.y()),
+                                 aCornerBox.x() * ((1 - aCosAngle) * aRotDir.x() * aRotDir.y() + aSinAngle * aRotDir.z()) + aCornerBox.y() * (aCosAngle + (1 - aCosAngle) * aRotDir.y()**2) + aCornerBox.z() * ((1 - aCosAngle) * aRotDir.y() * aRotDir.z() - aSinAngle * aRotDir.x()),
+                                 aCornerBox.x() * ((1 - aCosAngle) * aRotDir.x() * aRotDir.z() - aSinAngle * aRotDir.y()) + aCornerBox.y() * ((1 - aCosAngle) * aRotDir.y() * aRotDir.z() + aSinAngle * aRotDir.x()) + aCornerBox.z() * (aCosAngle + (1 - aCosAngle) * aRotDir.z()**2))
+aCornerBox1 = GeomAPI.GeomAPI_Pnt(aCornerBox1.x() * (aCosAngle + (1 - aCosAngle) * aRotDir.x()**2) + aCornerBox1.y() * ((1 - aCosAngle) * aRotDir.x() * aRotDir.y() - aSinAngle * aRotDir.z()) + aCornerBox1.z() * ((1 - aCosAngle) * aRotDir.x() * aRotDir.z() + aSinAngle * aRotDir.y()),
+                                  aCornerBox1.x() * ((1 - aCosAngle) * aRotDir.x() * aRotDir.y() + aSinAngle * aRotDir.z()) + aCornerBox1.y() * (aCosAngle + (1 - aCosAngle) * aRotDir.y()**2) + aCornerBox1.z() * ((1 - aCosAngle) * aRotDir.y() * aRotDir.z() - aSinAngle * aRotDir.x()),
+                                  aCornerBox1.x() * ((1 - aCosAngle) * aRotDir.x() * aRotDir.z() - aSinAngle * aRotDir.y()) + aCornerBox1.y() * ((1 - aCosAngle) * aRotDir.y() * aRotDir.z() + aSinAngle * aRotDir.x()) + aCornerBox1.z() * (aCosAngle + (1 - aCosAngle) * aRotDir.z()**2))
+checkRotatedBox(Rotation_2, aCornerBox, aCornerBox1)
+
+aCornerPara.setY(aCornerPara.y() + ParamWidth.value())
+aCornerPara1 = GeomAPI.GeomAPI_Pnt(aCornerPara.x() + ParamDepth.value(), aCornerPara.y() - ParamWidth.value(), aCornerPara.z() + ParamHeight.value())
+aCornerPara = GeomAPI.GeomAPI_Pnt(aCornerPara.x() * (aCosAngle + (1 - aCosAngle) * aRotDir.x()**2) + aCornerPara.y() * ((1 - aCosAngle) * aRotDir.x() * aRotDir.y() - aSinAngle * aRotDir.z()) + aCornerPara.z() * ((1 - aCosAngle) * aRotDir.x() * aRotDir.z() + aSinAngle * aRotDir.y()),
+                                  aCornerPara.x() * ((1 - aCosAngle) * aRotDir.x() * aRotDir.y() + aSinAngle * aRotDir.z()) + aCornerPara.y() * (aCosAngle + (1 - aCosAngle) * aRotDir.y()**2) + aCornerPara.z() * ((1 - aCosAngle) * aRotDir.y() * aRotDir.z() - aSinAngle * aRotDir.x()),
+                                  aCornerPara.x() * ((1 - aCosAngle) * aRotDir.x() * aRotDir.z() - aSinAngle * aRotDir.y()) + aCornerPara.y() * ((1 - aCosAngle) * aRotDir.y() * aRotDir.z() + aSinAngle * aRotDir.x()) + aCornerPara.z() * (aCosAngle + (1 - aCosAngle) * aRotDir.z()**2))
+aCornerPara1 = GeomAPI.GeomAPI_Pnt(aCornerPara1.x() * (aCosAngle + (1 - aCosAngle) * aRotDir.x()**2) + aCornerPara1.y() * ((1 - aCosAngle) * aRotDir.x() * aRotDir.y() - aSinAngle * aRotDir.z()) + aCornerPara1.z() * ((1 - aCosAngle) * aRotDir.x() * aRotDir.z() + aSinAngle * aRotDir.y()),
+                                   aCornerPara1.x() * ((1 - aCosAngle) * aRotDir.x() * aRotDir.y() + aSinAngle * aRotDir.z()) + aCornerPara1.y() * (aCosAngle + (1 - aCosAngle) * aRotDir.y()**2) + aCornerPara1.z() * ((1 - aCosAngle) * aRotDir.y() * aRotDir.z() - aSinAngle * aRotDir.x()),
+                                   aCornerPara1.x() * ((1 - aCosAngle) * aRotDir.x() * aRotDir.z() - aSinAngle * aRotDir.y()) + aCornerPara1.y() * ((1 - aCosAngle) * aRotDir.y() * aRotDir.z() + aSinAngle * aRotDir.x()) + aCornerPara1.z() * (aCosAngle + (1 - aCosAngle) * aRotDir.z()**2))
+checkRotatedBox(Rotation_3, aCornerPara, aCornerPara1)
+
+# Test 4. Compose a non-closed shell of the box faces and check it is not a box
+Shell_objects = ["Rotation_3_1/Rotated_Face_1", "Rotation_3_1/Rotated_Face_2", "Rotation_3_1/Rotated_Face_3", "Rotation_3_1/Rotated_Face_4"]
+checkShellNotBox(Part_1_doc, Shell_objects)
+
+# Test 5. Compose a shell of all box faces
+Shell_objects = ["Rotation_3_1/Rotated_Face_1", "Rotation_3_1/Rotated_Face_2", "Rotation_3_1/Rotated_Face_3", "Rotation_3_1/Rotated_Face_4", "Rotation_3_1/Rotated_Face_5", "Rotation_3_1/Rotated_Face_6"]
+checkShellRotatedBox(Part_1_doc, Shell_objects, aCornerPara, aCornerPara1)
+
+model.end()
diff --git a/src/GeomAPI/Test/TestCone.py b/src/GeomAPI/Test/TestCone.py
new file mode 100644 (file)
index 0000000..2c74a19
--- /dev/null
@@ -0,0 +1,221 @@
+## Copyright (C) 2018-20xx  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<mailto:webmaster.salome@opencascade.com>
+##
+
+from GeomAPI import *
+from SketchAPI import *
+
+from salome.shaper import model
+
+import math
+
+TOLERANCE = 1.e-7
+
+def assertCircle(theEdge, theCenter, theRadius):
+    assert(theEdge.isCircle())
+    aCircle = theEdge.circle()
+    assert(aCircle.center().distance(theCenter) < TOLERANCE), "({}, {}, {}) != expected ({}, {}, {})".format(aCircle.center().x(), aCircle.center().y(), aCircle.center().z(), theCenter.x(), theCenter.y(), theCenter.z())
+    assert(math.fabs(aCircle.radius() - theRadius) < TOLERANCE), "Radius {} != {}".format(aCircle.radius(), theRadius)
+
+def checkCircleEdge(theDocument, theEdgeName, theCenter, theRadius):
+    anEdge = model.addEdge(theDocument, [model.selection("EDGE", theEdgeName)])
+    aShape = anEdge.result().resultSubShapePair()[0].shape()
+    assert(aShape.isEdge())
+    assertCircle(aShape.edge(), theCenter, theRadius)
+    theDocument.removeFeature(anEdge.feature())
+
+def checkCircleFace(theDocument, theFaceName, theCenter, theRadius):
+    aFaceFeature = model.addFace(theDocument, [model.selection("FACE", theFaceName)])
+    aShape = aFaceFeature.result().resultSubShapePair()[0].shape()
+    assert(aShape.isFace())
+    aFace = aShape.face();
+    aSubs = aFace.subShapes(GeomAPI.GeomAPI_Shape.EDGE)
+    assert(aSubs.size() == 1)
+    assertCircle(aSubs[0].edge(), theCenter, theRadius)
+    theDocument.removeFeature(aFaceFeature.feature())
+
+def assertEllipse(theEdge, theFirstFocus, theSecondFocus, theMajorRadius, theMinorRadius):
+    assert(theEdge.isEllipse())
+    anEllipse = theEdge.ellipse()
+    assert(anEllipse.firstFocus().distance(theFirstFocus) < TOLERANCE), "({}, {}, {}) != expected ({}, {}, {})".format(anEllipse.firstFocus().x(), anEllipse.firstFocus().y(), anEllipse.firstFocus().z(), theFirstFocus.x(), theFirstFocus.y(), theFirstFocus.z())
+    assert(anEllipse.secondFocus().distance(theSecondFocus) < TOLERANCE), "({}, {}, {}) != expected ({}, {}, {})".format(anEllipse.secondFocus().x(), anEllipse.secondFocus().y(), anEllipse.secondFocus().z(), theSecondFocus.x(), theSecondFocus.y(), theSecondFocus.z())
+    assert(math.fabs(anEllipse.majorRadius() - theMajorRadius) < TOLERANCE), "Major radius {} != {}".format(anEllipse.majorRadius(), theMajorRadius)
+    assert(math.fabs(anEllipse.minorRadius() - theMinorRadius) < TOLERANCE), "Minor radius {} != {}".format(anEllipse.minorRadius(), theMinorRadius)
+
+def checkEllipseEdge(theDocument, theEdgeName, theFirstFocus, theSecondFocus, theMajorRadius, theMinorRadius):
+    anEdge = model.addEdge(theDocument, [model.selection("EDGE", theEdgeName)])
+    aShape = anEdge.result().resultSubShapePair()[0].shape()
+    assert(aShape.isEdge())
+    assertEllipse(aShape.edge(), theFirstFocus, theSecondFocus, theMajorRadius, theMinorRadius)
+    theDocument.removeFeature(anEdge.feature())
+
+def checkEllipseFace(theDocument, theFaceName, theFirstFocus, theSecondFocus, theMajorRadius, theMinorRadius):
+    aFaceFeature = model.addFace(theDocument, [model.selection("FACE", theFaceName)])
+    aShape = aFaceFeature.result().resultSubShapePair()[0].shape()
+    assert(aShape.isFace())
+    aFace = aShape.face();
+    aSubs = aFace.subShapes(GeomAPI.GeomAPI_Shape.EDGE)
+    assert(aSubs.size() == 1)
+    assertEllipse(aSubs[0].edge(), theFirstFocus, theSecondFocus, theMajorRadius, theMinorRadius)
+    theDocument.removeFeature(aFaceFeature.feature())
+
+def assertCone(theCone, theApex, theAxis, theSemiAngle, theRadius1, theRadius2, theHeight):
+    assert(theCone is not None)
+    anApex = theCone.apex()
+    anAxis = theCone.axis()
+    assert(anApex.distance(theApex) < TOLERANCE), "({}, {}, {}) != expected ({}, {}, {})".format(anApex.x(), anApex.y(), anApex.z(), theApex.x(), theApex.y(), theApex.z())
+    assert(anAxis.isParallel(theAxis, TOLERANCE)), "dir({}, {}, {}) is not parallel to dir({}, {}, {})".format(anAxis.x(), anAxis.y(), anAxis.z(), theAxis.x(), theAxis.y(), theAxis.z())
+    assert(math.fabs(theCone.semiAngle() - theSemiAngle) < TOLERANCE), "SemiAngle {} != {}".format(theCone.semiAngle(), theSemiAngle)
+    assert(math.fabs(theCone.radius1() - theRadius1) < TOLERANCE), "Radius1 {} != {}".format(theCone.radius1(), theRadius1)
+    assert(math.fabs(theCone.radius2() - theRadius2) < TOLERANCE), "Radius2 {} != {}".format(theCone.radius2(), theRadius2)
+    assert(math.fabs(theCone.height() - theHeight) < TOLERANCE), "Height {} != {}".format(theCone.height(), theHeight)
+
+def checkConeFace(theDocument, theFaceName, theApex, theAxis, theSemiAngle, theRadius1, theRadius2, theHeight):
+    # check conical face
+    aFace = model.addFace(theDocument, [model.selection("FACE", theFaceName)])
+    aShape = aFace.result().resultSubShapePair()[0].shape()
+    assert(aShape.isFace())
+    assertCone(aShape.face().getCone(), theApex, theAxis, theSemiAngle, theRadius1, theRadius2, theHeight)
+    theDocument.removeFeature(aFace.feature())
+
+def checkConeShell(theDocument, theFaceNames, theApex, theAxis, theSemiAngle, theRadius1, theRadius2, theHeight):
+    # check conical shell
+    aSelection = []
+    for name in theFaceNames:
+        aSelection.append(model.selection("FACE", name))
+    aShell = model.addShell(theDocument, aSelection)
+    aShape = aShell.result().resultSubShapePair()[0].shape()
+    assert(aShape.isShell())
+    assertCone(aShape.shell().getCone(), theApex, theAxis, theSemiAngle, theRadius1, theRadius2, theHeight)
+    theDocument.removeFeature(aShell.feature())
+
+def checkConeAll(theDocument, theFeature, theFaceName, theApex, theAxis, theSemiAngle, theRadius1, theRadius2, theHeight):
+    # check solid
+    aShape = theFeature.result().resultSubShapePair()[0].shape()
+    assert(aShape.isSolid())
+    assertCone(aShape.solid().getCone(), theApex, theAxis, theSemiAngle, theRadius1, theRadius2, theHeight)
+
+    checkConeShell(theDocument, [theFaceName], theApex, theAxis, theSemiAngle, theRadius1, theRadius2, theHeight)
+    checkConeFace(theDocument, theFaceName, theApex, theAxis, theSemiAngle, theRadius1, theRadius2, theHeight)
+
+def checkSegment(theDocument, theEdgeName, theStartPoint, theEndPoint):
+    anEdgeFeature = model.addEdge(theDocument, [model.selection("EDGE", theEdgeName)])
+    aShape = anEdgeFeature.result().resultSubShapePair()[0].shape()
+    assert(aShape.isEdge())
+    anEdge = aShape.edge()
+    assert(anEdge.isLine())
+    assert(anEdge.firstPoint().distance(theStartPoint) < TOLERANCE)
+    assert(anEdge.lastPoint().distance(theEndPoint) < TOLERANCE)
+    theDocument.removeFeature(anEdgeFeature.feature())
+
+def checkVertex(theDocument, theVertexName, theCoordinates):
+    aVertex = model.addVertex(theDocument, [model.selection("VERTEX", theVertexName)])
+    aShape = aVertex.result().resultSubShapePair()[0].shape()
+    assert(aShape.isVertex())
+    assert(aShape.vertex().point().distance(theCoordinates) < TOLERANCE)
+    theDocument.removeFeature(aVertex.feature())
+
+def semiAngle(theRadius1, theRadius2, theHeight):
+    return math.atan(math.fabs(theRadius1 - theRadius2) / theHeight)
+
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+ParamR1 = model.addParameter(Part_1_doc, "R1", "50")
+ParamR2 = model.addParameter(Part_1_doc, "R2", "5")
+ParamH = model.addParameter(Part_1_doc, "H", "70")
+ParamShift = model.addParameter(Part_1_doc, "Shift", "5")
+ParamAngle = model.addParameter(Part_1_doc, "Angle", "60")
+Cone_1 = model.addCone(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), "R1", "R2", "H")
+model.do()
+
+# Test 1. Check cone
+aSemiAngle = semiAngle(ParamR1.value(), ParamR2.value(), ParamH.value())
+anApex = GeomAPI.GeomAPI_Pnt(0, 0, ParamR1.value() / math.tan(aSemiAngle))
+anAxis = GeomAPI.GeomAPI_Dir(0, 0, 1)
+checkConeAll(Part_1_doc, Cone_1, "Cone_1_1/Face_1", anApex, anAxis, aSemiAngle, ParamR2.value(), ParamR1.value(), ParamH.value())
+checkCircleFace(Part_1_doc, "Cone_1_1/Face_2", GeomAPI.GeomAPI_Pnt(0, 0, ParamH.value()), ParamR2.value())
+checkCircleEdge(Part_1_doc, "Cone_1_1/Face_1&Cone_1_1/Face_2", GeomAPI.GeomAPI_Pnt(0, 0, ParamH.value()), ParamR2.value())
+checkCircleFace(Part_1_doc, "Cone_1_1/Face_3", GeomAPI.GeomAPI_Pnt(0, 0, 0), ParamR1.value())
+checkCircleEdge(Part_1_doc, "Cone_1_1/Face_1&Cone_1_1/Face_3", GeomAPI.GeomAPI_Pnt(0, 0, 0), ParamR1.value())
+checkSegment(Part_1_doc, "Cone_1_1/Face_1", GeomAPI.GeomAPI_Pnt(ParamR1.value(), 0, 0), GeomAPI.GeomAPI_Pnt(ParamR2.value(), 0, ParamH.value()))
+
+# Test 2. Update cone radii
+ParamR1.setValue(0)
+model.do()
+aSemiAngle = semiAngle(ParamR1.value(), ParamR2.value(), ParamH.value())
+anApex.setZ(0)
+checkConeAll(Part_1_doc, Cone_1, "Cone_1_1/Face_1", anApex, anAxis, aSemiAngle, ParamR1.value(), ParamR2.value(), ParamH.value())
+checkCircleFace(Part_1_doc, "Cone_1_1/Face_2", GeomAPI.GeomAPI_Pnt(0, 0, ParamH.value()), ParamR2.value())
+checkCircleEdge(Part_1_doc, "Cone_1_1/Face_1&Cone_1_1/Face_2", GeomAPI.GeomAPI_Pnt(0, 0, ParamH.value()), ParamR2.value())
+checkSegment(Part_1_doc, "Cone_1_1/Face_1", GeomAPI.GeomAPI_Pnt(0, 0, 0), GeomAPI.GeomAPI_Pnt(ParamR2.value(), 0, ParamH.value()))
+checkVertex(Part_1_doc, "Cone_1_1/Vertex_2", GeomAPI.GeomAPI_Pnt(0, 0, 0))
+
+ParamR2.setValue(50)
+ParamR1.setValue(10)
+model.do()
+aSemiAngle = semiAngle(ParamR1.value(), ParamR2.value(), ParamH.value())
+anApex.setZ(-ParamR1.value() / math.tan(aSemiAngle))
+checkConeAll(Part_1_doc, Cone_1, "Cone_1_1/Face_1", anApex, anAxis, aSemiAngle, ParamR1.value(), ParamR2.value(), ParamH.value())
+checkCircleFace(Part_1_doc, "Cone_1_1/Face_2", GeomAPI.GeomAPI_Pnt(0, 0, ParamH.value()), ParamR2.value())
+checkCircleEdge(Part_1_doc, "Cone_1_1/Face_1&Cone_1_1/Face_2", GeomAPI.GeomAPI_Pnt(0, 0, ParamH.value()), ParamR2.value())
+checkCircleFace(Part_1_doc, "Cone_1_1/Face_3", GeomAPI.GeomAPI_Pnt(0, 0, 0), ParamR1.value())
+checkCircleEdge(Part_1_doc, "Cone_1_1/Face_1&Cone_1_1/Face_3", GeomAPI.GeomAPI_Pnt(0, 0, 0), ParamR1.value())
+checkSegment(Part_1_doc, "Cone_1_1/Face_1", GeomAPI.GeomAPI_Pnt(ParamR1.value(), 0, 0), GeomAPI.GeomAPI_Pnt(ParamR2.value(), 0, ParamH.value()))
+
+# Test 3. Translate cone
+Translation_1 = model.addTranslation(Part_1_doc, [model.selection("SOLID", "Cone_1_1")], model.selection("EDGE", "PartSet/OX"), "Shift")
+anApex.setX(anApex.x() + ParamShift.value())
+checkConeAll(Part_1_doc, Translation_1, "Translation_1_1/Translated_Face_3", anApex, anAxis, aSemiAngle, ParamR1.value(), ParamR2.value(), ParamH.value())
+checkCircleFace(Part_1_doc, "Translation_1_1/Translated_Face_2", GeomAPI.GeomAPI_Pnt(ParamShift.value(), 0, ParamH.value()), ParamR2.value())
+checkCircleEdge(Part_1_doc, "Translation_1_1/Translated_Face_3&Translation_1_1/Translated_Face_2", GeomAPI.GeomAPI_Pnt(ParamShift.value(), 0, ParamH.value()), ParamR2.value())
+checkCircleFace(Part_1_doc, "Translation_1_1/Translated_Face_1", GeomAPI.GeomAPI_Pnt(ParamShift.value(), 0, 0), ParamR1.value())
+checkCircleEdge(Part_1_doc, "Translation_1_1/Translated_Face_3&Translation_1_1/Translated_Face_1", GeomAPI.GeomAPI_Pnt(ParamShift.value(), 0, 0), ParamR1.value())
+checkSegment(Part_1_doc, "Translation_1_1/Translated_Face_3", GeomAPI.GeomAPI_Pnt(ParamR1.value() + ParamShift.value(), 0, 0), GeomAPI.GeomAPI_Pnt(ParamR2.value() + ParamShift.value(), 0, ParamH.value()))
+
+# Test 4. Rotate cone
+Rotation_1 = model.addRotation(Part_1_doc, [model.selection("SOLID", "Translation_1_1")], model.selection("EDGE", "PartSet/OY"), "Angle")
+anAngle = ParamAngle.value() * math.pi / 180.0
+anAxis = GeomAPI.GeomAPI_Dir(math.sin(anAngle), 0, math.cos(anAngle))
+x, z = anApex.x(), anApex.z()
+anApex.setX(x * math.cos(anAngle) + z * math.sin(anAngle))
+anApex.setZ(-x * math.sin(anAngle) + z * math.cos(anAngle))
+checkConeAll(Part_1_doc, Rotation_1, "Rotation_1_1/Rotated_Face_3", anApex, anAxis, aSemiAngle, ParamR1.value(), ParamR2.value(), ParamH.value())
+aCenter = GeomAPI.GeomAPI_Pnt(ParamShift.value() * math.cos(anAngle) + ParamH.value() * math.sin(anAngle), 0, -ParamShift.value() * math.sin(anAngle) + ParamH.value() * math.cos(anAngle))
+checkCircleFace(Part_1_doc, "Rotation_1_1/Rotated_Face_2", aCenter, ParamR2.value())
+checkCircleEdge(Part_1_doc, "Rotation_1_1/Rotated_Face_3&Rotation_1_1/Rotated_Face_2", aCenter, ParamR2.value())
+aCenter = GeomAPI.GeomAPI_Pnt(ParamShift.value() * math.cos(anAngle), 0, -ParamShift.value() * math.sin(anAngle))
+checkCircleFace(Part_1_doc, "Rotation_1_1/Rotated_Face_1", aCenter, ParamR1.value())
+checkCircleEdge(Part_1_doc, "Rotation_1_1/Rotated_Face_3&Rotation_1_1/Rotated_Face_1", aCenter, ParamR1.value())
+
+# Test 5. Split cone by plane and check conical shell and elliptic face
+Plane_4 = model.addPlane(Part_1_doc, model.selection("FACE", "PartSet/YOZ"), 20, False)
+Partition_1 = model.addPartition(Part_1_doc, [model.selection("SOLID", "Rotation_1_1"), model.selection("FACE", "Plane_1")])
+checkConeShell(Part_1_doc, ["Partition_1_1_1/Modified_Face_2_2", "Partition_1_1_2/Modified_Face_2_2"], anApex, anAxis, aSemiAngle, ParamR1.value(), ParamR2.value(), ParamH.value())
+
+aFirstFocus = GeomAPI.GeomAPI_Pnt(20, 0, 31.062397266842858)
+aSecondFocus = GeomAPI.GeomAPI_Pnt(20, 0, -1.0935246846933797)
+aMajorRadius = 27.91915871311068
+aMinorRadius = 22.824955511666207
+checkEllipseFace(Part_1_doc, "Partition_1_1_2/Modified_Face_2_1", aFirstFocus, aSecondFocus, aMajorRadius, aMinorRadius)
+checkEllipseEdge(Part_1_doc, "Partition_1_1_1/Modified_Face_1_divided_1_e_1", aFirstFocus, aSecondFocus, aMajorRadius, aMinorRadius)
+
+model.end()
diff --git a/src/GeomAPI/Test/TestCylinder.py b/src/GeomAPI/Test/TestCylinder.py
new file mode 100644 (file)
index 0000000..726b837
--- /dev/null
@@ -0,0 +1,149 @@
+## Copyright (C) 2018-20xx  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<mailto:webmaster.salome@opencascade.com>
+##
+
+from GeomAPI import *
+from SketchAPI import *
+
+from salome.shaper import model
+
+import math
+
+TOLERANCE = 1.e-7
+
+def assertCylinder(theCylinder, theLocation, theAxis, theRadius, theHeight):
+    assert(theCylinder is not None)
+    aLoc = theCylinder.location()
+    aDir = theCylinder.axis()
+    assert(aLoc.distance(theLocation) < TOLERANCE), "({}, {}, {}) != expected ({}, {}, {})".format(aLoc.x(), aLoc.y(), aLoc.z(), theLocation.x(), theLocation.y(), theLocation.z())
+    assert(aDir.isParallel(theAxis, TOLERANCE)), "dir({}, {}, {}) is not parallel to dir({}, {}, {})".format(aDir.x(), aDir.y(), aDir.z(), theAxis.x(), theAxis.y(), theAxis.z())
+    assert(math.fabs(theCylinder.radius() - theRadius) < TOLERANCE), "Radius {} != {}".format(theCylinder.radius(), theRadius)
+    assert(math.fabs(theCylinder.height() - theHeight) < TOLERANCE), "Height {} != {}".format(theCylinder.height(), theHeight)
+
+def checkCylinderFace(theDocument, theFaceName, theLocation, theAxis, theRadius, theHeight):
+    aFace = model.addFace(theDocument, [model.selection("FACE", theFaceName)])
+    aShape = aFace.result().resultSubShapePair()[0].shape()
+    assert(aShape.isFace())
+    assertCylinder(aShape.face().getCylinder(), theLocation, theAxis, theRadius, theHeight)
+    theDocument.removeFeature(aFace.feature())
+
+def checkCylinderShell(theDocument, theFaceNames, theLocation, theAxis, theRadius, theHeight):
+    aSelection = []
+    for name in theFaceNames:
+        aSelection.append(model.selection("FACE", name))
+    aShell = model.addShell(theDocument, aSelection)
+    aShape = aShell.result().resultSubShapePair()[0].shape()
+    assert(aShape.isShell())
+    assertCylinder(aShape.shell().getCylinder(), theLocation, theAxis, theRadius, theHeight)
+    theDocument.removeFeature(aShell.feature())
+
+def checkCylinderAll(theDocument, theFeature, theFaceName, theLocation, theAxis, theRadius, theHeight):
+    aShape = theFeature.result().resultSubShapePair()[0].shape()
+    assert(aShape.isSolid())
+    assertCylinder(aShape.solid().getCylinder(), theLocation, theAxis, theRadius, theHeight)
+
+    checkCylinderShell(theDocument, [theFaceName], theLocation, theAxis, theRadius, theHeight)
+    checkCylinderFace(theDocument, theFaceName, theLocation, theAxis, theRadius, theHeight)
+
+def checkNonCylinder(theFeature):
+    aShape = theFeature.result().resultSubShapePair()[0].shape()
+    assert(aShape.isSolid())
+    assert(aShape.solid().getCylinder() is None)
+
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+ParamH = model.addParameter(Part_1_doc, "H", "10")
+ParamR = model.addParameter(Part_1_doc, "R", "10")
+ParamAngle = model.addParameter(Part_1_doc, "Angle", "30")
+Cylinder_1 = model.addCylinder(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), "2*R", "H")
+Sketch_1 = model.addSketch(Part_1_doc, model.selection("FACE", "Cylinder_1_1/Face_2"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "Cylinder_1_1/Face_1&Cylinder_1_1/Face_2__cc"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 10)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], "R")
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f")], model.selection(), "H", 0)
+Sketch_2 = model.addSketch(Part_1_doc, model.standardPlane("XOZ"))
+SketchLine_1 = Sketch_2.addLine(5, 20, 0, 20)
+SketchLine_2 = Sketch_2.addLine(0, 20, 0, 30)
+SketchLine_3 = Sketch_2.addLine(0, 30, 5, 30)
+SketchLine_4 = Sketch_2.addLine(5, 30, 5, 20)
+SketchConstraintCoincidence_2 = Sketch_2.setCoincident(SketchLine_4.endPoint(), SketchLine_1.startPoint())
+SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintHorizontal_1 = Sketch_2.setHorizontal(SketchLine_1.result())
+SketchConstraintVertical_1 = Sketch_2.setVertical(SketchLine_2.result())
+SketchConstraintHorizontal_2 = Sketch_2.setHorizontal(SketchLine_3.result())
+SketchConstraintVertical_2 = Sketch_2.setVertical(SketchLine_4.result())
+SketchConstraintLength_1 = Sketch_2.setLength(SketchLine_1.result(), "R/2")
+SketchConstraintLength_2 = Sketch_2.setLength(SketchLine_2.result(), "H")
+SketchIntersectionPoint_1 = Sketch_2.addIntersectionPoint(model.selection("EDGE", "Extrusion_1_1/Generated_Face_1&Extrusion_1_1/To_Face_1"), False)
+[SketchPoint_2, SketchPoint_3] = SketchIntersectionPoint_1.intersectionPoints()
+SketchConstraintCoincidence_6 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_1.result())
+SketchProjection_2 = Sketch_2.addProjection(model.selection("EDGE", "PartSet/OZ"), False)
+SketchLine_5 = SketchProjection_2.createdFeature()
+SketchConstraintCoincidence_7 = Sketch_2.setCoincident(SketchLine_2.endPoint(), SketchLine_5.result())
+model.do()
+Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_1r-SketchLine_2r-SketchLine_3r-SketchLine_4r")], model.selection("EDGE", "PartSet/OZ"), 270, 0)
+model.do()
+
+# Test 1. Check cylinders
+aLoc1 = GeomAPI.GeomAPI_Pnt(0, 0, 0)
+aLoc2 = GeomAPI.GeomAPI_Pnt(0, 0, 2 * ParamH.value())
+aLoc3 = GeomAPI.GeomAPI_Pnt(0, 0, 3 * ParamH.value())
+anAxis = GeomAPI.GeomAPI_Dir(0, 0, 1)
+checkCylinderAll(Part_1_doc, Cylinder_1, "Cylinder_1_1/Face_1", aLoc1, anAxis, 2 * ParamR.value(), ParamH.value())
+checkCylinderAll(Part_1_doc, Extrusion_1, "Extrusion_1_1/Generated_Face_1", aLoc2, anAxis, ParamR.value(), ParamH.value())
+checkNonCylinder(Revolution_1)
+checkCylinderShell(Part_1_doc, ["Revolution_1_1/Generated_Face_4"], aLoc3, anAxis, 0.5 * ParamR.value(), ParamH.value())
+checkCylinderFace(Part_1_doc, "Revolution_1_1/Generated_Face_4", aLoc3, anAxis, 0.5 * ParamR.value(), ParamH.value())
+
+# Test 2. Rotate cylinders
+Rotation_1 = model.addRotation(Part_1_doc, [model.selection("SOLID", "Cylinder_1_1")], model.selection("EDGE", "PartSet/OX"), "Angle")
+Rotation_2 = model.addRotation(Part_1_doc, [model.selection("SOLID", "Extrusion_1_1")], model.selection("EDGE", "PartSet/OX"), "Angle")
+Rotation_3 = model.addRotation(Part_1_doc, [model.selection("SOLID", "Revolution_1_1")], model.selection("EDGE", "PartSet/OX"), "Angle")
+
+anAngle = ParamAngle.value() * math.pi / 180.0
+aCosAngle = math.cos(anAngle)
+aSinAngle = math.sin(anAngle)
+anAxis = GeomAPI.GeomAPI_Dir(0, anAxis.y() * aCosAngle - anAxis.z() * aSinAngle, anAxis.y() * aSinAngle + anAxis.z() * aCosAngle)
+aLoc1 = GeomAPI.GeomAPI_Pnt(0, aLoc1.y() * aCosAngle - aLoc1.z() * aSinAngle, aLoc1.y() * aSinAngle + aLoc1.z() * aCosAngle)
+aLoc2 = GeomAPI.GeomAPI_Pnt(0, aLoc2.y() * aCosAngle - aLoc2.z() * aSinAngle, aLoc2.y() * aSinAngle + aLoc2.z() * aCosAngle)
+aLoc3 = GeomAPI.GeomAPI_Pnt(0, aLoc3.y() * aCosAngle - aLoc3.z() * aSinAngle, aLoc3.y() * aSinAngle + aLoc3.z() * aCosAngle)
+checkCylinderAll(Part_1_doc, Rotation_1, "Rotation_1_1/Rotated_Face_3", aLoc1, anAxis, 2 * ParamR.value(), ParamH.value())
+checkCylinderAll(Part_1_doc, Rotation_2, "Rotation_2_1/Rotated_Face_3", aLoc2, anAxis, ParamR.value(), ParamH.value())
+checkNonCylinder(Rotation_3)
+checkCylinderShell(Part_1_doc, ["Rotation_3_1/Rotated_Face_5"], aLoc3, anAxis, 0.5 * ParamR.value(), ParamH.value())
+checkCylinderFace(Part_1_doc, "Rotation_3_1/Rotated_Face_5", aLoc3, anAxis, 0.5 * ParamR.value(), ParamH.value())
+
+# Test 3. Split cylinder and compose a shell
+Plane_4 = model.addPlane(Part_1_doc, model.selection("FACE", "PartSet/XOY"), "2.2*H", False)
+Plane_5 = model.addPlane(Part_1_doc, model.selection("FACE", "PartSet/XOZ"), "H", False)
+Partition_1_objects = [model.selection("SOLID", "Rotation_3_1"), model.selection("FACE", "Plane_1"), model.selection("FACE", "Plane_2")]
+Partition_1 = model.addPartition(Part_1_doc, Partition_1_objects)
+
+Shell_1_objects = ["Partition_1_1_1/Modified_Face_3_5", "Partition_1_1_4/Modified_Face_3_3", "Partition_1_1_2/Modified_Face_1_divided_2_1"]
+checkCylinderShell(Part_1_doc, Shell_1_objects, aLoc3, anAxis, 0.5 * ParamR.value(), ParamH.value())
+
+model.end()
diff --git a/src/GeomAPI/Test/TestPolygon.py b/src/GeomAPI/Test/TestPolygon.py
new file mode 100644 (file)
index 0000000..c05c388
--- /dev/null
@@ -0,0 +1,102 @@
+## Copyright (C) 2018-20xx  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<mailto:webmaster.salome@opencascade.com>
+##
+
+from GeomAPI import *
+from SketchAPI import *
+
+from salome.shaper import model
+
+import math
+
+TOLERANCE = 1.e-7
+
+def assertRectangle(theWire):
+    assert(theWire is not None)
+    aCorners = [GeomAPI.GeomAPI_Pnt(0, 0, 0)]
+    assert(theWire.isRectangle(aCorners))
+
+def checkRectangleFace(theDocument, theFaceName):
+    aFace = model.addFace(theDocument, [model.selection("FACE", theFaceName)])
+    aShape = aFace.result().resultSubShapePair()[0].shape()
+    assert(aShape.isFace())
+    aSubs = aShape.subShapes(GeomAPI.GeomAPI_Shape.WIRE)
+    assert(aSubs.size() == 1)
+    assertRectangle(aSubs[0].wire())
+    theDocument.removeFeature(aFace.feature())
+
+def checkRectangleWire(theDocument, theEdgeNames):
+    aSelection = []
+    for name in theEdgeNames:
+        aSelection.append(model.selection("EDGE", name))
+    aWire = model.addWire(theDocument, aSelection)
+    aShape = aWire.result().resultSubShapePair()[0].shape()
+    assert(aShape.isWire())
+    assertRectangle(aShape.wire())
+    theDocument.removeFeature(aWire.feature())
+
+
+def assertPolygon(theWire):
+    assert(theWire is not None)
+    aCorners = [GeomAPI.GeomAPI_Pnt(0, 0, 0)]
+    assert(theWire.isPolygon(aCorners))
+
+def checkPolygonWire(theDocument, theEdgeNames):
+    aSelection = []
+    for name in theEdgeNames:
+        aSelection.append(model.selection("EDGE", name))
+    aWire = model.addWire(theDocument, aSelection)
+    aShape = aWire.result().resultSubShapePair()[0].shape()
+    assert(aShape.isWire())
+    assertPolygon(aShape.wire())
+    theDocument.removeFeature(aWire.feature())
+
+def checkPolyline(theDocument, theVertexNames):
+    aSelection = []
+    for name in theVertexNames:
+        aSelection.append(model.selection("VERTEX", name))
+    aWire = model.addPolyline3D(Part_1_doc, aSelection, False)
+    aShape = aWire.result().resultSubShapePair()[0].shape()
+    assert(aShape.isWire())
+    assertPolygon(aShape.wire())
+    theDocument.removeFeature(aWire.feature())
+
+
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Box_1 = model.addBox(Part_1_doc, 10, 10, 10)
+model.do()
+
+# Test 1. Check face/wire of box is a rectangle
+checkRectangleFace(Part_1_doc, "Box_1_1/Top")
+Wire_edges = ["Box_1_1/Left&Box_1_1/Top", "Box_1_1/Front&Box_1_1/Top", "Box_1_1/Right&Box_1_1/Top", "Box_1_1/Back&Box_1_1/Top"]
+checkRectangleWire(Part_1_doc, Wire_edges)
+
+# Test 2. Build a polygon from edges
+Wire_edges = ["Box_1_1/Left&Box_1_1/Bottom", "Box_1_1/Front&Box_1_1/Left", "Box_1_1/Left&Box_1_1/Top", "Box_1_1/Back&Box_1_1/Top", "Box_1_1/Right&Box_1_1/Top"]
+checkPolygonWire(Part_1_doc, Wire_edges)
+
+# Test 3. Build a polygon from vertices
+Poly_vertices = ["Box_1_1/Back&Box_1_1/Left&Box_1_1/Bottom", "Box_1_1/Front&Box_1_1/Left&Box_1_1/Bottom", "Box_1_1/Front&Box_1_1/Right&Box_1_1/Bottom", "Box_1_1/Front&Box_1_1/Right&Box_1_1/Top", "Box_1_1/Front&Box_1_1/Left&Box_1_1/Top", "Box_1_1/Back&Box_1_1/Right&Box_1_1/Top"]
+checkPolyline(Part_1_doc, Poly_vertices)
+
+model.end()
diff --git a/src/GeomAPI/Test/TestSphere.py b/src/GeomAPI/Test/TestSphere.py
new file mode 100644 (file)
index 0000000..043270f
--- /dev/null
@@ -0,0 +1,125 @@
+## Copyright (C) 2018-20xx  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<mailto:webmaster.salome@opencascade.com>
+##
+
+from GeomAPI import *
+from SketchAPI import *
+
+from salome.shaper import model
+
+import math
+
+TOLERANCE = 1.e-7
+
+def checkArc(theDocument, theEdgeName, theCenter, theRadius):
+    # check edge (arc of circle)
+    anEdge = model.addEdge(theDocument, [model.selection("EDGE", theEdgeName)])
+    aShape = anEdge.result().resultSubShapePair()[0].shape()
+    assert(aShape.isEdge())
+    anArcEdge = aShape.edge()
+    assert(anArcEdge.isArc())
+    aCircle = anArcEdge.circle()
+    assert(aCircle.center().distance(theCenter) < TOLERANCE)
+    assert(math.fabs(aCircle.radius() - theRadius) < TOLERANCE)
+    theDocument.removeFeature(anEdge.feature())
+
+def assertSphere(theSphere, theCenter, theRadius):
+    assert(theSphere is not None)
+    assert(theSphere.center().distance(theCenter) < TOLERANCE)
+    assert(math.fabs(theSphere.radius() - theRadius) < TOLERANCE)
+
+def checkSphereFace(theDocument, theFaceName, theCenter, theRadius):
+    # check spherical face
+    aFace = model.addFace(theDocument, [model.selection("FACE", theFaceName)])
+    aShape = aFace.result().resultSubShapePair()[0].shape()
+    assert(aShape.isFace())
+    assertSphere(aShape.face().getSphere(), theCenter, theRadius)
+    theDocument.removeFeature(aFace.feature())
+
+def checkSphereShell(theDocument, theFaceName, theCenter, theRadius):
+    # check spherical shell
+    aShell = model.addShell(theDocument, [model.selection("FACE", theFaceName)])
+    aShape = aShell.result().resultSubShapePair()[0].shape()
+    assert(aShape.isShell())
+    assertSphere(aShape.shell().getSphere(), theCenter, theRadius)
+    theDocument.removeFeature(aShell.feature())
+
+def checkSphereAll(theDocument, theFeature, theFaceName, theCenter, theRadius):
+    # check solid
+    aShape = theFeature.result().resultSubShapePair()[0].shape()
+    assert(aShape.isSolid())
+    assertSphere(aShape.solid().getSphere(), theCenter, theRadius)
+
+    checkSphereShell(theDocument, theFaceName, theCenter, theRadius)
+    checkSphereFace(theDocument, theFaceName, theCenter, theRadius)
+    checkArc(theDocument, theFaceName, theCenter, theRadius)
+
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+ParamR = model.addParameter(Part_1_doc, "R", "50")
+ParamShift = model.addParameter(Part_1_doc, "Shift", "30")
+ParamAngle = model.addParameter(Part_1_doc, "Angle", "30")
+ParamAperture = model.addParameter(Part_1_doc, "Aperture", "360")
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchArc_1 = Sketch_1.addArc(0, 0, 0, -50, 0, 50, False)
+SketchArc_1.results()[1].setColor(225, 0, 0)
+SketchLine_1 = Sketch_1.addLine(0, 50, 0, -50)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchArc_1.endPoint(), SketchLine_1.startPoint())
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchArc_1.startPoint(), SketchLine_1.endPoint())
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchAPI_Point(SketchPoint_1).coordinates(), SketchArc_1.center())
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchArc_1.center(), SketchLine_1.result())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_1.result())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchArc_1.results()[1], "R")
+model.do()
+
+# Test 1. Compose sphere
+Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchArc_1_2f-SketchLine_1f")], model.selection("EDGE", "Sketch_1/Edge-SketchLine_1"), "Aperture", 0)
+aCenter = GeomAPI.GeomAPI_Pnt(0, 0, 0)
+checkSphereAll(Part_1_doc, Revolution_1, "Revolution_1_1/Generated_Face_2", aCenter, ParamR.value())
+
+# Test 2. Translate sphere
+Translation_1 = model.addTranslation(Part_1_doc, [model.selection("SOLID", "Revolution_1_1")], model.selection("EDGE", "PartSet/OX"), "Shift")
+aCenter.setX(aCenter.x() + ParamShift.value())
+checkSphereAll(Part_1_doc, Translation_1, "Translation_1_1/Translated_Face_1", aCenter, ParamR.value())
+
+# Test 3. Rotate sphere
+Rotation_1 = model.addRotation(Part_1_doc, [model.selection("SOLID", "Translation_1_1")], model.selection("EDGE", "PartSet/OY"), "Angle")
+anAngle = ParamAngle.value() * math.pi / 180.0
+aCenter.setX(ParamShift.value() * math.cos(anAngle))
+aCenter.setZ(-ParamShift.value() * math.sin(anAngle))
+checkSphereAll(Part_1_doc, Rotation_1, "Rotation_1_1/Rotated_Face_1", aCenter, ParamR.value())
+
+# Test 4. Check result by changing parameters
+ParamR.setValue(100)
+model.do()
+checkSphereAll(Part_1_doc, Rotation_1, "Rotation_1_1/Rotated_Face_1", aCenter, ParamR.value())
+
+ParamAperture.setValue(270)
+model.do()
+checkSphereFace(Part_1_doc, "Rotation_1_1/Rotated_Face_3", aCenter, ParamR.value())
+checkSphereShell(Part_1_doc, "Rotation_1_1/Rotated_Face_3", aCenter, ParamR.value())
+checkArc(Part_1_doc, "Rotation_1_1/Rotated_Face_3&Rotation_1_1/Rotated_Face_1", aCenter, ParamR.value())
+checkArc(Part_1_doc, "Rotation_1_1/Rotated_Face_3&Rotation_1_1/Rotated_Face_2", aCenter, ParamR.value())
+
+model.end()
diff --git a/src/GeomAPI/Test/TestTorus.py b/src/GeomAPI/Test/TestTorus.py
new file mode 100644 (file)
index 0000000..25c7f3e
--- /dev/null
@@ -0,0 +1,95 @@
+## Copyright (C) 2018-20xx  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<mailto:webmaster.salome@opencascade.com>
+##
+
+from GeomAPI import *
+from SketchAPI import *
+
+from salome.shaper import model
+
+import math
+
+TOLERANCE = 1.e-7
+
+def assertTorus(theTorus, theCenter, theAxis, theMajorRadius, theMinorRadius):
+    assert(theTorus is not None)
+    aCenter = theTorus.center()
+    aDir = theTorus.direction()
+    assert(aCenter.distance(theCenter) < TOLERANCE), "({}, {}, {}) != expected ({}, {}, {})".format(aCenter.x(), aCenter.y(), aCenter.z(), theCenter.x(), theCenter.y(), theCenter.z())
+    assert(aDir.isParallel(theAxis, TOLERANCE)), "dir({}, {}, {}) is not parallel to dir({}, {}, {})".format(aDir.x(), aDir.y(), aDir.z(), theAxis.x(), theAxis.y(), theAxis.z())
+    assert(math.fabs(theTorus.majorRadius() - theMajorRadius) < TOLERANCE), "Major radius {} != {}".format(theTorus.majorRadius(), theMajorRadius)
+    assert(math.fabs(theTorus.minorRadius() - theMinorRadius) < TOLERANCE), "Minor radius {} != {}".format(theTorus.minorRadius(), theMinorRadius)
+
+def checkTorusFace(theDocument, theFaceName, theCenter, theAxis, theMajorRadius, theMinorRadius):
+    aFace = model.addFace(theDocument, [model.selection("FACE", theFaceName)])
+    aShape = aFace.result().resultSubShapePair()[0].shape()
+    assert(aShape.isFace())
+    assertTorus(aShape.face().getTorus(), theCenter, theAxis, theMajorRadius, theMinorRadius)
+    theDocument.removeFeature(aFace.feature())
+
+def checkTorusShell(theDocument, theFaceNames, theCenter, theAxis, theMajorRadius, theMinorRadius):
+    aSelection = []
+    for name in theFaceNames:
+        aSelection.append(model.selection("FACE", name))
+    aShell = model.addShell(theDocument, aSelection)
+    aShape = aShell.result().resultSubShapePair()[0].shape()
+    assert(aShape.isShell())
+    assertTorus(aShape.shell().getTorus(), theCenter, theAxis, theMajorRadius, theMinorRadius)
+    theDocument.removeFeature(aShell.feature())
+
+def checkTorusAll(theDocument, theFeature, theFaceName, theCenter, theAxis, theMajorRadius, theMinorRadius):
+    aShape = theFeature.result().resultSubShapePair()[0].shape()
+    assert(aShape.isSolid())
+    assertTorus(aShape.solid().getTorus(), theCenter, theAxis, theMajorRadius, theMinorRadius)
+
+    checkTorusShell(theDocument, [theFaceName], theCenter, theAxis, theMajorRadius, theMinorRadius)
+    checkTorusFace(theDocument, theFaceName, theCenter, theAxis, theMajorRadius, theMinorRadius)
+
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+ParamRMax = model.addParameter(Part_1_doc, "RMax", "15")
+ParamRMin = model.addParameter(Part_1_doc, "RMin", "5")
+ParamAngle = model.addParameter(Part_1_doc, "Angle", "30")
+Torus_1 = model.addTorus(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), "RMax", "RMin")
+model.do()
+
+# Test 1. Check torus
+aCenter = GeomAPI.GeomAPI_Pnt(0, 0, 0)
+anAxis = GeomAPI.GeomAPI_Dir(0, 0, 1)
+checkTorusAll(Part_1_doc, Torus_1, "Torus_1_1/Face_1", aCenter, anAxis, ParamRMax.value(), ParamRMin.value())
+
+# Test 2. Rotate torus
+Rotation_1 = model.addRotation(Part_1_doc, [model.selection("SOLID", "Torus_1_1")], model.selection("EDGE", "PartSet/OX"), "Angle")
+anAngle = ParamAngle.value() * math.pi / 180.0
+aCosAngle = math.cos(anAngle)
+aSinAngle = math.sin(anAngle)
+aCenter = GeomAPI.GeomAPI_Pnt(0, aCenter.y() * aCosAngle - aCenter.z() * aSinAngle, aCenter.y() * aSinAngle + aCenter.z() * aCosAngle)
+anAxis = GeomAPI.GeomAPI_Dir(0, anAxis.y() * aCosAngle - anAxis.z() * aSinAngle, anAxis.y() * aSinAngle + anAxis.z() * aCosAngle)
+checkTorusAll(Part_1_doc, Rotation_1, "Rotation_1_1/Rotated_Face_1", aCenter, anAxis, ParamRMax.value(), ParamRMin.value())
+
+# Test 3. Split torus and compose a shell
+Partition_1_objects = [model.selection("SOLID", "Rotation_1_1"), model.selection("FACE", "PartSet/YOZ"), model.selection("FACE", "PartSet/XOZ"), model.selection("FACE", "PartSet/XOY")]
+Partition_1 = model.addPartition(Part_1_doc, Partition_1_objects)
+Shell_1_objects = ["Partition_1_1_6/Modified_Face_4_4", "Partition_1_1_7/Modified_Face_4_3", "Partition_1_1_7/Modified_Face_1_divided_2_1", "Partition_1_1_5/Modified_Face_4_4"]
+checkTorusShell(Part_1_doc, Shell_1_objects, aCenter, anAxis, ParamRMax.value(), ParamRMin.value())
+
+model.end()