mesh_graph/elements/
vertex.rs1use tracing::{error, instrument};
2
3use crate::{CircularHalfedgesIterator, MeshGraph, error_none, utils::unwrap_or_return};
4
5use super::{FaceId, HalfedgeId, VertexId};
6
7#[derive(Debug, Default, Clone, Copy)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12pub struct Vertex {
13 pub outgoing_halfedge: Option<HalfedgeId>,
20}
21
22impl Vertex {
23 #[instrument(skip(mesh_graph))]
27 pub fn incoming_halfedge(&self, mesh_graph: &MeshGraph) -> Option<HalfedgeId> {
28 mesh_graph
29 .halfedges
30 .get(
31 self.outgoing_halfedge
32 .or_else(error_none!("Vertex has no outgoing halfedge"))?,
33 )
34 .or_else(error_none!("Outgoing halfedge not found"))
35 .and_then(|halfedge| halfedge.twin.or_else(error_none!("Twin is None")))
36 }
37
38 #[instrument(skip(mesh_graph))]
42 pub fn outgoing_halfedges<'a>(
43 &self,
44 mesh_graph: &'a MeshGraph,
45 ) -> CircularHalfedgesIterator<'a> {
46 CircularHalfedgesIterator::new(
47 self.outgoing_halfedge,
48 mesh_graph,
49 |he, mesh_graph| {
50 mesh_graph
51 .halfedges
52 .get(he)
53 .or_else(error_none!("Halfedge is None"))?
54 .cw_rotated_neighbour(mesh_graph)
55 },
56 100_000,
57 )
58 }
59
60 #[instrument(skip(mesh_graph))]
64 pub fn incoming_halfedges(&self, mesh_graph: &MeshGraph) -> impl Iterator<Item = HalfedgeId> {
65 self.outgoing_halfedges(mesh_graph).filter_map(|he_id| {
66 mesh_graph
67 .halfedges
68 .get(he_id)
69 .or_else(error_none!("Halfedge is None"))?
70 .twin
71 .or_else(error_none!("Twin is None"))
72 })
73 }
74
75 #[instrument(skip(mesh_graph))]
79 pub fn faces(&self, mesh_graph: &MeshGraph) -> impl Iterator<Item = FaceId> {
80 self.outgoing_halfedges(mesh_graph).filter_map(|he| {
81 mesh_graph
82 .halfedges
83 .get(he)
84 .or_else(error_none!("Halfedge is None"))?
85 .face
86 })
87 }
88
89 #[instrument(skip(mesh_graph))]
93 pub fn neighbours(&self, mesh_graph: &MeshGraph) -> impl Iterator<Item = VertexId> {
94 self.outgoing_halfedges(mesh_graph).filter_map(|he| {
95 mesh_graph
96 .halfedges
97 .get(he)
98 .or_else(error_none!("Halfedge is None"))
99 .map(|he| he.end_vertex)
100 })
101 }
102
103 #[inline]
105 #[instrument(skip(mesh_graph))]
106 pub fn degree(&self, mesh_graph: &MeshGraph) -> usize {
107 self.neighbours(mesh_graph).count()
108 }
109
110 #[instrument(skip(mesh_graph))]
112 pub fn is_boundary(&self, mesh_graph: &MeshGraph) -> bool {
113 if let Some(outgoing_he) = self.outgoing_halfedge {
114 mesh_graph
115 .halfedges
116 .get(outgoing_he)
117 .or_else(error_none!("Outgoing halfedge not found"))
118 .map(|he| he.is_boundary())
119 .unwrap_or(false)
120 } else {
121 error!("Outgoing halfedge is None");
122 false
123 }
124 }
125
126 #[instrument(skip(mesh_graph))]
131 pub fn one_ring(&self, mesh_graph: &MeshGraph) -> impl Iterator<Item = HalfedgeId> {
132 self.incoming_halfedges(mesh_graph)
133 .collect::<Vec<_>>()
134 .into_iter()
135 .rev()
136 .filter_map(|he| {
137 mesh_graph
138 .halfedges
139 .get(he)
140 .or_else(error_none!("Outgoing halfedge not found"))?
141 .cw_rotated_neighbour(mesh_graph)
142 })
143 }
144
145 pub fn boundary_halfedgdes<'a>(
149 &self,
150 mesh_graph: &'a MeshGraph,
151 ) -> CircularHalfedgesIterator<'a> {
152 let Some(out_he_id) = self.outgoing_halfedge else {
153 return CircularHalfedgesIterator::empty(mesh_graph);
154 };
155
156 let he = unwrap_or_return!(
157 mesh_graph.halfedges.get(out_he_id),
158 "Halfedge not found",
159 CircularHalfedgesIterator::empty(mesh_graph)
160 );
161
162 if !he.is_boundary() {
163 return CircularHalfedgesIterator::empty(mesh_graph);
164 }
165
166 CircularHalfedgesIterator::new(
167 self.outgoing_halfedge,
168 mesh_graph,
169 |he_id, mesh_graph| {
170 let end_v_id = mesh_graph
171 .halfedges
172 .get(he_id)
173 .or_else(error_none!("Halfedge not found"))?
174 .end_vertex;
175 let next_he_id = mesh_graph
176 .vertices
177 .get(end_v_id)
178 .or_else(error_none!("Vertex not found"))?
179 .outgoing_halfedge?;
180
181 let next_he = mesh_graph
182 .halfedges
183 .get(next_he_id)
184 .or_else(error_none!("Halfedge not found"))?;
185
186 if next_he.is_boundary() {
187 Some(next_he_id)
188 } else {
189 None
190 }
191 },
192 usize::MAX,
193 )
194 }
195}