189 lines
4.9 KiB
C++
189 lines
4.9 KiB
C++
/**
|
||
* @file kd_tree.h
|
||
* @brief k-d 树 (K-Dimensional Tree) 空间划分结构
|
||
*
|
||
* 通过逐维交替分割的超平面将空间递归二分。
|
||
* 适合中规模数据集的最邻近搜索和范围查询。
|
||
*
|
||
* @ingroup spatial
|
||
*/
|
||
|
||
#pragma once
|
||
#include "vde/spatial/spatial_index.h"
|
||
#include <vector>
|
||
|
||
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<Point3D> 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 <typename T>
|
||
class KDTree : public SpatialIndex<T> {
|
||
public:
|
||
/**
|
||
* @brief 批量构建 k-d 树
|
||
*
|
||
* 递归选择分裂轴和分裂点进行自顶向下构建。
|
||
* 构建复杂度 O(n log n)。
|
||
*
|
||
* @param items 待索引的对象数组
|
||
*/
|
||
void build(const std::vector<T>& 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<T> query_range(const AABB3D& range) const override;
|
||
|
||
/**
|
||
* @brief K 近邻查询
|
||
*
|
||
* 最优优先搜索:从根节点出发,使用优先队列按距离剪枝。
|
||
* 平均 O(log n),最坏 O(n)。
|
||
*
|
||
* @param point 查询点
|
||
* @param k 返回数量
|
||
* @return 最近的 k 个对象
|
||
*/
|
||
std::vector<T> query_knn(const Point3D& point, size_t k) const override;
|
||
|
||
/**
|
||
* @brief 射线查询
|
||
*
|
||
* @param ray 查询射线
|
||
* @return 与射线相交的对象列表
|
||
*/
|
||
std::vector<T> 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<T> items_; /**< 对象数组 */
|
||
std::vector<Node> 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<T>& result) const;
|
||
|
||
/**
|
||
* @brief 递归射线查询
|
||
*
|
||
* @param node_idx 当前节点索引
|
||
* @param ray 查询射线
|
||
* @param result 累积结果
|
||
*/
|
||
void query_ray_recursive(int node_idx, const Ray3Dd& ray,
|
||
std::vector<T>& result) const;
|
||
};
|
||
|
||
} // namespace vde::spatial
|