1use std::collections::BTreeMap;
23
24use axiolid_mesh::TriMesh;
25use thiserror::Error;
26
27#[derive(Debug, Clone, PartialEq, Eq, Error)]
29#[non_exhaustive]
30pub enum TopologyError {
31 #[error("triangle {triangle} names vertex {vertex}, but the mesh has {vertices}")]
33 IndexOutOfRange {
34 triangle: usize,
36 vertex: u32,
38 vertices: usize,
40 },
41 #[error("triangle {triangle} repeats a vertex")]
43 DegenerateTriangle {
44 triangle: usize,
46 },
47 #[error(
49 "the mesh is not a two-manifold: {edges} edges on three or more \
50 triangles, {vertices} vertices whose triangles form several fans"
51 )]
52 NonManifold {
53 edges: usize,
55 vertices: usize,
57 },
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62#[non_exhaustive]
63pub enum SurfaceKind {
64 Orientable {
66 genus: u32,
68 },
69 NonOrientable {
72 crosscaps: u32,
74 },
75}
76
77pub type EdgeLoop = Vec<u32>;
80
81#[derive(Debug, Clone, PartialEq, Eq)]
83#[non_exhaustive]
84pub struct ComponentTopology {
85 pub triangles: Vec<usize>,
87 pub vertices: usize,
89 pub edges: usize,
91 pub euler_characteristic: i64,
93 pub boundary_loops: usize,
95 pub orientable: bool,
97 pub consistently_oriented: bool,
100 pub surface: SurfaceKind,
103 pub homology_basis: Option<Vec<EdgeLoop>>,
107}
108
109#[derive(Debug, Clone, PartialEq, Eq)]
111#[non_exhaustive]
112pub struct MeshTopology {
113 pub components: Vec<ComponentTopology>,
116}
117
118#[derive(Debug, Clone, Copy)]
120struct Use {
121 triangle: usize,
122 forward: bool,
125}
126
127pub fn topology(mesh: &TriMesh) -> Result<MeshTopology, TopologyError> {
136 let triangles: Vec<[u32; 3]> = mesh
137 .indices
138 .chunks_exact(3)
139 .map(|t| [t[0], t[1], t[2]])
140 .collect();
141 let vertex_count = mesh.positions.len();
142 for (triangle, t) in triangles.iter().enumerate() {
143 if let Some(&vertex) = t.iter().find(|&&v| v as usize >= vertex_count) {
144 return Err(TopologyError::IndexOutOfRange {
145 triangle,
146 vertex,
147 vertices: vertex_count,
148 });
149 }
150 if t[0] == t[1] || t[1] == t[2] || t[2] == t[0] {
151 return Err(TopologyError::DegenerateTriangle { triangle });
152 }
153 }
154
155 let mut edges: BTreeMap<(u32, u32), Vec<Use>> = BTreeMap::new();
156 for (triangle, t) in triangles.iter().enumerate() {
157 for k in 0..3 {
158 let (a, b) = (t[k], t[(k + 1) % 3]);
159 edges.entry((a.min(b), a.max(b))).or_default().push(Use {
160 triangle,
161 forward: a < b,
162 });
163 }
164 }
165 let non_manifold_edges = edges.values().filter(|uses| uses.len() > 2).count();
166 let non_manifold_vertices = count_split_vertices(&triangles, &edges, vertex_count);
167 if non_manifold_edges > 0 || non_manifold_vertices > 0 {
168 return Err(TopologyError::NonManifold {
169 edges: non_manifold_edges,
170 vertices: non_manifold_vertices,
171 });
172 }
173
174 let mut neighbours: Vec<Vec<(usize, bool)>> = vec![Vec::new(); triangles.len()];
178 for uses in edges.values() {
179 if let [a, b] = uses[..] {
180 let flips = a.forward == b.forward;
183 neighbours[a.triangle].push((b.triangle, flips));
184 neighbours[b.triangle].push((a.triangle, flips));
185 }
186 }
187 let mut component_of = vec![usize::MAX; triangles.len()];
188 let mut flip = vec![false; triangles.len()];
189 let mut components = Vec::new();
190 for seed in 0..triangles.len() {
191 if component_of[seed] != usize::MAX {
192 continue;
193 }
194 let id = components.len();
195 component_of[seed] = id;
196 let mut members = vec![seed];
197 let mut orientable = true;
198 let mut consistent = true;
199 let mut next = 0;
200 while next < members.len() {
201 let t = members[next];
202 next += 1;
203 for &(n, flips) in &neighbours[t] {
204 consistent &= !flips;
205 let wanted = flip[t] ^ flips;
206 if component_of[n] == usize::MAX {
207 component_of[n] = id;
208 flip[n] = wanted;
209 members.push(n);
210 } else if flip[n] != wanted {
211 orientable = false;
212 }
213 }
214 }
215 members.sort_unstable();
216 components.push((members, orientable, consistent));
217 }
218
219 let mut out = Vec::with_capacity(components.len());
220 for (members, orientable, consistent) in components {
221 let component = component_of[members[0]];
222 let mut used: Vec<u32> = members.iter().flat_map(|&t| triangles[t]).collect();
223 used.sort_unstable();
224 used.dedup();
225 let own_edges: Vec<(&(u32, u32), &Vec<Use>)> = edges
226 .iter()
227 .filter(|(_, uses)| component_of[uses[0].triangle] == component)
228 .collect();
229 let boundary: Vec<(u32, u32)> = own_edges
230 .iter()
231 .filter(|(_, uses)| uses.len() == 1)
232 .map(|(&edge, _)| edge)
233 .collect();
234 let boundary_loops = count_loops(&boundary);
235 let characteristic = used.len() as i64 - own_edges.len() as i64 + members.len() as i64;
236 let deficit = 2 - characteristic - boundary_loops as i64;
239 let surface = if orientable {
240 SurfaceKind::Orientable {
241 genus: u32::try_from(deficit / 2).unwrap_or(0),
242 }
243 } else {
244 SurfaceKind::NonOrientable {
245 crosscaps: u32::try_from(deficit).unwrap_or(0),
246 }
247 };
248 let homology_basis =
249 (orientable && boundary.is_empty()).then(|| tree_cotree(&members, &own_edges));
250 out.push(ComponentTopology {
251 triangles: members,
252 vertices: used.len(),
253 edges: own_edges.len(),
254 euler_characteristic: characteristic,
255 boundary_loops,
256 orientable,
257 consistently_oriented: consistent,
258 surface,
259 homology_basis,
260 });
261 }
262 Ok(MeshTopology { components: out })
263}
264
265fn count_split_vertices(
268 triangles: &[[u32; 3]],
269 edges: &BTreeMap<(u32, u32), Vec<Use>>,
270 vertex_count: usize,
271) -> usize {
272 let mut incident: Vec<Vec<usize>> = vec![Vec::new(); vertex_count];
273 for (t, tri) in triangles.iter().enumerate() {
274 for &v in tri {
275 incident[v as usize].push(t);
276 }
277 }
278 let mut split = 0;
279 for (v, around) in incident.iter().enumerate() {
280 if around.len() < 2 {
281 continue;
282 }
283 let v = v as u32;
284 let slot = |t: usize| around.iter().position(|&u| u == t);
285 let mut parent: Vec<usize> = (0..around.len()).collect();
286 for (i, &t) in around.iter().enumerate() {
287 for &w in &triangles[t] {
288 if w == v {
289 continue;
290 }
291 for other in &edges[&(v.min(w), v.max(w))] {
292 if let Some(j) = slot(other.triangle) {
293 union(&mut parent, i, j);
294 }
295 }
296 }
297 }
298 let fans = (0..around.len())
299 .filter(|&i| find(&mut parent, i) == i)
300 .count();
301 if fans > 1 {
302 split += 1;
303 }
304 }
305 split
306}
307
308fn find(parent: &mut [usize], mut i: usize) -> usize {
309 while parent[i] != i {
310 parent[i] = parent[parent[i]];
311 i = parent[i];
312 }
313 i
314}
315
316fn union(parent: &mut [usize], a: usize, b: usize) {
317 let (a, b) = (find(parent, a), find(parent, b));
318 if a != b {
319 parent[a.max(b)] = a.min(b);
320 }
321}
322
323fn count_loops(boundary: &[(u32, u32)]) -> usize {
326 let mut index: BTreeMap<u32, usize> = BTreeMap::new();
327 for &(a, b) in boundary {
328 let n = index.len();
329 index.entry(a).or_insert(n);
330 let n = index.len();
331 index.entry(b).or_insert(n);
332 }
333 let mut parent: Vec<usize> = (0..index.len()).collect();
334 for &(a, b) in boundary {
335 union(&mut parent, index[&a], index[&b]);
336 }
337 (0..parent.len())
338 .filter(|&i| find(&mut parent, i) == i)
339 .count()
340}
341
342fn tree_cotree(members: &[usize], own_edges: &[(&(u32, u32), &Vec<Use>)]) -> Vec<EdgeLoop> {
347 let mut adjacent: BTreeMap<u32, Vec<u32>> = BTreeMap::new();
350 for (&(a, b), _) in own_edges {
351 adjacent.entry(a).or_default().push(b);
352 adjacent.entry(b).or_default().push(a);
353 }
354 let root = *adjacent.keys().next().expect("a component has vertices");
355 let mut parent: BTreeMap<u32, u32> = BTreeMap::new();
356 let mut depth: BTreeMap<u32, usize> = BTreeMap::new();
357 parent.insert(root, root);
358 depth.insert(root, 0);
359 let mut queue = std::collections::VecDeque::from([root]);
360 while let Some(v) = queue.pop_front() {
361 for &w in &adjacent[&v] {
362 if let std::collections::btree_map::Entry::Vacant(slot) = parent.entry(w) {
363 slot.insert(v);
364 depth.insert(w, depth[&v] + 1);
365 queue.push_back(w);
366 }
367 }
368 }
369 let in_tree = |a: u32, b: u32| parent[&a] == b || parent[&b] == a;
371
372 let slot: BTreeMap<usize, usize> = members.iter().enumerate().map(|(i, &t)| (t, i)).collect();
374 let mut dual: Vec<usize> = (0..members.len()).collect();
375 let mut leftover = Vec::new();
376 for (&(a, b), uses) in own_edges {
377 if in_tree(a, b) {
378 continue;
379 }
380 let (s, t) = (slot[&uses[0].triangle], slot[&uses[1].triangle]);
381 if find(&mut dual, s) == find(&mut dual, t) {
382 leftover.push((a, b));
383 } else {
384 union(&mut dual, s, t);
385 }
386 }
387 leftover
388 .into_iter()
389 .map(|(a, b)| {
390 let (mut up_a, mut up_b) = (vec![a], vec![b]);
392 let (mut x, mut y) = (a, b);
393 while depth[&x] > depth[&y] {
394 x = parent[&x];
395 up_a.push(x);
396 }
397 while depth[&y] > depth[&x] {
398 y = parent[&y];
399 up_b.push(y);
400 }
401 while x != y {
402 x = parent[&x];
403 y = parent[&y];
404 up_a.push(x);
405 up_b.push(y);
406 }
407 up_b.pop();
409 up_a.extend(up_b.into_iter().rev());
410 up_a
411 })
412 .collect()
413}