/** * @file octree.h * @brief 八叉树 (Octree) 空间划分结构 * * 将三维空间递归剖分为八个等大子立方体的树形结构。 * 适合点云、体素等均匀分布数据的空间查询与加速。 * * @ingroup spatial */ #pragma once #include "vde/core/aabb.h" #include "vde/spatial/spatial_index.h" #include namespace vde::spatial { using core::Point3D; using core::Vector3D; using core::Ray3Dd; using core::Triangle3D; template struct OctreeNode; template 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 octree(8, 16 // max_depth, max_items); * octree.build(points); * auto neighbors = octree.query_knn(query_point, 5); * @endcode * * @see SpatialIndex, BVH, KDTree */ template class Octree : public SpatialIndex { 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& 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 query_range(const AABB3D& range) const override; /** * @brief K 近邻查询 * * 使用优先队列按距离剪枝遍历八叉树节点。 * * @param point 查询点 * @param k 返回数量 * @return 最近的 k 个对象 */ std::vector query_knn(const Point3D& point, size_t k) const override; /** * @brief 射线查询 * * 遍历被射线穿过的八叉树节点,收集其中相交对象。 * * @param ray 查询射线 * @return 按命中顺序排列的相交对象列表 */ std::vector 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& result) const; /** * @brief 递归射线查询 * * @param node_idx 当前节点索引 * @param ray 查询射线 * @param result 累积结果 */ void query_ray_recursive(int node_idx, const Ray3Dd& ray, std::vector& result) const; /** * @brief 获取对象空间位置(用于剖分分配) * * @param item 空间对象 * @return 对象的代表点 */ static Point3D get_position(const T& item); std::shared_ptr> data_; /**< 八叉树内部数据 */ int max_depth_; /**< 最大递归深度 */ int max_items_; /**< 节点剖分阈值 */ }; } // namespace vde::spatial