2026-07-24 11:02:06 +00:00
|
|
|
/**
|
|
|
|
|
* @file r_tree.h
|
|
|
|
|
* @brief R 树 (R-Tree) 动态空间索引
|
|
|
|
|
*
|
|
|
|
|
* 高度平衡的树形结构,每个节点存储其子节点的最小包围矩形(MBR)。
|
|
|
|
|
* 适合需要频繁插入和删除的动态数据集。
|
|
|
|
|
*
|
|
|
|
|
* @ingroup spatial
|
|
|
|
|
*/
|
|
|
|
|
|
2026-07-23 05:27:51 +00:00
|
|
|
#pragma once
|
|
|
|
|
#include "vde/spatial/spatial_index.h"
|
2026-07-23 06:19:19 +00:00
|
|
|
#include "vde/core/aabb.h"
|
2026-07-23 12:36:39 +00:00
|
|
|
#include <queue>
|
|
|
|
|
#include <limits>
|
2026-07-23 05:27:51 +00:00
|
|
|
|
|
|
|
|
namespace vde::spatial {
|
2026-07-23 08:19:24 +00:00
|
|
|
using core::Point3D;
|
|
|
|
|
using core::Vector3D;
|
|
|
|
|
using core::Ray3Dd;
|
|
|
|
|
using core::Triangle3D;
|
2026-07-23 05:27:51 +00:00
|
|
|
|
2026-07-24 11:02:06 +00:00
|
|
|
/**
|
|
|
|
|
* @brief R 树节点
|
|
|
|
|
*
|
|
|
|
|
* 内部节点存储子节点索引列表;叶节点存储对象索引列表。
|
|
|
|
|
* 每个节点维护其所有子条目的最小包围盒。
|
|
|
|
|
*
|
|
|
|
|
* @tparam T 被索引的空间对象类型
|
|
|
|
|
*/
|
2026-07-23 12:36:39 +00:00
|
|
|
template <typename T>
|
|
|
|
|
struct RTreeNode {
|
2026-07-24 11:02:06 +00:00
|
|
|
AABB3D bbox; /**< 节点包围盒(子节点/对象 MBR 的并集) */
|
|
|
|
|
bool is_leaf = true; /**< 是否为叶节点 */
|
|
|
|
|
std::vector<size_t> children; /**< 内部节点:子节点索引列表 */
|
|
|
|
|
std::vector<size_t> item_indices; /**< 叶节点:对象索引列表 */
|
|
|
|
|
size_t parent = static_cast<size_t>(-1); /**< 父节点索引 */
|
2026-07-23 12:36:39 +00:00
|
|
|
};
|
|
|
|
|
|
2026-07-24 11:02:06 +00:00
|
|
|
/**
|
|
|
|
|
* @brief R 树动态空间索引
|
|
|
|
|
*
|
|
|
|
|
* R 树是一种高度平衡的树,所有叶节点位于同一层。
|
|
|
|
|
* 插入时选择包围盒扩展最小的子节点;溢出时分裂节点。
|
|
|
|
|
* 支持增量插入和删除,适合数据集频繁变化的场景。
|
|
|
|
|
*
|
|
|
|
|
* 构建: O(n log n)(逐项插入)
|
|
|
|
|
* 范围查询: O(log n + k)
|
|
|
|
|
* KNN 查询: O(log n + k log k)
|
|
|
|
|
* 插入: O(log n)
|
|
|
|
|
* 删除: O(log n)
|
|
|
|
|
*
|
|
|
|
|
* @tparam T 被索引的空间对象类型
|
|
|
|
|
*
|
|
|
|
|
* @note R 树在动态场景(如编辑中的几何体)中比 BVH 更合适。
|
|
|
|
|
* BVH 查询效率更高但构建后不易修改。
|
|
|
|
|
*
|
|
|
|
|
* @code{.cpp}
|
|
|
|
|
* RTree<Point3D> rtree;
|
|
|
|
|
* rtree.build(points);
|
|
|
|
|
* rtree.insert(new_point); // 增量插入
|
|
|
|
|
* auto nearby = rtree.query_range(search_box);
|
|
|
|
|
* @endcode
|
|
|
|
|
*
|
|
|
|
|
* @see SpatialIndex, BVH, RStarTree
|
|
|
|
|
*/
|
2026-07-23 05:27:51 +00:00
|
|
|
template <typename T>
|
|
|
|
|
class RTree : public SpatialIndex<T> {
|
|
|
|
|
public:
|
2026-07-24 11:02:06 +00:00
|
|
|
/**
|
|
|
|
|
* @brief 批量构建 R 树
|
|
|
|
|
*
|
|
|
|
|
* 清空已有数据,将所有 items 逐个插入。
|
|
|
|
|
* 构建复杂度 O(n log n)。
|
|
|
|
|
*
|
|
|
|
|
* @param items 待索引的对象数组
|
|
|
|
|
*/
|
2026-07-23 05:27:51 +00:00
|
|
|
void build(const std::vector<T>& items) override;
|
2026-07-24 11:02:06 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 增量插入对象
|
|
|
|
|
*
|
|
|
|
|
* 从根节点出发选择包围盒扩展最小的子节点递归插入。
|
|
|
|
|
* 节点溢出时分裂。复杂度 O(log n)。
|
|
|
|
|
*
|
|
|
|
|
* @param item 待插入的空间对象
|
|
|
|
|
*/
|
2026-07-23 05:27:51 +00:00
|
|
|
void insert(const T& item) override;
|
2026-07-24 11:02:06 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 删除对象
|
|
|
|
|
*
|
|
|
|
|
* 定位到所在叶节点后移除。
|
|
|
|
|
* 复杂度 O(log n)。
|
|
|
|
|
*
|
|
|
|
|
* @param item 待删除的空间对象
|
|
|
|
|
* @return 删除成功返回 true
|
|
|
|
|
*/
|
2026-07-23 05:27:51 +00:00
|
|
|
bool remove(const T& item) override;
|
2026-07-24 11:02:06 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 范围查询
|
|
|
|
|
*
|
|
|
|
|
* 从根节点遍历,仅访问与 range 相交的子节点。
|
|
|
|
|
* 复杂度 O(log n + k)。
|
|
|
|
|
*
|
|
|
|
|
* @param range 查询包围盒
|
|
|
|
|
* @return 与 range 相交的对象列表
|
|
|
|
|
*/
|
2026-07-23 05:27:51 +00:00
|
|
|
std::vector<T> query_range(const AABB3D& range) const override;
|
2026-07-24 11:02:06 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief K 近邻查询
|
|
|
|
|
*
|
|
|
|
|
* 使用优先队列按 MBR 最小距离剪枝。
|
|
|
|
|
*
|
|
|
|
|
* @param point 查询点
|
|
|
|
|
* @param k 返回数量
|
|
|
|
|
* @return 最近的 k 个对象
|
|
|
|
|
*/
|
2026-07-23 05:27:51 +00:00
|
|
|
std::vector<T> query_knn(const Point3D& point, size_t k) const override;
|
2026-07-24 11:02:06 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 射线查询
|
|
|
|
|
*
|
|
|
|
|
* 从根节点递归,遍历被射线穿过的子节点。
|
|
|
|
|
*
|
|
|
|
|
* @param ray 查询射线
|
|
|
|
|
* @return 与射线相交的对象列表
|
|
|
|
|
*/
|
2026-07-23 05:27:51 +00:00
|
|
|
std::vector<T> query_ray(const Ray3Dd& ray) const override;
|
2026-07-24 11:02:06 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 清空 R 树
|
|
|
|
|
*/
|
2026-07-23 05:27:51 +00:00
|
|
|
void clear() override;
|
2026-07-24 11:02:06 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 对象数量
|
|
|
|
|
*
|
|
|
|
|
* @return 当前索引的对象总数
|
|
|
|
|
*/
|
2026-07-23 05:27:51 +00:00
|
|
|
size_t size() const override { return count_; }
|
2026-07-24 11:02:06 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief R 树节点总数
|
|
|
|
|
*
|
|
|
|
|
* @return 内部节点 + 叶节点数量
|
|
|
|
|
*/
|
2026-07-23 12:36:39 +00:00
|
|
|
size_t node_count() const { return nodes_.size(); }
|
|
|
|
|
|
2026-07-23 05:27:51 +00:00
|
|
|
private:
|
2026-07-24 11:02:06 +00:00
|
|
|
/**
|
|
|
|
|
* @brief 递归范围查询
|
|
|
|
|
*
|
|
|
|
|
* @param node_idx 当前节点索引
|
|
|
|
|
* @param range 查询包围盒
|
|
|
|
|
* @param result 累积结果
|
|
|
|
|
*/
|
2026-07-23 12:36:39 +00:00
|
|
|
void query_range_recursive(size_t node_idx, const AABB3D& range,
|
|
|
|
|
std::vector<T>& result) const;
|
|
|
|
|
|
2026-07-24 11:02:06 +00:00
|
|
|
std::vector<T> items_; /**< 对象数组 */
|
|
|
|
|
std::vector<AABB3D> item_bounds_; /**< 对象包围盒数组 */
|
|
|
|
|
std::vector<RTreeNode<T>> nodes_; /**< R 树节点数组 */
|
|
|
|
|
size_t root_idx_ = 0; /**< 根节点索引 */
|
|
|
|
|
size_t count_ = 0; /**< 对象计数 */
|
2026-07-23 05:27:51 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // namespace vde::spatial
|