/** * @file kd_tree.h * @brief k-d 树 (K-Dimensional Tree) 空间划分结构 * * 通过逐维交替分割的超平面将空间递归二分。 * 适合中规模数据集的最邻近搜索和范围查询。 * * @ingroup spatial */ #pragma once #include "vde/spatial/spatial_index.h" #include namespace vde::spatial { using core::AABB3D; using core::Point3D; using core::Vector3D; using core::Ray3Dd; using core::Triangle3D; /** * @brief k-d 树空间索引 * * 在每一层选择一个坐标轴,以该轴上中位数为分割面, * 将空间划分为两个半空间,递归构建二叉树。 * * 构建: O(n log n) 使用中位数分区 * 范围查询: O(n^(1−1/d) + k),3D 下约为 O(n^(2/3) + k) * KNN 查询: O(log n) 平均,最坏 O(n) * 射线查询: O(n^(2/3) + k) 近似 * * @tparam T 被索引的空间对象类型 * * @note k-d 树在维度约 2~6 时效率最佳。三维场景中, * BVH 对于射线查询更高效,Octree 对于均匀体数据更优。 * * @code{.cpp} * KDTree kdtree; * kdtree.build(points); * auto nearest = kdtree.query_knn(query_point, 1); * auto in_range = kdtree.query_range(AABB3D::from_minmax(min, max)); * @endcode * * @see SpatialIndex, BVH, Octree */ template class KDTree : public SpatialIndex { public: /** * @brief 批量构建 k-d 树 * * 递归选择分裂轴和分裂点进行自顶向下构建。 * 构建复杂度 O(n log n)。 * * @param items 待索引的对象数组 */ void build(const std::vector& items) override; /** * @brief 插入单个对象 * * @param item 待插入的空间对象 */ void insert(const T& item) override; /** * @brief 删除对象 * * @param item 待删除的空间对象 * @return 删除成功返回 true */ bool remove(const T& item) override; /** * @brief 范围查询 * * 遍历与查询 AABB 相交的 kd 树节点。 * * @param range 查询包围盒 * @return 与 range 相交的对象列表 */ std::vector query_range(const AABB3D& range) const override; /** * @brief K 近邻查询 * * 最优优先搜索:从根节点出发,使用优先队列按距离剪枝。 * 平均 O(log n),最坏 O(n)。 * * @param point 查询点 * @param k 返回数量 * @return 最近的 k 个对象 */ std::vector query_knn(const Point3D& point, size_t k) const override; /** * @brief 射线查询 * * @param ray 查询射线 * @return 与射线相交的对象列表 */ std::vector query_ray(const Ray3Dd& ray) const override; /** * @brief 清空 k-d 树 */ void clear() override; /** * @brief 对象数量 * * @return 当前索引的对象总数 */ size_t size() const override { return items_.size(); } private: /** * @brief k-d 树节点 */ struct Node { AABB3D bounds; /**< 节点包围盒 */ int item_idx = -1; /**< 叶节点对应的对象索引(-1 表示内部节点) */ int split_axis = 0; /**< 分裂轴:0=x, 1=y, 2=z */ int left = -1; /**< 左子节点索引 */ int right = -1; /**< 右子节点索引 */ }; std::vector items_; /**< 对象数组 */ std::vector nodes_; /**< 节点数组 */ int root_ = -1; /**< 根节点索引 */ /** * @brief 获取对象代表位置(用于空间排序) * * @param item 空间对象 * @return 对象的三维代表点 */ static Point3D get_position(const T& item); /** * @brief 获取对象的轴对齐包围盒 * * @param item 空间对象 * @return 包围盒 */ static AABB3D get_aabb(const T& item); /** * @brief 判断对象是否与射线相交 * * @param item 空间对象 * @param ray 查询射线 * @return 相交返回 true */ static bool ray_hits_item(const T& item, const Ray3Dd& ray); /** * @brief 递归构建 k-d 树 * * @param start items_ 中当前范围起始索引 * @param end items_ 中当前范围结束索引 * @return 新节点的索引 */ int build_recursive(int start, int end); /** * @brief 递归范围查询 * * @param node_idx 当前节点索引 * @param range 查询包围盒 * @param result 累积结果 */ void query_range_recursive(int node_idx, const AABB3D& range, std::vector& result) const; /** * @brief 递归射线查询 * * @param node_idx 当前节点索引 * @param ray 查询射线 * @param result 累积结果 */ void query_ray_recursive(int node_idx, const Ray3Dd& ray, std::vector& result) const; }; } // namespace vde::spatial