Skip to main content

oxiphysics_geometry/decimation/
functions_2.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5#[allow(unused_imports)]
6use super::functions::*;
7
8#[cfg(test)]
9mod tests_decimation_extended {
10    use super::*;
11    use crate::decimation::DecimationMetrics;
12    use crate::decimation::EdgeFeature;
13    use crate::decimation::EdgePriorityQueue;
14    use crate::decimation::MeshEdge;
15    use crate::decimation::NormalCone;
16    use crate::decimation::ProgressiveMeshSimple;
17
18    use crate::decimation::SimpleMesh;
19    fn small_mesh() -> SimpleMesh {
20        let mut m = SimpleMesh::new();
21        m.add_vertex([0.0, 0.0, 0.0]);
22        m.add_vertex([1.0, 0.0, 0.0]);
23        m.add_vertex([1.0, 1.0, 0.0]);
24        m.add_vertex([0.0, 1.0, 0.0]);
25        m.add_triangle(0, 1, 2);
26        m.add_triangle(0, 2, 3);
27        m
28    }
29    #[test]
30    fn test_vertex_clustering_large_cell_merges_all() {
31        let m = small_mesh();
32        let reduced = vertex_clustering_decimate(&m, 100.0);
33        assert_eq!(reduced.vertex_count(), 1);
34        assert_eq!(reduced.triangle_count(), 0);
35    }
36    #[test]
37    fn test_vertex_clustering_small_cell_preserves() {
38        let m = small_mesh();
39        let reduced = vertex_clustering_decimate(&m, 0.01);
40        assert_eq!(reduced.vertex_count(), m.vertex_count());
41    }
42    #[test]
43    fn test_vertex_clustering_empty_mesh() {
44        let m = SimpleMesh::new();
45        let reduced = vertex_clustering_decimate(&m, 1.0);
46        assert_eq!(reduced.vertex_count(), 0);
47    }
48    #[test]
49    fn test_vertex_clustering_mid_cell_reduces() {
50        let mut m = SimpleMesh::new();
51        m.add_vertex([0.0, 0.0, 0.0]);
52        m.add_vertex([0.01, 0.0, 0.0]);
53        m.add_vertex([1.0, 1.0, 0.0]);
54        m.add_triangle(0, 1, 2);
55        let reduced = vertex_clustering_decimate(&m, 0.1);
56        assert!(reduced.vertex_count() < m.vertex_count());
57    }
58    #[test]
59    fn test_progressive_mesh_initial() {
60        let m = small_mesh();
61        let pm = ProgressiveMeshSimple::new(m);
62        assert_eq!(pm.n_collapses(), 0);
63        assert_eq!(pm.n_triangles(), 2);
64    }
65    #[test]
66    fn test_progressive_mesh_collapse_edge() {
67        let m = small_mesh();
68        let mut pm = ProgressiveMeshSimple::new(m);
69        pm.collapse_edge(2, 1);
70        assert_eq!(pm.n_collapses(), 1);
71        assert!(pm.history[0].removed == 2);
72    }
73    #[test]
74    fn test_progressive_mesh_multiple_collapses() {
75        let m = small_mesh();
76        let mut pm = ProgressiveMeshSimple::new(m);
77        pm.collapse_edge(2, 1);
78        pm.collapse_edge(3, 0);
79        assert_eq!(pm.n_collapses(), 2);
80    }
81    #[test]
82    fn test_edge_priority_queue_empty() {
83        let q = EdgePriorityQueue::new();
84        assert!(q.is_empty());
85        assert_eq!(q.len(), 0);
86    }
87    #[test]
88    fn test_edge_priority_queue_push_pop() {
89        let mut q = EdgePriorityQueue::new();
90        q.push(MeshEdge {
91            v0: 0,
92            v1: 1,
93            cost: 2.0,
94        });
95        q.push(MeshEdge {
96            v0: 1,
97            v1: 2,
98            cost: 0.5,
99        });
100        q.push(MeshEdge {
101            v0: 0,
102            v1: 2,
103            cost: 1.0,
104        });
105        let e = q.pop().unwrap();
106        assert!((e.cost - 0.5).abs() < 1e-12, "cost={}", e.cost);
107    }
108    #[test]
109    fn test_edge_priority_queue_from_mesh() {
110        let m = small_mesh();
111        let q = EdgePriorityQueue::from_mesh(&m);
112        assert_eq!(q.len(), 5);
113    }
114    #[test]
115    fn test_edge_priority_queue_ordering() {
116        let mut q = EdgePriorityQueue::new();
117        for i in 0..10 {
118            q.push(MeshEdge {
119                v0: 0,
120                v1: i,
121                cost: (10 - i) as f64,
122            });
123        }
124        let mut last_cost = f64::NEG_INFINITY;
125        while let Some(e) = q.pop() {
126            assert!(e.cost >= last_cost);
127            last_cost = e.cost;
128        }
129    }
130    #[test]
131    fn test_normal_cone_from_normal() {
132        let nc = NormalCone::from_normal([0.0, 0.0, 1.0]);
133        assert!((nc.axis[2] - 1.0).abs() < 1e-10);
134        assert_eq!(nc.half_angle, 0.0);
135    }
136    #[test]
137    fn test_normal_cone_contains_same() {
138        let nc = NormalCone::from_normal([0.0, 0.0, 1.0]);
139        assert!(nc.contains([0.0, 0.0, 1.0], 1e-6));
140    }
141    #[test]
142    fn test_normal_cone_excludes_opposite() {
143        let nc = NormalCone::from_normal([0.0, 0.0, 1.0]);
144        assert!(!nc.contains([0.0, 0.0, -1.0], 0.01));
145    }
146    #[test]
147    fn test_normal_cone_merge() {
148        let nc1 = NormalCone::from_normal([1.0, 0.0, 0.0]);
149        let nc2 = NormalCone::from_normal([0.0, 1.0, 0.0]);
150        let merged = nc1.merge(&nc2);
151        assert!(merged.contains([1.0, 0.0, 0.0], 1e-6));
152        assert!(merged.contains([0.0, 1.0, 0.0], 1e-6));
153    }
154    #[test]
155    fn test_normal_cone_half_angle_deg() {
156        let nc = NormalCone {
157            axis: [0.0, 0.0, 1.0],
158            half_angle: std::f64::consts::PI / 4.0,
159        };
160        assert!((nc.half_angle_deg() - 45.0).abs() < 1e-10);
161    }
162    #[test]
163    fn test_classify_edge_smooth() {
164        let n0 = [0.0, 0.0, 1.0];
165        let n1 = [0.0, 0.0, 1.0];
166        let feat = classify_edge(n0, n1, 30.0);
167        assert_eq!(feat, EdgeFeature::Smooth);
168    }
169    #[test]
170    fn test_classify_edge_crease() {
171        let n0 = [0.0, 0.0, 1.0];
172        let n1 = [0.0, 1.0, 0.0];
173        let feat = classify_edge(n0, n1, 30.0);
174        assert_eq!(feat, EdgeFeature::Crease);
175    }
176    #[test]
177    fn test_feature_aware_cost_smooth() {
178        let cost = feature_aware_cost(1.0, EdgeFeature::Smooth, 100.0, 1000.0);
179        assert!((cost - 1.0).abs() < 1e-12);
180    }
181    #[test]
182    fn test_feature_aware_cost_crease() {
183        let cost = feature_aware_cost(1.0, EdgeFeature::Crease, 50.0, 1000.0);
184        assert!((cost - 50.0).abs() < 1e-12);
185    }
186    #[test]
187    fn test_feature_aware_cost_boundary() {
188        let cost = feature_aware_cost(2.0, EdgeFeature::Boundary, 50.0, 100.0);
189        assert!((cost - 200.0).abs() < 1e-12);
190    }
191    #[test]
192    fn test_decimation_stats_default() {
193        let s = DecimationMetrics::default();
194        assert_eq!(s.vertex_reduction_ratio(), 0.0);
195        assert_eq!(s.triangle_reduction_ratio(), 0.0);
196        assert_eq!(s.avg_qem_cost(), 0.0);
197    }
198    #[test]
199    fn test_decimation_stats_reduction() {
200        let s = DecimationMetrics {
201            original_vertices: 100,
202            original_triangles: 200,
203            reduced_vertices: 50,
204            reduced_triangles: 100,
205            n_collapses: 50,
206            total_qem_cost: 10.0,
207            max_qem_cost: 0.5,
208        };
209        assert!((s.vertex_reduction_ratio() - 0.5).abs() < 1e-10);
210        assert!((s.triangle_reduction_ratio() - 0.5).abs() < 1e-10);
211        assert!((s.avg_qem_cost() - 0.2).abs() < 1e-10);
212    }
213    #[test]
214    fn test_collect_decimation_metrics() {
215        let orig = small_mesh();
216        let mut pm = ProgressiveMeshSimple::new(orig.clone());
217        pm.collapse_edge(2, 1);
218        let stats = collect_decimation_metrics(&orig, &pm.current, pm.n_collapses(), 0.5, 0.5);
219        assert_eq!(stats.original_vertices, 4);
220        assert_eq!(stats.n_collapses, 1);
221    }
222    #[test]
223    fn test_mesh_edge_min_heap_ordering() {
224        use std::collections::BinaryHeap;
225        let mut heap = BinaryHeap::new();
226        heap.push(MeshEdge {
227            v0: 0,
228            v1: 1,
229            cost: 5.0,
230        });
231        heap.push(MeshEdge {
232            v0: 0,
233            v1: 2,
234            cost: 1.0,
235        });
236        heap.push(MeshEdge {
237            v0: 1,
238            v1: 2,
239            cost: 3.0,
240        });
241        let e = heap.pop().unwrap();
242        assert!(
243            (e.cost - 1.0).abs() < 1e-12,
244            "Expected min cost 1.0, got {}",
245            e.cost
246        );
247    }
248}
249#[cfg(test)]
250mod tests_qem_decimation {
251
252    use crate::decimation::QemDecimation;
253    use crate::decimation::SimpleMesh;
254    /// A flat quad: 4 vertices, 2 triangles (all interior edge).
255    fn quad_mesh() -> SimpleMesh {
256        let mut m = SimpleMesh::new();
257        m.add_vertex([0.0, 0.0, 0.0]);
258        m.add_vertex([1.0, 0.0, 0.0]);
259        m.add_vertex([1.0, 1.0, 0.0]);
260        m.add_vertex([0.0, 1.0, 0.0]);
261        m.add_triangle(0, 1, 2);
262        m.add_triangle(0, 2, 3);
263        m
264    }
265    /// A simple mesh with a sharp crease (two perpendicular quads).
266    fn crease_mesh() -> SimpleMesh {
267        let mut m = SimpleMesh::new();
268        m.add_vertex([0.0, 0.0, 0.0]);
269        m.add_vertex([1.0, 0.0, 0.0]);
270        m.add_vertex([1.0, 1.0, 0.0]);
271        m.add_vertex([0.0, 1.0, 0.0]);
272        m.add_vertex([0.0, 0.0, 1.0]);
273        m.add_vertex([1.0, 0.0, 1.0]);
274        m.add_triangle(0, 1, 2);
275        m.add_triangle(0, 2, 3);
276        m.add_triangle(0, 1, 4);
277        m.add_triangle(1, 5, 4);
278        m
279    }
280    #[test]
281    fn test_error_threshold_positive() {
282        let qd = QemDecimation::new(quad_mesh());
283        let thr = qd.compute_error_threshold(1.0);
284        assert!(thr > 0.0, "threshold must be positive, got {thr}");
285    }
286    #[test]
287    fn test_error_threshold_scales_with_factor() {
288        let qd = QemDecimation::new(crease_mesh());
289        let thr1 = qd.compute_error_threshold(0.001);
290        let thr2 = qd.compute_error_threshold(100.0);
291        assert!(thr2 >= thr1, "larger factor → larger or equal threshold");
292    }
293    #[test]
294    fn test_error_threshold_empty_mesh() {
295        let qd = QemDecimation::new(SimpleMesh::new());
296        let thr = qd.compute_error_threshold(1.0);
297        assert_eq!(thr, 0.0, "empty mesh → zero threshold");
298    }
299    #[test]
300    fn test_error_threshold_finite() {
301        let qd = QemDecimation::new(quad_mesh());
302        let thr = qd.compute_error_threshold(0.5);
303        assert!(thr.is_finite(), "threshold must be finite, got {thr}");
304    }
305    #[test]
306    fn test_preserve_boundary_no_collapse_with_negative_threshold() {
307        let mut qd = QemDecimation::new(quad_mesh());
308        let before = qd.mesh.triangle_count();
309        let n = qd.preserve_boundary(-1.0);
310        assert_eq!(n, 0, "negative threshold should collapse nothing");
311        assert_eq!(qd.mesh.triangle_count(), before);
312    }
313    #[test]
314    fn test_preserve_boundary_collapses_with_large_threshold() {
315        let mut qd = QemDecimation::new(quad_mesh());
316        let n = qd.preserve_boundary(1e10);
317        let _ = n;
318    }
319    #[test]
320    fn test_preserve_boundary_result_is_valid_mesh() {
321        let mut qd = QemDecimation::new(quad_mesh());
322        qd.preserve_boundary(1e6);
323        for tri in &qd.mesh.triangles {
324            for &vi in tri {
325                assert!(vi < qd.mesh.vertices.len(), "invalid vertex index {vi}");
326            }
327        }
328    }
329    #[test]
330    fn test_feature_score_flat_mesh_near_zero() {
331        let qd = QemDecimation::new(quad_mesh());
332        let scores = qd.compute_feature_score();
333        assert!(!scores.is_empty(), "should have scores for all edges");
334        for (&(_a, _b), &s) in &scores {
335            if s.is_finite() {
336                assert!(s >= 0.0, "score must be non-negative, got {s}");
337            }
338        }
339    }
340    #[test]
341    fn test_feature_score_crease_mesh_high_score() {
342        let qd = QemDecimation::new(crease_mesh());
343        let scores = qd.compute_feature_score();
344        let max_score = scores
345            .values()
346            .filter(|v| v.is_finite())
347            .cloned()
348            .fold(0.0f64, f64::max);
349        assert!(
350            max_score > 0.5,
351            "crease mesh should have a high feature score, got {max_score}"
352        );
353    }
354    #[test]
355    fn test_feature_score_boundary_edges_infinite() {
356        let mut m = SimpleMesh::new();
357        m.add_vertex([0.0, 0.0, 0.0]);
358        m.add_vertex([1.0, 0.0, 0.0]);
359        m.add_vertex([0.5, 1.0, 0.0]);
360        m.add_triangle(0, 1, 2);
361        let qd = QemDecimation::new(m);
362        let scores = qd.compute_feature_score();
363        for (&_, &s) in &scores {
364            assert!(
365                s.is_infinite(),
366                "boundary edge should have infinite score, got {s}"
367            );
368        }
369    }
370}