5
5
* triangulation, construct an instance of the matplotlib.tri.Triangulation
6
6
* class without specifying a triangles array.
7
7
*/
8
- #define PY_SSIZE_T_CLEAN
9
- #include " Python.h "
10
- # include " numpy_cpp.h "
8
+ #include < pybind11/pybind11.h >
9
+ #include < pybind11/numpy.h >
10
+
11
11
#ifdef _MSC_VER
12
12
/* The Qhull header does not declare this as extern "C", but only MSVC seems to
13
13
* do name mangling on global variables. We thus need to declare this before
@@ -16,18 +16,28 @@ extern "C" {
16
16
extern const char qh_version[];
17
17
}
18
18
#endif
19
+
19
20
#include " libqhull_r/qhull_ra.h"
20
21
#include < cstdio>
21
22
#include < vector>
22
23
23
-
24
24
#ifndef MPL_DEVNULL
25
25
#error "MPL_DEVNULL must be defined as the OS-equivalent of /dev/null"
26
26
#endif
27
27
28
28
#define STRINGIFY (x ) STR(x)
29
29
#define STR (x ) #x
30
30
31
+ namespace py = pybind11;
32
+ using namespace pybind11 ::literals;
33
+
34
+ // Input numpy array class.
35
+ typedef py::array_t <double , py::array::c_style | py::array::forcecast> CoordArray;
36
+
37
+ // Output numpy array class.
38
+ typedef py::array_t <int > IndexArray;
39
+
40
+
31
41
32
42
static const char * qhull_error_msg[6 ] = {
33
43
" " , /* 0 = qh_ERRnone */
@@ -64,17 +74,16 @@ get_facet_neighbours(const facetT* facet, std::vector<int>& tri_indices,
64
74
/* Return true if the specified points arrays contain at least 3 unique points,
65
75
* or false otherwise. */
66
76
static bool
67
- at_least_3_unique_points (npy_intp npoints, const double * x, const double * y)
77
+ at_least_3_unique_points (py:: ssize_t npoints, const double * x, const double * y)
68
78
{
69
- int i;
70
- const int unique1 = 0 ; /* First unique point has index 0. */
71
- int unique2 = 0 ; /* Second unique point index is 0 until set. */
79
+ const py::ssize_t unique1 = 0 ; /* First unique point has index 0. */
80
+ py::ssize_t unique2 = 0 ; /* Second unique point index is 0 until set. */
72
81
73
82
if (npoints < 3 ) {
74
83
return false ;
75
84
}
76
85
77
- for (i = 1 ; i < npoints; ++i) {
86
+ for (py:: ssize_t i = 1 ; i < npoints; ++i) {
78
87
if (unique2 == 0 ) {
79
88
/* Looking for second unique point. */
80
89
if (x[i] != x[unique1] || y[i] != y[unique1]) {
@@ -125,8 +134,8 @@ class QhullInfo {
125
134
/* Delaunay implementation method.
126
135
* If hide_qhull_errors is true then qhull error messages are discarded;
127
136
* if it is false then they are written to stderr. */
128
- static PyObject*
129
- delaunay_impl (npy_intp npoints, const double * x, const double * y,
137
+ static py::tuple
138
+ delaunay_impl (py:: ssize_t npoints, const double * x, const double * y,
130
139
bool hide_qhull_errors)
131
140
{
132
141
qhT qh_qh; /* qh variable type and name must be like */
@@ -179,11 +188,13 @@ delaunay_impl(npy_intp npoints, const double* x, const double* y,
179
188
exitcode = qh_new_qhull (qh, ndim, (int )npoints, points.data (), False,
180
189
(char *)" qhull d Qt Qbb Qc Qz" , NULL , error_file);
181
190
if (exitcode != qh_ERRnone) {
182
- PyErr_Format (PyExc_RuntimeError,
183
- " Error in qhull Delaunay triangulation calculation: %s (exitcode=%d)%s" ,
184
- qhull_error_msg[exitcode], exitcode,
185
- hide_qhull_errors ? " ; use python verbose option (-v) to see original qhull error." : " " );
186
- return NULL ;
191
+ std::string msg =
192
+ py::str (" Error in qhull Delaunay triangulation calculation: {} (exitcode={})" )
193
+ .format (qhull_error_msg[exitcode], exitcode).cast <std::string>();
194
+ if (hide_qhull_errors) {
195
+ msg += " ; use python verbose option (-v) to see original qhull error." ;
196
+ }
197
+ throw std::runtime_error (msg);
187
198
}
188
199
189
200
/* Split facets so that they only have 3 points each. */
@@ -204,12 +215,12 @@ delaunay_impl(npy_intp npoints, const double* x, const double* y,
204
215
std::vector<int > tri_indices (max_facet_id+1 );
205
216
206
217
/* Allocate Python arrays to return. */
207
- npy_intp dims[2 ] = {ntri, 3 };
208
- numpy::array_view< int , ndim> triangles (dims);
209
- int * triangles_ptr = triangles.data ();
218
+ int dims[2 ] = {ntri, 3 };
219
+ IndexArray triangles (dims);
220
+ int * triangles_ptr = triangles.mutable_data ();
210
221
211
- numpy::array_view< int , ndim> neighbors (dims);
212
- int * neighbors_ptr = neighbors.data ();
222
+ IndexArray neighbors (dims);
223
+ int * neighbors_ptr = neighbors.mutable_data ();
213
224
214
225
/* Determine triangles array and set tri_indices array. */
215
226
i = 0 ;
@@ -238,103 +249,54 @@ delaunay_impl(npy_intp npoints, const double* x, const double* y,
238
249
}
239
250
}
240
251
241
- PyObject* tuple = PyTuple_New (2 );
242
- if (tuple == 0 ) {
243
- throw std::runtime_error (" Failed to create Python tuple" );
244
- }
245
-
246
- PyTuple_SET_ITEM (tuple, 0 , triangles.pyobj ());
247
- PyTuple_SET_ITEM (tuple, 1 , neighbors.pyobj ());
248
- return tuple;
252
+ return py::make_tuple (triangles, neighbors);
249
253
}
250
254
251
255
/* Process Python arguments and call Delaunay implementation method. */
252
- static PyObject*
253
- delaunay (PyObject *self, PyObject *args )
256
+ static py::tuple
257
+ delaunay (const CoordArray& x, const CoordArray& y, int verbose )
254
258
{
255
- numpy::array_view<double , 1 > xarray;
256
- numpy::array_view<double , 1 > yarray;
257
- PyObject* ret;
258
- npy_intp npoints;
259
- const double * x;
260
- const double * y;
261
- int verbose = 0 ;
262
-
263
- if (!PyArg_ParseTuple (args, " O&O&i:delaunay" ,
264
- &xarray.converter_contiguous , &xarray,
265
- &yarray.converter_contiguous , &yarray,
266
- &verbose)) {
267
- return NULL ;
259
+ if (x.ndim () != 1 || y.ndim () != 1 ) {
260
+ throw std::invalid_argument (" x and y must be 1D arrays" );
268
261
}
269
262
270
- npoints = xarray.shape (0 );
271
- if (npoints != yarray.shape (0 )) {
272
- PyErr_SetString (PyExc_ValueError,
273
- " x and y must be 1D arrays of the same length" );
274
- return NULL ;
263
+ auto npoints = x.shape (0 );
264
+ if (npoints != y.shape (0 )) {
265
+ throw std::invalid_argument (" x and y must be 1D arrays of the same length" );
275
266
}
276
267
277
268
if (npoints < 3 ) {
278
- PyErr_SetString (PyExc_ValueError,
279
- " x and y arrays must have a length of at least 3" );
280
- return NULL ;
269
+ throw std::invalid_argument (" x and y arrays must have a length of at least 3" );
281
270
}
282
271
283
- x = xarray.data ();
284
- y = yarray.data ();
285
-
286
- if (!at_least_3_unique_points (npoints, x, y)) {
287
- PyErr_SetString (PyExc_ValueError,
288
- " x and y arrays must consist of at least 3 unique points" );
289
- return NULL ;
272
+ if (!at_least_3_unique_points (npoints, x.data (), y.data ())) {
273
+ throw std::invalid_argument (" x and y arrays must consist of at least 3 unique points" );
290
274
}
291
275
292
- CALL_CPP (" qhull.delaunay" ,
293
- (ret = delaunay_impl (npoints, x, y, verbose == 0 )));
294
-
295
- return ret;
296
- }
297
-
298
- /* Return qhull version string for assistance in debugging. */
299
- static PyObject*
300
- version (PyObject *self, PyObject *arg)
301
- {
302
- return PyBytes_FromString (qh_version);
276
+ return delaunay_impl (npoints, x.data (), y.data (), verbose == 0 );
303
277
}
304
278
305
- static PyMethodDef qhull_methods[] = {
306
- {" delaunay" , delaunay, METH_VARARGS,
307
- " delaunay(x, y, verbose, /)\n "
308
- " --\n\n "
309
- " Compute a Delaunay triangulation.\n "
310
- " \n "
311
- " Parameters\n "
312
- " ----------\n "
313
- " x, y : 1d arrays\n "
314
- " The coordinates of the point set, which must consist of at least\n "
315
- " three unique points.\n "
316
- " verbose : int\n "
317
- " Python's verbosity level.\n "
318
- " \n "
319
- " Returns\n "
320
- " -------\n "
321
- " triangles, neighbors : int arrays, shape (ntri, 3)\n "
322
- " Indices of triangle vertices and indices of triangle neighbors.\n "
323
- },
324
- {" version" , version, METH_NOARGS,
325
- " version()\n --\n\n "
326
- " Return the qhull version string." },
327
- {NULL , NULL , 0 , NULL }
328
- };
329
-
330
- static struct PyModuleDef qhull_module = {
331
- PyModuleDef_HEAD_INIT,
332
- " qhull" , " Computing Delaunay triangulations.\n " , -1 , qhull_methods
333
- };
334
-
335
- PyMODINIT_FUNC
336
- PyInit__qhull (void )
337
- {
338
- import_array ();
339
- return PyModule_Create (&qhull_module);
279
+ PYBIND11_MODULE (_qhull, m) {
280
+ m.doc () = " Computing Delaunay triangulations.\n " ;
281
+
282
+ m.def (" delaunay" , &delaunay, " x" _a, " y" _a, " verbose" _a,
283
+ " --\n\n "
284
+ " Compute a Delaunay triangulation.\n "
285
+ " \n "
286
+ " Parameters\n "
287
+ " ----------\n "
288
+ " x, y : 1d arrays\n "
289
+ " The coordinates of the point set, which must consist of at least\n "
290
+ " three unique points.\n "
291
+ " verbose : int\n "
292
+ " Python's verbosity level.\n "
293
+ " \n "
294
+ " Returns\n "
295
+ " -------\n "
296
+ " triangles, neighbors : int arrays, shape (ntri, 3)\n "
297
+ " Indices of triangle vertices and indices of triangle neighbors.\n " );
298
+
299
+ m.def (" version" , []() { return qh_version; },
300
+ " version()\n --\n\n "
301
+ " Return the qhull version string." );
340
302
}
0 commit comments