#![allow(dead_code)]
use std::collections::HashMap;
pub struct MeshBoundary {
pub loops: Vec<Vec<usize>>,
}
pub fn new_mesh_boundary() -> MeshBoundary {
MeshBoundary { loops: Vec::new() }
}
pub fn boundary_add_loop(b: &mut MeshBoundary, loop_verts: Vec<usize>) {
b.loops.push(loop_verts);
}
pub fn boundary_hole_count(b: &MeshBoundary) -> usize {
b.loops.len()
}
pub fn boundary_total_vertices(b: &MeshBoundary) -> usize {
b.loops.iter().map(|l| l.len()).sum()
}
pub fn detect_boundary_edges(faces: &[[usize; 3]]) -> Vec<(usize, usize)> {
let mut edge_count: HashMap<(usize, usize), usize> = HashMap::new();
for face in faces {
for i in 0..3 {
let a = face[i];
let b = face[(i + 1) % 3];
let key = if a < b { (a, b) } else { (b, a) };
*edge_count.entry(key).or_insert(0) += 1;
}
}
edge_count
.into_iter()
.filter(|(_, count)| *count == 1)
.map(|(k, _)| k)
.collect()
}
pub fn is_closed_mesh(faces: &[[usize; 3]]) -> bool {
detect_boundary_edges(faces).is_empty()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_mesh_boundary() {
let b = new_mesh_boundary();
assert_eq!(boundary_hole_count(&b), 0);
}
#[test]
fn test_boundary_add_loop() {
let mut b = new_mesh_boundary();
boundary_add_loop(&mut b, vec![0, 1, 2]);
assert_eq!(boundary_hole_count(&b), 1);
}
#[test]
fn test_boundary_total_vertices() {
let mut b = new_mesh_boundary();
boundary_add_loop(&mut b, vec![0, 1, 2]);
boundary_add_loop(&mut b, vec![3, 4]);
assert_eq!(boundary_total_vertices(&b), 5);
}
#[test]
fn test_detect_boundary_edges_open_mesh() {
let faces = vec![[0usize, 1, 2]];
let edges = detect_boundary_edges(&faces);
assert_eq!(edges.len(), 3);
}
#[test]
fn test_is_closed_mesh_open() {
let faces = vec![[0usize, 1, 2]];
assert!(!is_closed_mesh(&faces));
}
#[test]
fn test_detect_boundary_edges_shared_edge() {
let faces = vec![[0usize, 1, 2], [0, 2, 3]];
let edges = detect_boundary_edges(&faces);
assert_eq!(edges.len(), 4);
}
}