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