Skip to main content

doc_quad/geom/
simplify.rs

1// src/geom/simplify.rs
2use glam::Vec2;
3
4pub struct GeometrySimplifier;
5
6impl GeometrySimplifier {
7    /// 简化轮廓为四边形(恰好 4 个顶点,闭合后返回 5 个点)
8    pub fn simplify_to_quad(contour: &[Vec2]) -> Option<(Vec<Vec2>, bool)> {
9        if contour.len() < 4 {
10            return None;
11        }
12
13        // 去重
14        let mut pts: Vec<Vec2> = Vec::with_capacity(contour.len());
15        for &v in contour {
16            if pts.is_empty() || pts.last().unwrap().distance_squared(v) > 1e-4 {
17                pts.push(v);
18            }
19        }
20
21        if pts.len() < 4 {
22            return None;
23        }
24
25        let is_initially_closed = pts.first().unwrap().distance_squared(*pts.last().unwrap()) < 1e-4;
26        if is_initially_closed {
27            pts.pop();
28        }
29
30        if pts.len() < 4 {
31            return None;
32        }
33
34        if pts.len() == 4 {
35            let mut result = pts.clone();
36            result.push(result[0]); 
37            return Some((result, true));
38        }
39
40        // Visvalingam-Whyatt 核心坍缩逻辑
41        while pts.len() > 4 {
42            let mut min_area = f32::MAX;
43            let mut min_idx = 0;
44
45            let n = pts.len();
46            for i in 0..n {
47                let prev = pts[(i + n - 1) % n];
48                let curr = pts[i];
49                let next = pts[(i + 1) % n];
50
51                let area = Self::triangle_area(prev, curr, next);
52                if area < min_area {
53                    min_area = area;
54                    min_idx = i;
55                }
56            }
57
58            pts.remove(min_idx);
59        }
60
61        pts.push(pts[0]);
62        Some((pts, true))
63    }
64
65    #[inline]
66    fn triangle_area(a: Vec2, b: Vec2, c: Vec2) -> f32 {
67        ((a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y)) / 2.0).abs()
68    }
69}