1 | use super::{Drawable, PointCollection}; |
2 | use crate::style::{Color, ShapeStyle, SizeDesc}; |
3 | use plotters_backend::{BackendCoord, DrawingBackend, DrawingErrorKind}; |
4 | |
5 | /** |
6 | An element representing a single pixel. |
7 | |
8 | See [`crate::element::EmptyElement`] for more information and examples. |
9 | */ |
10 | pub struct Pixel<Coord> { |
11 | pos: Coord, |
12 | style: ShapeStyle, |
13 | } |
14 | |
15 | impl<Coord> Pixel<Coord> { |
16 | /** |
17 | Creates a new pixel. |
18 | |
19 | See [`crate::element::EmptyElement`] for more information and examples. |
20 | */ |
21 | pub fn new<P: Into<Coord>, S: Into<ShapeStyle>>(pos: P, style: S) -> Self { |
22 | Self { |
23 | pos: pos.into(), |
24 | style: style.into(), |
25 | } |
26 | } |
27 | } |
28 | |
29 | impl<'a, Coord> PointCollection<'a, Coord> for &'a Pixel<Coord> { |
30 | type Point = &'a Coord; |
31 | type IntoIter = std::iter::Once<&'a Coord>; |
32 | fn point_iter(self) -> Self::IntoIter { |
33 | std::iter::once(&self.pos) |
34 | } |
35 | } |
36 | |
37 | impl<Coord, DB: DrawingBackend> Drawable<DB> for Pixel<Coord> { |
38 | fn draw<I: Iterator<Item = BackendCoord>>( |
39 | &self, |
40 | mut points: I, |
41 | backend: &mut DB, |
42 | _: (u32, u32), |
43 | ) -> Result<(), DrawingErrorKind<DB::ErrorType>> { |
44 | if let Some((x, y)) = points.next() { |
45 | return backend.draw_pixel((x, y), self.style.color.to_backend_color()); |
46 | } |
47 | Ok(()) |
48 | } |
49 | } |
50 | |
51 | #[cfg (test)] |
52 | #[test] |
53 | fn test_pixel_element() { |
54 | use crate::prelude::*; |
55 | let da = crate::create_mocked_drawing_area(300, 300, |m| { |
56 | m.check_draw_pixel(|c, (x, y)| { |
57 | assert_eq!(x, 150); |
58 | assert_eq!(y, 152); |
59 | assert_eq!(c, RED.to_rgba()); |
60 | }); |
61 | |
62 | m.drop_check(|b| { |
63 | assert_eq!(b.num_draw_pixel_call, 1); |
64 | assert_eq!(b.draw_count, 1); |
65 | }); |
66 | }); |
67 | da.draw(&Pixel::new((150, 152), &RED)) |
68 | .expect("Drawing Failure" ); |
69 | } |
70 | |
71 | /// This is a deprecated type. Please use new name [`PathElement`] instead. |
72 | #[deprecated (note = "Use new name PathElement instead" )] |
73 | pub type Path<Coord> = PathElement<Coord>; |
74 | |
75 | /// An element of a series of connected lines |
76 | pub struct PathElement<Coord> { |
77 | points: Vec<Coord>, |
78 | style: ShapeStyle, |
79 | } |
80 | impl<Coord> PathElement<Coord> { |
81 | /// Create a new path |
82 | /// - `points`: The iterator of the points |
83 | /// - `style`: The shape style |
84 | /// - returns the created element |
85 | pub fn new<P: Into<Vec<Coord>>, S: Into<ShapeStyle>>(points: P, style: S) -> Self { |
86 | Self { |
87 | points: points.into(), |
88 | style: style.into(), |
89 | } |
90 | } |
91 | } |
92 | |
93 | impl<'a, Coord> PointCollection<'a, Coord> for &'a PathElement<Coord> { |
94 | type Point = &'a Coord; |
95 | type IntoIter = &'a [Coord]; |
96 | fn point_iter(self) -> &'a [Coord] { |
97 | &self.points |
98 | } |
99 | } |
100 | |
101 | impl<Coord, DB: DrawingBackend> Drawable<DB> for PathElement<Coord> { |
102 | fn draw<I: Iterator<Item = BackendCoord>>( |
103 | &self, |
104 | points: I, |
105 | backend: &mut DB, |
106 | _: (u32, u32), |
107 | ) -> Result<(), DrawingErrorKind<DB::ErrorType>> { |
108 | backend.draw_path(points, &self.style) |
109 | } |
110 | } |
111 | |
112 | #[cfg (test)] |
113 | #[test] |
114 | fn test_path_element() { |
115 | use crate::prelude::*; |
116 | let da = crate::create_mocked_drawing_area(300, 300, |m| { |
117 | m.check_draw_path(|c, s, path| { |
118 | assert_eq!(c, BLUE.to_rgba()); |
119 | assert_eq!(s, 5); |
120 | assert_eq!(path, vec![(100, 101), (105, 107), (150, 157)]); |
121 | }); |
122 | m.drop_check(|b| { |
123 | assert_eq!(b.num_draw_path_call, 1); |
124 | assert_eq!(b.draw_count, 1); |
125 | }); |
126 | }); |
127 | da.draw(&PathElement::new( |
128 | vec![(100, 101), (105, 107), (150, 157)], |
129 | Into::<ShapeStyle>::into(&BLUE).stroke_width(5), |
130 | )) |
131 | .expect("Drawing Failure" ); |
132 | } |
133 | |
134 | /// A rectangle element |
135 | pub struct Rectangle<Coord> { |
136 | points: [Coord; 2], |
137 | style: ShapeStyle, |
138 | margin: (u32, u32, u32, u32), |
139 | } |
140 | |
141 | impl<Coord> Rectangle<Coord> { |
142 | /// Create a new path |
143 | /// - `points`: The left upper and right lower corner of the rectangle |
144 | /// - `style`: The shape style |
145 | /// - returns the created element |
146 | pub fn new<S: Into<ShapeStyle>>(points: [Coord; 2], style: S) -> Self { |
147 | Self { |
148 | points, |
149 | style: style.into(), |
150 | margin: (0, 0, 0, 0), |
151 | } |
152 | } |
153 | |
154 | /// Set the margin of the rectangle |
155 | /// - `t`: The top margin |
156 | /// - `b`: The bottom margin |
157 | /// - `l`: The left margin |
158 | /// - `r`: The right margin |
159 | pub fn set_margin(&mut self, t: u32, b: u32, l: u32, r: u32) -> &mut Self { |
160 | self.margin = (t, b, l, r); |
161 | self |
162 | } |
163 | } |
164 | |
165 | impl<'a, Coord> PointCollection<'a, Coord> for &'a Rectangle<Coord> { |
166 | type Point = &'a Coord; |
167 | type IntoIter = &'a [Coord]; |
168 | fn point_iter(self) -> &'a [Coord] { |
169 | &self.points |
170 | } |
171 | } |
172 | |
173 | impl<Coord, DB: DrawingBackend> Drawable<DB> for Rectangle<Coord> { |
174 | fn draw<I: Iterator<Item = BackendCoord>>( |
175 | &self, |
176 | mut points: I, |
177 | backend: &mut DB, |
178 | _: (u32, u32), |
179 | ) -> Result<(), DrawingErrorKind<DB::ErrorType>> { |
180 | match (points.next(), points.next()) { |
181 | (Some(a), Some(b)) => { |
182 | let (mut a, mut b) = ((a.0.min(b.0), a.1.min(b.1)), (a.0.max(b.0), a.1.max(b.1))); |
183 | a.1 += self.margin.0 as i32; |
184 | b.1 -= self.margin.1 as i32; |
185 | a.0 += self.margin.2 as i32; |
186 | b.0 -= self.margin.3 as i32; |
187 | backend.draw_rect(a, b, &self.style, self.style.filled) |
188 | } |
189 | _ => Ok(()), |
190 | } |
191 | } |
192 | } |
193 | |
194 | #[cfg (test)] |
195 | #[test] |
196 | fn test_rect_element() { |
197 | use crate::prelude::*; |
198 | { |
199 | let da = crate::create_mocked_drawing_area(300, 300, |m| { |
200 | m.check_draw_rect(|c, s, f, u, d| { |
201 | assert_eq!(c, BLUE.to_rgba()); |
202 | assert_eq!(f, false); |
203 | assert_eq!(s, 5); |
204 | assert_eq!([u, d], [(100, 101), (105, 107)]); |
205 | }); |
206 | m.drop_check(|b| { |
207 | assert_eq!(b.num_draw_rect_call, 1); |
208 | assert_eq!(b.draw_count, 1); |
209 | }); |
210 | }); |
211 | da.draw(&Rectangle::new( |
212 | [(100, 101), (105, 107)], |
213 | Color::stroke_width(&BLUE, 5), |
214 | )) |
215 | .expect("Drawing Failure" ); |
216 | } |
217 | |
218 | { |
219 | let da = crate::create_mocked_drawing_area(300, 300, |m| { |
220 | m.check_draw_rect(|c, _, f, u, d| { |
221 | assert_eq!(c, BLUE.to_rgba()); |
222 | assert_eq!(f, true); |
223 | assert_eq!([u, d], [(100, 101), (105, 107)]); |
224 | }); |
225 | m.drop_check(|b| { |
226 | assert_eq!(b.num_draw_rect_call, 1); |
227 | assert_eq!(b.draw_count, 1); |
228 | }); |
229 | }); |
230 | da.draw(&Rectangle::new([(100, 101), (105, 107)], BLUE.filled())) |
231 | .expect("Drawing Failure" ); |
232 | } |
233 | } |
234 | |
235 | /// A circle element |
236 | pub struct Circle<Coord, Size: SizeDesc> { |
237 | center: Coord, |
238 | size: Size, |
239 | style: ShapeStyle, |
240 | } |
241 | |
242 | impl<Coord, Size: SizeDesc> Circle<Coord, Size> { |
243 | /// Create a new circle element |
244 | /// - `coord` The center of the circle |
245 | /// - `size` The radius of the circle |
246 | /// - `style` The style of the circle |
247 | /// - Return: The newly created circle element |
248 | pub fn new<S: Into<ShapeStyle>>(coord: Coord, size: Size, style: S) -> Self { |
249 | Self { |
250 | center: coord, |
251 | size, |
252 | style: style.into(), |
253 | } |
254 | } |
255 | } |
256 | |
257 | impl<'a, Coord, Size: SizeDesc> PointCollection<'a, Coord> for &'a Circle<Coord, Size> { |
258 | type Point = &'a Coord; |
259 | type IntoIter = std::iter::Once<&'a Coord>; |
260 | fn point_iter(self) -> std::iter::Once<&'a Coord> { |
261 | std::iter::once(&self.center) |
262 | } |
263 | } |
264 | |
265 | impl<Coord, DB: DrawingBackend, Size: SizeDesc> Drawable<DB> for Circle<Coord, Size> { |
266 | fn draw<I: Iterator<Item = BackendCoord>>( |
267 | &self, |
268 | mut points: I, |
269 | backend: &mut DB, |
270 | ps: (u32, u32), |
271 | ) -> Result<(), DrawingErrorKind<DB::ErrorType>> { |
272 | if let Some((x, y)) = points.next() { |
273 | let size = self.size.in_pixels(&ps).max(0) as u32; |
274 | return backend.draw_circle((x, y), size, &self.style, self.style.filled); |
275 | } |
276 | Ok(()) |
277 | } |
278 | } |
279 | |
280 | #[cfg (test)] |
281 | #[test] |
282 | fn test_circle_element() { |
283 | use crate::prelude::*; |
284 | let da = crate::create_mocked_drawing_area(300, 300, |m| { |
285 | m.check_draw_circle(|c, _, f, s, r| { |
286 | assert_eq!(c, BLUE.to_rgba()); |
287 | assert_eq!(f, false); |
288 | assert_eq!(s, (150, 151)); |
289 | assert_eq!(r, 20); |
290 | }); |
291 | m.drop_check(|b| { |
292 | assert_eq!(b.num_draw_circle_call, 1); |
293 | assert_eq!(b.draw_count, 1); |
294 | }); |
295 | }); |
296 | da.draw(&Circle::new((150, 151), 20, &BLUE)) |
297 | .expect("Drawing Failure" ); |
298 | } |
299 | |
300 | /// An element of a filled polygon |
301 | pub struct Polygon<Coord> { |
302 | points: Vec<Coord>, |
303 | style: ShapeStyle, |
304 | } |
305 | impl<Coord> Polygon<Coord> { |
306 | /// Create a new polygon |
307 | /// - `points`: The iterator of the points |
308 | /// - `style`: The shape style |
309 | /// - returns the created element |
310 | pub fn new<P: Into<Vec<Coord>>, S: Into<ShapeStyle>>(points: P, style: S) -> Self { |
311 | Self { |
312 | points: points.into(), |
313 | style: style.into(), |
314 | } |
315 | } |
316 | } |
317 | |
318 | impl<'a, Coord> PointCollection<'a, Coord> for &'a Polygon<Coord> { |
319 | type Point = &'a Coord; |
320 | type IntoIter = &'a [Coord]; |
321 | fn point_iter(self) -> &'a [Coord] { |
322 | &self.points |
323 | } |
324 | } |
325 | |
326 | impl<Coord, DB: DrawingBackend> Drawable<DB> for Polygon<Coord> { |
327 | fn draw<I: Iterator<Item = BackendCoord>>( |
328 | &self, |
329 | points: I, |
330 | backend: &mut DB, |
331 | _: (u32, u32), |
332 | ) -> Result<(), DrawingErrorKind<DB::ErrorType>> { |
333 | backend.fill_polygon(points, &self.style.color.to_backend_color()) |
334 | } |
335 | } |
336 | |
337 | #[cfg (test)] |
338 | #[test] |
339 | fn test_polygon_element() { |
340 | use crate::prelude::*; |
341 | let points = vec![(100, 100), (50, 500), (300, 400), (200, 300), (550, 200)]; |
342 | let expected_points = points.clone(); |
343 | |
344 | let da = crate::create_mocked_drawing_area(800, 800, |m| { |
345 | m.check_fill_polygon(move |c, p| { |
346 | assert_eq!(c, BLUE.to_rgba()); |
347 | assert_eq!(expected_points.len(), p.len()); |
348 | assert_eq!(expected_points, p); |
349 | }); |
350 | m.drop_check(|b| { |
351 | assert_eq!(b.num_fill_polygon_call, 1); |
352 | assert_eq!(b.draw_count, 1); |
353 | }); |
354 | }); |
355 | |
356 | da.draw(&Polygon::new(points.clone(), &BLUE)) |
357 | .expect("Drawing Failure" ); |
358 | } |
359 | |