visioncortex/path/
simplify.rs

1use crate::{PathI32, PointI32};
2
3use super::util::signed_area;
4
5pub(crate) struct PathSimplify;
6
7#[derive(Copy, Clone, Debug)]
8pub enum PathSimplifyMode {
9    None,
10    Polygon,
11    Spline,
12}
13
14#[derive(Copy, Clone)]
15pub enum Dir {
16    Up,
17    Right,
18    Down,
19    Left
20}
21
22impl Default for PathSimplifyMode {
23    fn default() -> Self {
24        Self::None
25    }
26}
27
28impl PathSimplify {
29
30    /// Returns a copy of a path after removing 1-pixel staircases.
31    /// 
32    /// Clockwiseness of path must be indicated to perform outset
33    pub fn remove_staircase(path: &PathI32, clockwise: bool) -> PathI32 {
34        let path = &path.path;
35        let len = path.len();
36
37        let segment_length = |i: usize, j: usize| -> i32 {
38            (path[i].x - path[j].x).abs() + (path[i].y - path[j].y).abs()
39        };
40
41        let mut result = PathI32::new();
42        if len == 0 {
43            return result;
44        }
45        for i in 0..len {
46            let j = (i + 1) % len;
47            let h = if i > 0 { i - 1 } else { len - 1 };
48            let keep = if i == 0 || i == len - 1 {
49                true
50            } else if segment_length(i, h) == 1 || segment_length(i, j) == 1 {
51                let area = signed_area(path[h], path[i], path[j]);
52                area != 0 && (area > 0) == clockwise
53            } else {
54                true
55            };
56            if keep {
57                result.add(path[i]);
58            }
59        }
60        result
61    }
62
63    pub fn limit_penalties(path: &PathI32) -> PathI32 {
64        let tolerance = 1.0;
65        let path = &path.path;
66        let len = path.len();
67        let past_delta = |from: usize, to: usize| -> f64 {
68            (from..to).skip(1).map(|i| {
69                Self::evaluate_penalty(path[from], path[i], path[to])
70            }).fold(0.0, |a, b| a.max(b)) // find max
71        };
72
73        let mut result = PathI32::new();
74        if len == 0 {
75            return result;
76        }
77        let mut last = 0;
78        for i in 0..len {
79            if i == 0 {
80                result.add(path[i]);
81            } else if i == last + 1 {
82                continue;
83            } else if past_delta(last, i) >= tolerance {
84                last = i - 1;
85                result.add(path[i-1]);
86            }
87            if i == len - 1 {
88                result.add(path[i]);
89            }
90        }
91        result
92    }
93
94    fn evaluate_penalty(a: PointI32, b: PointI32, c: PointI32) -> f64 {
95        let sq = |x| { (x * x) as f64 };
96        let l1 = (sq(a.x - b.x) + sq(a.y - b.y)).sqrt();
97        let l2 = (sq(b.x - c.x) + sq(b.y - c.y)).sqrt();
98        let l3 = (sq(c.x - a.x) + sq(c.y - a.y)).sqrt();
99        let p = (l1 + l2 + l3) / 2.0;
100        let area = (p * (p - l1) * (p - l2) * (p - l3)).sqrt();
101        area * area / l3
102    }
103}