2026-07-24 11:02:06 +00:00
|
|
|
/**
|
|
|
|
|
* @file octree.h
|
|
|
|
|
* @brief 八叉树 (Octree) 空间划分结构
|
|
|
|
|
*
|
|
|
|
|
* 将三维空间递归剖分为八个等大子立方体的树形结构。
|
|
|
|
|
* 适合点云、体素等均匀分布数据的空间查询与加速。
|
|
|
|
|
*
|
|
|
|
|
* @ingroup spatial
|
|
|
|
|
*/
|
|
|
|
|
|
2026-07-23 05:27:51 +00:00
|
|
|
#pragma once
|
2026-07-23 08:19:24 +00:00
|
|
|
#include "vde/core/aabb.h"
|
2026-07-23 05:27:51 +00:00
|
|
|
#include "vde/spatial/spatial_index.h"
|
2026-07-23 06:19:19 +00:00
|
|
|
#include <memory>
|
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-23 06:19:19 +00:00
|
|
|
template <typename T> struct OctreeNode;
|
|
|
|
|
template <typename T> struct OctreeData;
|
|
|
|
|
|
2026-07-24 11:02:06 +00:00
|
|
|
/**
|
|
|
|
|
* @brief 八叉树空间索引
|
|
|
|
|
*
|
|
|
|
|
* 将三维空间递归划分为 8 个等大的子立方体区域。
|
|
|
|
|
* 每个节点维护自身包围盒与所含对象列表。
|
|
|
|
|
* 当节点中对象数超过 max_items 且未达到最大深度时自动剖分。
|
|
|
|
|
*
|
|
|
|
|
* 构建: O(n) 逐项插入
|
|
|
|
|
* 范围查询: O(8^d + k),d 为最大深度,k 为结果数
|
|
|
|
|
* KNN 查询: O(8^d + k log k)
|
|
|
|
|
* 射线查询: O(8^d + k)
|
|
|
|
|
*
|
|
|
|
|
* @tparam T 被索引的空间对象类型
|
|
|
|
|
*
|
|
|
|
|
* @note 适用于体积数据、粒子系统、空间哈希等场景。
|
|
|
|
|
* 对非均匀分布的数据(如细长三角网格),BVH 通常更优。
|
|
|
|
|
*
|
|
|
|
|
* @code{.cpp}
|
2026-07-24 11:17:46 +00:00
|
|
|
* Octree<Point3D> octree(8, 16 // max_depth, max_items);
|
2026-07-24 11:02:06 +00:00
|
|
|
* octree.build(points);
|
|
|
|
|
* auto neighbors = octree.query_knn(query_point, 5);
|
|
|
|
|
* @endcode
|
|
|
|
|
*
|
|
|
|
|
* @see SpatialIndex, BVH, KDTree
|
|
|
|
|
*/
|
2026-07-23 05:27:51 +00:00
|
|
|
template <typename T>
|
|
|
|
|
class Octree : public SpatialIndex<T> {
|
|
|
|
|
public:
|
2026-07-24 11:02:06 +00:00
|
|
|
/**
|
|
|
|
|
* @brief 构造八叉树
|
|
|
|
|
*
|
|
|
|
|
* @param max_depth 最大递归深度(默认 8,对应 256³ 分辨率)
|
|
|
|
|
* @param max_items 节点剖分前可容纳的最大对象数(默认 16)
|
|
|
|
|
*/
|
2026-07-23 06:19:19 +00:00
|
|
|
explicit Octree(int max_depth = 8, int max_items = 16);
|
2026-07-24 11:02:06 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 批量构建八叉树
|
|
|
|
|
*
|
|
|
|
|
* 清空已有数据,将所有 items 逐个插入。
|
|
|
|
|
* 构建复杂度 O(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 增量插入对象
|
|
|
|
|
*
|
|
|
|
|
* 从根节点递归,到达容纳它的最深层节点。
|
|
|
|
|
* 若节点超出 max_items 且未达 max_depth,自动剖分。
|
|
|
|
|
*
|
|
|
|
|
* @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 删除对象
|
|
|
|
|
*
|
|
|
|
|
* @param item 待删除的空间对象
|
|
|
|
|
* @return 删除成功返回 true;对象不存在返回 false
|
|
|
|
|
*/
|
2026-07-23 05:27:51 +00:00
|
|
|
bool remove(const T& item) override;
|
2026-07-24 11:02:06 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 范围查询
|
|
|
|
|
*
|
|
|
|
|
* 从根节点递归,只访问与 range 相交的子节点。
|
|
|
|
|
*
|
|
|
|
|
* @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 近邻查询
|
|
|
|
|
*
|
|
|
|
|
* 使用优先队列按距离剪枝遍历八叉树节点。
|
|
|
|
|
*
|
|
|
|
|
* @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 清空八叉树
|
|
|
|
|
*/
|
2026-07-23 05:27:51 +00:00
|
|
|
void clear() override;
|
2026-07-24 11:02:06 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 当前索引中的对象数量
|
|
|
|
|
*
|
|
|
|
|
* @return 对象总数
|
|
|
|
|
*/
|
2026-07-23 06:19:19 +00:00
|
|
|
size_t size() const override;
|
2026-07-23 14:08:34 +00:00
|
|
|
|
2026-07-23 05:27:51 +00:00
|
|
|
private:
|
2026-07-24 11:02:06 +00:00
|
|
|
/**
|
|
|
|
|
* @brief 递归插入对象到指定节点
|
|
|
|
|
*
|
|
|
|
|
* @param node_idx 目标节点索引
|
|
|
|
|
* @param item 待插入的对象
|
|
|
|
|
* @param depth 当前递归深度
|
|
|
|
|
*/
|
2026-07-23 06:19:19 +00:00
|
|
|
void insert_recursive(int node_idx, const T& item, int depth);
|
2026-07-24 11:02:06 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 递归剖分节点
|
|
|
|
|
*
|
|
|
|
|
* 将节点等分为 8 个子立方体并分配现有对象。
|
|
|
|
|
*
|
|
|
|
|
* @param node_idx 当前节点索引
|
|
|
|
|
* @param depth 当前深度
|
|
|
|
|
*/
|
2026-07-23 06:19:19 +00:00
|
|
|
void subdivide_recursive(int node_idx, int depth);
|
2026-07-24 11:02:06 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 递归范围查询
|
|
|
|
|
*
|
|
|
|
|
* @param node_idx 当前节点索引
|
|
|
|
|
* @param range 查询包围盒
|
|
|
|
|
* @param result 累积结果
|
|
|
|
|
*/
|
2026-07-23 14:08:34 +00:00
|
|
|
void query_range_recursive(int node_idx, const AABB3D& range,
|
|
|
|
|
std::vector<T>& result) const;
|
2026-07-24 11:02:06 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 递归射线查询
|
|
|
|
|
*
|
|
|
|
|
* @param node_idx 当前节点索引
|
|
|
|
|
* @param ray 查询射线
|
|
|
|
|
* @param result 累积结果
|
|
|
|
|
*/
|
2026-07-23 14:08:34 +00:00
|
|
|
void query_ray_recursive(int node_idx, const Ray3Dd& ray,
|
|
|
|
|
std::vector<T>& result) const;
|
|
|
|
|
|
2026-07-24 11:02:06 +00:00
|
|
|
/**
|
|
|
|
|
* @brief 获取对象空间位置(用于剖分分配)
|
|
|
|
|
*
|
|
|
|
|
* @param item 空间对象
|
|
|
|
|
* @return 对象的代表点
|
|
|
|
|
*/
|
2026-07-23 14:08:34 +00:00
|
|
|
static Point3D get_position(const T& item);
|
|
|
|
|
|
2026-07-24 11:02:06 +00:00
|
|
|
std::shared_ptr<OctreeData<T>> data_; /**< 八叉树内部数据 */
|
|
|
|
|
int max_depth_; /**< 最大递归深度 */
|
|
|
|
|
int max_items_; /**< 节点剖分阈值 */
|
2026-07-23 05:27:51 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // namespace vde::spatial
|