diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 62078d8db..f9cc0917d 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -52,6 +52,7 @@ pybind11_add_module(${BINDINGS_MODULE_NAME} MODULE src/sdf/pyCamera.cc src/sdf/pyCapsule.cc src/sdf/pyCollision.cc + src/sdf/pyConvexDecomposition.cc src/sdf/pyCylinder.cc src/sdf/pyElement.cc src/sdf/pyEllipsoid.cc diff --git a/python/src/sdf/_gz_sdformat_pybind11.cc b/python/src/sdf/_gz_sdformat_pybind11.cc index d982e13f4..298cd1362 100644 --- a/python/src/sdf/_gz_sdformat_pybind11.cc +++ b/python/src/sdf/_gz_sdformat_pybind11.cc @@ -26,6 +26,7 @@ #include "pyCamera.hh" #include "pyCapsule.hh" #include "pyCollision.hh" +#include "pyConvexDecomposition.hh" #include "pyCylinder.hh" #include "pyElement.hh" #include "pyEllipsoid.hh" @@ -85,6 +86,7 @@ PYBIND11_MODULE(BINDINGS_MODULE_NAME, m) { sdf::python::defineCamera(m); sdf::python::defineCapsule(m); sdf::python::defineCollision(m); + sdf::python::defineConvexDecomposition(m); sdf::python::defineContact(m); sdf::python::defineCylinder(m); // PrintConfig has to be defined before Param and Element because it's used as diff --git a/python/src/sdf/pyConvexDecomposition.cc b/python/src/sdf/pyConvexDecomposition.cc new file mode 100644 index 000000000..aacd7ee42 --- /dev/null +++ b/python/src/sdf/pyConvexDecomposition.cc @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2024 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "pyMesh.hh" + +#include + +#include "sdf/ParserConfig.hh" +#include "sdf/Mesh.hh" + +using namespace pybind11::literals; + +namespace sdf +{ +// Inline bracket to help doxygen filtering. +inline namespace SDF_VERSION_NAMESPACE { +namespace python +{ +///////////////////////////////////////////////// +void defineConvexDecomposition(pybind11::object module) +{ + pybind11::class_(module, "ConvexDecomposition") + .def(pybind11::init<>()) + .def("max_convex_hulls", &sdf::ConvexDecomposition::MaxConvexHulls, + "Get the maximum number of convex hulls that can be generated.") + .def("set_max_convex_hulls", &sdf::ConvexDecomposition::SetMaxConvexHulls, + "Set the maximum number of convex hulls that can be generated.") + .def("__copy__", [](const sdf::ConvexDecomposition &self) { + return sdf::ConvexDecomposition(self); + }) + .def("__deepcopy__", [](const sdf::ConvexDecomposition &self, pybind11::dict) { + return sdf::ConvexDecomposition(self); + }, "memo"_a); +} +} // namespace python +} // namespace SDF_VERSION_NAMESPACE +} // namespace sdf diff --git a/python/src/sdf/pyConvexDecomposition.hh b/python/src/sdf/pyConvexDecomposition.hh new file mode 100644 index 000000000..78a58840f --- /dev/null +++ b/python/src/sdf/pyConvexDecomposition.hh @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SDFORMAT_PYTHON_CONVEX_DECOMPOSITION_HH_ +#define SDFORMAT_PYTHON_CONVEX_DECOMPOSITION_HH_ + +#include + +#include "sdf/Mesh.hh" + +#include "sdf/config.hh" + +namespace sdf +{ +// Inline bracket to help doxygen filtering. +inline namespace SDF_VERSION_NAMESPACE { +namespace python +{ +/// Define a pybind11 wrapper for an sdf::ConvexDecomposition +/** + * \param[in] module a pybind11 module to add the definition to + */ +void defineConvexDecomposition(pybind11::object module); +} // namespace python +} // namespace SDF_VERSION_NAMESPACE +} // namespace sdf + +#endif // SDFORMAT_PYTHON_CONVEX_DECOMPOSITION_HH_ diff --git a/python/src/sdf/pyMesh.cc b/python/src/sdf/pyMesh.cc index 85705f874..b9fdafd17 100644 --- a/python/src/sdf/pyMesh.cc +++ b/python/src/sdf/pyMesh.cc @@ -35,6 +35,23 @@ void defineMesh(pybind11::object module) pybind11::class_(module, "Mesh") .def(pybind11::init<>()) .def(pybind11::init()) + .def("optimization", &sdf::Mesh::Optimization, + "Get the mesh's optimization method.") + .def("optimization_str", &sdf::Mesh::OptimizationStr, + "Get the mesh's optimization method") + .def("set_optimization", + pybind11::overload_cast( + &sdf::Mesh::SetOptimization), + "Set the mesh optimization method.") + .def("set_optimization", + pybind11::overload_cast( + &sdf::Mesh::SetOptimization), + "Set the mesh optimization method.") + .def("convex_decomposition", &sdf::Mesh::ConvexDecomposition, + pybind11::return_value_policy::reference_internal, + "Get the associated ConvexDecomposition object") + .def("set_convex_decomposition", &sdf::Mesh::SetConvexDecomposition, + "Set the associated ConvexDecomposition object.") .def("uri", &sdf::Mesh::Uri, "Get the mesh's URI.") .def("set_uri", &sdf::Mesh::SetUri, @@ -67,6 +84,11 @@ void defineMesh(pybind11::object module) .def("__deepcopy__", [](const sdf::Mesh &self, pybind11::dict) { return sdf::Mesh(self); }, "memo"_a); + + pybind11::enum_(module, "MeshOptimization") + .value("NONE", sdf::MeshOptimization::NONE) + .value("CONVEX_HULL", sdf::MeshOptimization::CONVEX_HULL) + .value("CONVEX_DECOMPOSITION", sdf::MeshOptimization::CONVEX_DECOMPOSITION); } } // namespace python } // namespace SDF_VERSION_NAMESPACE diff --git a/python/test/pyMesh_TEST.py b/python/test/pyMesh_TEST.py index 64d89a57a..ac9840ea6 100644 --- a/python/test/pyMesh_TEST.py +++ b/python/test/pyMesh_TEST.py @@ -13,8 +13,9 @@ # limitations under the License. import copy -from gz_test_deps.sdformat import Mesh +from gz_test_deps.sdformat import Mesh, ConvexDecomposition from gz_test_deps.math import Vector3d +import gz_test_deps.sdformat as sdf import unittest @@ -23,6 +24,9 @@ class MeshTEST(unittest.TestCase): def test_default_construction(self): mesh = Mesh() + self.assertEqual("", mesh.optimization_str()) + self.assertEqual(sdf.MeshOptimization.NONE, mesh.optimization()) + self.assertEqual(None, mesh.convex_decomposition()) self.assertEqual("", mesh.file_path()) self.assertEqual("", mesh.uri()) self.assertEqual("", mesh.submesh()) @@ -32,19 +36,29 @@ def test_default_construction(self): def test_assigment(self): mesh = Mesh() + self.assertTrue(mesh.set_optimization("convex_decomposition")) mesh.set_uri("banana") mesh.set_submesh("watermelon") mesh.set_center_submesh(True) mesh.set_scale(Vector3d(0.5, 0.6, 0.7)) mesh.set_file_path("/pear") + convexDecomp = ConvexDecomposition() + convexDecomp.set_max_convex_hulls(10) + mesh.set_convex_decomposition(convexDecomp) + mesh2 = mesh + self.assertEqual("convex_decomposition", mesh2.optimization_str()) + self.assertEqual(sdf.MeshOptimization.CONVEX_DECOMPOSITION, mesh2.optimization()) self.assertEqual("banana", mesh2.uri()) self.assertEqual("watermelon", mesh2.submesh()) self.assertEqual(Vector3d(0.5, 0.6, 0.7), mesh2.scale()) self.assertTrue(mesh2.center_submesh()) self.assertEqual("/pear", mesh2.file_path()) + convexDecomp2 = mesh2.convex_decomposition() + self.assertEqual(10, convexDecomp2.max_convex_hulls()) + mesh.set_file_path("/apple") self.assertEqual("/apple", mesh2.file_path()) @@ -63,19 +77,29 @@ def test_assigment(self): def test_deepcopy_construction(self): mesh = Mesh() + self.assertTrue(mesh.set_optimization("convex_decomposition")) mesh.set_uri("banana") mesh.set_submesh("watermelon") mesh.set_center_submesh(True) mesh.set_scale(Vector3d(0.5, 0.6, 0.7)) mesh.set_file_path("/pear") + convexDecomp = ConvexDecomposition() + convexDecomp.set_max_convex_hulls(10) + mesh.set_convex_decomposition(convexDecomp) + mesh2 = copy.deepcopy(mesh) + self.assertEqual("convex_decomposition", mesh2.optimization_str()) + self.assertEqual(sdf.MeshOptimization.CONVEX_DECOMPOSITION, mesh2.optimization()) self.assertEqual("banana", mesh2.uri()) self.assertEqual("watermelon", mesh2.submesh()) self.assertEqual(Vector3d(0.5, 0.6, 0.7), mesh2.scale()) self.assertTrue(mesh2.center_submesh()) self.assertEqual("/pear", mesh2.file_path()) + convexDecomp2 = mesh2.convex_decomposition() + self.assertEqual(10, convexDecomp2.max_convex_hulls()) + mesh.set_file_path("/apple") mesh.set_scale(Vector3d(0.3, 0.2, 0.4)) mesh.set_center_submesh(False) @@ -92,6 +116,24 @@ def test_deepcopy_construction(self): def test_set(self): mesh = Mesh() + self.assertEqual("", mesh.optimization_str()) + self.assertTrue(mesh.set_optimization("convex_hull")) + self.assertEqual("convex_hull", mesh.optimization_str()) + self.assertEqual(sdf.MeshOptimization.CONVEX_HULL, mesh.optimization()) + mesh.set_optimization(sdf.MeshOptimization.CONVEX_DECOMPOSITION) + self.assertEqual("convex_decomposition", mesh.optimization_str()) + self.assertEqual(sdf.MeshOptimization.CONVEX_DECOMPOSITION, mesh.optimization()) + + self.assertFalse(mesh.set_optimization("invalid")) + mesh.set_optimization(sdf.MeshOptimization(99)) + self.assertEqual(sdf.MeshOptimization(99), mesh.optimization()) + self.assertEqual("", mesh.optimization_str()) + + convexDecomp = ConvexDecomposition() + convexDecomp.set_max_convex_hulls(10) + mesh.set_convex_decomposition(convexDecomp) + self.assertEqual(10, mesh.convex_decomposition().max_convex_hulls()) + self.assertEqual("", mesh.uri()) mesh.set_uri("http://myuri.com") self.assertEqual("http://myuri.com", mesh.uri())