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

189 lines
4.9 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @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