doc_quad/geom/
simplify.rs1use glam::Vec2;
3
4pub struct GeometrySimplifier;
5
6impl GeometrySimplifier {
7 pub fn simplify_to_quad(contour: &[Vec2]) -> Option<(Vec<Vec2>, bool)> {
9 if contour.len() < 4 {
10 return None;
11 }
12
13 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 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}