Skip to content

Commit 610260a

Browse files
committed
Port to nanobind
1 parent d39b180 commit 610260a

4 files changed

Lines changed: 148 additions & 147 deletions

File tree

CMakeLists.txt

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
cmake_minimum_required(VERSION 3.15...3.29)
22
project(mapbox_earcut LANGUAGES CXX)
33

4-
set(PYBIND11_NEWPYTHON ON)
5-
find_package(pybind11 CONFIG REQUIRED)
4+
find_package(Python COMPONENTS Interpreter Development
5+
REQUIRED)
6+
execute_process(
7+
COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir
8+
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE nanobind_ROOT)
9+
find_package(nanobind CONFIG REQUIRED)
610

7-
pybind11_add_module(mapbox_earcut src/main.cpp)
11+
nanobind_add_module(
12+
mapbox_earcut
13+
src/main.cpp
14+
)
815
target_include_directories(mapbox_earcut PRIVATE include)
9-
install(TARGETS mapbox_earcut DESTINATION .)
16+
install(TARGETS mapbox_earcut DESTINATION .)

pyproject.toml

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,19 @@ readme = "README.md"
66
authors = [{ name = "Samuel Kogler" }]
77
requires-python = ">=3.9"
88
classifiers = [
9-
"Development Status :: 5 - Production/Stable",
10-
"Intended Audience :: Developers",
11-
"License :: OSI Approved :: ISC License (ISCL)",
12-
"Programming Language :: C++",
13-
"Programming Language :: Python :: 3",
14-
"Programming Language :: Python :: 3.9",
15-
"Programming Language :: Python :: 3.10",
16-
"Programming Language :: Python :: 3.11",
17-
"Programming Language :: Python :: 3.12",
18-
"Programming Language :: Python :: 3.13",
19-
]
20-
dependencies = [
21-
"numpy",
9+
"Development Status :: 5 - Production/Stable",
10+
"Intended Audience :: Developers",
11+
"License :: OSI Approved :: ISC License (ISCL)",
12+
"Programming Language :: C++",
13+
"Programming Language :: Python :: 3",
14+
"Programming Language :: Python :: 3.9",
15+
"Programming Language :: Python :: 3.10",
16+
"Programming Language :: Python :: 3.11",
17+
"Programming Language :: Python :: 3.12",
18+
"Programming Language :: Python :: 3.13",
19+
"Programming Language :: Python :: 3.14",
2220
]
21+
dependencies = ["numpy"]
2322

2423
[project.urls]
2524
Source = "https://github.com/skogler/mapbox_earcut_python"
@@ -35,7 +34,7 @@ regex = '''(?sx)
3534
result = "{major}.{minor}.{patch}"
3635

3736
[build-system]
38-
requires = ["pybind11>=2.12", "scikit-build-core"]
37+
requires = ["nanobind>=2.9.2", "scikit-build-core"]
3938
build-backend = "scikit_build_core.build"
4039

4140
[tool.cibuildwheel]

src/main.cpp

Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,33 @@
11
#include "version.hpp"
22

3-
#include <pybind11/pybind11.h>
4-
#include <pybind11/numpy.h>
3+
#include <nanobind/nanobind.h>
4+
#include <nanobind/ndarray.h>
55
#include <mapbox/earcut.hpp>
66

7+
#include <array>
78
#include <iostream>
89
#include <vector>
910

1011
#define IDENT_TO_STR(x) #x
1112
#define MACRO_TO_STR(x) IDENT_TO_STR(x)
1213

13-
namespace py = pybind11;
14+
namespace nb = nanobind ;
15+
16+
template<typename CoordT>
17+
using VertexArray = nb::ndarray<CoordT, nb::shape<-1, 2>, nb::device::cpu>;
18+
19+
template<typename IndexT>
20+
using IndexArray = nb::ndarray<IndexT, nb::shape<-1>, nb::device::cpu>;
21+
1422

1523
//! vertices: (nverts, 2) numpy array
1624
//! ring_end_indices: the end indices for each ring. The last value must be equal to the number of input vertices.
1725
template<typename CoordT, typename IndexT>
18-
py::array_t<IndexT> triangulate(py::array_t<CoordT> vertices, py::array_t<IndexT> ring_end_indices)
26+
auto triangulate(const VertexArray<CoordT>& vertices, const IndexArray<IndexT>& ring_end_indices)
1927
{
20-
if (vertices.ndim() != 2)
21-
{
22-
throw std::domain_error("The shape of vertices is not (nverts, 2)!");
23-
}
24-
if (ring_end_indices.ndim() != 1)
25-
{
26-
throw std::domain_error("ring_end_indices must be one-dimensional!");
27-
}
28-
auto v = vertices.unchecked();
29-
if (v.shape(1) != 2)
30-
{
31-
throw std::domain_error("The second dimension of vertices is not 2!");
32-
}
33-
auto r = ring_end_indices.template unchecked<1>();
28+
29+
auto v = vertices;
30+
auto r = ring_end_indices;
3431
const auto num_rings = r.shape(0);
3532
const auto num_verts = v.shape(0);
3633
if (num_rings > 0)
@@ -46,6 +43,7 @@ py::array_t<IndexT> triangulate(py::array_t<CoordT> vertices, py::array_t<IndexT
4643
}
4744
using Point = std::array<CoordT, 2>;
4845
std::vector<std::vector<Point>> polygon;
46+
polygon.reserve(num_verts);
4947
for (int ring = 0; ring < r.shape(0); ++ring)
5048
{
5149
const int start = ring == 0 ? 0 : r(ring - 1);
@@ -61,23 +59,20 @@ py::array_t<IndexT> triangulate(py::array_t<CoordT> vertices, py::array_t<IndexT
6159
std::vector<Point> ring_verts;
6260
for (int idx = start; idx < end; ++idx)
6361
{
64-
ring_verts.push_back(Point{
65-
v(idx, 0),
66-
v(idx, 1)
67-
});
62+
ring_verts.push_back({v(idx, 0), v(idx, 1)});
6863
}
6964
polygon.push_back(ring_verts);
7065
}
7166

7267
std::vector<IndexT> indices = mapbox::earcut<IndexT>(polygon);
7368

74-
return py::array(
75-
indices.size(),
76-
indices.data()
77-
);
69+
return nb::ndarray<IndexT, nb::numpy, nb::shape<-1>>(
70+
indices.data(),
71+
{indices.size()}
72+
).cast();
7873
}
7974

80-
PYBIND11_MODULE(mapbox_earcut, m)
75+
NB_MODULE(mapbox_earcut, m)
8176
{
8277
m.attr("__version__") = MACRO_TO_STR(VERSION_MAJOR) "." MACRO_TO_STR(VERSION_MINOR) "." MACRO_TO_STR(VERSION_PATCH);
8378
m.doc() = R"pbdoc(

0 commit comments

Comments
 (0)