use crate::TriMesh;
use std::collections::BTreeMap;
struct Partition {
parent: Vec<usize>,
}
impl Partition {
fn new(count: usize) -> Self {
Self {
parent: (0..count).collect(),
}
}
fn find(&mut self, mut node: usize) -> usize {
while self.parent[node] != node {
self.parent[node] = self.parent[self.parent[node]];
node = self.parent[node];
}
node
}
fn union(&mut self, a: usize, b: usize) {
let (ra, rb) = (self.find(a), self.find(b));
if ra != rb {
self.parent[rb] = ra;
}
}
}
fn partition_of(mesh: &TriMesh) -> Partition {
let mut partition = Partition::new(mesh.positions.len());
for triangle in mesh.indices.chunks_exact(3) {
let first = triangle[0] as usize;
for corner in &triangle[1..] {
partition.union(first, *corner as usize);
}
}
partition
}
pub fn component_count(mesh: &TriMesh) -> usize {
if mesh.indices.is_empty() {
return 0;
}
let mut partition = partition_of(mesh);
let mut roots = std::collections::BTreeSet::new();
for index in &mesh.indices {
let root = partition.find(*index as usize);
roots.insert(root);
}
roots.len()
}
pub fn decompose(mesh: &TriMesh) -> Vec<TriMesh> {
if mesh.indices.is_empty() {
return Vec::new();
}
let mut partition = partition_of(mesh);
let mut order: BTreeMap<usize, usize> = BTreeMap::new();
let mut groups: Vec<Vec<usize>> = Vec::new();
for (triangle_index, triangle) in mesh.indices.chunks_exact(3).enumerate() {
let root = partition.find(triangle[0] as usize);
let slot = *order.entry(root).or_insert_with(|| {
groups.push(Vec::new());
groups.len() - 1
});
groups[slot].push(triangle_index);
}
if groups.len() == 1 && mesh.positions.len() == referenced(mesh) {
return vec![mesh.clone()];
}
groups
.into_iter()
.map(|triangles| extract(mesh, &triangles))
.collect()
}
fn referenced(mesh: &TriMesh) -> usize {
mesh.indices
.iter()
.collect::<std::collections::BTreeSet<_>>()
.len()
}
fn extract(mesh: &TriMesh, triangles: &[usize]) -> TriMesh {
let mut remap: BTreeMap<u32, u32> = BTreeMap::new();
let mut positions = Vec::new();
let mut indices = Vec::with_capacity(triangles.len() * 3);
for &triangle in triangles {
for corner in 0..3 {
let old = mesh.indices[triangle * 3 + corner];
let new = *remap.entry(old).or_insert_with(|| {
positions.push(mesh.positions[old as usize]);
(positions.len() - 1) as u32
});
indices.push(new);
}
}
TriMesh::new(positions, indices)
}
pub fn compose(meshes: &[TriMesh]) -> TriMesh {
let mut positions = Vec::new();
let mut indices = Vec::new();
for mesh in meshes {
let base = positions.len() as u32;
positions.extend_from_slice(&mesh.positions);
indices.extend(mesh.indices.iter().map(|index| index + base));
}
TriMesh::new(positions, indices)
}