Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port OpenVDB Python bindings from pybind11 to nanobind #1753

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
23 changes: 7 additions & 16 deletions openvdb/openvdb/python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,13 @@ if(NOT DEFINED PYOPENVDB_INSTALL_DIRECTORY)
list(APPEND OPENVDB_PYTHON_REQUIRED_COMPONENTS Interpreter)
endif()

if(USE_NUMPY)
list(APPEND OPENVDB_PYTHON_REQUIRED_COMPONENTS NumPy)
endif()

# Make sure find_package(Python) is only ever invoked once with all required components
find_package(Python COMPONENTS ${OPENVDB_PYTHON_REQUIRED_COMPONENTS})
find_package(pybind11 ${MINIMUM_PYBIND_VERSION} CONFIG REQUIRED)
find_package(Python 3.8 REQUIRED COMPONENTS ${OPENVDB_PYTHON_REQUIRED_COMPONENTS})
find_package(nanobind)
if(NOT nanobind_FOUND)
message(FATAL_ERROR "Could NOT find nanobind. Please install nanobind "
"(via pip install nanobind) and set nanobind_DIR at build time.")
endif()

openvdb_check_python_version(Python::Module
"${Python_VERSION}"
Expand All @@ -116,15 +116,6 @@ openvdb_check_python_version(Python::Module
"${FUTURE_MINIMUM_PYTHON_VERSION}")
list(APPEND OPENVDB_PYTHON_DEPS Python::Module)

if(USE_NUMPY)
openvdb_check_python_version(Python::NumPy
"${Python_NumPy_VERSION}"
"${Python_NumPy_INCLUDE_DIRS}"
"${MINIMUM_NUMPY_VERSION}"
"${FUTURE_MINIMUM_NUMPY_VERSION}")
list(APPEND OPENVDB_PYTHON_DEPS Python::NumPy)
endif()

##########################################################################

set(OPENVDB_PYTHON_MODULE_SOURCE_FILES
Expand All @@ -146,7 +137,7 @@ if(NOT DEFINED PYOPENVDB_INSTALL_DIRECTORY)
)
endif()

pybind11_add_module(pyopenvdb ${OPENVDB_PYTHON_MODULE_SOURCE_FILES})
nanobind_add_module(pyopenvdb ${OPENVDB_PYTHON_MODULE_SOURCE_FILES})

target_link_libraries(pyopenvdb PUBLIC
${OPENVDB_LIB}
Expand Down
59 changes: 22 additions & 37 deletions openvdb/openvdb/python/pyAccessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
#ifndef OPENVDB_PYACCESSOR_HAS_BEEN_INCLUDED
#define OPENVDB_PYACCESSOR_HAS_BEEN_INCLUDED

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <nanobind/nanobind.h>
#include <nanobind/stl/string.h>
#include <openvdb/openvdb.h>
#include "pyutil.h"

namespace pyAccessor {

namespace py = pybind11;
namespace nb = nanobind;
using namespace openvdb::OPENVDB_VERSION_NAME;


Expand Down Expand Up @@ -69,7 +69,7 @@ struct AccessorTraits<const _GridT>

static void notWritable()
{
throw py::type_error("accessor is read-only");
throw nb::type_error("accessor is read-only");
}
};
//@}
Expand Down Expand Up @@ -173,86 +173,71 @@ class AccessorWrap
}

/// @brief Define a Python wrapper class for this C++ class.
static void wrap(py::module_ m)
static void wrap(nb::module_ m)
{
const std::string
pyGridTypeName = pyutil::GridTraits<GridType>::name(),
pyValueTypeName = openvdb::typeNameAsString<typename GridType::ValueType>(),
pyAccessorTypeName = Traits::typeName();

py::class_<AccessorWrap>(m,
(pyGridTypeName + pyAccessorTypeName).c_str(), //pybind11 requires a unique class name for each template instantiation
nb::class_<AccessorWrap>(m,
(pyGridTypeName + pyAccessorTypeName).c_str(), //nanobind requires a unique class name for each template instantiation
(std::string(Traits::IsConst ? "Read-only" : "Read/write")
+ " access by (i, j, k) index coordinates to the voxels\nof a "
+ pyGridTypeName).c_str())
.def("copy", &AccessorWrap::copy,
("copy() -> " + pyAccessorTypeName + "\n\n"
"Return a copy of this accessor.").c_str())
"Return a copy of this accessor.")

.def("clear", &AccessorWrap::clear,
"clear()\n\n"
"Clear this accessor of all cached data.")

.def_property_readonly("parent", &AccessorWrap::parent,
.def_prop_ro("parent", &AccessorWrap::parent,
("this accessor's parent " + pyGridTypeName).c_str())

//
// Voxel access
//
.def("getValue", &AccessorWrap::getValue,
py::arg("ijk"),
("getValue(ijk) -> " + pyValueTypeName + "\n\n"
"Return the value of the voxel at coordinates (i, j, k).").c_str())
nb::arg("ijk"),
"Return the value of the voxel at coordinates (i, j, k).")

.def("getValueDepth", &AccessorWrap::getValueDepth,
py::arg("ijk"),
"getValueDepth(ijk) -> int\n\n"
nb::arg("ijk"),
"Return the tree depth (0 = root) at which the value of voxel\n"
"(i, j, k) resides. If (i, j, k) isn't explicitly represented in\n"
"the tree (i.e., it is implicitly a background voxel), return -1.")

.def("isVoxel", &AccessorWrap::isVoxel,
py::arg("ijk"),
"isVoxel(ijk) -> bool\n\n"
nb::arg("ijk"),
"Return True if voxel (i, j, k) resides at the leaf level of the tree.")

.def("probeValue", &AccessorWrap::probeValue,
py::arg("ijk"),
"probeValue(ijk) -> value, bool\n\n"
nb::arg("ijk"),
"Return the value of the voxel at coordinates (i, j, k)\n"
"together with the voxel's active state.")

.def("isValueOn", &AccessorWrap::isValueOn,
py::arg("ijk"),
"isValueOn(ijk) -> bool\n\n"
nb::arg("ijk"),
"Return the active state of the voxel at coordinates (i, j, k).")
.def("setActiveState", &AccessorWrap::setActiveState,
py::arg("ijk"), py::arg("on"),
"setActiveState(ijk, on)\n\n"
nb::arg("ijk"), nb::arg("on"),
"Mark voxel (i, j, k) as either active or inactive (True or False),\n"
"but don't change its value.")

.def("setValueOnly", &AccessorWrap::setValueOnly,
py::arg("ijk"), py::arg("value"),
"setValueOnly(ijk, value)\n\n"
nb::arg("ijk"), nb::arg("value"),
"Set the value of voxel (i, j, k), but don't change its active state.")

.def("setValueOn", &AccessorWrap::setValueOn,
py::arg("ijk"), py::arg("value") = py::none(),
"setValueOn(ijk, value)\n\n"
"Mark voxel (i, j, k) as active and set the voxel's value if specified.\n")
nb::arg("ijk"), nb::arg("value") = nb::none(),
"Mark voxel (i, j, k) as active and set the voxel's value if specified.")

.def("setValueOff", &AccessorWrap::setValueOff,
py::arg("ijk"), py::arg("value") = py::none(),
"setValueOff(ijk, value)\n\n"
nb::arg("ijk"), nb::arg("value") = nb::none(),
"Mark voxel (i, j, k) as inactive and set the voxel's value if specified.")

.def("isCached", &AccessorWrap::isCached,
py::arg("ijk"),
"isCached(ijk) -> bool\n\n"
"Return True if this accessor has cached the path to voxel (i, j, k).")

; // py::class_<ValueAccessor>
nb::arg("ijk"),
"Return True if this accessor has cached the path to voxel (i, j, k).");
}

