1use 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}