1use threecrate_core::{TriangleMesh, Result, Error, Point3f};
7use crate::MeshSimplifier;
8use nalgebra::{Matrix4, Vector4};
9use priority_queue::PriorityQueue;
11use std::collections::{HashMap, HashSet};
12use std::cmp::Ordering;
13
14#[derive(Debug, Clone)]
16struct EdgeCollapse {
17 vertex1: usize,
19 vertex2: usize,
21 new_position: Point3f,
23 cost: f64,
25}
26
27impl PartialEq for EdgeCollapse {
28 fn eq(&self, other: &Self) -> bool {
29 self.cost.total_cmp(&other.cost) == Ordering::Equal
30 }
31}
32
33impl Eq for EdgeCollapse {}
34
35impl PartialOrd for EdgeCollapse {
36 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
37 Some(self.cmp(other))
38 }
39}
40
41impl Ord for EdgeCollapse {
42 fn cmp(&self, other: &Self) -> Ordering {
43 other.cost.total_cmp(&self.cost)
45 }
46}
47
48#[allow(dead_code)]
50#[derive(Debug, Clone)]
51struct FaceInfo {
52 vertices: [usize; 3],
53 plane: Vector4<f64>, }
55
56#[derive(Debug, Clone)]
58struct VertexInfo {
59 position: Point3f,
60 quadric: Matrix4<f64>,
61 faces: HashSet<usize>,
62 neighbors: HashSet<usize>,
63}
64
65pub struct QuadricErrorSimplifier {
67 pub max_edge_length: Option<f32>,
69 pub preserve_boundary: bool,
70 pub feature_angle_threshold: f32,
71}
72
73impl Default for QuadricErrorSimplifier {
74 fn default() -> Self {
75 Self {
76 max_edge_length: None,
77 preserve_boundary: true,
78 feature_angle_threshold: 45.0_f32.to_radians(),
79 }
80 }
81}
82
83impl QuadricErrorSimplifier {
84 pub fn new() -> Self {
86 Self::default()
87 }
88
89 pub fn with_params(
91 max_edge_length: Option<f32>,
92 preserve_boundary: bool,
93 feature_angle_threshold: f32,
94 ) -> Self {
95 Self {
96 max_edge_length,
97 preserve_boundary,
98 feature_angle_threshold,
99 }
100 }
101
102 fn compute_plane(&self, v0: &Point3f, v1: &Point3f, v2: &Point3f) -> Vector4<f64> {
104 let edge1 = v1 - v0;
105 let edge2 = v2 - v0;
106 let normal = edge1.cross(&edge2).normalize();
107
108 if !normal.iter().all(|x| x.is_finite()) {
110 return Vector4::new(0.0, 0.0, 1.0, 0.0);
111 }
112
113 let d = -normal.dot(&v0.coords);
114 Vector4::new(normal.x as f64, normal.y as f64, normal.z as f64, d as f64)
115 }
116
117 fn plane_to_quadric(&self, plane: &Vector4<f64>) -> Matrix4<f64> {
119 let a = plane[0];
120 let b = plane[1];
121 let c = plane[2];
122 let d = plane[3];
123
124 Matrix4::new(
125 a*a, a*b, a*c, a*d,
126 a*b, b*b, b*c, b*d,
127 a*c, b*c, c*c, c*d,
128 a*d, b*d, c*d, d*d,
129 )
130 }
131
132 fn initialize_vertices(&self, mesh: &TriangleMesh) -> Vec<VertexInfo> {
134 let mut vertices: Vec<VertexInfo> = mesh.vertices.iter().enumerate().map(|(_i, &pos)| {
135 VertexInfo {
136 position: pos,
137 quadric: Matrix4::zeros(),
138 faces: HashSet::new(),
139 neighbors: HashSet::new(),
140 }
141 }).collect();
142
143 for (face_idx, face) in mesh.faces.iter().enumerate() {
145 let v0 = mesh.vertices[face[0]];
146 let v1 = mesh.vertices[face[1]];
147 let v2 = mesh.vertices[face[2]];
148
149 let plane = self.compute_plane(&v0, &v1, &v2);
150 let quadric = self.plane_to_quadric(&plane);
151
152 for &vertex_idx in face.iter() {
154 vertices[vertex_idx].quadric += quadric;
155 vertices[vertex_idx].faces.insert(face_idx);
156 }
157
158 vertices[face[0]].neighbors.insert(face[1]);
160 vertices[face[0]].neighbors.insert(face[2]);
161 vertices[face[1]].neighbors.insert(face[0]);
162 vertices[face[1]].neighbors.insert(face[2]);
163 vertices[face[2]].neighbors.insert(face[0]);
164 vertices[face[2]].neighbors.insert(face[1]);
165 }
166
167 vertices
168 }
169
170 fn find_boundary_edges(&self, mesh: &TriangleMesh) -> HashSet<(usize, usize)> {
172 let mut edge_count: HashMap<(usize, usize), usize> = HashMap::new();
173 for face in &mesh.faces {
174 let edges = [
175 (face[0].min(face[1]), face[0].max(face[1])),
176 (face[1].min(face[2]), face[1].max(face[2])),
177 (face[2].min(face[0]), face[2].max(face[0])),
178 ];
179 for edge in edges.iter() {
180 *edge_count.entry(*edge).or_insert(0) += 1;
181 }
182 }
183 edge_count.into_iter()
184 .filter(|(_, count)| *count == 1)
185 .map(|(edge, _)| edge)
186 .collect()
187 }
188
189 fn find_boundary_edges_from_faces(&self, faces: &[[usize; 3]]) -> HashSet<(usize, usize)> {
191 let mut edge_count: HashMap<(usize, usize), usize> = HashMap::new();
192
193 for face in faces {
194 let edges = [
195 (face[0].min(face[1]), face[0].max(face[1])),
196 (face[1].min(face[2]), face[1].max(face[2])),
197 (face[2].min(face[0]), face[2].max(face[0])),
198 ];
199
200 for edge in edges.iter() {
201 *edge_count.entry(*edge).or_insert(0) += 1;
202 }
203 }
204
205 edge_count.into_iter()
206 .filter(|(_, count)| *count == 1)
207 .map(|(edge, _)| edge)
208 .collect()
209 }
210
211 fn get_boundary_vertices(&self, boundary_edges: &HashSet<(usize, usize)>) -> HashSet<usize> {
213 let mut boundary_verts = HashSet::new();
214 for &(v1, v2) in boundary_edges {
215 boundary_verts.insert(v1);
216 boundary_verts.insert(v2);
217 }
218 boundary_verts
219 }
220
221 fn compute_collapse_cost(&self, v1_idx: usize, v2_idx: usize, vertices: &[VertexInfo]) -> Option<EdgeCollapse> {
223 let v1 = &vertices[v1_idx];
224 let v2 = &vertices[v2_idx];
225
226 if let Some(max_len) = self.max_edge_length {
228 let edge_len = (v1.position - v2.position).magnitude();
229 if edge_len > max_len {
230 return None;
231 }
232 }
233
234 let q_combined = v1.quadric + v2.quadric;
236
237 let q_3x3 = q_combined.fixed_view::<3, 3>(0, 0);
240 let q_3x1 = q_combined.fixed_view::<3, 1>(0, 3);
241
242 let optimal_pos = if let Some(inv_q) = q_3x3.try_inverse() {
243 let optimal_homogeneous = -inv_q * q_3x1;
244 Point3f::new(
245 optimal_homogeneous[0] as f32,
246 optimal_homogeneous[1] as f32,
247 optimal_homogeneous[2] as f32,
248 )
249 } else {
250 Point3f::from((v1.position.coords + v2.position.coords) * 0.5)
252 };
253
254 let pos_homogeneous = Vector4::new(
256 optimal_pos.x as f64,
257 optimal_pos.y as f64,
258 optimal_pos.z as f64,
259 1.0,
260 );
261
262 let cost = (pos_homogeneous.transpose() * q_combined * pos_homogeneous)[0];
263
264 Some(EdgeCollapse {
265 vertex1: v1_idx,
266 vertex2: v2_idx,
267 new_position: optimal_pos,
268 cost,
269 })
270 }
271
272 fn generate_edge_collapses(&self, vertices: &[VertexInfo], boundary_edges: &HashSet<(usize, usize)>) -> Vec<EdgeCollapse> {
274 let mut collapses = Vec::new();
275
276 let boundary_verts = if self.preserve_boundary {
278 self.get_boundary_vertices(boundary_edges)
279 } else {
280 HashSet::new()
281 };
282
283 for (v1_idx, vertex) in vertices.iter().enumerate() {
284 for &v2_idx in &vertex.neighbors {
285 if v1_idx < v2_idx { if self.preserve_boundary {
288 if boundary_verts.contains(&v1_idx) || boundary_verts.contains(&v2_idx) {
289 continue;
290 }
291 }
292 if let Some(collapse) = self.compute_collapse_cost(v1_idx, v2_idx, vertices) {
293 collapses.push(collapse);
294 }
295 }
296 }
297 }
298 collapses
299 }
300
301 fn apply_collapse(
303 &self,
304 collapse: &EdgeCollapse,
305 vertices: &mut Vec<VertexInfo>,
306 faces: &mut Vec<[usize; 3]>,
307 vertex_mapping: &mut HashMap<usize, usize>,
308 ) -> Result<()> {
309 let v1_idx = collapse.vertex1;
310 let v2_idx = collapse.vertex2;
311
312 let v2_quadric = vertices[v2_idx].quadric.clone();
314 vertices[v1_idx].position = collapse.new_position;
315 vertices[v1_idx].quadric += v2_quadric;
316
317 for face in faces.iter_mut() {
319 for vertex_ref in face.iter_mut() {
320 if *vertex_ref == v2_idx {
321 *vertex_ref = v1_idx;
322 }
323 }
324 }
325
326 faces.retain(|face| {
328 face[0] != face[1] && face[1] != face[2] && face[2] != face[0]
329 });
330
331 vertex_mapping.insert(v2_idx, v1_idx);
333
334 let v2_neighbors = vertices[v2_idx].neighbors.clone();
336 for &neighbor in &v2_neighbors {
337 if neighbor != v1_idx {
338 vertices[v1_idx].neighbors.insert(neighbor);
339 vertices[neighbor].neighbors.remove(&v2_idx);
340 vertices[neighbor].neighbors.insert(v1_idx);
341 }
342 }
343 vertices[v1_idx].neighbors.remove(&v2_idx);
344
345 vertices[v2_idx].neighbors.clear();
347 vertices[v2_idx].faces.clear();
348
349 Ok(())
350 }
351
352 fn rebuild_mesh(&self, vertices: &[VertexInfo], faces: &[[usize; 3]]) -> TriangleMesh {
354 let mut old_to_new: HashMap<usize, usize> = HashMap::new();
356 let mut new_vertices = Vec::new();
357
358 for (old_idx, vertex) in vertices.iter().enumerate() {
360 if !vertex.neighbors.is_empty() || !vertex.faces.is_empty() {
361 old_to_new.insert(old_idx, new_vertices.len());
362 new_vertices.push(vertex.position);
363 }
364 }
365
366 let new_faces: Vec<[usize; 3]> = faces.iter()
368 .filter_map(|face| {
369 if let (Some(&new_v0), Some(&new_v1), Some(&new_v2)) = (
370 old_to_new.get(&face[0]),
371 old_to_new.get(&face[1]),
372 old_to_new.get(&face[2]),
373 ) {
374 Some([new_v0, new_v1, new_v2])
375 } else {
376 None
377 }
378 })
379 .collect();
380
381 TriangleMesh::from_vertices_and_faces(new_vertices, new_faces)
382 }
383}
384
385impl MeshSimplifier for QuadricErrorSimplifier {
386 fn simplify(&self, mesh: &TriangleMesh, reduction_ratio: f32) -> Result<TriangleMesh> {
388 if mesh.is_empty() {
389 return Err(Error::InvalidData("Mesh is empty".to_string()));
390 }
391
392 if !(0.0..=1.0).contains(&reduction_ratio) {
393 return Err(Error::InvalidData("Reduction ratio must be between 0.0 and 1.0".to_string()));
394 }
395
396 if reduction_ratio == 0.0 {
397 return Ok(mesh.clone());
398 }
399
400 let target_face_count = ((1.0 - reduction_ratio) * mesh.faces.len() as f32) as usize;
401
402 let mut vertices = self.initialize_vertices(mesh);
404 let mut faces = mesh.faces.clone();
405 let mut vertex_mapping = HashMap::new();
406
407 let boundary_edges = self.find_boundary_edges(mesh);
409
410 let mut collapse_queue = PriorityQueue::new();
412 let initial_collapses = self.generate_edge_collapses(&vertices, &boundary_edges);
413
414 for (idx, collapse) in initial_collapses.into_iter().enumerate() {
415 collapse_queue.push(idx, collapse);
416 }
417
418 let mut collapse_counter = 0;
420 while faces.len() > target_face_count && !collapse_queue.is_empty() {
421 if let Some((_, collapse)) = collapse_queue.pop() {
422 if vertices[collapse.vertex1].neighbors.contains(&collapse.vertex2) {
424 self.apply_collapse(&collapse, &mut vertices, &mut faces, &mut vertex_mapping)?;
425 collapse_counter += 1;
426 if collapse_counter % 100 == 0 {
428 collapse_queue.clear();
429 let current_boundary_edges = self.find_boundary_edges_from_faces(&faces);
431 let new_collapses = self.generate_edge_collapses(&vertices, ¤t_boundary_edges);
432 for (idx, collapse) in new_collapses.into_iter().enumerate() {
433 collapse_queue.push(collapse_counter * 1000 + idx, collapse);
434 }
435 }
436 }
437 }
438 }
439
440 Ok(self.rebuild_mesh(&vertices, &faces))
441 }
442}
443
444#[cfg(test)]
445mod tests {
446 use super::*;
447 use nalgebra::Point3;
448
449 #[test]
450 fn test_quadric_error_simplifier_creation() {
451 let simplifier = QuadricErrorSimplifier::new();
452 assert!(simplifier.preserve_boundary);
453 assert!(simplifier.max_edge_length.is_none());
454 }
455
456 #[test]
457 fn test_plane_computation() {
458 let simplifier = QuadricErrorSimplifier::new();
459 let v0 = Point3::new(0.0, 0.0, 0.0);
460 let v1 = Point3::new(1.0, 0.0, 0.0);
461 let v2 = Point3::new(0.0, 1.0, 0.0);
462
463 let plane = simplifier.compute_plane(&v0, &v1, &v2);
464
465 assert!((plane[0]).abs() < 1e-6);
467 assert!((plane[1]).abs() < 1e-6);
468 assert!((plane[2] - 1.0).abs() < 1e-6);
469 assert!((plane[3]).abs() < 1e-6);
470 }
471
472 #[test]
473 fn test_empty_mesh() {
474 let simplifier = QuadricErrorSimplifier::new();
475 let mesh = TriangleMesh::new();
476
477 let result = simplifier.simplify(&mesh, 0.5);
478 assert!(result.is_err());
479 }
480
481 #[test]
482 fn test_zero_reduction() {
483 let simplifier = QuadricErrorSimplifier::new();
484 let vertices = vec![
485 Point3::new(0.0, 0.0, 0.0),
486 Point3::new(1.0, 0.0, 0.0),
487 Point3::new(0.5, 1.0, 0.0),
488 ];
489 let faces = vec![[0, 1, 2]];
490 let mesh = TriangleMesh::from_vertices_and_faces(vertices, faces);
491
492 let result = simplifier.simplify(&mesh, 0.0).unwrap();
493 assert_eq!(result.vertex_count(), 3);
494 assert_eq!(result.face_count(), 1);
495 }
496
497 #[test]
498 fn test_invalid_reduction_ratio() {
499 let simplifier = QuadricErrorSimplifier::new();
500 let vertices = vec![
501 Point3::new(0.0, 0.0, 0.0),
502 Point3::new(1.0, 0.0, 0.0),
503 Point3::new(0.5, 1.0, 0.0),
504 ];
505 let faces = vec![[0, 1, 2]];
506 let mesh = TriangleMesh::from_vertices_and_faces(vertices, faces);
507
508 assert!(simplifier.simplify(&mesh, -0.1).is_err());
509 assert!(simplifier.simplify(&mesh, 1.1).is_err());
510 }
511
512 #[test]
513 fn test_quadric_matrix_computation() {
514 let simplifier = QuadricErrorSimplifier::new();
515 let plane = Vector4::new(1.0, 0.0, 0.0, -1.0); let quadric = simplifier.plane_to_quadric(&plane);
518
519 assert!((quadric[(0, 0)] - 1.0).abs() < 1e-10);
521 assert!((quadric[(1, 1)] - 0.0).abs() < 1e-10);
522 assert!((quadric[(2, 2)] - 0.0).abs() < 1e-10);
523 assert!((quadric[(3, 3)] - 1.0).abs() < 1e-10);
524 }
525
526 #[test]
527 fn test_tetrahedron_simplification() {
528 let simplifier = QuadricErrorSimplifier::new();
529 let vertices = vec![
531 Point3::new(0.0, 0.0, 0.0),
532 Point3::new(1.0, 0.0, 0.0),
533 Point3::new(0.5, 1.0, 0.0),
534 Point3::new(0.5, 0.5, 1.0),
535 ];
536 let faces = vec![
537 [0, 1, 2],
538 [0, 1, 3],
539 [0, 2, 3],
540 [1, 2, 3],
541 ];
542 let mesh = TriangleMesh::from_vertices_and_faces(vertices, faces);
543 let simplified = simplifier.simplify(&mesh, 0.5).unwrap();
544 assert!(simplified.face_count() <= mesh.face_count());
546 assert!(simplified.vertex_count() <= mesh.vertex_count());
547 }
548
549 #[test]
550 fn test_grid_boundary_preservation() {
551 let simplifier = QuadricErrorSimplifier::new();
553
554 let grid_size = 11;
556 let spacing = 400.0;
557 let z = -2000.0;
558
559 let mut vertices = Vec::new();
560 for y in 0..grid_size {
561 for x in 0..grid_size {
562 vertices.push(Point3::new(
563 x as f32 * spacing,
564 y as f32 * spacing,
565 z,
566 ));
567 }
568 }
569
570 let mut faces = Vec::new();
572 for y in 0..(grid_size - 1) {
573 for x in 0..(grid_size - 1) {
574 let top_left = y * grid_size + x;
575 let top_right = top_left + 1;
576 let bottom_left = (y + 1) * grid_size + x;
577 let bottom_right = bottom_left + 1;
578
579 faces.push([top_left, bottom_left, top_right]);
581 faces.push([top_right, bottom_left, bottom_right]);
582 }
583 }
584
585 let mesh = TriangleMesh::from_vertices_and_faces(vertices.clone(), faces.clone());
586
587 println!("Original mesh: {} vertices, {} faces", mesh.vertex_count(), mesh.face_count());
588
589 let mut original_boundary_verts = std::collections::HashSet::new();
591 for i in 0..grid_size {
592 original_boundary_verts.insert(i);
594 original_boundary_verts.insert((grid_size - 1) * grid_size + i);
596 original_boundary_verts.insert(i * grid_size);
598 original_boundary_verts.insert(i * grid_size + (grid_size - 1));
600 }
601
602 println!("Original boundary vertices: {}", original_boundary_verts.len());
603
604 let boundary_edges = simplifier.find_boundary_edges(&mesh);
606 println!("Detected boundary edges: {}", boundary_edges.len());
607
608 let mut detected_boundary_verts = std::collections::HashSet::new();
610 for &(v1, v2) in &boundary_edges {
611 detected_boundary_verts.insert(v1);
612 detected_boundary_verts.insert(v2);
613 }
614 println!("Detected boundary vertices: {}", detected_boundary_verts.len());
615
616 let simplified = simplifier.simplify(&mesh, 0.5).unwrap();
618
619 println!("Simplified mesh: {} vertices, {} faces", simplified.vertex_count(), simplified.face_count());
620
621 let mut boundary_positions = std::collections::HashSet::new();
624 for &idx in &original_boundary_verts {
625 let pos = vertices[idx];
626 boundary_positions.insert((
627 (pos.x * 100.0) as i32,
628 (pos.y * 100.0) as i32,
629 (pos.z * 100.0) as i32,
630 ));
631 }
632
633 let mut simplified_positions = std::collections::HashSet::new();
634 for &pos in &simplified.vertices {
635 simplified_positions.insert((
636 (pos.x * 100.0) as i32,
637 (pos.y * 100.0) as i32,
638 (pos.z * 100.0) as i32,
639 ));
640 }
641
642 let preserved_boundary_count = boundary_positions.intersection(&simplified_positions).count();
643 println!("Preserved boundary vertices: {}/{}", preserved_boundary_count, boundary_positions.len());
644
645 assert!(preserved_boundary_count as f32 / boundary_positions.len() as f32 > 0.9,
648 "Expected at least 90% of boundary vertices to be preserved, but only {}% were preserved",
649 (preserved_boundary_count as f32 / boundary_positions.len() as f32 * 100.0));
650 }
651}