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

186 lines
4.9 KiB
C++

/**
* @file octree.h
* @brief 八叉树 (Octree) 空间划分结构
*
* 将三维空间递归剖分为八个等大子立方体的树形结构。
* 适合点云、体素等均匀分布数据的空间查询与加速。
*
* @ingroup spatial
*/
#pragma once
#include "vde/core/aabb.h"
#include "vde/spatial/spatial_index.h"
#include <memory>
namespace vde::spatial {
using core::Point3D;
using core::Vector3D;
using core::Ray3Dd;
using core::Triangle3D;
template <typename T> struct OctreeNode;
template <typename T> struct OctreeData;
/**
* @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}
* Octree<Point3D> octree(/*max_depth=*/8, /*max_items=*/16);
* octree.build(points);
* auto neighbors = octree.query_knn(query_point, 5);
* @endcode
*
* @see SpatialIndex, BVH, KDTree
*/
template <typename T>
class Octree : public SpatialIndex<T> {
public:
/**
* @brief 构造八叉树
*
* @param max_depth 最大递归深度(默认 8,对应 256³ 分辨率)
* @param max_items 节点剖分前可容纳的最大对象数(默认 16)
*/
explicit Octree(int max_depth = 8, int max_items = 16);
/**
* @brief 批量构建八叉树
*
* 清空已有数据,将所有 items 逐个插入。
* 构建复杂度 O(n)。
*
* @param items 待索引的对象数组
*/
void build(const std::vector<T>& items) override;
/**
* @brief 增量插入对象
*
* 从根节点递归,到达容纳它的最深层节点。
* 若节点超出 max_items 且未达 max_depth,自动剖分。
*
* @param item 待插入的空间对象
*/
void insert(const T& item) override;
/**
* @brief 删除对象
*
* @param item 待删除的空间对象
* @return 删除成功返回 true;对象不存在返回 false
*/
bool remove(const T& item) override;
/**
* @brief 范围查询
*
* 从根节点递归,只访问与 range 相交的子节点。
*
* @param range 查询包围盒
* @return 与 range 相交的对象列表
*/
std::vector<T> query_range(const AABB3D& range) const override;
/**
* @brief K 近邻查询
*
* 使用优先队列按距离剪枝遍历八叉树节点。
*
* @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 清空八叉树
*/
void clear() override;
/**
* @brief 当前索引中的对象数量
*
* @return 对象总数
*/
size_t size() const override;
private:
/**
* @brief 递归插入对象到指定节点
*
* @param node_idx 目标节点索引
* @param item 待插入的对象
* @param depth 当前递归深度
*/
void insert_recursive(int node_idx, const T& item, int depth);
/**
* @brief 递归剖分节点
*
* 将节点等分为 8 个子立方体并分配现有对象。
*
* @param node_idx 当前节点索引
* @param depth 当前深度
*/
void subdivide_recursive(int node_idx, int depth);
/**
* @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;
/**
* @brief 获取对象空间位置(用于剖分分配)
*
* @param item 空间对象
* @return 对象的代表点
*/
static Point3D get_position(const T& item);
std::shared_ptr<OctreeData<T>> data_; /**< 八叉树内部数据 */
int max_depth_; /**< 最大递归深度 */
int max_items_; /**< 节点剖分阈值 */
};
} // namespace vde::spatial