plotters_backend/rasterizer/
path.rs

1use crate::BackendCoord;
2
3fn get_dir_vector(from: BackendCoord, to: BackendCoord, flag: bool) -> ((f64, f64), (f64, f64)) {
4    let v = (i64::from(to.0 - from.0), i64::from(to.1 - from.1));
5    let l = ((v.0 * v.0 + v.1 * v.1) as f64).sqrt();
6
7    let v = (v.0 as f64 / l, v.1 as f64 / l);
8
9    if flag {
10        (v, (v.1, -v.0))
11    } else {
12        (v, (-v.1, v.0))
13    }
14}
15
16fn compute_polygon_vertex(triple: &[BackendCoord; 3], d: f64) -> BackendCoord {
17    let (a_t, a_n) = get_dir_vector(triple[0], triple[1], false);
18    let (b_t, b_n) = get_dir_vector(triple[2], triple[1], true);
19
20    let a_p = (
21        f64::from(triple[1].0) + d * a_n.0,
22        f64::from(triple[1].1) + d * a_n.1,
23    );
24    let b_p = (
25        f64::from(triple[1].0) + d * b_n.0,
26        f64::from(triple[1].1) + d * b_n.1,
27    );
28
29    // u * a_t + a_p = v * b_t + b_p
30    // u * a_t.0 - v * b_t.0 = b_p.0 - a_p.0
31    // u * a_t.1 - v * b_t.1 = b_p.1 - a_p.1
32    if a_p.0 as i32 == b_p.0 as i32 && a_p.1 as i32 == b_p.1 as i32 {
33        return (a_p.0 as i32, a_p.1 as i32);
34    }
35
36    let a0 = a_t.0;
37    let b0 = -b_t.0;
38    let c0 = b_p.0 - a_p.0;
39    let a1 = a_t.1;
40    let b1 = -b_t.1;
41    let c1 = b_p.1 - a_p.1;
42
43    // This is the coner case that
44    if (a0 * b1 - a1 * b0).abs() < 1e-10 {
45        return (a_p.0 as i32, a_p.1 as i32);
46    }
47
48    let u = (c0 * b1 - c1 * b0) / (a0 * b1 - a1 * b0);
49
50    let x = a_p.0 + u * a_t.0;
51    let y = a_p.1 + u * a_t.1;
52
53    (x.round() as i32, y.round() as i32)
54}
55
56fn traverse_vertices<'a>(
57    mut vertices: impl Iterator<Item = &'a BackendCoord>,
58    width: u32,
59    mut op: impl FnMut(BackendCoord),
60) {
61    let mut a = vertices.next().unwrap();
62    let mut b = vertices.next().unwrap();
63
64    while a == b {
65        a = b;
66        if let Some(new_b) = vertices.next() {
67            b = new_b;
68        } else {
69            return;
70        }
71    }
72
73    let (_, n) = get_dir_vector(*a, *b, false);
74
75    op((
76        (f64::from(a.0) + n.0 * f64::from(width) / 2.0).round() as i32,
77        (f64::from(a.1) + n.1 * f64::from(width) / 2.0).round() as i32,
78    ));
79
80    let mut recent = [(0, 0), *a, *b];
81
82    for p in vertices {
83        if *p == recent[2] {
84            continue;
85        }
86        recent.swap(0, 1);
87        recent.swap(1, 2);
88        recent[2] = *p;
89        op(compute_polygon_vertex(&recent, f64::from(width) / 2.0));
90    }
91
92    let b = recent[1];
93    let a = recent[2];
94
95    let (_, n) = get_dir_vector(a, b, true);
96
97    op((
98        (f64::from(a.0) + n.0 * f64::from(width) / 2.0).round() as i32,
99        (f64::from(a.1) + n.1 * f64::from(width) / 2.0).round() as i32,
100    ));
101}
102
103/// Covert a path with >1px stroke width into polygon.
104pub fn polygonize(vertices: &[BackendCoord], stroke_width: u32) -> Vec<BackendCoord> {
105    if vertices.len() < 2 {
106        return vec![];
107    }
108
109    let mut ret = vec![];
110
111    traverse_vertices(vertices.iter(), stroke_width, |v| ret.push(v));
112    traverse_vertices(vertices.iter().rev(), stroke_width, |v| ret.push(v));
113
114    ret
115}