private:
Expand Down
11 changes: 5 additions & 6 deletions openvdb/openvdb/python/pyFloatGrid.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,17 @@

/// Create a Python wrapper for each supported Grid type.
void
exportFloatGrid(py::module_ m)
exportFloatGrid(nb::module_ m)
{
pyGrid::exportGrid<FloatGrid>(m);
pyGrid::exportScalarGrid<FloatGrid>(m);
#ifdef PY_OPENVDB_WRAP_ALL_GRID_TYPES
pyGrid::exportGrid<DoubleGrid>(m);
pyGrid::exportScalarGrid<DoubleGrid>(m);
#endif

m.def("createLevelSetSphere",
&pyGrid::createLevelSetSphere<FloatGrid>,
py::arg("radius"), py::arg("center")=openvdb::Coord(), py::arg("voxelSize")=1.0,
py::arg("halfWidth")=openvdb::LEVEL_SET_HALF_WIDTH,
"createLevelSetSphere(radius, center, voxelSize, halfWidth) -> FloatGrid\n\n"
nb::arg("radius"), nb::arg("center")=openvdb::Coord(), nb::arg("voxelSize")=1.0,
nb::arg("halfWidth")=openvdb::LEVEL_SET_HALF_WIDTH,
"Return a grid containing a narrow-band level set representation\n"
"of a sphere.");
}
Loading
Loading