Files
ViewDesignEngine/include/vde/spatial/kd_tree.h
T

189 lines
4.9 KiB
C++
Raw Normal View History

/**
* @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^(11/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