diff --git a/src/main/concaveman.py b/src/main/concaveman.py new file mode 100644 index 0000000..925ee3d --- /dev/null +++ b/src/main/concaveman.py @@ -0,0 +1,28 @@ +import numpy as np + +import cppimport +concaveman = cppimport.imp("cpp.concaveman") + + +def concaveman2d(points, hull, concavity=2.0, lengthThreshold=0.0): + points = np.array(points).astype(np.double) + hull = np.array(hull).astype(np.int32) + + if len(points.shape) != 2: + raise ValueError('points must be a 2-D array') + + if len(hull.shape) != 1: + raise ValueError('hull must be a 1-D array') + + if np.any(hull >= len(points)) or np.any(hull < 0): + raise ValueError('hull indices out of bounds') + + concave_points = concaveman.concaveman( + points, hull, + concavity, lengthThreshold) + + return concave_points + + +if __name__ == '__main__': + print(concaveman2d([[0, 0], [.25, .15], [1, 0], [1, 1]], [0, 2, 3])) diff --git a/src/main/cpp/concaveman.cpp b/src/main/cpp/concaveman.cpp index 10798bc..dc9de8a 100644 --- a/src/main/cpp/concaveman.cpp +++ b/src/main/cpp/concaveman.cpp @@ -1,7 +1,8 @@ -#if 0 -g++ -std=c++11 -shared concaveman.cpp -o libconcaveman.so -exit 0 -#endif +/* +<% +setup_pybind11(cfg) +%> +*/ // // Author: Stanislaw Adaszewski, 2019 @@ -9,57 +10,76 @@ exit 0 #include "concaveman.h" -extern "C" { - void pyconcaveman2d(double *points_c, size_t num_points, - int *hull_points_c, size_t num_hull_points, - double concavity, double lengthThreshold, - double **concave_points_c, size_t *num_concave_points, - void(**p_free)(void*)); -} +#include +#include + +namespace py = pybind11; + +#define DEBUG 0 + +py::array_t ccm( + py::array_t points_, + py::array_t hull_points_, + double concavity, double lengthThreshold) { + + auto points_c = points_.unchecked<2>(); + auto num_points = points_c.shape(0); + if (points_c.shape(1) != 2) { + throw std::invalid_argument("points must be 2d"); + } -void pyconcaveman2d(double *points_c, size_t num_points, - int *hull_points_c, size_t num_hull_points, - double concavity, double lengthThreshold, - double **p_concave_points_c, - size_t *p_num_concave_points, - void(**p_free)(void*)) { + auto hull_points_c = hull_points_.unchecked<1>(); + auto num_hull_points = hull_points_c.shape(0); +#if DEBUG std::cout << "pyconcaveman2d(), concavity: " << concavity << " lengthThreshold: " << lengthThreshold << std::endl; +#endif typedef double T; typedef std::array point_type; std::vector points(num_points); for (auto i = 0; i < num_points; i++) { - points[i] = { points_c[i << 1], points_c[(i << 1) + 1] }; + points[i] = { points_c(i, 0), points_c(i, 1) }; } +#if DEBUG std::cout << "points:" << std::endl; for (auto &p : points) std::cout << p[0] << " " << p[1] << std::endl; +#endif - std::vector hull(num_hull_points); + std::vector hull(num_hull_points); for (auto i = 0; i < num_hull_points; i++) { - hull[i] = hull_points_c[i]; + hull[i] = hull_points_c(i); } +#if DEBUG std::cout << "hull:" << std::endl; for (auto &i : hull) std::cout << i << std::endl; +#endif auto concave_points = concaveman(points, hull, concavity, lengthThreshold); +#if DEBUG std::cout << "concave_points:" << std::endl; for (auto &p : concave_points) std::cout << p[0] << " " << p[1] << std::endl; +#endif - double *concave_points_c = *p_concave_points_c = (double*) malloc(sizeof(double) * 2 * concave_points.size()); - for (auto i = 0; i < concave_points.size(); i++) { - concave_points_c[i << 1] = concave_points[i][0]; - concave_points_c[(i << 1) + 1] = concave_points[i][1]; + py::array_t result({concave_points.size(), size_t(2)}); + auto result_ptr = result.mutable_unchecked<2>(); + + for (size_t i = 0; i < concave_points.size(); i++) { + result_ptr(i, 0) = concave_points[i][0]; + result_ptr(i, 1) = concave_points[i][1]; } - *p_num_concave_points = concave_points.size(); - *p_free = free; + return result; +} + +PYBIND11_MODULE(concaveman, m) { + m.def("concaveman", &ccm); } diff --git a/src/main/python/demo.py b/src/main/demo.py similarity index 88% rename from src/main/python/demo.py rename to src/main/demo.py index 8d26803..36db949 100644 --- a/src/main/python/demo.py +++ b/src/main/demo.py @@ -7,7 +7,7 @@ from scipy.spatial import ConvexHull -with open('../../../data/points-1k.json', 'r') as f: +with open('../../data/points-1k.json', 'r') as f: pts = json.load(f) pts = np.array(pts) diff --git a/src/main/python/concaveman.py b/src/main/python/concaveman.py deleted file mode 100644 index 398c725..0000000 --- a/src/main/python/concaveman.py +++ /dev/null @@ -1,52 +0,0 @@ -import cffi -import numpy as np - - -_ffi = cffi.FFI() -_ffi.cdef('void pyconcaveman2d(double *points_c, size_t num_points, int *hull_points_c, size_t num_hull_points, double concavity, double lengthThreshold, double **p_concave_points_c, size_t *p_num_concave_points, void (**p_free)(void*));') -_lib = _ffi.dlopen('/Users/sadaszewski/Documents/workspace/concaveman-cpp/src/main/cpp/libconcaveman.so') - - -def concaveman2d(points, hull, concavity=2.0, lengthThreshold=0.0): - points = np.array(points).astype(np.double) - hull = np.array(hull).astype(np.int32) - - if len(points.shape) != 2: - raise ValueError('points must be a 2-D array') - - if len(hull.shape) != 1: - raise ValueError('hull must be a 1-D array') - - if np.any(hull >= len(points)) or np.any(hull < 0): - raise ValueError('hull indices out of bounds') - - p_concave_points_c = _ffi.new('double**') - p_num_concave_points = _ffi.new('size_t*') - p_free = _ffi.new('void(**)(void*)') - - points_c = _ffi.cast('double*', points.ctypes.data) - hull_c = _ffi.cast('int*', hull.ctypes.data) - _lib.pyconcaveman2d(points_c, len(points), - hull_c, len(hull), - concavity, lengthThreshold, - p_concave_points_c, p_num_concave_points, - p_free) - - num_concave_points = p_num_concave_points[0] - concave_points_c = p_concave_points_c[0] - - buffer = _ffi.buffer(concave_points_c, 8 * 2 * num_concave_points) - - concave_points = np.frombuffer(buffer, dtype=np.double) - concave_points = concave_points.reshape((num_concave_points, 2)) - concave_points = concave_points.copy() - - print('concave_points:', concave_points) - - p_free[0](concave_points_c) - - return concave_points - - -if __name__ == '__main__': - concaveman2d([[0, 0], [.25, .15], [1, 0], [1, 1]], [0, 2, 3])