1use core::fmt;
5
6use axiolid_core::Point3;
7
8use super::{HalfedgeMesh, Link, VertexId, NONE};
9
10#[non_exhaustive]
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub enum HalfedgeBuildError {
17 IncompleteTriangle {
19 index_count: usize,
21 },
22 FaceTooSmall {
24 face: usize,
26 corners: usize,
28 },
29 IndexOutOfRange {
31 face: usize,
33 index: u32,
35 position_count: usize,
37 },
38 DegenerateFace {
40 face: usize,
42 vertex: u32,
44 },
45 NonManifoldEdge {
47 a: u32,
49 b: u32,
51 faces: usize,
53 },
54 InconsistentOrientation {
57 from: u32,
59 to: u32,
61 },
62 NonManifoldVertex {
65 vertex: u32,
67 },
68 TooLarge,
70}
71
72impl fmt::Display for HalfedgeBuildError {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 match self {
75 Self::IncompleteTriangle { index_count } => {
76 write!(f, "index count {index_count} is not divisible by three")
77 }
78 Self::FaceTooSmall { face, corners } => {
79 write!(f, "face {face} has {corners} corners, fewer than three")
80 }
81 Self::IndexOutOfRange {
82 face,
83 index,
84 position_count,
85 } => write!(
86 f,
87 "face {face} names position {index}, but there are {position_count}"
88 ),
89 Self::DegenerateFace { face, vertex } => {
90 write!(f, "face {face} visits vertex {vertex} twice")
91 }
92 Self::NonManifoldEdge { a, b, faces } => {
93 write!(f, "edge {a}-{b} is shared by {faces} faces")
94 }
95 Self::InconsistentOrientation { from, to } => write!(
96 f,
97 "two faces traverse edge {from}->{to} in the same direction"
98 ),
99 Self::NonManifoldVertex { vertex } => {
100 write!(f, "vertex {vertex} joins more than one fan of faces")
101 }
102 Self::TooLarge => write!(f, "mesh exceeds 32-bit element ids"),
103 }
104 }
105}
106
107impl std::error::Error for HalfedgeBuildError {}
108
109#[derive(Clone, Copy)]
111struct Use {
112 lower: u32,
113 upper: u32,
114 from: u32,
115 to: u32,
116 corner: u32,
117}
118
119pub(super) fn build<F: AsRef<[u32]>>(
120 positions: Vec<Point3>,
121 faces: &[F],
122) -> Result<HalfedgeMesh, HalfedgeBuildError> {
123 let position_count = positions.len();
124 let corner_count: usize = faces.iter().map(|face| face.as_ref().len()).sum();
125 if position_count >= NONE as usize
128 || faces.len() >= NONE as usize
129 || corner_count.saturating_mul(2) >= NONE as usize
130 {
131 return Err(HalfedgeBuildError::TooLarge);
132 }
133
134 let mut face_start = Vec::with_capacity(faces.len() + 1);
135 let mut uses = Vec::with_capacity(corner_count);
136 let mut sorted = Vec::new();
137 for (face_index, face) in faces.iter().enumerate() {
138 let face = face.as_ref();
139 if face.len() < 3 {
140 return Err(HalfedgeBuildError::FaceTooSmall {
141 face: face_index,
142 corners: face.len(),
143 });
144 }
145 if let Some(&index) = face.iter().find(|&&i| i as usize >= position_count) {
146 return Err(HalfedgeBuildError::IndexOutOfRange {
147 face: face_index,
148 index,
149 position_count,
150 });
151 }
152 sorted.clear();
153 sorted.extend_from_slice(face);
154 sorted.sort_unstable();
155 if let Some(pair) = sorted.windows(2).find(|pair| pair[0] == pair[1]) {
156 return Err(HalfedgeBuildError::DegenerateFace {
157 face: face_index,
158 vertex: pair[0],
159 });
160 }
161 face_start.push(uses.len() as u32);
162 for (i, &from) in face.iter().enumerate() {
163 let to = face[(i + 1) % face.len()];
164 uses.push(Use {
165 lower: from.min(to),
166 upper: from.max(to),
167 from,
168 to,
169 corner: uses.len() as u32,
170 });
171 }
172 }
173 face_start.push(uses.len() as u32);
174
175 let mut order = uses.clone();
178 order.sort_unstable_by_key(|u| (u.lower, u.upper, u.corner));
179
180 let mut corner_halfedge = vec![NONE; corner_count];
181 let mut links: Vec<Link> = Vec::with_capacity(corner_count * 2);
182 let mut group_start = 0;
183 while group_start < order.len() {
184 let first = order[group_start];
185 let mut group_end = group_start + 1;
186 while group_end < order.len()
187 && (order[group_end].lower, order[group_end].upper) == (first.lower, first.upper)
188 {
189 group_end += 1;
190 }
191 let group = &order[group_start..group_end];
192 if group.len() > 2 {
193 return Err(HalfedgeBuildError::NonManifoldEdge {
194 a: first.lower,
195 b: first.upper,
196 faces: group.len(),
197 });
198 }
199 if group.len() == 2 && group[1].from == first.from {
200 return Err(HalfedgeBuildError::InconsistentOrientation {
201 from: first.from,
202 to: first.to,
203 });
204 }
205 let h = links.len() as u32;
206 links.push(Link {
208 target: first.to,
209 ..super::DEAD_LINK
210 });
211 links.push(Link {
212 target: first.from,
213 ..super::DEAD_LINK
214 });
215 corner_halfedge[first.corner as usize] = h;
216 if let Some(second) = group.get(1) {
217 corner_halfedge[second.corner as usize] = h + 1;
218 }
219 group_start = group_end;
220 }
221
222 let mut face_halfedge = Vec::with_capacity(faces.len());
224 for face in 0..faces.len() {
225 let start = face_start[face] as usize;
226 let end = face_start[face + 1] as usize;
227 let len = end - start;
228 for corner in start..end {
229 let h = corner_halfedge[corner] as usize;
230 let next = corner_halfedge[start + (corner - start + 1) % len];
231 let prev = corner_halfedge[start + (corner - start + len - 1) % len];
232 links[h].next = next;
233 links[h].prev = prev;
234 links[h].face = face as u32;
235 }
236 face_halfedge.push(corner_halfedge[start]);
237 }
238
239 let mut vertex_out = vec![NONE; position_count];
241 let mut boundary_out = vec![NONE; position_count];
242 let mut out_count = vec![0u32; position_count];
243 for h in 0..links.len() {
244 let source = links[h ^ 1].target as usize;
245 out_count[source] += 1;
246 if links[h].face == NONE {
247 if boundary_out[source] != NONE {
248 return Err(HalfedgeBuildError::NonManifoldVertex {
249 vertex: source as u32,
250 });
251 }
252 boundary_out[source] = h as u32;
253 }
254 if vertex_out[source] == NONE {
255 vertex_out[source] = h as u32;
256 }
257 }
258 for h in 0..links.len() {
259 if links[h].face == NONE {
260 let next = boundary_out[links[h].target as usize];
263 links[h].next = next;
264 links[next as usize].prev = h as u32;
265 }
266 }
267 for v in 0..position_count {
268 if boundary_out[v] != NONE {
269 vertex_out[v] = boundary_out[v];
270 }
271 }
272
273 let edge_count = links.len() / 2;
274 let mesh = HalfedgeMesh {
275 positions,
276 vertex_live: vec![true; position_count],
277 vertex_out,
278 edge_live: vec![true; edge_count],
279 links,
280 face_live: vec![true; face_halfedge.len()],
281 live_faces: face_halfedge.len(),
282 face_halfedge,
283 live_vertices: position_count,
284 live_edges: edge_count,
285 };
286
287 for (v, &count) in out_count.iter().enumerate() {
289 if count == 0 {
290 continue;
291 }
292 let orbit = mesh.outgoing_halfedges(VertexId(v as u32)).count() as u32;
293 if orbit != count {
294 return Err(HalfedgeBuildError::NonManifoldVertex { vertex: v as u32 });
295 }
296 }
297 debug_assert!(mesh.validate().is_ok(), "{:?}", mesh.validate());
298 Ok(mesh)
299}