1use std::collections::{BTreeMap, BTreeSet};
27
28use axiolid_mesh::TriMesh;
29use thiserror::Error;
30
31#[derive(Debug, Clone, PartialEq, Eq, Error)]
33#[non_exhaustive]
34pub enum TopologyError {
35 #[error("triangle {triangle} names vertex {vertex}, but the mesh has {vertices}")]
37 IndexOutOfRange {
38 triangle: usize,
40 vertex: u32,
42 vertices: usize,
44 },
45 #[error("triangle {triangle} repeats a vertex")]
47 DegenerateTriangle {
48 triangle: usize,
50 },
51 #[error(
53 "the mesh is not a two-manifold: {edges} edges on three or more \
54 triangles, {vertices} vertices whose triangles form several fans"
55 )]
56 NonManifold {
57 edges: usize,
59 vertices: usize,
61 },
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66#[non_exhaustive]
67pub enum SurfaceKind {
68 Orientable {
70 genus: u32,
72 },
73 NonOrientable {
76 crosscaps: u32,
78 },
79}
80
81pub type EdgeLoop = Vec<u32>;
84
85#[derive(Debug, Clone, PartialEq, Eq)]
87#[non_exhaustive]
88pub struct ComponentTopology {
89 pub triangles: Vec<usize>,
91 pub vertices: usize,
93 pub edges: usize,
95 pub euler_characteristic: i64,
97 pub boundary_loops: usize,
99 pub orientable: bool,
101 pub consistently_oriented: bool,
104 pub surface: SurfaceKind,
107 pub homology_basis: Option<Vec<EdgeLoop>>,
115}
116
117#[derive(Debug, Clone, PartialEq, Eq)]
119#[non_exhaustive]
120pub struct MeshTopology {
121 pub components: Vec<ComponentTopology>,
124}
125
126#[derive(Debug, Clone, Copy)]
128struct Use {
129 triangle: usize,
130 forward: bool,
133}
134
135pub fn topology(mesh: &TriMesh) -> Result<MeshTopology, TopologyError> {
144 let triangles: Vec<[u32; 3]> = mesh
145 .indices
146 .as_chunks::<3>()
147 .0
148 .iter()
149 .map(|t| [t[0], t[1], t[2]])
150 .collect();
151 let vertex_count = mesh.positions.len();
152 for (triangle, t) in triangles.iter().enumerate() {
153 if let Some(&vertex) = t.iter().find(|&&v| v as usize >= vertex_count) {
154 return Err(TopologyError::IndexOutOfRange {
155 triangle,
156 vertex,
157 vertices: vertex_count,
158 });
159 }
160 if t[0] == t[1] || t[1] == t[2] || t[2] == t[0] {
161 return Err(TopologyError::DegenerateTriangle { triangle });
162 }
163 }
164
165 let mut edges: BTreeMap<(u32, u32), Vec<Use>> = BTreeMap::new();
166 for (triangle, t) in triangles.iter().enumerate() {
167 for k in 0..3 {
168 let (a, b) = (t[k], t[(k + 1) % 3]);
169 edges.entry((a.min(b), a.max(b))).or_default().push(Use {
170 triangle,
171 forward: a < b,
172 });
173 }
174 }
175 let non_manifold_edges = edges.values().filter(|uses| uses.len() > 2).count();
176 let non_manifold_vertices = count_split_vertices(&triangles, &edges, vertex_count);
177 if non_manifold_edges > 0 || non_manifold_vertices > 0 {
178 return Err(TopologyError::NonManifold {
179 edges: non_manifold_edges,
180 vertices: non_manifold_vertices,
181 });
182 }
183
184 let mut neighbours: Vec<Vec<(usize, bool)>> = vec![Vec::new(); triangles.len()];
188 for uses in edges.values() {
189 if let [a, b] = uses[..] {
190 let flips = a.forward == b.forward;
193 neighbours[a.triangle].push((b.triangle, flips));
194 neighbours[b.triangle].push((a.triangle, flips));
195 }
196 }
197 let mut component_of = vec![usize::MAX; triangles.len()];
198 let mut flip = vec![false; triangles.len()];
199 let mut components = Vec::new();
200 for seed in 0..triangles.len() {
201 if component_of[seed] != usize::MAX {
202 continue;
203 }
204 let id = components.len();
205 component_of[seed] = id;
206 let mut members = vec![seed];
207 let mut orientable = true;
208 let mut consistent = true;
209 let mut next = 0;
210 while next < members.len() {
211 let t = members[next];
212 next += 1;
213 for &(n, flips) in &neighbours[t] {
214 consistent &= !flips;
215 let wanted = flip[t] ^ flips;
216 if component_of[n] == usize::MAX {
217 component_of[n] = id;
218 flip[n] = wanted;
219 members.push(n);
220 } else if flip[n] != wanted {
221 orientable = false;
222 }
223 }
224 }
225 members.sort_unstable();
226 components.push((members, orientable, consistent));
227 }
228
229 let mut out = Vec::with_capacity(components.len());
230 for (members, orientable, consistent) in components {
231 let component = component_of[members[0]];
232 let mut used: Vec<u32> = members.iter().flat_map(|&t| triangles[t]).collect();
233 used.sort_unstable();
234 used.dedup();
235 let own_edges: Vec<(&(u32, u32), &Vec<Use>)> = edges
236 .iter()
237 .filter(|(_, uses)| component_of[uses[0].triangle] == component)
238 .collect();
239 let boundary: Vec<(u32, u32)> = own_edges
240 .iter()
241 .filter(|(_, uses)| uses.len() == 1)
242 .map(|(&edge, _)| edge)
243 .collect();
244 let boundary_loops = count_loops(&boundary);
245 let characteristic = used.len() as i64 - own_edges.len() as i64 + members.len() as i64;
246 let deficit = 2 - characteristic - boundary_loops as i64;
249 let surface = if orientable {
250 SurfaceKind::Orientable {
251 genus: u32::try_from(deficit / 2).unwrap_or(0),
252 }
253 } else {
254 SurfaceKind::NonOrientable {
255 crosscaps: u32::try_from(deficit).unwrap_or(0),
256 }
257 };
258 let homology_basis = Some(homology_generators(&members, &own_edges, &boundary));
259 out.push(ComponentTopology {
260 triangles: members,
261 vertices: used.len(),
262 edges: own_edges.len(),
263 euler_characteristic: characteristic,
264 boundary_loops,
265 orientable,
266 consistently_oriented: consistent,
267 surface,
268 homology_basis,
269 });
270 }
271 Ok(MeshTopology { components: out })
272}
273
274fn count_split_vertices(
277 triangles: &[[u32; 3]],
278 edges: &BTreeMap<(u32, u32), Vec<Use>>,
279 vertex_count: usize,
280) -> usize {
281 let mut incident: Vec<Vec<usize>> = vec![Vec::new(); vertex_count];
282 for (t, tri) in triangles.iter().enumerate() {
283 for &v in tri {
284 incident[v as usize].push(t);
285 }
286 }
287 let mut split = 0;
288 for (v, around) in incident.iter().enumerate() {
289 if around.len() < 2 {
290 continue;
291 }
292 let v = v as u32;
293 let slot = |t: usize| around.iter().position(|&u| u == t);
294 let mut parent: Vec<usize> = (0..around.len()).collect();
295 for (i, &t) in around.iter().enumerate() {
296 for &w in &triangles[t] {
297 if w == v {
298 continue;
299 }
300 for other in &edges[&(v.min(w), v.max(w))] {
301 if let Some(j) = slot(other.triangle) {
302 union(&mut parent, i, j);
303 }
304 }
305 }
306 }
307 let fans = (0..around.len())
308 .filter(|&i| find(&mut parent, i) == i)
309 .count();
310 if fans > 1 {
311 split += 1;
312 }
313 }
314 split
315}
316
317fn find(parent: &mut [usize], mut i: usize) -> usize {
318 while parent[i] != i {
319 parent[i] = parent[parent[i]];
320 i = parent[i];
321 }
322 i
323}
324
325fn union(parent: &mut [usize], a: usize, b: usize) {
326 let (a, b) = (find(parent, a), find(parent, b));
327 if a != b {
328 parent[a.max(b)] = a.min(b);
329 }
330}
331
332fn count_loops(boundary: &[(u32, u32)]) -> usize {
335 let mut index: BTreeMap<u32, usize> = BTreeMap::new();
336 for &(a, b) in boundary {
337 let n = index.len();
338 index.entry(a).or_insert(n);
339 let n = index.len();
340 index.entry(b).or_insert(n);
341 }
342 let mut parent: Vec<usize> = (0..index.len()).collect();
343 for &(a, b) in boundary {
344 union(&mut parent, index[&a], index[&b]);
345 }
346 (0..parent.len())
347 .filter(|&i| find(&mut parent, i) == i)
348 .count()
349}
350
351fn homology_generators(
363 members: &[usize],
364 own_edges: &[(&(u32, u32), &Vec<Use>)],
365 boundary: &[(u32, u32)],
366) -> Vec<EdgeLoop> {
367 let mut adjacent: BTreeMap<u32, Vec<u32>> = BTreeMap::new();
370 for (&(a, b), _) in own_edges {
371 adjacent.entry(a).or_default().push(b);
372 adjacent.entry(b).or_default().push(a);
373 }
374 let root = *adjacent.keys().next().expect("a component has vertices");
375 let mut parent: BTreeMap<u32, u32> = BTreeMap::new();
376 let mut depth: BTreeMap<u32, usize> = BTreeMap::new();
377 parent.insert(root, root);
378 depth.insert(root, 0);
379 let mut queue = std::collections::VecDeque::from([root]);
380 while let Some(v) = queue.pop_front() {
381 for &w in &adjacent[&v] {
382 if let std::collections::btree_map::Entry::Vacant(slot) = parent.entry(w) {
383 slot.insert(v);
384 depth.insert(w, depth[&v] + 1);
385 queue.push_back(w);
386 }
387 }
388 }
389 let in_tree = |a: u32, b: u32| parent[&a] == b || parent[&b] == a;
391
392 let loops = boundary_edge_loops(boundary);
395 let mut loop_of: BTreeMap<(u32, u32), usize> = BTreeMap::new();
396 for (index, l) in loops.iter().enumerate() {
397 for k in 0..l.len() {
398 let (a, b) = (l[k], l[(k + 1) % l.len()]);
399 loop_of.insert((a.min(b), a.max(b)), index);
400 }
401 }
402
403 let slot: BTreeMap<usize, usize> = members.iter().enumerate().map(|(i, &t)| (t, i)).collect();
407 let mut dual: Vec<usize> = (0..members.len() + loops.len()).collect();
408 let mut leftover = Vec::new();
409 for (&(a, b), uses) in own_edges {
410 if in_tree(a, b) {
411 continue;
412 }
413 let (s, t) = if let [x, y] = uses[..] {
414 (slot[&x.triangle], slot[&y.triangle])
415 } else {
416 let node = members.len() + loop_of[&(a, b)];
417 (slot[&uses[0].triangle], node)
418 };
419 if find(&mut dual, s) == find(&mut dual, t) {
420 leftover.push((a, b));
421 } else {
422 union(&mut dual, s, t);
423 }
424 }
425 let mut basis: Vec<EdgeLoop> = leftover
426 .into_iter()
427 .map(|(a, b)| close_through_tree(a, b, &parent, &depth))
428 .collect();
429 let keep = loops.len().saturating_sub(1);
430 basis.extend(loops.into_iter().take(keep));
431 basis
432}
433
434fn close_through_tree(
438 a: u32,
439 b: u32,
440 parent: &BTreeMap<u32, u32>,
441 depth: &BTreeMap<u32, usize>,
442) -> EdgeLoop {
443 let (mut up_a, mut up_b) = (vec![a], vec![b]);
444 let (mut x, mut y) = (a, b);
445 while depth[&x] > depth[&y] {
446 x = parent[&x];
447 up_a.push(x);
448 }
449 while depth[&y] > depth[&x] {
450 y = parent[&y];
451 up_b.push(y);
452 }
453 while x != y {
454 x = parent[&x];
455 y = parent[&y];
456 up_a.push(x);
457 up_b.push(y);
458 }
459 up_b.pop();
461 up_a.extend(up_b.into_iter().rev());
462 up_a
463}
464
465fn boundary_edge_loops(boundary: &[(u32, u32)]) -> Vec<EdgeLoop> {
469 let mut adjacent: BTreeMap<u32, Vec<u32>> = BTreeMap::new();
470 for &(a, b) in boundary {
471 adjacent.entry(a).or_default().push(b);
472 adjacent.entry(b).or_default().push(a);
473 }
474 let mut used: BTreeSet<(u32, u32)> = BTreeSet::new();
475 let mut loops = Vec::new();
476 for start in adjacent.keys().copied().collect::<Vec<_>>() {
477 while let Some(&first) = adjacent[&start]
478 .iter()
479 .find(|&&n| !used.contains(&(start.min(n), start.max(n))))
480 {
481 let mut vertices = vec![start];
482 let (mut cur, mut next) = (start, first);
483 loop {
484 used.insert((cur.min(next), cur.max(next)));
485 if next == start {
486 break;
487 }
488 vertices.push(next);
489 let after = adjacent[&next]
490 .iter()
491 .copied()
492 .find(|&n| !used.contains(&(next.min(n), next.max(n))))
493 .expect("a boundary loop closes on a two-manifold");
494 cur = next;
495 next = after;
496 }
497 loops.push(vertices);
498 }
499 }
500 loops
501}