1use super::{Drawable, PointCollection};
2use crate::style::{Color, ShapeStyle, SizeDesc};
3use plotters_backend::{BackendCoord, DrawingBackend, DrawingErrorKind};
4
5/**
6An element representing a single pixel.
7
8See [`crate::element::EmptyElement`] for more information and examples.
9*/
10pub struct Pixel<Coord> {
11 pos: Coord,
12 style: ShapeStyle,
13}
14
15impl<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
29impl<'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
37impl<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]
53fn 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")]
73pub type Path<Coord> = PathElement<Coord>;
74
75/// An element of a series of connected lines
76pub struct PathElement<Coord> {
77 points: Vec<Coord>,
78 style: ShapeStyle,
79}
80impl<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
93impl<'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
101impl<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]
114fn 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
135pub struct Rectangle<Coord> {
136 points: [Coord; 2],
137 style: ShapeStyle,
138 margin: (u32, u32, u32, u32),
139}
140
141impl<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
165impl<'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
173impl<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]
196fn 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
236pub struct Circle<Coord, Size: SizeDesc> {
237 center: Coord,
238 size: Size,
239 style: ShapeStyle,
240}
241
242impl<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
257impl<'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
265impl<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]
282fn 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
301pub struct Polygon<Coord> {
302 points: Vec<Coord>,
303 style: ShapeStyle,
304}
305impl<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
318impl<'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
326impl<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]
339fn 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