From 6159d89386e55cd2d30fac911a7e20e49a798aef Mon Sep 17 00:00:00 2001 From: Monty Montgomery Date: Sun, 15 May 2022 00:47:44 -0400 Subject: [PATCH] Numerous changes to python bindings for clean stub generation I'm uncertain about earlier/alternate versions, but the current version of mainline pybind11_stubgen requires a number of changes in the python binding cpp files to succeed: __repr__ implementation added for some basic types Added default descriptions via py::arg_v variant syntax for those cases where a __repr__ implementation made no sense (eg, for a default sonstructor of a complex type) Moved class declarations to precede first use in arguments/returns Added trailing default arguments, so that mandatory args did not follow optional args (without resorting to kwargs trickery). --- libsrc/csg/python_csg.cpp | 2 +- libsrc/geom2d/python_geom2d.cpp | 4 +- libsrc/meshing/python_mesh.cpp | 153 +++++++++++++++---------------- libsrc/occ/python_occ.cpp | 4 +- libsrc/occ/python_occ_basic.cpp | 25 ++--- libsrc/occ/python_occ_shapes.cpp | 16 +++- 6 files changed, 105 insertions(+), 99 deletions(-) diff --git a/libsrc/csg/python_csg.cpp b/libsrc/csg/python_csg.cpp index 676e249a..92592f00 100644 --- a/libsrc/csg/python_csg.cpp +++ b/libsrc/csg/python_csg.cpp @@ -616,7 +616,7 @@ However, when r = 0, the top part becomes a point(tip) and meshing fails! trafo)); }), py::arg("solid1"), py::arg("solid2"), - py::arg("trafo")=Transformation<3>(Vec<3>(0,0,0)) + py::arg_v("trafo", Transformation<3>(Vec<3>(0,0,0)), "Transformation<3>(Vec<3>(0,0,0))") ) .def("NameEdge", [] (CSGeometry & self, shared_ptr s1, shared_ptr s2, string name) { diff --git a/libsrc/geom2d/python_geom2d.cpp b/libsrc/geom2d/python_geom2d.cpp index ab232f13..16ece7b1 100644 --- a/libsrc/geom2d/python_geom2d.cpp +++ b/libsrc/geom2d/python_geom2d.cpp @@ -202,7 +202,7 @@ NGCORE_API_EXPORT void ExportGeom2d(py::module &m) self.AppendSegment (spex); }, py::arg("func"), py::arg("leftdomain") = 1, py::arg("rightdomain") = py::int_(0), - py::arg("bc")=NGDummyArgument(), py::arg("maxh")=1e99, + py::arg_v("bc", NGDummyArgument(), "NGDummyArgument()"), py::arg("maxh")=1e99, "Curve is given as parametrization on the interval [0,1]") .def("SetMaterial", &SplineGeometry2d::SetMaterial) @@ -439,7 +439,7 @@ NGCORE_API_EXPORT void ExportGeom2d(py::module &m) .def("Move", &Solid2d::Move) .def("Scale", static_cast(&Solid2d::Scale)) .def("Scale", static_cast)>(&Solid2d::Scale)) - .def("Rotate", &Solid2d::RotateDeg, py::arg("angle"), py::arg("center")=Point<2>{0,0}) + .def("Rotate", &Solid2d::RotateDeg, py::arg("angle"), py::arg_v("center", Point<2>{0,0}, "Point<2>{0,0}")) ; diff --git a/libsrc/meshing/python_mesh.cpp b/libsrc/meshing/python_mesh.cpp index 5f390832..30e5da7d 100644 --- a/libsrc/meshing/python_mesh.cpp +++ b/libsrc/meshing/python_mesh.cpp @@ -167,7 +167,47 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) py::implicitly_convertible(); #endif // NG_MPI4PY - + py::class_> (m, "Vec3d") + .def(py::init()) + .def(py::init([](py::tuple v) + { + return Vec<3> { v[0].cast(), v[1].cast(), + v[2].cast() }; + })) + .def ("__str__", &ToString>) + .def(py::self==py::self) + .def(py::self+py::self) + .def(py::self-py::self) + .def(-py::self) + .def(double()*py::self) + .def(py::self*double()) + .def("Norm", &Vec<3>::Length) + .def("__getitem__", [](Vec<3>& vec, int index) { return vec[index]; }) + .def("__len__", [](Vec<3>& /*unused*/) { return 3; }) + ; + + py::implicitly_convertible>(); + + py::class_> (m, "Vec2d") + .def(py::init()) + .def(py::init( [] (std::pair xy) + { + return Vec<2>{xy.first, xy.second}; + })) + .def ("__str__", &ToString>) + .def(py::self==py::self) + .def(py::self+py::self) + .def(py::self-py::self) + .def(-py::self) + .def(double()*py::self) + .def(py::self*double()) + .def("Norm", &Vec<2>::Length) + .def("__getitem__", [](Vec<2>& vec, int index) { return vec[index]; }) + .def("__len__", [](Vec<2>& /*unused*/) { return 2; }) + ; + + py::implicitly_convertible>(); + py::class_(m, "NGDummyArgument") .def("__bool__", []( NGDummyArgument &self ) { return false; } ) ; @@ -219,47 +259,6 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) np_array.at(2)))); }); - py::class_> (m, "Vec2d") - .def(py::init()) - .def(py::init( [] (std::pair xy) - { - return Vec<2>{xy.first, xy.second}; - })) - .def ("__str__", &ToString>) - .def(py::self==py::self) - .def(py::self+py::self) - .def(py::self-py::self) - .def(-py::self) - .def(double()*py::self) - .def(py::self*double()) - .def("Norm", &Vec<2>::Length) - .def("__getitem__", [](Vec<2>& vec, int index) { return vec[index]; }) - .def("__len__", [](Vec<2>& /*unused*/) { return 2; }) - ; - - py::implicitly_convertible>(); - - py::class_> (m, "Vec3d") - .def(py::init()) - .def(py::init([](py::tuple v) - { - return Vec<3> { v[0].cast(), v[1].cast(), - v[2].cast() }; - })) - .def ("__str__", &ToString>) - .def(py::self==py::self) - .def(py::self+py::self) - .def(py::self-py::self) - .def(-py::self) - .def(double()*py::self) - .def(py::self*double()) - .def("Norm", &Vec<3>::Length) - .def("__getitem__", [](Vec<3>& vec, int index) { return vec[index]; }) - .def("__len__", [](Vec<3>& /*unused*/) { return 3; }) - ; - - py::implicitly_convertible>(); - m.def ("Vec", FunctionPointer ([] (double x, double y, double z) { return global_trafo(Vec<3>(x,y,z)); })); m.def("Vec", [](py::array_t np_array) @@ -410,7 +409,7 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) newel->SetIndex(index); return newel; }), - py::arg("index")=1,py::arg("vertices"), + py::arg("index")=1,py::arg_v("vertices", std::vector(), "[]"), "create volume element" ) .def("__repr__", &ToString) @@ -470,7 +469,7 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) throw NgException("Inconsistent number of vertices in Element2D"); return newel; }), - py::arg("index")=1,py::arg("vertices"), + py::arg("index")=1,py::arg_v("vertices", std::vector(), "[]"), "create surface element" ) .def_property("index", &Element2d::GetIndex, &Element2d::SetIndex) @@ -651,6 +650,38 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) .def("RestrictH", &NetgenGeometry::RestrictH) ; + typedef MeshingParameters MP; + auto mp = py::class_ (m, "MeshingParameters") + .def(py::init<>()) + .def(py::init([](MeshingParameters* other, py::kwargs kwargs) + { + MeshingParameters mp; + if(other) mp = *other; + CreateMPfromKwargs(mp, kwargs, false); + return mp; + }), py::arg("mp")=nullptr, meshingparameter_description.c_str()) + .def("__str__", &ToString) + .def("RestrictH", [](MP & mp, double x, double y, double z, double h) + { + mp.meshsize_points.Append ( MeshingParameters::MeshSizePoint(Point<3> (x,y,z), h)); + }, py::arg("x"), py::arg("y"), py::arg("z"), py::arg("h") + ) + .def("RestrictH", [](MP & mp, const Point<3>& p, double h) + { + mp.meshsize_points.Append ({p, h}); + }, py::arg("p"), py::arg("h")) + .def("RestrictHLine", [](MP& mp, const Point<3>& p1, const Point<3>& p2, + double maxh) + { + int steps = int(Dist(p1, p2) / maxh) + 2; + auto v = p2 - p1; + for (int i = 0; i <= steps; i++) + { + mp.meshsize_points.Append({p1 + double(i)/steps * v, maxh}); + } + }, py::arg("p1"), py::arg("p2"), py::arg("maxh")) + ; + py::class_>(m, "Mesh") // .def(py::init<>("create empty mesh")) @@ -663,7 +694,7 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) mesh -> SetGeometry (nullptr); return mesh; } ), - py::arg("dim")=3, py::arg("comm")=NgMPI_Comm{} + py::arg("dim")=3, py::arg_v("comm", NgMPI_Comm(), "NgMPI_Comm()") ) .def(NGSPickle()) .def_property_readonly("comm", [](const Mesh & amesh) -> NgMPI_Comm @@ -1449,38 +1480,6 @@ project_boundaries : Optional[str] = None .value("MESHVOLUME", MESHCONST_OPTVOLUME) ; - typedef MeshingParameters MP; - auto mp = py::class_ (m, "MeshingParameters") - .def(py::init<>()) - .def(py::init([](MeshingParameters* other, py::kwargs kwargs) - { - MeshingParameters mp; - if(other) mp = *other; - CreateMPfromKwargs(mp, kwargs, false); - return mp; - }), py::arg("mp")=nullptr, meshingparameter_description.c_str()) - .def("__str__", &ToString) - .def("RestrictH", [](MP & mp, double x, double y, double z, double h) - { - mp.meshsize_points.Append ( MeshingParameters::MeshSizePoint(Point<3> (x,y,z), h)); - }, py::arg("x"), py::arg("y"), py::arg("z"), py::arg("h") - ) - .def("RestrictH", [](MP & mp, const Point<3>& p, double h) - { - mp.meshsize_points.Append ({p, h}); - }, py::arg("p"), py::arg("h")) - .def("RestrictHLine", [](MP& mp, const Point<3>& p1, const Point<3>& p2, - double maxh) - { - int steps = int(Dist(p1, p2) / maxh) + 2; - auto v = p2 - p1; - for (int i = 0; i <= steps; i++) - { - mp.meshsize_points.Append({p1 + double(i)/steps * v, maxh}); - } - }, py::arg("p1"), py::arg("p2"), py::arg("maxh")) - ; - m.def("SetTestoutFile", FunctionPointer ([] (const string & filename) { delete testout; diff --git a/libsrc/occ/python_occ.cpp b/libsrc/occ/python_occ.cpp index e0b67031..0cc39c2e 100644 --- a/libsrc/occ/python_occ.cpp +++ b/libsrc/occ/python_occ.cpp @@ -282,7 +282,7 @@ DLL_HEADER void ExportNgOCC(py::module &m) mesh->SendRecvMesh(); } return mesh; - }, py::arg("mp") = nullptr, py::arg("comm")=NgMPI_Comm{}, + }, py::arg("mp") = static_cast(nullptr), py::arg_v("comm", NgMPI_Comm(), "NgMPI_Comm()"), py::call_guard(), (meshingparameter_description + occparameter_description).c_str()) .def_property_readonly("shape", [](const OCCGeometry & self) { return self.GetShape(); }) @@ -356,7 +356,7 @@ DLL_HEADER void ExportNgOCC(py::module &m) cout << "IsMaterial = " << material_tool->IsMaterial(label) << endl; // cout << "IsVisMaterial = " << vismaterial_tool->IsMaterial(label) << endl; } - }, py::arg("shape")=TopoDS_Shape()); + }, py::arg_v("shape", TopoDS_Shape(), "TopoDS_Shape()")); } diff --git a/libsrc/occ/python_occ_basic.cpp b/libsrc/occ/python_occ_basic.cpp index 05fdb1ee..87e63af5 100644 --- a/libsrc/occ/python_occ_basic.cpp +++ b/libsrc/occ/python_occ_basic.cpp @@ -21,6 +21,12 @@ using namespace netgen; DLL_HEADER void ExportNgOCCBasic(py::module &m) { + auto pyAx1 = py::class_(m, "Axis", "an OCC axis in 3d"); + auto pyAx2 = py::class_(m, "gp_Ax2"); + auto pyAx2d = py::class_(m, "gp_Ax2d", "2d OCC coordinate system"); + auto pyAx3 = py::class_(m, "Axes", "an OCC coordinate system in 3d"); + auto pyDirectionalInterval = py::class_ (m, "DirectionalInterval"); + py::class_(m, "gp_Pnt", "3d OCC point") .def(py::init([] (py::tuple pnt) { @@ -130,14 +136,12 @@ DLL_HEADER void ExportNgOCCBasic(py::module &m) return str.str(); }) ; - - py::class_(m, "Axis", "an OCC axis in 3d") - .def(py::init([](gp_Pnt p, gp_Dir d) { + + pyAx1.def(py::init([](gp_Pnt p, gp_Dir d) { return gp_Ax1(p,d); }), py::arg("p"), py::arg("d")) ; - py::class_(m, "gp_Ax2") - .def(py::init([](gp_Pnt p, gp_Dir d) { + pyAx2.def(py::init([](gp_Pnt p, gp_Dir d) { return gp_Ax2(p,d); })) .def(py::init([](const gp_Ax3 & ax3) { @@ -145,8 +149,7 @@ DLL_HEADER void ExportNgOCCBasic(py::module &m) })) ; - py::class_(m, "Axes", "an OCC coordinate system in 3d") - .def(py::init([](gp_Pnt p, gp_Dir N, gp_Dir Vx) { + pyAx3.def(py::init([](gp_Pnt p, gp_Dir N, gp_Dir Vx) { return gp_Ax3(p,N, Vx); }), py::arg("p")=gp_Pnt(0,0,0), py::arg("n")=gp_Vec(0,0,1), py::arg("h")=gp_Vec(1,0,0)) .def(py::init()) @@ -268,10 +271,9 @@ DLL_HEADER void ExportNgOCCBasic(py::module &m) - py::class_(m, "gp_Ax2d", "2d OCC coordinate system") - .def(py::init([](gp_Pnt2d p, gp_Dir2d d) { + pyAx2d.def(py::init([](gp_Pnt2d p, gp_Dir2d d) { return gp_Ax2d(p,d); - }), py::arg("p")=gp_Pnt2d(0,0), py::arg("d")=gp_Dir2d(1,0)) + }), py::arg("p")=gp_Pnt2d(0,0), py::arg_v("d", gp_Dir2d(1,0), "gp_Dir2d(1,0)"), "Create an axis in a plane") ; py::class_(m, "gp_GTrsf") @@ -330,8 +332,7 @@ DLL_HEADER void ExportNgOCCBasic(py::module &m) ; - py::class_ (m, "DirectionalInterval") - .def("__str__", [](DirectionalInterval self) + pyDirectionalInterval.def("__str__", [](DirectionalInterval self) { stringstream str; str << "(" << self.minval << ", " << self.maxval << ")"; diff --git a/libsrc/occ/python_occ_shapes.cpp b/libsrc/occ/python_occ_shapes.cpp index f29d4c61..bd67b0a2 100644 --- a/libsrc/occ/python_occ_shapes.cpp +++ b/libsrc/occ/python_occ_shapes.cpp @@ -69,6 +69,8 @@ #include #include #include +#include +#include #include #include @@ -668,7 +670,10 @@ DLL_HEADER void ExportNgOCCShapes(py::module &m) .export_values() ; - + auto pyListOfShapes_Forward = py::class_ (m, "ListOfShapes"); + auto pyGeom2d_Curve_Forward = py::class_ (m, "Geom2d_Curve"); + py::class_,3>,size_t>>(m, "ArrayOfTriangles"); + py::class_ (m, "TopoDS_Shape") .def("__str__", [] (const TopoDS_Shape & shape) { @@ -1543,7 +1548,7 @@ DLL_HEADER void ExportNgOCCShapes(py::module &m) bool operator==(ListOfShapesIterator it2) const { return ptr == it2.ptr; } }; - py::class_ (m, "ListOfShapes") + pyListOfShapes_Forward .def("__iter__", [](ListOfShapes &s) { return py::make_iterator(ListOfShapesIterator(&*s.begin()), ListOfShapesIterator(&*s.end())); @@ -1715,7 +1720,8 @@ DLL_HEADER void ExportNgOCCShapes(py::module &m) { Identify(me, other, name, type, occ2ng(trafo)); }, py::arg("other"), py::arg("name"), - py::arg("type")=Identifications::PERIODIC, py::arg("trafo"), + py::arg("type")=Identifications::PERIODIC, + py::arg_v("trafo", gp_Trsf(), "gp_Trsf()"), "Identify shapes for periodic meshing") ; @@ -1731,7 +1737,7 @@ DLL_HEADER void ExportNgOCCShapes(py::module &m) - py::class_ (m, "Geom2d_Curve") + pyGeom2d_Curve_Forward .def("Trim", [](Handle(Geom2d_Curve) curve, double u1, double u2) -> Handle(Geom2d_Curve) { return new Geom2d_TrimmedCurve (curve, u1, u2); @@ -2434,7 +2440,7 @@ degen_tol : double }, py::arg("edges"), py::arg("tol")=1e-8, py::arg("shared")=true); py::class_> (m, "WorkPlane") - .def(py::init(), py::arg("axes")=gp_Ax3(), py::arg("pos")=gp_Ax2d()) + .def(py::init(), py::arg_v("axes", gp_Ax3(), "gp_Ax3()"), py::arg_v("pos", gp_Ax2d(), "gp_Ax2d()")) .def_property_readonly("cur_loc", &WorkPlane::CurrentLocation) .def_property_readonly("cur_dir", &WorkPlane::CurrentDirection) .def_property_readonly("start_pnt", &WorkPlane::StartPnt)