|
2 | 2 | #include <pybind11/stl.h> |
3 | 3 | #include <numpy.hpp> |
4 | 4 | #include <typeinfo> |
| 5 | +#include "rdp.hpp" |
5 | 6 |
|
6 | 7 | namespace py = pybind11; |
7 | 8 |
|
@@ -2868,6 +2869,88 @@ void array_creation_registry(py::module_& m) { |
2868 | 2869 | register_array_float<5>(m); |
2869 | 2870 | } |
2870 | 2871 |
|
| 2872 | +// --- RDP helpers: bridge ndarray/list <-> Eigen types --- |
| 2873 | + |
| 2874 | +// Helper: extract Nx2 or Nx3 Eigen matrix from ndarray_base |
| 2875 | +static rdp::RowVectors ndarray_to_eigen(const ndarray_base& base, int& input_cols) { |
| 2876 | + int dims = base.ndim(); |
| 2877 | + if (dims != 2) throw std::invalid_argument("rdp: expected 2D array"); |
| 2878 | + auto shape_vec = base.shape(); |
| 2879 | + int rows = shape_vec[0].cast<int>(); |
| 2880 | + int cols = shape_vec[1].cast<int>(); |
| 2881 | + if (cols != 2 && cols != 3) throw std::invalid_argument("rdp: expected Nx2 or Nx3 array"); |
| 2882 | + input_cols = cols; |
| 2883 | + |
| 2884 | + if (auto* p = dynamic_cast<const ndarray<float64>*>(&base)) { |
| 2885 | + const double* ptr = p->data.get_array().data(); |
| 2886 | + if (cols == 3) { |
| 2887 | + return Eigen::Map<const rdp::RowVectors>(ptr, rows, 3); |
| 2888 | + } else { |
| 2889 | + rdp::RowVectors result(rows, 3); |
| 2890 | + result.setZero(); |
| 2891 | + Eigen::Map<const rdp::RowVectorsNx2> map2(ptr, rows, 2); |
| 2892 | + result.leftCols(2) = map2; |
| 2893 | + return result; |
| 2894 | + } |
| 2895 | + } |
| 2896 | + if (auto* p = dynamic_cast<const ndarray<int_>*>(&base)) { |
| 2897 | + const int_* iptr = p->data.get_array().data(); |
| 2898 | + rdp::RowVectors result(rows, 3); |
| 2899 | + result.setZero(); |
| 2900 | + for (int i = 0; i < rows; ++i) |
| 2901 | + for (int j = 0; j < cols; ++j) |
| 2902 | + result(i, j) = static_cast<double>(iptr[i * cols + j]); |
| 2903 | + return result; |
| 2904 | + } |
| 2905 | + throw std::invalid_argument("rdp: unsupported ndarray dtype"); |
| 2906 | +} |
| 2907 | + |
| 2908 | +// Helper: convert vector<vector<double>> to Eigen Nx3 (zero-pad 2D to 3D) |
| 2909 | +static rdp::RowVectors vec2d_to_eigen_d(const std::vector<std::vector<double>>& coords, int& input_cols) { |
| 2910 | + int rows = (int)coords.size(); |
| 2911 | + if (rows == 0) throw std::invalid_argument("rdp: empty coords"); |
| 2912 | + int cols = (int)coords[0].size(); |
| 2913 | + if (cols != 2 && cols != 3) throw std::invalid_argument("rdp: expected Nx2 or Nx3"); |
| 2914 | + input_cols = cols; |
| 2915 | + rdp::RowVectors result(rows, 3); |
| 2916 | + result.setZero(); |
| 2917 | + for (int i = 0; i < rows; ++i) |
| 2918 | + for (int j = 0; j < cols; ++j) |
| 2919 | + result(i, j) = coords[i][j]; |
| 2920 | + return result; |
| 2921 | +} |
| 2922 | + |
| 2923 | +// Helper: convert vector<vector<int_>> to Eigen Nx3 |
| 2924 | +static rdp::RowVectors vec2d_to_eigen_i(const std::vector<std::vector<int_>>& coords, int& input_cols) { |
| 2925 | + int rows = (int)coords.size(); |
| 2926 | + if (rows == 0) throw std::invalid_argument("rdp: empty coords"); |
| 2927 | + int cols = (int)coords[0].size(); |
| 2928 | + if (cols != 2 && cols != 3) throw std::invalid_argument("rdp: expected Nx2 or Nx3"); |
| 2929 | + input_cols = cols; |
| 2930 | + rdp::RowVectors result(rows, 3); |
| 2931 | + result.setZero(); |
| 2932 | + for (int i = 0; i < rows; ++i) |
| 2933 | + for (int j = 0; j < cols; ++j) |
| 2934 | + result(i, j) = static_cast<double>(coords[i][j]); |
| 2935 | + return result; |
| 2936 | +} |
| 2937 | + |
| 2938 | +// Helper: Eigen RowVectors result -> ndarray_base (return Nx2 or Nx3) |
| 2939 | +static std::unique_ptr<ndarray_base> eigen_to_ndarray(const rdp::RowVectors& mat, int output_cols) { |
| 2940 | + int N = (int)mat.rows(); |
| 2941 | + std::vector<std::vector<double>> data(N, std::vector<double>(output_cols)); |
| 2942 | + for (int i = 0; i < N; ++i) |
| 2943 | + for (int j = 0; j < output_cols; ++j) |
| 2944 | + data[i][j] = mat(i, j); |
| 2945 | + return std::unique_ptr<ndarray_base>(new ndarray<float64>(data)); |
| 2946 | +} |
| 2947 | + |
| 2948 | +// Helper: Eigen VectorXi mask -> ndarray_base (1D int array) |
| 2949 | +static std::unique_ptr<ndarray_base> mask_to_ndarray(const Eigen::VectorXi& mask) { |
| 2950 | + std::vector<int_> data(mask.size()); |
| 2951 | + for (int i = 0; i < mask.size(); ++i) data[i] = mask[i]; |
| 2952 | + return std::unique_ptr<ndarray_base>(new ndarray<int_>(data)); |
| 2953 | +} |
2871 | 2954 |
|
2872 | 2955 | PYBIND11_MODULE(numpy, m) { |
2873 | 2956 | m.doc() = "Python bindings for pkpy::numpy::ndarray using pybind11"; |
@@ -3535,4 +3618,65 @@ PYBIND11_MODULE(numpy, m) { |
3535 | 3618 | py::arg("arr2"), |
3536 | 3619 | py::arg("rtol") = 1e-5, |
3537 | 3620 | py::arg("atol") = 1e-8); |
| 3621 | + |
| 3622 | + // --- RDP bindings --- |
| 3623 | + |
| 3624 | + // rdp from list<list<double>> |
| 3625 | + m.def("rdp", [](std::vector<std::vector<double>> coords, double epsilon, bool recursive) -> std::unique_ptr<ndarray_base> { |
| 3626 | + int input_cols; |
| 3627 | + auto eigen_coords = vec2d_to_eigen_d(coords, input_cols); |
| 3628 | + auto result = rdp::douglas_simplify(eigen_coords, epsilon, recursive); |
| 3629 | + return eigen_to_ndarray(result, input_cols); |
| 3630 | + }, py::arg("coords"), py::arg("epsilon") = 0.0, py::arg("recursive") = true); |
| 3631 | + |
| 3632 | + // rdp from list<list<int>> |
| 3633 | + m.def("rdp", [](std::vector<std::vector<int_>> coords, double epsilon, bool recursive) -> std::unique_ptr<ndarray_base> { |
| 3634 | + int input_cols; |
| 3635 | + auto eigen_coords = vec2d_to_eigen_i(coords, input_cols); |
| 3636 | + auto result = rdp::douglas_simplify(eigen_coords, epsilon, recursive); |
| 3637 | + return eigen_to_ndarray(result, input_cols); |
| 3638 | + }, py::arg("coords"), py::arg("epsilon") = 0.0, py::arg("recursive") = true); |
| 3639 | + |
| 3640 | + // rdp from ndarray |
| 3641 | + m.def("rdp", [](const ndarray_base& coords, double epsilon, bool recursive) -> std::unique_ptr<ndarray_base> { |
| 3642 | + int input_cols; |
| 3643 | + auto eigen_coords = ndarray_to_eigen(coords, input_cols); |
| 3644 | + auto result = rdp::douglas_simplify(eigen_coords, epsilon, recursive); |
| 3645 | + return eigen_to_ndarray(result, input_cols); |
| 3646 | + }, py::arg("coords"), py::arg("epsilon") = 0.0, py::arg("recursive") = true); |
| 3647 | + |
| 3648 | + // rdp_mask from list<list<double>> |
| 3649 | + m.def("rdp_mask", [](std::vector<std::vector<double>> coords, double epsilon, bool recursive) -> std::unique_ptr<ndarray_base> { |
| 3650 | + int input_cols; |
| 3651 | + auto eigen_coords = vec2d_to_eigen_d(coords, input_cols); |
| 3652 | + auto mask = rdp::douglas_simplify_mask(eigen_coords, epsilon, recursive); |
| 3653 | + return mask_to_ndarray(mask); |
| 3654 | + }, py::arg("coords"), py::arg("epsilon") = 0.0, py::arg("recursive") = true); |
| 3655 | + |
| 3656 | + // rdp_mask from list<list<int>> |
| 3657 | + m.def("rdp_mask", [](std::vector<std::vector<int_>> coords, double epsilon, bool recursive) -> std::unique_ptr<ndarray_base> { |
| 3658 | + int input_cols; |
| 3659 | + auto eigen_coords = vec2d_to_eigen_i(coords, input_cols); |
| 3660 | + auto mask = rdp::douglas_simplify_mask(eigen_coords, epsilon, recursive); |
| 3661 | + return mask_to_ndarray(mask); |
| 3662 | + }, py::arg("coords"), py::arg("epsilon") = 0.0, py::arg("recursive") = true); |
| 3663 | + |
| 3664 | + // rdp_mask from ndarray |
| 3665 | + m.def("rdp_mask", [](const ndarray_base& coords, double epsilon, bool recursive) -> std::unique_ptr<ndarray_base> { |
| 3666 | + int input_cols; |
| 3667 | + auto eigen_coords = ndarray_to_eigen(coords, input_cols); |
| 3668 | + auto mask = rdp::douglas_simplify_mask(eigen_coords, epsilon, recursive); |
| 3669 | + return mask_to_ndarray(mask); |
| 3670 | + }, py::arg("coords"), py::arg("epsilon") = 0.0, py::arg("recursive") = true); |
| 3671 | + |
| 3672 | + // Create pocket_numpy module that re-exports rdp/rdp_mask from numpy |
| 3673 | + { |
| 3674 | + py_GlobalRef pocket_numpy_mod = py_newmodule("pocket_numpy"); |
| 3675 | + if (py_getattr(m.ptr(), py_name("rdp"))) { |
| 3676 | + py_setattr(pocket_numpy_mod, py_name("rdp"), py_retval()); |
| 3677 | + } |
| 3678 | + if (py_getattr(m.ptr(), py_name("rdp_mask"))) { |
| 3679 | + py_setattr(pocket_numpy_mod, py_name("rdp_mask"), py_retval()); |
| 3680 | + } |
| 3681 | + } |
3538 | 3682 | } |
0 commit comments