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