1use std::cmp::Reverse;
8
9use hashbrown::{HashMap, HashSet};
10use tracing::{debug, info};
11
12use crate::adjacency::MeshAdjacency;
13use crate::types::Mesh;
14
15#[derive(Debug, Clone)]
17pub struct ComponentAnalysis {
18 pub component_count: usize,
20 pub components: Vec<Vec<u32>>,
22 pub largest_component_size: usize,
24 pub smallest_component_size: usize,
26}
27
28impl ComponentAnalysis {
29 pub fn is_connected(&self) -> bool {
31 self.component_count == 1
32 }
33
34 pub fn largest_component(&self) -> &[u32] {
36 self.components.first().map(|v| v.as_slice()).unwrap_or(&[])
37 }
38}
39
40impl std::fmt::Display for ComponentAnalysis {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 writeln!(f, "Component Analysis:")?;
43 writeln!(f, " Connected components: {}", self.component_count)?;
44 if self.component_count > 0 {
45 writeln!(
46 f,
47 " Largest component: {} faces",
48 self.largest_component_size
49 )?;
50 writeln!(
51 f,
52 " Smallest component: {} faces",
53 self.smallest_component_size
54 )?;
55 if self.component_count > 1 {
56 writeln!(f, " Component sizes:")?;
57 for (i, comp) in self.components.iter().enumerate() {
58 writeln!(f, " Component {}: {} faces", i + 1, comp.len())?;
59 }
60 }
61 }
62 Ok(())
63 }
64}
65
66pub fn find_connected_components(mesh: &Mesh) -> ComponentAnalysis {
97 if mesh.faces.is_empty() {
98 return ComponentAnalysis {
99 component_count: 0,
100 components: Vec::new(),
101 largest_component_size: 0,
102 smallest_component_size: 0,
103 };
104 }
105
106 let adjacency = MeshAdjacency::build(&mesh.faces);
107 let face_count = mesh.faces.len();
108
109 let mut face_neighbors: Vec<Vec<u32>> = vec![Vec::new(); face_count];
111 for faces in adjacency.edge_to_faces.values() {
112 if faces.len() == 2 {
113 let f0 = faces[0];
114 let f1 = faces[1];
115 face_neighbors[f0 as usize].push(f1);
116 face_neighbors[f1 as usize].push(f0);
117 }
118 }
119
120 let mut visited = vec![false; face_count];
122 let mut components: Vec<Vec<u32>> = Vec::new();
123
124 for start_face in 0..face_count {
125 if visited[start_face] {
126 continue;
127 }
128
129 let mut component = Vec::new();
131 let mut queue = vec![start_face as u32];
132 visited[start_face] = true;
133
134 while let Some(face_idx) = queue.pop() {
135 component.push(face_idx);
136
137 for &neighbor in &face_neighbors[face_idx as usize] {
138 if !visited[neighbor as usize] {
139 visited[neighbor as usize] = true;
140 queue.push(neighbor);
141 }
142 }
143 }
144
145 components.push(component);
146 }
147
148 components.sort_by_key(|c| Reverse(c.len()));
150
151 let component_count = components.len();
152 let largest_component_size = components.first().map(|c| c.len()).unwrap_or(0);
153 let smallest_component_size = components.last().map(|c| c.len()).unwrap_or(0);
154
155 info!(
156 "Found {} connected component(s) in mesh with {} faces",
157 component_count, face_count
158 );
159
160 if component_count > 1 {
161 debug!(
162 "Component sizes: {:?}",
163 components.iter().map(|c| c.len()).collect::<Vec<_>>()
164 );
165 }
166
167 ComponentAnalysis {
168 component_count,
169 components,
170 largest_component_size,
171 smallest_component_size,
172 }
173}
174
175pub fn split_into_components(mesh: &Mesh) -> Vec<Mesh> {
208 let analysis = find_connected_components(mesh);
209
210 if analysis.component_count <= 1 {
211 return vec![mesh.clone()];
213 }
214
215 info!(
216 "Splitting mesh into {} components",
217 analysis.component_count
218 );
219
220 let mut result = Vec::with_capacity(analysis.component_count);
221
222 for (comp_idx, face_indices) in analysis.components.iter().enumerate() {
223 let mut used_vertices: HashSet<u32> = HashSet::new();
225 for &face_idx in face_indices {
226 let face = &mesh.faces[face_idx as usize];
227 used_vertices.insert(face[0]);
228 used_vertices.insert(face[1]);
229 used_vertices.insert(face[2]);
230 }
231
232 let mut old_to_new: HashMap<u32, u32> = HashMap::new();
234 let mut new_vertices = Vec::with_capacity(used_vertices.len());
235
236 for old_idx in used_vertices {
237 let new_idx = new_vertices.len() as u32;
238 old_to_new.insert(old_idx, new_idx);
239 new_vertices.push(mesh.vertices[old_idx as usize].clone());
240 }
241
242 let new_faces: Vec<[u32; 3]> = face_indices
244 .iter()
245 .map(|&face_idx| {
246 let face = &mesh.faces[face_idx as usize];
247 [
248 *old_to_new.get(&face[0]).unwrap(),
249 *old_to_new.get(&face[1]).unwrap(),
250 *old_to_new.get(&face[2]).unwrap(),
251 ]
252 })
253 .collect();
254
255 debug!(
256 "Component {}: {} vertices, {} faces",
257 comp_idx + 1,
258 new_vertices.len(),
259 new_faces.len()
260 );
261
262 result.push(Mesh {
263 vertices: new_vertices,
264 faces: new_faces,
265 });
266 }
267
268 result
269}
270
271pub fn keep_largest_component(mesh: &mut Mesh) -> usize {
303 let analysis = find_connected_components(mesh);
304
305 if analysis.component_count <= 1 {
306 return 0;
307 }
308
309 let components_removed = analysis.component_count - 1;
310
311 info!(
312 "Keeping largest component ({} faces), removing {} smaller component(s)",
313 analysis.largest_component_size, components_removed
314 );
315
316 let largest = split_into_components(mesh).into_iter().next().unwrap();
318
319 mesh.vertices = largest.vertices;
321 mesh.faces = largest.faces;
322
323 components_removed
324}
325
326pub fn remove_small_components(mesh: &mut Mesh, min_faces: usize) -> usize {
347 let analysis = find_connected_components(mesh);
348
349 if analysis.component_count <= 1 {
350 if analysis.largest_component_size < min_faces {
351 mesh.vertices.clear();
353 mesh.faces.clear();
354 return 1;
355 }
356 return 0;
357 }
358
359 let components_to_keep: Vec<&Vec<u32>> = analysis
361 .components
362 .iter()
363 .filter(|c| c.len() >= min_faces)
364 .collect();
365
366 if components_to_keep.is_empty() {
367 mesh.vertices.clear();
368 mesh.faces.clear();
369 return analysis.component_count;
370 }
371
372 let components_removed = analysis.component_count - components_to_keep.len();
373
374 if components_removed == 0 {
375 return 0;
376 }
377
378 info!(
379 "Removing {} component(s) with fewer than {} faces",
380 components_removed, min_faces
381 );
382
383 let face_indices_to_keep: HashSet<u32> = components_to_keep
385 .iter()
386 .flat_map(|c| c.iter().copied())
387 .collect();
388
389 let mut used_vertices: HashSet<u32> = HashSet::new();
391 for &face_idx in &face_indices_to_keep {
392 let face = &mesh.faces[face_idx as usize];
393 used_vertices.insert(face[0]);
394 used_vertices.insert(face[1]);
395 used_vertices.insert(face[2]);
396 }
397
398 let mut old_to_new: HashMap<u32, u32> = HashMap::new();
400 let mut new_vertices = Vec::with_capacity(used_vertices.len());
401
402 for old_idx in 0..mesh.vertices.len() as u32 {
403 if used_vertices.contains(&old_idx) {
404 let new_idx = new_vertices.len() as u32;
405 old_to_new.insert(old_idx, new_idx);
406 new_vertices.push(mesh.vertices[old_idx as usize].clone());
407 }
408 }
409
410 let new_faces: Vec<[u32; 3]> = (0..mesh.faces.len())
412 .filter(|&i| face_indices_to_keep.contains(&(i as u32)))
413 .map(|i| {
414 let face = &mesh.faces[i];
415 [
416 *old_to_new.get(&face[0]).unwrap(),
417 *old_to_new.get(&face[1]).unwrap(),
418 *old_to_new.get(&face[2]).unwrap(),
419 ]
420 })
421 .collect();
422
423 mesh.vertices = new_vertices;
424 mesh.faces = new_faces;
425
426 components_removed
427}
428
429#[cfg(test)]
430mod tests {
431 use super::*;
432 use crate::Vertex;
433
434 fn create_single_triangle() -> Mesh {
435 let mut mesh = Mesh::new();
436 mesh.vertices.push(Vertex::from_coords(0.0, 0.0, 0.0));
437 mesh.vertices.push(Vertex::from_coords(1.0, 0.0, 0.0));
438 mesh.vertices.push(Vertex::from_coords(0.0, 1.0, 0.0));
439 mesh.faces.push([0, 1, 2]);
440 mesh
441 }
442
443 fn create_two_disconnected_triangles() -> Mesh {
444 let mut mesh = Mesh::new();
445 mesh.vertices.push(Vertex::from_coords(0.0, 0.0, 0.0));
447 mesh.vertices.push(Vertex::from_coords(1.0, 0.0, 0.0));
448 mesh.vertices.push(Vertex::from_coords(0.0, 1.0, 0.0));
449 mesh.vertices.push(Vertex::from_coords(10.0, 0.0, 0.0));
451 mesh.vertices.push(Vertex::from_coords(11.0, 0.0, 0.0));
452 mesh.vertices.push(Vertex::from_coords(10.0, 1.0, 0.0));
453 mesh.faces.push([0, 1, 2]);
454 mesh.faces.push([3, 4, 5]);
455 mesh
456 }
457
458 fn create_two_connected_triangles() -> Mesh {
459 let mut mesh = Mesh::new();
460 mesh.vertices.push(Vertex::from_coords(0.0, 0.0, 0.0));
461 mesh.vertices.push(Vertex::from_coords(1.0, 0.0, 0.0));
462 mesh.vertices.push(Vertex::from_coords(0.5, 1.0, 0.0));
463 mesh.vertices.push(Vertex::from_coords(1.5, 1.0, 0.0));
464 mesh.faces.push([0, 1, 2]);
466 mesh.faces.push([1, 3, 2]);
467 mesh
468 }
469
470 fn create_three_components() -> Mesh {
471 let mut mesh = Mesh::new();
472
473 mesh.vertices.push(Vertex::from_coords(0.0, 0.0, 0.0));
475 mesh.vertices.push(Vertex::from_coords(1.0, 0.0, 0.0));
476 mesh.vertices.push(Vertex::from_coords(0.5, 1.0, 0.0));
477 mesh.vertices.push(Vertex::from_coords(1.5, 1.0, 0.0));
478 mesh.faces.push([0, 1, 2]);
479 mesh.faces.push([1, 3, 2]);
480
481 mesh.vertices.push(Vertex::from_coords(10.0, 0.0, 0.0));
483 mesh.vertices.push(Vertex::from_coords(11.0, 0.0, 0.0));
484 mesh.vertices.push(Vertex::from_coords(10.0, 1.0, 0.0));
485 mesh.faces.push([4, 5, 6]);
486
487 mesh.vertices.push(Vertex::from_coords(20.0, 0.0, 0.0));
489 mesh.vertices.push(Vertex::from_coords(21.0, 0.0, 0.0));
490 mesh.vertices.push(Vertex::from_coords(20.0, 1.0, 0.0));
491 mesh.faces.push([7, 8, 9]);
492
493 mesh
494 }
495
496 #[test]
497 fn test_empty_mesh() {
498 let mesh = Mesh::new();
499 let analysis = find_connected_components(&mesh);
500 assert_eq!(analysis.component_count, 0);
501 assert!(!analysis.is_connected());
502 }
503
504 #[test]
505 fn test_single_component() {
506 let mesh = create_single_triangle();
507 let analysis = find_connected_components(&mesh);
508 assert_eq!(analysis.component_count, 1);
509 assert!(analysis.is_connected());
510 assert_eq!(analysis.largest_component_size, 1);
511 }
512
513 #[test]
514 fn test_two_disconnected_components() {
515 let mesh = create_two_disconnected_triangles();
516 let analysis = find_connected_components(&mesh);
517 assert_eq!(analysis.component_count, 2);
518 assert!(!analysis.is_connected());
519 assert_eq!(analysis.largest_component_size, 1);
520 assert_eq!(analysis.smallest_component_size, 1);
521 }
522
523 #[test]
524 fn test_connected_triangles() {
525 let mesh = create_two_connected_triangles();
526 let analysis = find_connected_components(&mesh);
527 assert_eq!(analysis.component_count, 1);
528 assert!(analysis.is_connected());
529 assert_eq!(analysis.largest_component_size, 2);
530 }
531
532 #[test]
533 fn test_three_components_sorted_by_size() {
534 let mesh = create_three_components();
535 let analysis = find_connected_components(&mesh);
536 assert_eq!(analysis.component_count, 3);
537 assert_eq!(analysis.largest_component_size, 2);
538 assert_eq!(analysis.smallest_component_size, 1);
539 assert_eq!(analysis.components[0].len(), 2);
541 }
542
543 #[test]
544 fn test_split_single_component() {
545 let mesh = create_single_triangle();
546 let components = split_into_components(&mesh);
547 assert_eq!(components.len(), 1);
548 assert_eq!(components[0].face_count(), 1);
549 assert_eq!(components[0].vertex_count(), 3);
550 }
551
552 #[test]
553 fn test_split_two_components() {
554 let mesh = create_two_disconnected_triangles();
555 let components = split_into_components(&mesh);
556 assert_eq!(components.len(), 2);
557 assert_eq!(components[0].vertex_count(), 3);
559 assert_eq!(components[1].vertex_count(), 3);
560 assert_eq!(components[0].face_count(), 1);
561 assert_eq!(components[1].face_count(), 1);
562 }
563
564 #[test]
565 fn test_split_preserves_geometry() {
566 let mesh = create_two_disconnected_triangles();
567 let components = split_into_components(&mesh);
568
569 for comp in &components {
571 for face in &comp.faces {
572 assert!(face[0] < comp.vertices.len() as u32);
573 assert!(face[1] < comp.vertices.len() as u32);
574 assert!(face[2] < comp.vertices.len() as u32);
575 }
576 }
577 }
578
579 #[test]
580 fn test_keep_largest_single_component() {
581 let mut mesh = create_single_triangle();
582 let removed = keep_largest_component(&mut mesh);
583 assert_eq!(removed, 0);
584 assert_eq!(mesh.face_count(), 1);
585 }
586
587 #[test]
588 fn test_keep_largest_multiple_components() {
589 let mut mesh = create_three_components();
590 let removed = keep_largest_component(&mut mesh);
591 assert_eq!(removed, 2);
592 assert_eq!(mesh.face_count(), 2); }
594
595 #[test]
596 fn test_remove_small_components_none_removed() {
597 let mut mesh = create_three_components();
598 let removed = remove_small_components(&mut mesh, 1);
599 assert_eq!(removed, 0);
600 assert_eq!(mesh.face_count(), 4); }
602
603 #[test]
604 fn test_remove_small_components_some_removed() {
605 let mut mesh = create_three_components();
606 let removed = remove_small_components(&mut mesh, 2);
607 assert_eq!(removed, 2); assert_eq!(mesh.face_count(), 2); }
610
611 #[test]
612 fn test_remove_small_components_all_removed() {
613 let mut mesh = create_three_components();
614 let removed = remove_small_components(&mut mesh, 10);
615 assert_eq!(removed, 3);
616 assert_eq!(mesh.face_count(), 0);
617 assert_eq!(mesh.vertex_count(), 0);
618 }
619
620 #[test]
621 fn test_component_analysis_display() {
622 let mesh = create_three_components();
623 let analysis = find_connected_components(&mesh);
624 let output = format!("{}", analysis);
625 assert!(output.contains("Connected components: 3"));
626 assert!(output.contains("Largest component: 2 faces"));
627 }
628}