four_bar/plot/
dashed_line.rs

1use super::{to_f, to_i};
2use plotters::{
3    element::{Drawable, PointCollection},
4    style::{ShapeStyle, SizeDesc},
5};
6use plotters_backend::{BackendCoord, DrawingBackend, DrawingErrorKind};
7
8pub(crate) struct DashedPath<I: Iterator + Clone, Size: SizeDesc> {
9    points: I,
10    size: Size,
11    spacing: Size,
12    style: ShapeStyle,
13}
14
15impl<I: Iterator + Clone, Size: SizeDesc> DashedPath<I, Size> {
16    pub(crate) fn new<I0, S>(points: I0, size: Size, spacing: Size, style: S) -> Self
17    where
18        I0: IntoIterator<IntoIter = I>,
19        S: Into<ShapeStyle>,
20    {
21        Self {
22            points: points.into_iter(),
23            size,
24            spacing,
25            style: style.into(),
26        }
27    }
28
29    pub(crate) fn series(self) -> std::iter::Once<Self> {
30        std::iter::once(self)
31    }
32}
33
34impl<'a, I: Iterator + Clone, Size: SizeDesc> PointCollection<'a, I::Item>
35    for &'a DashedPath<I, Size>
36{
37    type Point = I::Item;
38    type IntoIter = I;
39
40    fn point_iter(self) -> Self::IntoIter {
41        self.points.clone()
42    }
43}
44
45impl<I0: Iterator + Clone, Size: SizeDesc, DB: DrawingBackend> Drawable<DB>
46    for DashedPath<I0, Size>
47{
48    fn draw<I: Iterator<Item = BackendCoord>>(
49        &self,
50        mut points: I,
51        backend: &mut DB,
52        ps: (u32, u32),
53    ) -> Result<(), DrawingErrorKind<DB::ErrorType>> {
54        let mut start = match points.next() {
55            Some(c) => to_f(c),
56            None => return Ok(()),
57        };
58        let size = self.size.in_pixels(&ps).max(0) as f32;
59        if size == 0. {
60            return Ok(());
61        }
62        let spacing = self.spacing.in_pixels(&ps).max(0) as f32;
63        let mut dist = 0.;
64        let mut is_solid = true;
65        let mut queue = vec![to_i(start)];
66        for curr in points {
67            let end = to_f(curr);
68            // Loop for solid and spacing
69            while start != end {
70                let (dx, dy) = (end.0 - start.0, end.1 - start.1);
71                let d = dx.hypot(dy);
72                let size = if is_solid { size } else { spacing };
73                let left = size - dist;
74                // Set next point to `start`
75                if left < d {
76                    let t = left / d;
77                    start = (start.0 + dx * t, start.1 + dy * t);
78                    dist += left;
79                } else {
80                    start = end;
81                    dist += d;
82                }
83                // Draw if needed
84                if is_solid {
85                    queue.push(to_i(start));
86                }
87                if size <= dist {
88                    if is_solid {
89                        backend.draw_path(queue.drain(..), &self.style)?;
90                    } else {
91                        queue.push(to_i(start));
92                    }
93                    dist = 0.;
94                    is_solid = !is_solid;
95                }
96            }
97        }
98        if queue.len() > 1 {
99            backend.draw_path(queue, &self.style)?;
100        }
101        Ok(())
102    }
103}