1use super::functions::*;
6use std::collections::{HashMap, HashSet};
7
8#[derive(Debug, Clone)]
10pub struct EdgeCollapseRecord {
11 pub removed: usize,
13 pub target: usize,
15 pub deleted_triangles: Vec<[usize; 3]>,
17}
18pub struct ProgressiveMeshSimple {
20 pub current: SimpleMesh,
22 pub history: Vec<EdgeCollapseRecord>,
24}
25impl ProgressiveMeshSimple {
26 pub fn new(mesh: SimpleMesh) -> Self {
28 Self {
29 current: mesh,
30 history: Vec::new(),
31 }
32 }
33 pub fn collapse_edge(&mut self, src: usize, dst: usize) {
38 let mut deleted = Vec::new();
39 let mut remaining = Vec::new();
40 for tri in &self.current.triangles {
41 let has_src = tri.contains(&src);
42 let has_dst = tri.contains(&dst);
43 if has_src && has_dst {
44 deleted.push(*tri);
45 } else if has_src {
46 let new_tri = tri.map(|v| if v == src { dst } else { v });
47 remaining.push(new_tri);
48 } else {
49 remaining.push(*tri);
50 }
51 }
52 self.history.push(EdgeCollapseRecord {
53 removed: src,
54 target: dst,
55 deleted_triangles: deleted,
56 });
57 self.current.triangles = remaining;
58 }
59 pub fn n_collapses(&self) -> usize {
61 self.history.len()
62 }
63 pub fn n_triangles(&self) -> usize {
65 self.current.triangle_count()
66 }
67}
68pub struct ProgressiveMesh {
71 pub mesh: SimpleMesh,
73 pub history: Vec<CollapseRecord>,
75}
76impl ProgressiveMesh {
77 pub fn new(mesh: SimpleMesh) -> Self {
79 Self {
80 mesh,
81 history: Vec::new(),
82 }
83 }
84 pub fn collapse_one(&mut self) -> bool {
89 if self.mesh.triangle_count() <= 3 {
90 return false;
91 }
92 let quadrics = compute_vertex_quadrics(&self.mesh);
93 let mut edge_set: std::collections::HashSet<(usize, usize)> =
94 std::collections::HashSet::new();
95 for &[a, b, c] in &self.mesh.triangles {
96 edge_set.insert((a.min(b), a.max(b)));
97 edge_set.insert((b.min(c), b.max(c)));
98 edge_set.insert((a.min(c), a.max(c)));
99 }
100 let mut best_cost = f64::MAX;
101 let mut best_v1 = 0usize;
102 let mut best_v2 = 0usize;
103 let mut best_pos = [0.0f64; 3];
104 for (v1, v2) in &edge_set {
105 let (cost, pos) = edge_collapse_cost(
106 self.mesh.vertices[*v1],
107 self.mesh.vertices[*v2],
108 &quadrics[*v1],
109 &quadrics[*v2],
110 );
111 if cost < best_cost {
112 best_cost = cost;
113 best_v1 = *v1;
114 best_v2 = *v2;
115 best_pos = pos;
116 }
117 }
118 let old_pos = self.mesh.vertices[best_v1];
119 let record = CollapseRecord {
120 v_kept: best_v1,
121 v_removed: best_v2,
122 old_pos,
123 new_pos: best_pos,
124 };
125 collapse_edge(&mut self.mesh, best_v1, best_v2, best_pos);
126 self.history.push(record);
127 true
128 }
129 pub fn decimate_to(&mut self, target_triangles: usize) {
131 while self.mesh.triangle_count() > target_triangles {
132 if !self.collapse_one() {
133 break;
134 }
135 }
136 }
137}
138#[derive(Debug, Clone)]
140pub struct CollapseRecord {
141 pub v_kept: usize,
143 pub v_removed: usize,
145 pub old_pos: [f64; 3],
147 pub new_pos: [f64; 3],
149}
150#[derive(Debug, Clone, Copy, PartialEq)]
152pub struct MeshEdge {
153 pub v0: usize,
155 pub v1: usize,
157 pub cost: f64,
159}
160pub struct EdgePriorityQueue {
162 pub(super) heap: std::collections::BinaryHeap<MeshEdge>,
163}
164impl EdgePriorityQueue {
165 pub fn new() -> Self {
167 Self {
168 heap: std::collections::BinaryHeap::new(),
169 }
170 }
171 pub fn push(&mut self, edge: MeshEdge) {
173 self.heap.push(edge);
174 }
175 pub fn pop(&mut self) -> Option<MeshEdge> {
177 self.heap.pop()
178 }
179 pub fn len(&self) -> usize {
181 self.heap.len()
182 }
183 pub fn is_empty(&self) -> bool {
185 self.heap.is_empty()
186 }
187 pub fn from_mesh(mesh: &SimpleMesh) -> Self {
189 let mut q = Self::new();
190 let mut seen: HashSet<(usize, usize)> = HashSet::new();
191 for tri in &mesh.triangles {
192 for &(a, b) in &[(tri[0], tri[1]), (tri[1], tri[2]), (tri[0], tri[2])] {
193 let key = if a < b { (a, b) } else { (b, a) };
194 if seen.insert(key) {
195 let va = mesh.vertices[a];
196 let vb = mesh.vertices[b];
197 let cost =
198 (va[0] - vb[0]).powi(2) + (va[1] - vb[1]).powi(2) + (va[2] - vb[2]).powi(2);
199 q.push(MeshEdge {
200 v0: key.0,
201 v1: key.1,
202 cost,
203 });
204 }
205 }
206 }
207 q
208 }
209}
210#[derive(Clone)]
212pub struct SimpleMesh {
213 pub vertices: Vec<[f64; 3]>,
215 pub triangles: Vec<[usize; 3]>,
217}
218impl SimpleMesh {
219 pub fn new() -> Self {
221 Self {
222 vertices: Vec::new(),
223 triangles: Vec::new(),
224 }
225 }
226 pub fn add_vertex(&mut self, v: [f64; 3]) -> usize {
228 let idx = self.vertices.len();
229 self.vertices.push(v);
230 idx
231 }
232 pub fn add_triangle(&mut self, a: usize, b: usize, c: usize) {
234 self.triangles.push([a, b, c]);
235 }
236 pub fn vertex_count(&self) -> usize {
238 self.vertices.len()
239 }
240 pub fn triangle_count(&self) -> usize {
242 self.triangles.len()
243 }
244 pub fn triangle_normal(&self, t_idx: usize) -> [f64; 3] {
246 let [a, b, c] = self.triangles[t_idx];
247 let va = self.vertices[a];
248 let vb = self.vertices[b];
249 let vc = self.vertices[c];
250 let ab = [vb[0] - va[0], vb[1] - va[1], vb[2] - va[2]];
251 let ac = [vc[0] - va[0], vc[1] - va[1], vc[2] - va[2]];
252 let n = cross(ab, ac);
253 normalize(n)
254 }
255 pub fn triangle_area(&self, t_idx: usize) -> f64 {
257 let [a, b, c] = self.triangles[t_idx];
258 let va = self.vertices[a];
259 let vb = self.vertices[b];
260 let vc = self.vertices[c];
261 let ab = [vb[0] - va[0], vb[1] - va[1], vb[2] - va[2]];
262 let ac = [vc[0] - va[0], vc[1] - va[1], vc[2] - va[2]];
263 let cp = cross(ab, ac);
264 0.5 * length(cp)
265 }
266 pub fn surface_area(&self) -> f64 {
268 (0..self.triangle_count())
269 .map(|i| self.triangle_area(i))
270 .sum()
271 }
272}
273pub struct QuadricMatrix {
275 pub q: [[f64; 4]; 4],
277}
278impl QuadricMatrix {
279 pub fn zero() -> Self {
281 Self { q: [[0.0; 4]; 4] }
282 }
283 pub fn from_plane(a: f64, b: f64, c: f64, d: f64) -> Self {
286 let p = [a, b, c, d];
287 let mut q = [[0.0f64; 4]; 4];
288 for i in 0..4 {
289 for j in 0..4 {
290 q[i][j] = p[i] * p[j];
291 }
292 }
293 Self { q }
294 }
295 pub fn add(&self, other: &Self) -> Self {
297 let mut result = Self::zero();
298 for i in 0..4 {
299 for j in 0..4 {
300 result.q[i][j] = self.q[i][j] + other.q[i][j];
301 }
302 }
303 result
304 }
305 pub fn vertex_error(&self, v: [f64; 3]) -> f64 {
307 let h = [v[0], v[1], v[2], 1.0];
308 let mut result = 0.0;
309 for i in 0..4 {
310 for j in 0..4 {
311 result += h[i] * self.q[i][j] * h[j];
312 }
313 }
314 result
315 }
316}
317#[derive(Debug, Clone, Copy)]
321pub struct NormalCone {
322 pub axis: [f64; 3],
324 pub half_angle: f64,
326}
327impl NormalCone {
328 pub fn from_normal(n: [f64; 3]) -> Self {
330 Self {
331 axis: normalize3(n),
332 half_angle: 0.0,
333 }
334 }
335 pub fn merge(&self, other: &NormalCone) -> NormalCone {
337 let axis_new = normalize3(add3(self.axis, other.axis));
338 let angle1 = dot3(self.axis, axis_new).clamp(-1.0, 1.0).acos() + self.half_angle;
339 let angle2 = dot3(other.axis, axis_new).clamp(-1.0, 1.0).acos() + other.half_angle;
340 NormalCone {
341 axis: axis_new,
342 half_angle: angle1.max(angle2),
343 }
344 }
345 pub fn contains(&self, n: [f64; 3], tol: f64) -> bool {
347 let n_hat = normalize3(n);
348 let cos_angle = dot3(self.axis, n_hat).clamp(-1.0, 1.0);
349 let angle = cos_angle.acos();
350 angle <= self.half_angle + tol
351 }
352 pub fn half_angle_deg(&self) -> f64 {
354 self.half_angle.to_degrees()
355 }
356}
357#[derive(Debug, Clone, Copy, PartialEq)]
359pub enum EdgeFeature {
360 Smooth,
362 Crease,
364 Boundary,
366}
367#[derive(Debug, Clone, Default)]
369pub struct DecimationMetrics {
370 pub original_vertices: usize,
372 pub original_triangles: usize,
374 pub reduced_vertices: usize,
376 pub reduced_triangles: usize,
378 pub n_collapses: usize,
380 pub total_qem_cost: f64,
382 pub max_qem_cost: f64,
384}
385impl DecimationMetrics {
386 pub fn vertex_reduction_ratio(&self) -> f64 {
388 if self.original_vertices == 0 {
389 return 0.0;
390 }
391 1.0 - self.reduced_vertices as f64 / self.original_vertices as f64
392 }
393 pub fn triangle_reduction_ratio(&self) -> f64 {
395 if self.original_triangles == 0 {
396 return 0.0;
397 }
398 1.0 - self.reduced_triangles as f64 / self.original_triangles as f64
399 }
400 pub fn avg_qem_cost(&self) -> f64 {
402 if self.n_collapses == 0 {
403 return 0.0;
404 }
405 self.total_qem_cost / self.n_collapses as f64
406 }
407}
408pub struct QemDecimation {
411 pub mesh: SimpleMesh,
413 pub(super) quadrics: Vec<QuadricMatrix>,
415}
416impl QemDecimation {
417 pub fn new(mesh: SimpleMesh) -> Self {
419 let quadrics = compute_vertex_quadrics(&mesh);
420 Self { mesh, quadrics }
421 }
422 pub fn recompute_quadrics(&mut self) {
424 self.quadrics = compute_vertex_quadrics(&self.mesh);
425 }
426 pub fn compute_error_threshold(&self, scale_factor: f64) -> f64 {
435 let avg_edge = average_edge_length(&self.mesh);
436 if avg_edge < 1e-12 {
437 return 0.0;
438 }
439 let scale_sq = avg_edge * avg_edge;
440 let n_verts = self.mesh.vertex_count();
441 if n_verts < 2 {
442 return scale_factor * scale_sq;
443 }
444 let mut sampled_cost = 0.0f64;
445 let mut sampled_count = 0usize;
446 let step = (n_verts / 32).max(1);
447 for tri in self.mesh.triangles.iter().step_by(step) {
448 let v0 = tri[0];
449 let v1 = tri[1];
450 if v0 >= self.quadrics.len() || v1 >= self.quadrics.len() {
451 continue;
452 }
453 let (cost, _) = edge_collapse_cost(
454 self.mesh.vertices[v0],
455 self.mesh.vertices[v1],
456 &self.quadrics[v0],
457 &self.quadrics[v1],
458 );
459 sampled_cost += cost;
460 sampled_count += 1;
461 }
462 let mean_cost = if sampled_count > 0 {
463 sampled_cost / sampled_count as f64
464 } else {
465 1.0
466 };
467 (scale_factor * scale_sq * mean_cost).max(1e-12)
468 }
469 pub fn preserve_boundary(&mut self, threshold: f64) -> usize {
477 let boundary_edges = find_boundary_edges(&self.mesh);
478 let boundary_set: HashSet<(usize, usize)> = boundary_edges
479 .iter()
480 .flat_map(|&(a, b)| [(a.min(b), a.max(b)), (a.min(b), a.max(b))])
481 .collect();
482 let mut n_collapses = 0usize;
483 let mut keep_going = true;
484 while keep_going {
485 keep_going = false;
486 self.recompute_quadrics();
487 let mut best: Option<(f64, [f64; 3], usize, usize)> = None;
488 for tri in &self.mesh.triangles {
489 for k in 0..3 {
490 let v0 = tri[k];
491 let v1 = tri[(k + 1) % 3];
492 let key = (v0.min(v1), v0.max(v1));
493 if boundary_set.contains(&key) {
494 continue;
495 }
496 if v0 >= self.quadrics.len() || v1 >= self.quadrics.len() {
497 continue;
498 }
499 let (cost, opt) = edge_collapse_cost(
500 self.mesh.vertices[v0],
501 self.mesh.vertices[v1],
502 &self.quadrics[v0],
503 &self.quadrics[v1],
504 );
505 if cost <= threshold && best.as_ref().is_none_or(|b| cost < b.0) {
506 best = Some((cost, opt, v0, v1));
507 }
508 }
509 }
510 if let Some((_cost, opt, v0, v1)) = best {
511 self.mesh.vertices[v0] = opt;
512 for tri in &mut self.mesh.triangles {
513 for idx in tri.iter_mut() {
514 if *idx == v1 {
515 *idx = v0;
516 }
517 }
518 }
519 self.mesh
520 .triangles
521 .retain(|tri| tri[0] != tri[1] && tri[1] != tri[2] && tri[0] != tri[2]);
522 n_collapses += 1;
523 keep_going = true;
524 }
525 }
526 n_collapses
527 }
528 pub fn compute_feature_score(&self) -> HashMap<(usize, usize), f64> {
537 let mut edge_faces: HashMap<(usize, usize), Vec<usize>> = HashMap::new();
538 for (fi, tri) in self.mesh.triangles.iter().enumerate() {
539 for k in 0..3 {
540 let a = tri[k];
541 let b = tri[(k + 1) % 3];
542 let key = (a.min(b), a.max(b));
543 edge_faces.entry(key).or_default().push(fi);
544 }
545 }
546 let mut scores: HashMap<(usize, usize), f64> = HashMap::new();
547 for (&edge, faces) in &edge_faces {
548 if faces.len() != 2 {
549 scores.insert(edge, f64::INFINITY);
550 continue;
551 }
552 let n0 = self.mesh.triangle_normal(faces[0]);
553 let n1 = self.mesh.triangle_normal(faces[1]);
554 let cos_a = (dot3(n0, n1)).clamp(-1.0, 1.0);
555 let score = 1.0 - cos_a;
556 scores.insert(edge, score);
557 }
558 scores
559 }
560}
561#[derive(Debug, Clone)]
563pub struct DecimationStats {
564 pub original_vertices: usize,
566 pub original_triangles: usize,
568 pub result_vertices: usize,
570 pub result_triangles: usize,
572 pub reduction_ratio: f64,
574 pub total_qem_error: f64,
576}
577impl DecimationStats {
578 pub fn compute(
580 original_v: usize,
581 original_t: usize,
582 result: &SimpleMesh,
583 total_qem_error: f64,
584 ) -> Self {
585 let reduction_ratio = if original_t > 0 {
586 1.0 - result.triangle_count() as f64 / original_t as f64
587 } else {
588 0.0
589 };
590 Self {
591 original_vertices: original_v,
592 original_triangles: original_t,
593 result_vertices: result.vertex_count(),
594 result_triangles: result.triangle_count(),
595 reduction_ratio,
596 total_qem_error,
597 }
598 }
599}