#pragma once #include "vde/core/point.h" #include "vde/core/aabb.h" #include #include namespace vde::mesh { struct Halfedge { int vertex_index = -1; int face_index = -1; int next_index = -1; int prev_index = -1; int opposite_index = -1; }; struct Face { int halfedge_index = -1; int valence = 3; }; class HalfedgeMesh { public: HalfedgeMesh() = default; // ── Construction ── void clear(); int add_vertex(const Point3D& p); int add_face(const std::vector& vertex_indices); void build_from_triangles(const std::vector& verts, const std::vector>& tris); // ── Accessors ── [[nodiscard]] size_t num_vertices() const { return vertices_.size(); } [[nodiscard]] size_t num_faces() const { return faces_.size(); } [[nodiscard]] size_t num_edges() const { return halfedges_.size() / 2; } [[nodiscard]] const Point3D& vertex(size_t idx) const { return vertices_[idx]; } [[nodiscard]] const Face& face(size_t idx) const { return faces_[idx]; } [[nodiscard]] const Halfedge& halfedge(size_t idx) const { return halfedges_[idx]; } void set_vertex(size_t idx, const Point3D& p) { vertices_[idx] = p; normals_dirty_ = true; } // ── Topology queries ── [[nodiscard]] std::vector face_vertices(int fi) const; [[nodiscard]] std::vector vertex_faces(int vi) const; [[nodiscard]] bool is_boundary_edge(int hei) const { return halfedges_[hei].face_index < 0; } [[nodiscard]] bool is_boundary_vertex(int vi) const; // ── Normals ── [[nodiscard]] Vector3D face_normal(int fi) const; [[nodiscard]] Vector3D vertex_normal(int vi) const; void update_normals(); // ── Bounds ── [[nodiscard]] AABB3D bounds() const; // ── Iterators for range-based for ── class FaceRange; class VertexOneRing; [[nodiscard]] FaceRange faces_range() const; [[nodiscard]] VertexOneRing vertex_one_ring(int vi) const; private: std::vector vertices_; std::vector halfedges_; std::vector faces_; std::vector face_normals_; std::vector vertex_normals_; bool normals_dirty_ = true; int find_or_create_edge(int v0, int v1); }; } // namespace vde::mesh