plotters_arrows/
lib.rs

1//! # plotters-arrows
2//!
3//! Simple arrow shapes for [plotters](::plotters) crate.
4
5use plotters::element::{Drawable, PointCollection};
6use plotters::prelude::*;
7use plotters::style::SizeDesc;
8use plotters_backend::{BackendCoord, DrawingErrorKind};
9
10#[derive(Clone)]
11pub struct ThinArrow<Coord, Size: SizeDesc> {
12    points: [Coord; 2],
13    head: Size,
14    width: Size,
15    style: ShapeStyle,
16}
17
18impl<Coord> ThinArrow<Coord, i32> {
19    pub fn new(nock: Coord, tip: Coord, style: impl Into<ShapeStyle>) -> Self {
20        Self {
21            points: [nock, tip],
22            head: 5,
23            width: 5,
24            style: style.into(),
25        }
26    }
27}
28
29impl<Coord, Size: SizeDesc> ThinArrow<Coord, Size> {
30    pub fn new_detail(
31        nock: Coord,
32        tip: Coord,
33        head: Size,
34        width: Size,
35        style: impl Into<ShapeStyle>,
36    ) -> Self {
37        Self {
38            points: [nock, tip],
39            head,
40            width,
41            style: style.into(),
42        }
43    }
44
45    pub fn head(self, head: Size) -> Self {
46        Self { head, ..self }
47    }
48
49    pub fn width(self, width: Size) -> Self {
50        Self { width, ..self }
51    }
52}
53
54impl<'a, Coord: 'a, Size: SizeDesc> PointCollection<'a, Coord> for &'a ThinArrow<Coord, Size> {
55    type Point = &'a Coord;
56
57    type IntoIter = &'a [Coord];
58
59    fn point_iter(self) -> Self::IntoIter {
60        &self.points
61    }
62}
63
64impl<DB: DrawingBackend, Coord, Size: SizeDesc> Drawable<DB> for ThinArrow<Coord, Size> {
65    fn draw<I: Iterator<Item = BackendCoord>>(
66        &self,
67        mut pos: I,
68        backend: &mut DB,
69        parent_dim: (u32, u32),
70    ) -> Result<(), DrawingErrorKind<DB::ErrorType>> {
71        let nock = pos.next();
72        let tip = pos.next();
73        debug_assert!(nock.is_some());
74        debug_assert!(tip.is_some());
75        if let (Some(nock), Some(tip)) = (nock, tip) {
76            let head = self.head.in_pixels(&parent_dim) as f64;
77            let width = self.width.in_pixels(&parent_dim) as f64;
78            if nock != tip {
79                let dx = (tip.0 - nock.0) as f64;
80                let dy = (tip.1 - nock.1) as f64;
81                let d = f64::sqrt(dx * dx + dy * dy);
82                let head = head.min(d);
83                let width = width.min(d);
84                let half_width = width * 0.5;
85                let left = (
86                    tip.0 + ((-head * dx + half_width * dy) / d) as i32,
87                    tip.1 + ((-head * dy - half_width * dx) / d) as i32,
88                );
89                let right = (
90                    tip.0 + ((-head * dx - half_width * dy) / d) as i32,
91                    tip.1 + ((-head * dy + half_width * dx) / d) as i32,
92                );
93                backend.draw_line(nock, tip, &self.style)?;
94                backend.draw_line(tip, left, &self.style)?;
95                backend.draw_line(tip, right, &self.style)?;
96            }
97        }
98        Ok(())
99    }
100}
101
102#[derive(Clone)]
103pub struct TriangleArrow<Coord, Size: SizeDesc> {
104    points: [Coord; 2],
105    head: Size,
106    width: Size,
107    style: ShapeStyle,
108}
109
110impl<Coord> TriangleArrow<Coord, i32> {
111    pub fn new(nock: Coord, tip: Coord, style: impl Into<ShapeStyle>) -> Self {
112        Self {
113            points: [nock, tip],
114            head: 5,
115            width: 5,
116            style: style.into(),
117        }
118    }
119}
120
121impl<Coord, Size: SizeDesc> TriangleArrow<Coord, Size> {
122    pub fn new_detail(
123        nock: Coord,
124        tip: Coord,
125        head: Size,
126        width: Size,
127        style: impl Into<ShapeStyle>,
128    ) -> Self {
129        Self {
130            points: [nock, tip],
131            head,
132            width,
133            style: style.into(),
134        }
135    }
136
137    pub fn head(self, head: Size) -> Self {
138        Self { head, ..self }
139    }
140
141    pub fn width(self, width: Size) -> Self {
142        Self { width, ..self }
143    }
144}
145
146impl<'a, Coord: 'a, Size: SizeDesc> PointCollection<'a, Coord> for &'a TriangleArrow<Coord, Size> {
147    type Point = &'a Coord;
148
149    type IntoIter = &'a [Coord];
150
151    fn point_iter(self) -> Self::IntoIter {
152        &self.points
153    }
154}
155
156impl<DB: DrawingBackend, Coord, Size: SizeDesc> Drawable<DB> for TriangleArrow<Coord, Size> {
157    fn draw<I: Iterator<Item = BackendCoord>>(
158        &self,
159        mut pos: I,
160        backend: &mut DB,
161        parent_dim: (u32, u32),
162    ) -> Result<(), DrawingErrorKind<DB::ErrorType>> {
163        let nock = pos.next();
164        let tip = pos.next();
165        debug_assert!(nock.is_some());
166        debug_assert!(tip.is_some());
167        if let (Some(nock), Some(tip)) = (nock, tip) {
168            let head = self.head.in_pixels(&parent_dim) as f64;
169            let width = self.width.in_pixels(&parent_dim) as f64;
170            if nock != tip {
171                let dx = (tip.0 - nock.0) as f64;
172                let dy = (tip.1 - nock.1) as f64;
173                let d = f64::sqrt(dx * dx + dy * dy);
174                let head = head.min(d);
175                let width = width.min(d);
176                let half_width = width * 0.5;
177                let left = (
178                    tip.0 + ((-head * dx + half_width * dy) / d) as i32,
179                    tip.1 + ((-head * dy - half_width * dx) / d) as i32,
180                );
181                let right = (
182                    tip.0 + ((-head * dx - half_width * dy) / d) as i32,
183                    tip.1 + ((-head * dy + half_width * dx) / d) as i32,
184                );
185                backend.draw_line(nock, tip, &self.style)?;
186                backend.fill_polygon(vec![tip, left, right, tip], &self.style)?;
187            }
188        }
189        Ok(())
190    }
191}