mesh_graph/elements/
halfedge.rs

1use crate::MeshGraph;
2
3use super::{FaceId, HalfedgeId, VertexId};
4
5/// A directional edge that points from one vertex to another and is (optionally) part of a face.
6/// If it's not part of a face, it's called a boundary halfedge.
7///
8/// <img src="https://raw.githubusercontent.com/Synphonyte/mesh-graph/refs/heads/main/docs/halfedge/all.svg" alt="Connectivity" style="max-width: 28em" />
9#[derive(Debug, Clone, Copy)]
10pub struct Halfedge {
11    /// The vertex that this halfedge points to.
12    ///
13    /// <img src="https://raw.githubusercontent.com/Synphonyte/mesh-graph/refs/heads/main/docs/halfedge/end_vertex.svg" alt="Connectivity" style="max-width: 28em" />
14    pub end_vertex: VertexId,
15
16    /// The face associated to this halfedge. `None` if `self` is a boundary halfedge.
17    ///
18    /// <img src="https://raw.githubusercontent.com/Synphonyte/mesh-graph/refs/heads/main/docs/halfedge/face.svg" alt="Connectivity" style="max-width: 28em" />
19    pub face: Option<FaceId>,
20
21    /// This is the halfedge opposite.
22    /// It points backwards compared to this halfedge (from end_vertex to start_vertex).
23    /// After the mesh graph is constructed, this field is always `Some(...)`, meaning
24    /// that every halfedge has a twin.
25    ///
26    /// <img src="https://raw.githubusercontent.com/Synphonyte/mesh-graph/refs/heads/main/docs/halfedge/twin.svg" alt="Connectivity" style="max-width: 28em" />
27    pub twin: Option<HalfedgeId>,
28
29    /// The next halfedge in the face. `None` if `self` is a boundary halfedge.
30    ///
31    /// <img src="https://raw.githubusercontent.com/Synphonyte/mesh-graph/refs/heads/main/docs/halfedge/next.svg" alt="Connectivity" style="max-width: 28em" />
32    pub next: Option<HalfedgeId>,
33}
34
35impl Halfedge {
36    /// Start vertex from which this halfedge points away
37    ///
38    /// <img src="https://raw.githubusercontent.com/Synphonyte/mesh-graph/refs/heads/main/docs/halfedge/start_vertex.svg" alt="Connectivity" style="max-width: 28em" />
39    pub fn start_vertex(&self, mesh_graph: &MeshGraph) -> VertexId {
40        mesh_graph.halfedges[self.twin()].end_vertex
41    }
42
43    /// Same as the field `twin` but expects there to be a `Some` which is the case if
44    /// the mesh graph is constructed correctly.
45    ///
46    /// <img src="https://raw.githubusercontent.com/Synphonyte/mesh-graph/refs/heads/main/docs/halfedge/twin.svg" alt="Connectivity" style="max-width: 28em" />
47    #[inline]
48    pub fn twin(&self) -> HalfedgeId {
49        self.twin.expect("Twin should be connected by now")
50    }
51
52    /// Previous halfedge that shares the same face. `None` if `self` is a boundary halfedge.
53    ///
54    /// <img src="https://raw.githubusercontent.com/Synphonyte/mesh-graph/refs/heads/main/docs/halfedge/prev.svg" alt="Connectivity" style="max-width: 28em" />
55    pub fn prev(&self, mesh_graph: &MeshGraph) -> Option<HalfedgeId> {
56        // TODO : this only works for triangle meshes
57        self.next
58            .map(|next_id| mesh_graph.halfedges[next_id].next.unwrap())
59    }
60
61    /// In counter-clockwise order next halfedge that has the same start vertex
62    ///
63    /// <img src="https://raw.githubusercontent.com/Synphonyte/mesh-graph/refs/heads/main/docs/halfedge/ccw_rotated_neighbour.svg" alt="Connectivity" style="max-width: 28em" />
64    pub fn ccw_rotated_neighbour(&self, mesh_graph: &MeshGraph) -> Option<HalfedgeId> {
65        self.prev(mesh_graph)
66            .map(|prev| mesh_graph.halfedges[prev].twin())
67    }
68
69    /// In clockwise order next halfedge that has the same start vertex
70    ///
71    /// <img src="https://raw.githubusercontent.com/Synphonyte/mesh-graph/refs/heads/main/docs/halfedge/cw_rotated_neighbour.svg" alt="Connectivity" style="max-width: 28em" />
72    pub fn cw_rotated_neighbour(&self, mesh_graph: &MeshGraph) -> Option<HalfedgeId> {
73        mesh_graph.halfedges[self.twin()].next
74    }
75
76    /// Length of the halfedge squared.
77    pub fn length_squared(&self, mesh_graph: &MeshGraph) -> f32 {
78        let start = mesh_graph.positions[self.start_vertex(mesh_graph)];
79        let end = mesh_graph.positions[self.end_vertex];
80
81        start.distance_squared(end)
82    }
83
84    /// Returns `true` if there is no face adjacent to this halfedge.
85    #[inline]
86    pub fn is_boundary(&self) -> bool {
87        self.face.is_none()
88    }
89
90    /// Returns `true` if the start or end vertex is a boundary vertex.
91    pub fn is_adjacent_to_boundary(&self, mesh_graph: &MeshGraph) -> bool {
92        mesh_graph.vertices[self.start_vertex(mesh_graph)].is_boundary(mesh_graph)
93            || mesh_graph.vertices[self.end_vertex].is_boundary(mesh_graph)
94    }
95}