diff --git a/build.py b/build.py index 4ef9668..6c7fc22 100644 --- a/build.py +++ b/build.py @@ -6,7 +6,7 @@ from setuptools import setup, find_packages name = "cubao_headers" -version = "0.0.5" +version = "0.0.6" pattern = ["*"] diff --git a/include/cubao/polyline_in_polygon.hpp b/include/cubao/polyline_in_polygon.hpp index f0b6b03..943238c 100644 --- a/include/cubao/polyline_in_polygon.hpp +++ b/include/cubao/polyline_in_polygon.hpp @@ -26,44 +26,50 @@ polyline_in_polygon(const RowVectors &polyline, // const FastCrossing &fc) { auto intersections = fc.intersections(polyline); - // pt, (t, s), cur_label=(poly1, seg1), tree_label=(poly2, seg2) auto ruler = PolylineRuler(polyline, fc.is_wgs84()); - // 0.0, [r1, r2, ..., length] - const int N = intersections.size() + 1; - Eigen::VectorXd ranges(N); - int idx = -1; - for (auto &inter : intersections) { - int seg_idx = std::get<2>(inter)[1]; - double t = std::get<1>(inter)[0]; - double r = ruler.range(seg_idx, t); - ranges[++idx] = r; + if (intersections.empty()) { + int inside = point_in_polygon(polyline.block(0, 0, 1, 2), polygon)[0]; + if (!inside) { + return {}; + } + return PolylineChunks{ + {{0, 0.0, 0.0, ruler.N() - 2, 1.0, ruler.length()}, polyline}}; } - ranges[++idx] = ruler.length(); - RowVectorsNx2 midpoints(N, 3); + // pt, (t, s), cur_label=(poly1, seg1), tree_label=(poly2, seg2) + const int N = intersections.size() + 2; + // init ranges + Eigen::VectorXd ranges(N); { - idx = 0; - double r = 0.0; - while (idx < N) { - double rr = ranges[idx]; - midpoints.row(idx) = ruler.at((r + rr) / 2.0).head(2); - r = rr; - ++idx; + int idx = -1; + ranges[++idx] = 0.0; + for (auto &inter : intersections) { + int seg_idx = std::get<2>(inter)[1]; + double t = std::get<1>(inter)[0]; + double r = ruler.range(seg_idx, t); + ranges[++idx] = r; } + ranges[++idx] = ruler.length(); + } + // ranges o------o--------o-----------------o + // midpts ^ ^ ^ + RowVectorsNx2 midpoints(N - 1, 2); + for (int i = 0; i < N - 1; ++i) { + double rr = (ranges[i] + ranges[i + 1]) / 2.0; + midpoints.row(i) = ruler.along(rr).head(2); } auto mask = point_in_polygon(midpoints, polygon); PolylineChunks ret; { - idx = 0; - double r = 0.0; - while (idx < N) { - if (mask[idx] && ranges[idx] > r) { - auto [seg1, t1] = ruler.segment_index_t(r); - auto [seg2, t2] = ruler.segment_index_t(ranges[idx]); - ret.emplace(std::make_tuple(seg1, t1, r, seg2, t2, ranges[idx]), - ruler.lineSliceAlong(r, ranges[idx])); + for (int i = 0; i < N - 1; ++i) { + double r1 = ranges[i]; + double r2 = ranges[i + 1]; + if (r2 <= r1 || mask[i] == 0) { + continue; } - r = ranges[idx]; - ++idx; + auto [seg1, t1] = ruler.segment_index_t(r1); + auto [seg2, t2] = ruler.segment_index_t(r2); + ret.emplace(std::make_tuple(seg1, t1, r1, seg2, t2, r2), + ruler.lineSliceAlong(r1, r2)); } } return ret; diff --git a/include/cubao/pybind11_fast_crossing.hpp b/include/cubao/pybind11_fast_crossing.hpp index 4ae77c4..9944310 100644 --- a/include/cubao/pybind11_fast_crossing.hpp +++ b/include/cubao/pybind11_fast_crossing.hpp @@ -24,86 +24,139 @@ using rvp = py::return_value_policy; CUBAO_INLINE void bind_fast_crossing(py::module &m) { py::class_(m, "FastCrossing", py::module_local()) - .def(py::init(), py::kw_only(), "is_wgs84"_a = false) + .def(py::init(), py::kw_only(), "is_wgs84"_a = false, + "Initialize FastCrossing object.\n\n" + ":param is_wgs84: Whether coordinates are in WGS84 format, " + "defaults to false") // add polyline .def("add_polyline", - py::overload_cast( &FastCrossing::add_polyline), "polyline"_a, py::kw_only(), "index"_a = -1, - "add polyline to tree, you can " - "provide your own polyline index, " - "default: -1") + "Add polyline to the tree.\n\n" + ":param polyline: The polyline to add\n" + ":param index: Custom polyline index, defaults to -1\n" + ":return: The index of the added polyline") .def("add_polyline", - py::overload_cast< const Eigen::Ref &, int>(&FastCrossing::add_polyline), "polyline"_a, py::kw_only(), "index"_a = -1, - "add polyline to tree, you can " - "provide your own polyline index, " - "default: -1") - // finish + "Add polyline to the tree (alternative format).\n\n" + ":param polyline: The polyline to add\n" + ":param index: Custom polyline index, defaults to -1\n" + ":return: The index of the added polyline") + + .def("finish", &FastCrossing::finish, + "Finalize indexing after adding all polylines") - .def("finish", &FastCrossing::finish, "finish to finalize indexing") // intersections .def("intersections", py::overload_cast<>(&FastCrossing::intersections, py::const_), - "all segment intersections in tree") - .def("intersections", - py::overload_cast &, int>( - &FastCrossing::intersections, py::const_), - py::kw_only(), "z_offset_range"_a, "self_intersection"_a = 2, - "segment intersections in tree, filter by some condition") + "Get all segment intersections in the tree") + + .def( + "intersections", + py::overload_cast &, int>( + &FastCrossing::intersections, py::const_), + py::kw_only(), "z_offset_range"_a, "self_intersection"_a = 2, + "Get segment intersections in the tree, filtered by conditions.\n\n" + ":param z_offset_range: Z-offset range for filtering\n" + ":param self_intersection: Self-intersection parameter, defaults " + "to 2") + .def("intersections", py::overload_cast(&FastCrossing::intersections, py::const_), - "from"_a, "to"_a, py::kw_only(), "dedup"_a = true, - "crossing intersections with [from, to] segment " - "(sorted by t ratio)") + "start"_a, "to"_a, py::kw_only(), "dedup"_a = true, + "Get crossing intersections with [start, to] segment.\n\n" + ":param from: Start point of the segment\n" + ":param to: End point of the segment\n" + ":param dedup: Whether to remove duplicates, defaults to true\n" + ":return: Sorted intersections by t ratio") + .def("intersections", py::overload_cast( &FastCrossing::intersections, py::const_), "polyline"_a, py::kw_only(), "dedup"_a = true, - "crossing intersections with polyline (sorted by t ratio)") + "Get crossing intersections with a polyline.\n\n" + ":param polyline: The polyline to check intersections with\n" + ":param dedup: Whether to remove duplicates, defaults to true\n" + ":return: Sorted intersections by t ratio") + .def("intersections", py::overload_cast< const Eigen::Ref &, bool>(&FastCrossing::intersections, py::const_), "polyline"_a, py::kw_only(), "dedup"_a = true, - "crossing intersections with polyline (sorted by t ratio)") + "Get crossing intersections with a polyline (alternative " + "format).\n\n" + ":param polyline: The polyline to check intersections with\n" + ":param dedup: Whether to remove duplicates, defaults to true\n" + ":return: Sorted intersections by t ratio") + .def("intersections", py::overload_cast(&FastCrossing::intersections, py::const_), "polyline"_a, py::kw_only(), "z_min"_a, "z_max"_a, "dedup"_a = true, - "crossing intersections with polyline (sorted by t ratio)") + "Get crossing intersections with a polyline, filtered by Z " + "range.\n\n" + ":param polyline: The polyline to check intersections with\n" + ":param z_min: Minimum Z value for filtering\n" + ":param z_max: Maximum Z value for filtering\n" + ":param dedup: Whether to remove duplicates, defaults to true\n" + ":return: Sorted intersections by t ratio") + .def( "intersections", py::overload_cast< const Eigen::Ref &, double, double, bool>(&FastCrossing::intersections, py::const_), "polyline"_a, py::kw_only(), "z_min"_a, "z_max"_a, "dedup"_a = true, - "crossing intersections with polyline (sorted by t ratio)") + "Get crossing intersections with a polyline, filtered by Z range " + "(alternative format).\n\n" + ":param polyline: The polyline to check intersections with\n" + ":param z_min: Minimum Z value for filtering\n" + ":param z_max: Maximum Z value for filtering\n" + ":param dedup: Whether to remove duplicates, defaults to true\n" + ":return: Sorted intersections by t ratio") + // segment_index .def("segment_index", py::overload_cast(&FastCrossing::segment_index, py::const_), - "index"_a) + "index"_a, + "Get segment index for a given index.\n\n" + ":param index: The index to query\n" + ":return: The segment index") + .def("segment_index", py::overload_cast( &FastCrossing::segment_index, py::const_), - "indexes"_a) + "indexes"_a, + "Get segment indexes for given indexes.\n\n" + ":param indexes: The indexes to query\n" + ":return: The segment indexes") + // point_index .def("point_index", py::overload_cast(&FastCrossing::point_index, py::const_), - "index"_a) + "index"_a, + "Get point index for a given index.\n\n" + ":param index: The index to query\n" + ":return: The point index") + .def("point_index", py::overload_cast( &FastCrossing::point_index, py::const_), - "indexes"_a) + "indexes"_a, + "Get point indexes for given indexes.\n\n" + ":param indexes: The indexes to query\n" + ":return: The point indexes") + // within .def("within", py::overload_cast &, bool, bool>(&FastCrossing::within, py::const_), py::kw_only(), // "polygon"_a, // "segment_wise"_a = true, // - "sort"_a = true) + "sort"_a = true, + "Find polylines within a polygon.\n\n" + ":param polygon: The polygon to check against\n" + ":param segment_wise: Whether to return segment-wise results, " + "defaults to true\n" + ":param sort: Whether to sort the results, defaults to true\n" + ":return: Polylines within the polygon") + .def("within", py::overload_cast(&FastCrossing::within, py::const_), @@ -129,18 +197,40 @@ CUBAO_INLINE void bind_fast_crossing(py::module &m) "height"_a, // "heading"_a = 0.0, // "segment_wise"_a = true, // - "sort"_a = true) + "sort"_a = true, + "Find polylines within a rotated rectangle.\n\n" + ":param center: Center of the rectangle\n" + ":param width: Width of the rectangle\n" + ":param height: Height of the rectangle\n" + ":param heading: Heading angle of the rectangle, defaults to 0.0\n" + ":param segment_wise: Whether to return segment-wise results, " + "defaults to true\n" + ":param sort: Whether to sort the results, defaults to true\n" + ":return: Polylines within the rotated rectangle") + // nearest .def("nearest", py::overload_cast( &FastCrossing::nearest, py::const_), "position"_a, py::kw_only(), // - "return_squared_l2"_a = false) + "return_squared_l2"_a = false, + "Find the nearest point to a given position.\n\n" + ":param position: The query position\n" + ":param return_squared_l2: Whether to return squared L2 distance, " + "defaults to false\n" + ":return: Nearest point information") + .def("nearest", py::overload_cast( &FastCrossing::nearest, py::const_), "index"_a, py::kw_only(), // - "return_squared_l2"_a = false) + "return_squared_l2"_a = false, + "Find the nearest point to a given index.\n\n" + ":param index: The query index\n" + ":param return_squared_l2: Whether to return squared L2 distance, " + "defaults to false\n" + ":return: Nearest point information") + .def("nearest", py::overload_cast, // k @@ -156,36 +246,90 @@ CUBAO_INLINE void bind_fast_crossing(py::module &m) "radius"_a = std::nullopt, // "sort"_a = true, // "return_squared_l2"_a = false, // - "filter"_a = std::nullopt) + "filter"_a = std::nullopt, + "Find k nearest points to a given position with optional " + "filtering.\n\n" + ":param position: The query position\n" + ":param k: Number of nearest neighbors to find (optional)\n" + ":param radius: Search radius (optional)\n" + ":param sort: Whether to sort the results, defaults to true\n" + ":param return_squared_l2: Whether to return squared L2 distance, " + "defaults to false\n" + ":param filter: Optional filter parameters\n" + ":return: Nearest points information") + // coordinates .def("coordinates", py::overload_cast(&FastCrossing::coordinates, py::const_), - "polyline_index"_a, "segment_index"_a, "ratio"_a) + "polyline_index"_a, "segment_index"_a, "ratio"_a, + "Get coordinates at a specific position on a polyline.\n\n" + ":param polyline_index: Index of the polyline\n" + ":param segment_index: Index of the segment within the polyline\n" + ":param ratio: Ratio along the segment (0 to 1)\n" + ":return: Coordinates at the specified position") + .def("coordinates", py::overload_cast( &FastCrossing::coordinates, py::const_), - "index"_a, "ratio"_a) + "index"_a, "ratio"_a, + "Get coordinates at a specific position on a polyline " + "(alternative format).\n\n" + ":param index: Combined index of polyline and segment\n" + ":param ratio: Ratio along the segment (0 to 1)\n" + ":return: Coordinates at the specified position") + .def("coordinates", py::overload_cast( &FastCrossing::coordinates, py::const_), - "intersection"_a, "second"_a = true) + "intersection"_a, "second"_a = true, + "Get coordinates of an intersection.\n\n" + ":param intersection: The intersection object\n" + ":param second: Whether to use the second polyline of the " + "intersection, defaults to true\n" + ":return: Coordinates of the intersection") + // arrow .def("arrow", py::overload_cast(&FastCrossing::arrow, py::const_), - py::kw_only(), "polyline_index"_a, "point_index"_a) + py::kw_only(), "polyline_index"_a, "point_index"_a, + "Get an arrow (position and direction) at a specific point on a " + "polyline.\n\n" + ":param polyline_index: Index of the polyline\n" + ":param point_index: Index of the point within the polyline\n" + ":return: Arrow (position and direction)") + // - .def("is_wgs84", &FastCrossing::is_wgs84) - .def("num_poylines", &FastCrossing::num_poylines) + .def( + "is_wgs84", &FastCrossing::is_wgs84, + "Check if the coordinates are in WGS84 format.\n\n" + ":return: True if coordinates are in WGS84 format, False otherwise") + + .def("num_poylines", &FastCrossing::num_poylines, + "Get the number of polylines in the FastCrossing object.\n\n" + ":return: Number of polylines") + .def("polyline_rulers", &FastCrossing::polyline_rulers, - rvp::reference_internal) + rvp::reference_internal, + "Get all polyline rulers.\n\n" + ":return: Dictionary of polyline rulers") + .def("polyline_ruler", &FastCrossing::polyline_ruler, "index"_a, - rvp::reference_internal) + rvp::reference_internal, + "Get a specific polyline ruler.\n\n" + ":param index: Index of the polyline\n" + ":return: Polyline ruler for the specified index") + // export .def("bush", &FastCrossing::export_bush, "autobuild"_a = true, - rvp::reference_internal) - .def("quiver", &FastCrossing::export_quiver, rvp::reference_internal) - // - ; + rvp::reference_internal, + "Export the internal FlatBush index.\n\n" + ":param autobuild: Whether to automatically build the index if " + "not already built, defaults to true\n" + ":return: FlatBush index") + + .def("quiver", &FastCrossing::export_quiver, rvp::reference_internal, + "Export the internal Quiver object.\n\n" + ":return: Quiver object"); } } // namespace cubao diff --git a/include/cubao/pybind11_flatbush.hpp b/include/cubao/pybind11_flatbush.hpp index 24c1667..c0725c5 100644 --- a/include/cubao/pybind11_flatbush.hpp +++ b/include/cubao/pybind11_flatbush.hpp @@ -25,9 +25,13 @@ CUBAO_INLINE void bind_flatbush(py::module &m) { using FlatBush = flatbush::FlatBush; py::class_(m, "FlatBush", py::module_local()) - .def(py::init<>()) - .def(py::init(), "reserve"_a) - .def("reserve", &FlatBush::Reserve) + .def(py::init<>(), "Initialize an empty FlatBush index.") + .def(py::init(), "reserve"_a, + "Initialize a FlatBush index with a reserved capacity.\n\n" + ":param reserve: Number of items to reserve space for") + .def("reserve", &FlatBush::Reserve, + "Reserve space for a number of items.\n\n" + ":param n: Number of items to reserve space for") .def("add", py::overload_cast &, int>(&FlatBush::Add), "polyline"_a, // - py::kw_only(), "label0"_a = -1) - + py::kw_only(), "label0"_a = -1, + "Add a polyline to the index.\n\n" + ":param polyline: Polyline coordinates\n" + ":param label0: Label for the polyline (optional)\n" + ":return: Index of the added item") .def( "add", [](FlatBush &self, // @@ -51,14 +66,27 @@ CUBAO_INLINE void bind_flatbush(py::module &m) label0, label1); }, "box"_a, py::kw_only(), // - "label0"_a = -1, "label1"_a = -1) - .def("finish", &FlatBush::Finish) - // - .def("boxes", &FlatBush::boxes, rvp::reference_internal) - .def("labels", &FlatBush::labels, rvp::reference_internal) - .def("box", &FlatBush::box, "index"_a) - .def("label", &FlatBush::label, "index"_a) - // + "label0"_a = -1, "label1"_a = -1, + "Add a bounding box to the index using a vector.\n\n" + ":param box: Vector of [minX, minY, maxX, maxY]\n" + ":param label0: First label (optional)\n" + ":param label1: Second label (optional)\n" + ":return: Index of the added item") + .def("finish", &FlatBush::Finish, "Finish the index construction.") + .def("boxes", &FlatBush::boxes, rvp::reference_internal, + "Get all bounding boxes in the index.\n\n" + ":return: Reference to the vector of bounding boxes") + .def("labels", &FlatBush::labels, rvp::reference_internal, + "Get all labels in the index.\n\n" + ":return: Reference to the vector of labels") + .def("box", &FlatBush::box, "index"_a, + "Get the bounding box for a specific index.\n\n" + ":param index: Index of the item\n" + ":return: Bounding box of the item") + .def("label", &FlatBush::label, "index"_a, + "Get the label for a specific index.\n\n" + ":param index: Index of the item\n" + ":return: Label of the item") .def( "search", [](const FlatBush &self, // @@ -67,13 +95,22 @@ CUBAO_INLINE void bind_flatbush(py::module &m) return self.Search(minX, minY, maxX, maxY); }, "minX"_a, "minY"_a, // - "maxX"_a, "maxY"_a) + "maxX"_a, "maxY"_a, + "Search for items within a bounding box.\n\n" + ":param minX: Minimum X coordinate of the search box\n" + ":param minY: Minimum Y coordinate of the search box\n" + ":param maxX: Maximum X coordinate of the search box\n" + ":param maxY: Maximum Y coordinate of the search box\n" + ":return: Vector of indices of items within the search box") .def( "search", [](const FlatBush &self, const Eigen::Vector4d &bbox) { return self.Search(bbox[0], bbox[1], bbox[2], bbox[3]); }, - "bbox"_a) + "bbox"_a, + "Search for items within a bounding box using a vector.\n\n" + ":param bbox: Vector of [minX, minY, maxX, maxY]\n" + ":return: Vector of indices of items within the search box") .def( "search", [](const FlatBush &self, // @@ -81,9 +118,14 @@ CUBAO_INLINE void bind_flatbush(py::module &m) const Eigen::Vector2d &max) { return self.Search(min[0], min[1], max[0], max[1]); }, - "min"_a, "max"_a) - .def("size", &FlatBush::Size) - // - ; + "min"_a, "max"_a, + "Search for items within a bounding box using min and max " + "vectors.\n\n" + ":param min: Vector of [minX, minY]\n" + ":param max: Vector of [maxX, maxY]\n" + ":return: Vector of indices of items within the search box") + .def("size", &FlatBush::Size, + "Get the number of items in the index.\n\n" + ":return: Number of items in the index"); } } // namespace cubao diff --git a/include/cubao/pybind11_nanoflann_kdtree.hpp b/include/cubao/pybind11_nanoflann_kdtree.hpp index 0b700f0..1c1edc1 100644 --- a/include/cubao/pybind11_nanoflann_kdtree.hpp +++ b/include/cubao/pybind11_nanoflann_kdtree.hpp @@ -25,48 +25,95 @@ CUBAO_INLINE void bind_nanoflann_kdtree(py::module &m) { using KdTree = cubao::KdTree; py::class_(m, "KdTree", py::module_local()) - .def(py::init(), "leafsize"_a = 10) - .def(py::init(), "points"_a) - .def(py::init &>(), "points"_a) + .def(py::init(), "leafsize"_a = 10, + "Initialize KdTree with specified leaf size.\n\n" + ":param leafsize: Maximum number of points in leaf node, defaults " + "to 10") + .def(py::init(), "points"_a, + "Initialize KdTree with 3D points.\n\n" + ":param points: 3D points to initialize the tree") + .def(py::init &>(), "points"_a, + "Initialize KdTree with 2D points.\n\n" + ":param points: 2D points to initialize the tree") // - .def("points", &KdTree::points, rvp::reference_internal) + .def("points", &KdTree::points, rvp::reference_internal, + "Get the points in the KdTree.\n\n" + ":return: Reference to the points in the tree") // .def("add", py::overload_cast(&KdTree::add), - "points"_a) + "points"_a, + "Add 3D points to the KdTree.\n\n" + ":param points: 3D points to add") .def("add", py::overload_cast &>( &KdTree::add), - "points"_a) + "points"_a, + "Add 2D points to the KdTree.\n\n" + ":param points: 2D points to add") // - .def("reset", &KdTree::reset) - .def("reset_index", &KdTree::reset_index) - .def("build_index", &KdTree::build_index, "force_rebuild"_a = false) + .def("reset", &KdTree::reset, "Reset the KdTree, clearing all points.") + .def("reset_index", &KdTree::reset_index, + "Reset the index of the KdTree.") + .def("build_index", &KdTree::build_index, "force_rebuild"_a = false, + "Build the KdTree index.\n\n" + ":param force_rebuild: Force rebuilding the index even if already " + "built, defaults to false") // - .def("leafsize", &KdTree::leafsize) - .def("set_leafsize", &KdTree::set_leafsize, "value"_a) + .def("leafsize", &KdTree::leafsize, + "Get the current leaf size of the KdTree.\n\n" + ":return: Current leaf size") + .def("set_leafsize", &KdTree::set_leafsize, "value"_a, + "Set the leaf size of the KdTree.\n\n" + ":param value: New leaf size value") // .def("nearest", py::overload_cast(&KdTree::nearest, py::const_), - "position"_a, py::kw_only(), "return_squared_l2"_a = false) + "position"_a, py::kw_only(), "return_squared_l2"_a = false, + "Find the nearest point to the given position.\n\n" + ":param position: Query position\n" + ":param return_squared_l2: If true, return squared L2 distance, " + "defaults to false\n" + ":return: Tuple of (index, distance)") .def("nearest", py::overload_cast(&KdTree::nearest, py::const_), - "index"_a, py::kw_only(), "return_squared_l2"_a = false) + "index"_a, py::kw_only(), "return_squared_l2"_a = false, + "Find the nearest point to the point at the given index.\n\n" + ":param index: Index of the query point\n" + ":param return_squared_l2: If true, return squared L2 distance, " + "defaults to false\n" + ":return: Tuple of (index, distance)") // - .def("nearest", - py::overload_cast( - &KdTree::nearest, py::const_), - "position"_a, py::kw_only(), - "k"_a, // - "sort"_a = true, // - "return_squared_l2"_a = false) - .def("nearest", - py::overload_cast( - &KdTree::nearest, py::const_), - "position"_a, py::kw_only(), - "radius"_a, // - "sort"_a = true, // - "return_squared_l2"_a = false) + .def( + "nearest", + py::overload_cast( + &KdTree::nearest, py::const_), + "position"_a, py::kw_only(), + "k"_a, // + "sort"_a = true, // + "return_squared_l2"_a = false, + "Find k nearest points to the given position.\n\n" + ":param position: Query position\n" + ":param k: Number of nearest neighbors to find\n" + ":param sort: If true, sort results by distance, defaults to true\n" + ":param return_squared_l2: If true, return squared L2 distances, " + "defaults to false\n" + ":return: Tuple of (indices, distances)") + .def( + "nearest", + py::overload_cast( + &KdTree::nearest, py::const_), + "position"_a, py::kw_only(), + "radius"_a, // + "sort"_a = true, // + "return_squared_l2"_a = false, + "Find all points within a given radius of the query position.\n\n" + ":param position: Query position\n" + ":param radius: Search radius\n" + ":param sort: If true, sort results by distance, defaults to true\n" + ":param return_squared_l2: If true, return squared L2 distances, " + "defaults to false\n" + ":return: Tuple of (indices, distances)") // ; } diff --git a/include/cubao/pybind11_quiver.hpp b/include/cubao/pybind11_quiver.hpp index 4da9d67..6b68516 100644 --- a/include/cubao/pybind11_quiver.hpp +++ b/include/cubao/pybind11_quiver.hpp @@ -29,14 +29,18 @@ CUBAO_INLINE void bind_quiver(py::module &m) using Arrow = cubao::Arrow; py::class_(m, "Arrow", py::module_local()) // - .def(py::init<>()) - .def(py::init(), "position"_a) + .def(py::init<>(), "Default constructor for Arrow") + .def(py::init(), "position"_a, + "Constructor for Arrow with position") .def(py::init(), - "position"_a, "direction"_a) + "position"_a, "direction"_a, + "Constructor for Arrow with position and direction") // - .def("label", py::overload_cast<>(&Arrow::label, py::const_)) + .def("label", py::overload_cast<>(&Arrow::label, py::const_), + "Get the label of the Arrow") .def("label", py::overload_cast(&Arrow::label), - "new_value"_a, rvp::reference_internal) + "new_value"_a, rvp::reference_internal, + "Set the label of the Arrow") .def("label", py::overload_cast(&Arrow::polyline_index, py::const_)) + py::overload_cast<>(&Arrow::polyline_index, py::const_), + "Get the polyline index of the Arrow") .def("polyline_index", py::overload_cast(&Arrow::polyline_index), - "new_value"_a, rvp::reference_internal) + "new_value"_a, rvp::reference_internal, + "Set the polyline index of the Arrow") .def("segment_index", - py::overload_cast<>(&Arrow::segment_index, py::const_)) + py::overload_cast<>(&Arrow::segment_index, py::const_), + "Get the segment index of the Arrow") .def("segment_index", py::overload_cast(&Arrow::segment_index), - "new_value"_a, rvp::reference_internal) + "new_value"_a, rvp::reference_internal, + "Set the segment index of the Arrow") // - .def("t", py::overload_cast<>(&Arrow::t, py::const_)) + .def("t", py::overload_cast<>(&Arrow::t, py::const_), + "Get the t parameter of the Arrow") .def("t", py::overload_cast(&Arrow::t), "new_value"_a, - rvp::reference_internal) - .def("range", py::overload_cast<>(&Arrow::range, py::const_)) + rvp::reference_internal, "Set the t parameter of the Arrow") + .def("range", py::overload_cast<>(&Arrow::range, py::const_), + "Get the range of the Arrow") .def("range", py::overload_cast(&Arrow::range), "new_value"_a, - rvp::reference_internal) + rvp::reference_internal, "Set the range of the Arrow") // - .def("reset_index", py::overload_cast<>(&Arrow::reset_index)) + .def("reset_index", py::overload_cast<>(&Arrow::reset_index), + "Reset the index of the Arrow") .def("has_index", py::overload_cast(&Arrow::has_index, py::const_), - "check_range"_a = true) + "check_range"_a = true, "Check if the Arrow has a valid index") // - .def("position", py::overload_cast<>(&Arrow::position, py::const_)) + .def("position", py::overload_cast<>(&Arrow::position, py::const_), + "Get the position of the Arrow") .def("position", py::overload_cast(&Arrow::position), - "new_value"_a, rvp::reference_internal) - .def("direction", py::overload_cast<>(&Arrow::direction, py::const_)) + "new_value"_a, rvp::reference_internal, + "Set the position of the Arrow") + .def("direction", py::overload_cast<>(&Arrow::direction, py::const_), + "Get the direction of the Arrow") .def("direction", py::overload_cast(&Arrow::direction), - rvp::reference_internal) - .def("forward", py::overload_cast<>(&Arrow::direction, py::const_)) - .def("leftward", py::overload_cast<>(&Arrow::leftward, py::const_)) - .def("upward", py::overload_cast<>(&Arrow::upward, py::const_)) - .def("Frenet", py::overload_cast<>(&Arrow::Frenet, py::const_)) + rvp::reference_internal, "Set the direction of the Arrow") + .def("forward", py::overload_cast<>(&Arrow::direction, py::const_), + "Get the forward direction of the Arrow") + .def("leftward", py::overload_cast<>(&Arrow::leftward, py::const_), + "Get the leftward direction of the Arrow") + .def("upward", py::overload_cast<>(&Arrow::upward, py::const_), + "Get the upward direction of the Arrow") + .def("Frenet", py::overload_cast<>(&Arrow::Frenet, py::const_), + "Get the Frenet frame of the Arrow") // - .def("heading", py::overload_cast<>(&Arrow::heading, py::const_)) + .def("heading", py::overload_cast<>(&Arrow::heading, py::const_), + "Get the heading of the Arrow") .def("heading", py::overload_cast(&Arrow::heading), - "new_value"_a, rvp::reference_internal) + "new_value"_a, rvp::reference_internal, + "Set the heading of the Arrow") .def_static("_heading", py::overload_cast(&Arrow::_heading), - "heading"_a) - .def_static("_heading", - py::overload_cast(&Arrow::_heading), - "east"_a, "north"_a) + "heading"_a, "Convert heading to unit vector") + .def_static( + "_heading", py::overload_cast(&Arrow::_heading), + "east"_a, "north"_a, "Convert east and north components to heading") // .def_static("_unit_vector", &Arrow::_unit_vector, "vector"_a, - "with_eps"_a = true) - .def_static("_angle", &Arrow::_angle, "vec"_a, py::kw_only(), "ref"_a) + "with_eps"_a = true, "Normalize a vector to unit length") + .def_static("_angle", &Arrow::_angle, "vec"_a, py::kw_only(), "ref"_a, + "Calculate angle between two vectors") // .def("__repr__", [](const Arrow &self) { @@ -109,9 +131,13 @@ CUBAO_INLINE void bind_quiver(py::module &m) dir[0], dir[1], dir[2], // self.heading()); }) - .def("copy", [](const Arrow &self) -> Arrow { return self; }) - .def("__copy__", - [](const Arrow &self, py::dict) -> Arrow { return self; }) + .def( + "copy", [](const Arrow &self) -> Arrow { return self; }, + "Create a copy of the Arrow") + .def( + "__copy__", + [](const Arrow &self, py::dict) -> Arrow { return self; }, + "Create a copy of the Arrow") // ; @@ -119,128 +145,159 @@ CUBAO_INLINE void bind_quiver(py::module &m) auto pyQuiver = py::class_(m, "Quiver", py::module_local()) // - .def(py::init<>()) - .def(py::init(), "anchor_lla"_a) + .def(py::init<>(), "Default constructor for Quiver") + .def(py::init(), "anchor_lla"_a, + "Constructor for Quiver with anchor LLA coordinates") // - .def_static("_k", &Quiver::k) - .def("k", [](const Quiver &self) { return self.k_; }) - .def("inv_k", [](const Quiver &self) { return self.inv_k_; }) - .def("anchor", [](const Quiver &self) { return self.anchor_; }) - .def("is_wgs84", [](const Quiver &self) { return self.is_wgs84_; }) + .def_static("_k", &Quiver::k, "Get the constant k") + .def( + "k", [](const Quiver &self) { return self.k_; }, + "Get the k value of the Quiver") + .def( + "inv_k", [](const Quiver &self) { return self.inv_k_; }, + "Get the inverse k value of the Quiver") + .def( + "anchor", [](const Quiver &self) { return self.anchor_; }, + "Get the anchor point of the Quiver") + .def( + "is_wgs84", [](const Quiver &self) { return self.is_wgs84_; }, + "Check if the Quiver is using WGS84 coordinates") // - .def("forwards", &Quiver::forwards, "arrow"_a, "delta_x"_a) - .def("leftwards", &Quiver::leftwards, "arrow"_a, "delta_y"_a) - .def("upwards", &Quiver::upwards, "arrow"_a, "delta_z"_a) + .def("forwards", &Quiver::forwards, "arrow"_a, "delta_x"_a, + "Move the Arrow forward by delta_x") + .def("leftwards", &Quiver::leftwards, "arrow"_a, "delta_y"_a, + "Move the Arrow leftward by delta_y") + .def("upwards", &Quiver::upwards, "arrow"_a, "delta_z"_a, + "Move the Arrow upward by delta_z") .def("towards", &Quiver::towards, "arrow"_a, "delta_frenet"_a, - py::kw_only(), "update_direction"_a = true) + py::kw_only(), "update_direction"_a = true, + "Move the Arrow in Frenet coordinates") // .def("update", &Quiver::update, "arrow"_a, "delta_enu"_a, - py::kw_only(), "update_direction"_a = true) + py::kw_only(), "update_direction"_a = true, + "Update the Arrow's position and optionally direction") // .def("enu2lla", py::overload_cast(&Quiver::enu2lla, py::const_), - "coords"_a) + "coords"_a, "Convert ENU coordinates to LLA") .def("enu2lla", py::overload_cast(&Quiver::enu2lla, py::const_), - "coords"_a) + "coords"_a, "Convert multiple ENU coordinates to LLA") .def("lla2enu", py::overload_cast(&Quiver::lla2enu, py::const_), - "coords"_a) + "coords"_a, "Convert LLA coordinates to ENU") .def("lla2enu", py::overload_cast(&Quiver::lla2enu, py::const_), - "coords"_a) + "coords"_a, "Convert multiple LLA coordinates to ENU") // ; // FilterParams using FilterParams = Quiver::FilterParams; py::class_(pyQuiver, "FilterParams", py::module_local()) - .def(py::init<>()) - .def("x_slots", py::overload_cast<>(&FilterParams::x_slots, py::const_)) + .def(py::init<>(), "Default constructor for FilterParams") + .def("x_slots", py::overload_cast<>(&FilterParams::x_slots, py::const_), + "Get the x slots of the FilterParams") .def("x_slots", py::overload_cast &>( &FilterParams::x_slots), - rvp::reference_internal) - .def("y_slots", py::overload_cast<>(&FilterParams::y_slots, py::const_)) + rvp::reference_internal, "Set the x slots of the FilterParams") + .def("y_slots", py::overload_cast<>(&FilterParams::y_slots, py::const_), + "Get the y slots of the FilterParams") .def("y_slots", py::overload_cast &>( &FilterParams::y_slots), - rvp::reference_internal) - .def("z_slots", py::overload_cast<>(&FilterParams::z_slots, py::const_)) + rvp::reference_internal, "Set the y slots of the FilterParams") + .def("z_slots", py::overload_cast<>(&FilterParams::z_slots, py::const_), + "Get the z slots of the FilterParams") .def("z_slots", py::overload_cast &>( &FilterParams::z_slots), - rvp::reference_internal) + rvp::reference_internal, "Set the z slots of the FilterParams") .def("angle_slots", - py::overload_cast<>(&FilterParams::angle_slots, py::const_)) + py::overload_cast<>(&FilterParams::angle_slots, py::const_), + "Get the angle slots of the FilterParams") .def("angle_slots", py::overload_cast &>( &FilterParams::angle_slots), - rvp::reference_internal) - .def("is_trivial", &FilterParams::is_trivial) + rvp::reference_internal, "Set the angle slots of the FilterParams") + .def("is_trivial", &FilterParams::is_trivial, + "Check if the FilterParams is trivial") // ; using KdQuiver = cubao::KdQuiver; py::class_(m, "KdQuiver", py::module_local()) - .def(py::init<>()) - .def(py::init(), "anchor_lla"_a) + .def(py::init<>(), "Default constructor for KdQuiver") + .def(py::init(), "anchor_lla"_a, + "Constructor for KdQuiver with anchor LLA coordinates") // add .def("add", py::overload_cast(&KdQuiver::add), - "polyline"_a, "index"_a = -1) + "polyline"_a, "index"_a = -1, "Add a polyline to the KdQuiver") .def("add", py::overload_cast &, int>( &KdQuiver::add), - "polyline"_a, "index"_a = -1) + "polyline"_a, "index"_a = -1, "Add a 2D polyline to the KdQuiver") // nearest .def("nearest", py::overload_cast( &KdQuiver::nearest, py::const_), "position"_a, py::kw_only(), // - "return_squared_l2"_a = false) + "return_squared_l2"_a = false, + "Find the nearest point to the given position") .def("nearest", py::overload_cast(&KdQuiver::nearest, py::const_), "index"_a, py::kw_only(), // - "return_squared_l2"_a = false) + "return_squared_l2"_a = false, + "Find the nearest point to the point at the given index") .def("nearest", py::overload_cast( &KdQuiver::nearest, py::const_), "position"_a, py::kw_only(), // "k"_a, // "sort"_a = true, // - "return_squared_l2"_a = false) + "return_squared_l2"_a = false, + "Find k nearest points to the given position") .def("nearest", py::overload_cast( &KdQuiver::nearest, py::const_), "position"_a, py::kw_only(), // "radius"_a, // "sort"_a = true, // - "return_squared_l2"_a = false) + "return_squared_l2"_a = false, + "Find all points within a given radius of the query position") // positions - .def("positions", py::overload_cast<>(&KdQuiver::positions, py::const_)) + .def("positions", py::overload_cast<>(&KdQuiver::positions, py::const_), + "Get all positions in the KdQuiver") .def("positions", py::overload_cast(&KdQuiver::positions, py::const_), - "indexes"_a) + "indexes"_a, "Get positions for the given indexes") // directions - .def("directions", &KdQuiver::directions, "indexes"_a) + .def("directions", &KdQuiver::directions, "indexes"_a, + "Get directions for the given indexes") // arrows - .def("arrows", &KdQuiver::arrows, "indexes"_a) + .def("arrows", &KdQuiver::arrows, "indexes"_a, + "Get arrows for the given indexes") // arrow .def("arrow", py::overload_cast(&KdQuiver::arrow, py::const_), - "point_index"_a) + "point_index"_a, "Get the arrow at the given point index") .def("arrow", py::overload_cast(&KdQuiver::arrow, py::const_), - "polyline_index"_a, "segment_index"_a) + "polyline_index"_a, "segment_index"_a, + "Get the arrow at the given polyline and segment indices") .def("arrow", py::overload_cast(&KdQuiver::arrow, py::const_), - "polyline_index"_a, "segment_index"_a, py::kw_only(), "t"_a) + "polyline_index"_a, "segment_index"_a, py::kw_only(), "t"_a, + "Get the arrow at the given polyline, segment indices, and t " + "parameter") .def("arrow", py::overload_cast(&KdQuiver::arrow, py::const_), - "polyline_index"_a, py::kw_only(), "range"_a) + "polyline_index"_a, py::kw_only(), "range"_a, + "Get the arrow at the given polyline index and range") // filter .def_static("_filter", py::overload_cast &, // @@ -252,7 +309,8 @@ CUBAO_INLINE void bind_quiver(py::module &m) "arrows"_a, // "arrow"_a, // "params"_a, // - "is_wgs84"_a = false) + "is_wgs84"_a = false, + "Filter arrows based on the given parameters") .def("filter", py::overload_cast(&KdQuiver::index, py::const_), - "point_index"_a) + "point_index"_a, "Get the index for the given point index") .def("index", py::overload_cast(&KdQuiver::index, py::const_), - "polyline_index"_a, "segment_index"_a); + "polyline_index"_a, "segment_index"_a, + "Get the index for the given polyline and segment indices"); } } // namespace cubao