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}