plotters/element/
basic_shapes.rs

1use super::{Drawable, PointCollection};
2use crate::style::{Color, ShapeStyle, SizeDesc};
3use plotters_backend::{BackendCoord, DrawingBackend, DrawingErrorKind};
4
5/// An element of a single pixel
6pub struct Pixel<Coord> {
7    pos: Coord,
8    style: ShapeStyle,
9}
10
11impl<Coord> Pixel<Coord> {
12    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
20impl<'a, Coord> PointCollection<'a, Coord> for &'a Pixel<Coord> {
21    type Point = &'a Coord;
22    type IntoIter = std::iter::Once<&'a Coord>;
23    fn point_iter(self) -> Self::IntoIter {
24        std::iter::once(&self.pos)
25    }
26}
27
28impl<Coord, DB: DrawingBackend> Drawable<DB> for Pixel<Coord> {
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]
44fn 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")]
63pub type Path<Coord> = PathElement<Coord>;
64
65/// An element of a series of connected lines
66pub struct PathElement<Coord> {
67    points: Vec<Coord>,
68    style: ShapeStyle,
69}
70impl<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
75    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
83impl<'a, Coord> PointCollection<'a, Coord> for &'a PathElement<Coord> {
84    type Point = &'a Coord;
85    type IntoIter = &'a [Coord];
86    fn point_iter(self) -> &'a [Coord] {
87        &self.points
88    }
89}
90
91impl<Coord, DB: DrawingBackend> Drawable<DB> for PathElement<Coord> {
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]
104fn 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
125pub struct Rectangle<Coord> {
126    points: [Coord; 2],
127    style: ShapeStyle,
128    margin: (u32, u32, u32, u32),
129}
130
131impl<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
136    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
149    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
155impl<'a, Coord> PointCollection<'a, Coord> for &'a Rectangle<Coord> {
156    type Point = &'a Coord;
157    type IntoIter = &'a [Coord];
158    fn point_iter(self) -> &'a [Coord] {
159        &self.points
160    }
161}
162
163impl<Coord, DB: DrawingBackend> Drawable<DB> for Rectangle<Coord> {
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]
186fn 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
226pub struct Circle<Coord, Size: SizeDesc> {
227    center: Coord,
228    size: Size,
229    style: ShapeStyle,
230}
231
232impl<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
238    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
247impl<'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>;
250    fn point_iter(self) -> std::iter::Once<&'a Coord> {
251        std::iter::once(&self.center)
252    }
253}
254
255impl<Coord, DB: DrawingBackend, Size: SizeDesc> Drawable<DB> for Circle<Coord, Size> {
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]
272fn 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
291pub struct Polygon<Coord> {
292    points: Vec<Coord>,
293    style: ShapeStyle,
294}
295impl<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
300    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
308impl<'a, Coord> PointCollection<'a, Coord> for &'a Polygon<Coord> {
309    type Point = &'a Coord;
310    type IntoIter = &'a [Coord];
311    fn point_iter(self) -> &'a [Coord] {
312        &self.points
313    }
314}
315
316impl<Coord, DB: DrawingBackend> Drawable<DB> for Polygon<Coord> {
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]
329fn 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}