Files
ViewDesignEngine/include/vde/mesh/halfedge_mesh.h
T

253 lines
7.7 KiB
C++
Raw Normal View History

#pragma once
#include "vde/core/point.h"
#include "vde/core/aabb.h"
#include <vector>
#include <cstdint>
namespace vde::mesh {
using core::AABB3D;
using core::Vector3D;
using core::Point3D;
/**
* @brief 半边数据结构元素
*
* 每条半边存储:起点顶点索引、所属面索引、前驱/后继/对侧半边索引。
* 对侧半边通过 opposite_index 配对,相邻两半边索引总是 {2k, 2k+1}。
*
* @ingroup mesh
*/
struct Halfedge {
int vertex_index = -1; ///< 半边起点(终点 = next 半边的起点)
int face_index = -1; ///< 所属面的索引(边界半边为 -1)
int next_index = -1; ///< 沿面的下一条半边
int prev_index = -1; ///< 沿面的上一条半边
int opposite_index = -1; ///< 对侧半边索引(反方向半边)
};
/**
* @brief 面元素
*
* 存储面的起始半边索引和边数(阶)。
*
* @ingroup mesh
*/
struct Face {
int halfedge_index = -1; ///< 该面的任意一条半边
int valence = 3; ///< 边数(三角面 = 3
};
/**
* @brief 半边数据结构三角网格
*
* 核心拓扑数据结构,支持 O(1) 访问面的邻面、顶点的邻面环、
* 边界检测等拓扑查询。数据内部一致性由构造方法保证。
*
* 数据成员:
* - vertices_: 顶点位置数组
* - halfedges_: 半边数组,相邻索引为对侧半边(0↔1, 2↔3, ...)
* - faces_: 面数组,每个面对应一条起始半边
* - face_normals_ / vertex_normals_: 延迟计算的法向缓存
*
* @ingroup mesh
*/
class HalfedgeMesh {
public:
HalfedgeMesh() = default;
// ── Construction ──────────────────────────────────────
/**
* @brief 清空所有数据
*/
void clear();
/**
* @brief 添加顶点
* @param p 3D 坐标
* @return 新顶点的索引(0-based
*/
int add_vertex(const Point3D& p);
/**
* @brief 添加面
* @param vertex_indices 按逆时针顺序排列的顶点索引(至少 3 个)
* @return 新面的索引,失败返回 -1
* @note 自动创建/查找半边并设置对侧关系;面法向遵循右手定则
*/
int add_face(const std::vector<int>& vertex_indices);
/**
* @brief 从三角形列表批量构建半边形网格
* @param verts 顶点位置数组
* @param tris 三角形索引数组,每个 {i0,i1,i2}
* @note 等价于反复调用 add_vertex + add_face,但内部做批处理优化
* @code{.cpp}
* HalfedgeMesh mesh;
* mesh.build_from_triangles(vertices, triangles);
* @endcode
*/
void build_from_triangles(const std::vector<Point3D>& verts,
const std::vector<std::array<int, 3>>& tris);
// ── Accessors ─────────────────────────────────────────
/**
* @brief 顶点数量
* @return vertices_ 大小
*/
[[nodiscard]] size_t num_vertices() const { return vertices_.size(); }
/**
* @brief 面数量
* @return faces_ 大小
*/
[[nodiscard]] size_t num_faces() const { return faces_.size(); }
/**
* @brief 边数量
* @return halfedges_.size() / 2
*/
[[nodiscard]] size_t num_edges() const { return halfedges_.size() / 2; }
/**
* @brief 访问顶点
* @param idx 顶点索引,0 ≤ idx < num_vertices()
* @return 顶点坐标只读引用
*/
[[nodiscard]] const Point3D& vertex(size_t idx) const { return vertices_[idx]; }
/**
* @brief 访问面
* @param idx 面索引
* @return Face 只读引用
*/
[[nodiscard]] const Face& face(size_t idx) const { return faces_[idx]; }
/**
* @brief 访问半边
* @param idx 半边索引
* @return Halfedge 只读引用
*/
[[nodiscard]] const Halfedge& halfedge(size_t idx) const { return halfedges_[idx]; }
/**
* @brief 更新顶点位置
* @param idx 顶点索引
* @param p 新坐标
* @note 标记法向缓存为脏,下次查询时重新计算
*/
void set_vertex(size_t idx, const Point3D& p) { vertices_[idx] = p; normals_dirty_ = true; }
// ── Topology queries ──────────────────────────────────
/**
* @brief 获取面的所有顶点索引(按环绕顺序)
* @param fi 面索引
* @return 顶点索引序列
*/
[[nodiscard]] std::vector<int> face_vertices(int fi) const;
/**
* @brief 获取顶点的邻面索引环
* @param vi 顶点索引
* @return 所有以 vi 为顶点的面索引
*/
[[nodiscard]] std::vector<int> vertex_faces(int vi) const;
/**
* @brief 是否为边界半边
* @param hei 半边索引
* @return 没有所属面时为 true
*/
[[nodiscard]] bool is_boundary_edge(int hei) const { return halfedges_[hei].face_index < 0; }
/**
* @brief 是否为边界顶点
* @param vi 顶点索引
* @return 邻接任何边界半边时为 true
*/
[[nodiscard]] bool is_boundary_vertex(int vi) const;
// ── Normals ───────────────────────────────────────────
/**
* @brief 面法向(几何法向,非归一化?由实现决定)
* @param fi 面索引
* @return (v1-v0)×(v2-v0) 归一化结果
*/
[[nodiscard]] Vector3D face_normal(int fi) const;
/**
* @brief 顶点法向(邻面法向的面积加权平均)
* @param vi 顶点索引
* @return 归一化顶点法向
*/
[[nodiscard]] Vector3D vertex_normal(int vi) const;
/**
* @brief 重新计算所有面法向和顶点法向
* @note 修改顶点位置后自动标记脏位,调用此方法触发重新计算
*/
void update_normals();
// ── Bounds ────────────────────────────────────────────
/**
* @brief 网格包围盒
* @return AABB3D 轴对齐包围盒
*/
[[nodiscard]] AABB3D bounds() const;
// ── Iterators for range-based for ─────────────────────
/**
* @brief 面迭代器范围(支持 for-range)
*/
class FaceRange;
/**
* @brief 顶点单邻环迭代器
*/
class VertexOneRing;
/**
* @brief 获取所有面范围(range-based for 支持)
* @return FaceRange 对象
* @code{.cpp}
* for (auto& f : mesh.faces_range()) { ... }
* @endcode
*/
[[nodiscard]] FaceRange faces_range() const;
/**
* @brief 获取顶点的 1-ring 邻域迭代器
* @param vi 顶点索引
* @return VertexOneRing 对象
* @code{.cpp}
* for (int vj : mesh.vertex_one_ring(vi)) {
* // vj 是 vi 的直接邻居
* }
* @endcode
*/
[[nodiscard]] VertexOneRing vertex_one_ring(int vi) const;
private:
std::vector<Point3D> vertices_; ///< 顶点位置
std::vector<Halfedge> halfedges_; ///< 半边数组(成对存储)
std::vector<Face> faces_; ///< 面数组
std::vector<Vector3D> face_normals_; ///< 面法向缓存
std::vector<Vector3D> vertex_normals_; ///< 顶点法向缓存
bool normals_dirty_ = true; ///< 法向脏标记
/**
* @brief 查找或创建半边 (v0→v1)
* @param v0 起点顶点索引
* @param v1 终点顶点索引
* @return 半边索引
*/
int find_or_create_edge(int v0, int v1);
};
} // namespace vde::mesh