1use super::{Drawable, PointCollection};
2use crate::style::{Color, ShapeStyle, SizeDesc};
3use plotters_backend::{BackendCoord, DrawingBackend, DrawingErrorKind};
4
5#[inline]
6fn to_i((x, y): (f32, f32)) -> (i32, i32) {
7 (x.round() as i32, y.round() as i32)
8}
9
10#[inline]
11fn to_f((x, y): (i32, i32)) -> (f32, f32) {
12 (x as f32, y as f32)
13}
14
15pub struct Pixel<Coord> {
21 pos: Coord,
22 style: ShapeStyle,
23}
24
25impl<Coord> Pixel<Coord> {
26 pub fn new<P: Into<Coord>, S: Into<ShapeStyle>>(pos: P, style: S) -> Self {
32 Self {
33 pos: pos.into(),
34 style: style.into(),
35 }
36 }
37}
38
39impl<'a, Coord> PointCollection<'a, Coord> for &'a Pixel<Coord> {
40 type Point = &'a Coord;
41 type IntoIter = std::iter::Once<&'a Coord>;
42 fn point_iter(self) -> Self::IntoIter {
43 std::iter::once(&self.pos)
44 }
45}
46
47impl<Coord, DB: DrawingBackend> Drawable<DB> for Pixel<Coord> {
48 fn draw<I: Iterator<Item = BackendCoord>>(
49 &self,
50 mut points: I,
51 backend: &mut DB,
52 _: (u32, u32),
53 ) -> Result<(), DrawingErrorKind<DB::ErrorType>> {
54 if let Some((x, y)) = points.next() {
55 return backend.draw_pixel((x, y), self.style.color.to_backend_color());
56 }
57 Ok(())
58 }
59}
60
61#[cfg(test)]
62#[test]
63fn test_pixel_element() {
64 use crate::prelude::*;
65 let da = crate::create_mocked_drawing_area(300, 300, |m| {
66 m.check_draw_pixel(|c, (x, y)| {
67 assert_eq!(x, 150);
68 assert_eq!(y, 152);
69 assert_eq!(c, RED.to_rgba());
70 });
71
72 m.drop_check(|b| {
73 assert_eq!(b.num_draw_pixel_call, 1);
74 assert_eq!(b.draw_count, 1);
75 });
76 });
77 da.draw(&Pixel::new((150, 152), RED))
78 .expect("Drawing Failure");
79}
80
81#[deprecated(note = "Use new name PathElement instead")]
83pub type Path<Coord> = PathElement<Coord>;
84
85pub struct PathElement<Coord> {
87 points: Vec<Coord>,
88 style: ShapeStyle,
89}
90impl<Coord> PathElement<Coord> {
91 pub fn new<P: Into<Vec<Coord>>, S: Into<ShapeStyle>>(points: P, style: S) -> Self {
96 Self {
97 points: points.into(),
98 style: style.into(),
99 }
100 }
101}
102
103impl<'a, Coord> PointCollection<'a, Coord> for &'a PathElement<Coord> {
104 type Point = &'a Coord;
105 type IntoIter = &'a [Coord];
106 fn point_iter(self) -> &'a [Coord] {
107 &self.points
108 }
109}
110
111impl<Coord, DB: DrawingBackend> Drawable<DB> for PathElement<Coord> {
112 fn draw<I: Iterator<Item = BackendCoord>>(
113 &self,
114 points: I,
115 backend: &mut DB,
116 _: (u32, u32),
117 ) -> Result<(), DrawingErrorKind<DB::ErrorType>> {
118 backend.draw_path(points, &self.style)
119 }
120}
121
122#[cfg(test)]
123#[test]
124fn test_path_element() {
125 use crate::prelude::*;
126 let da = crate::create_mocked_drawing_area(300, 300, |m| {
127 m.check_draw_path(|c, s, path| {
128 assert_eq!(c, BLUE.to_rgba());
129 assert_eq!(s, 5);
130 assert_eq!(path, vec![(100, 101), (105, 107), (150, 157)]);
131 });
132 m.drop_check(|b| {
133 assert_eq!(b.num_draw_path_call, 1);
134 assert_eq!(b.draw_count, 1);
135 });
136 });
137 da.draw(&PathElement::new(
138 vec![(100, 101), (105, 107), (150, 157)],
139 Into::<ShapeStyle>::into(BLUE).stroke_width(5),
140 ))
141 .expect("Drawing Failure");
142}
143
144pub struct DashedPathElement<I: Iterator + Clone, Size: SizeDesc> {
148 points: I,
149 size: Size,
150 spacing: Size,
151 style: ShapeStyle,
152}
153
154impl<I: Iterator + Clone, Size: SizeDesc> DashedPathElement<I, Size> {
155 pub fn new<I0, S>(points: I0, size: Size, spacing: Size, style: S) -> Self
162 where
163 I0: IntoIterator<IntoIter = I>,
164 S: Into<ShapeStyle>,
165 {
166 Self {
167 points: points.into_iter(),
168 size,
169 spacing,
170 style: style.into(),
171 }
172 }
173}
174
175impl<'a, I: Iterator + Clone, Size: SizeDesc> PointCollection<'a, I::Item>
176 for &'a DashedPathElement<I, Size>
177{
178 type Point = I::Item;
179 type IntoIter = I;
180 fn point_iter(self) -> Self::IntoIter {
181 self.points.clone()
182 }
183}
184
185impl<I0: Iterator + Clone, Size: SizeDesc, DB: DrawingBackend> Drawable<DB>
186 for DashedPathElement<I0, Size>
187{
188 fn draw<I: Iterator<Item = BackendCoord>>(
189 &self,
190 mut points: I,
191 backend: &mut DB,
192 ps: (u32, u32),
193 ) -> Result<(), DrawingErrorKind<DB::ErrorType>> {
194 let mut start = match points.next() {
195 Some(c) => to_f(c),
196 None => return Ok(()),
197 };
198 let size = self.size.in_pixels(&ps).max(0) as f32;
199 if size == 0. {
200 return Ok(());
201 }
202 let spacing = self.spacing.in_pixels(&ps).max(0) as f32;
203 let mut dist = 0.;
204 let mut is_solid = true;
205 let mut queue = vec![to_i(start)];
206 for curr in points {
207 let end = to_f(curr);
208 while start != end {
210 let (dx, dy) = (end.0 - start.0, end.1 - start.1);
211 let d = dx.hypot(dy);
212 let size = if is_solid { size } else { spacing };
213 let left = size - dist;
214 if left < d {
216 let t = left / d;
217 start = (start.0 + dx * t, start.1 + dy * t);
218 dist += left;
219 } else {
220 start = end;
221 dist += d;
222 }
223 if is_solid {
225 queue.push(to_i(start));
226 }
227 if size <= dist {
228 if is_solid {
229 backend.draw_path(queue.drain(..), &self.style)?;
230 } else {
231 queue.push(to_i(start));
232 }
233 dist = 0.;
234 is_solid = !is_solid;
235 }
236 }
237 }
238 if queue.len() > 1 {
239 backend.draw_path(queue, &self.style)?;
240 }
241 Ok(())
242 }
243}
244
245#[cfg(test)]
246#[test]
247fn test_dashed_path_element() {
248 use crate::prelude::*;
249 let check_list = std::cell::RefCell::new(vec![
250 vec![(100, 100), (100, 103), (100, 105)],
251 vec![(100, 107), (100, 112)],
252 vec![(100, 114), (100, 119)],
253 vec![(100, 119), (100, 120)],
254 ]);
255 let da = crate::create_mocked_drawing_area(300, 300, |m| {
256 m.check_draw_path(move |c, s, path| {
257 assert_eq!(c, BLUE.to_rgba());
258 assert_eq!(s, 7);
259 assert_eq!(path, check_list.borrow_mut().remove(0));
260 });
261 m.drop_check(|b| {
262 assert_eq!(b.num_draw_path_call, 3);
263 assert_eq!(b.draw_count, 3);
264 });
265 });
266 da.draw(&DashedPathElement::new(
267 vec![(100, 100), (100, 103), (100, 120)],
268 5.,
269 2.,
270 BLUE.stroke_width(7),
271 ))
272 .expect("Drawing Failure");
273}
274
275pub struct DottedPathElement<I: Iterator + Clone, Size: SizeDesc, Marker> {
279 points: I,
280 shift: Size,
281 spacing: Size,
282 func: Box<dyn Fn(BackendCoord) -> Marker>,
283}
284
285impl<I: Iterator + Clone, Size: SizeDesc, Marker> DottedPathElement<I, Size, Marker> {
286 pub fn new<I0, F>(points: I0, shift: Size, spacing: Size, func: F) -> Self
293 where
294 I0: IntoIterator<IntoIter = I>,
295 F: Fn(BackendCoord) -> Marker + 'static,
296 {
297 Self {
298 points: points.into_iter(),
299 shift,
300 spacing,
301 func: Box::new(func),
302 }
303 }
304}
305
306impl<'a, I: Iterator + Clone, Size: SizeDesc, Marker> PointCollection<'a, I::Item>
307 for &'a DottedPathElement<I, Size, Marker>
308{
309 type Point = I::Item;
310 type IntoIter = I;
311 fn point_iter(self) -> Self::IntoIter {
312 self.points.clone()
313 }
314}
315
316impl<I0, Size, DB, Marker> Drawable<DB> for DottedPathElement<I0, Size, Marker>
317where
318 I0: Iterator + Clone,
319 Size: SizeDesc,
320 DB: DrawingBackend,
321 Marker: crate::element::IntoDynElement<'static, DB, BackendCoord>,
322{
323 fn draw<I: Iterator<Item = BackendCoord>>(
324 &self,
325 mut points: I,
326 backend: &mut DB,
327 ps: (u32, u32),
328 ) -> Result<(), DrawingErrorKind<DB::ErrorType>> {
329 let mut shift = self.shift.in_pixels(&ps).max(0) as f32;
330 let mut start = match points.next() {
331 Some(start_i) => {
332 if shift == 0. {
334 let mk = (self.func)(start_i).into_dyn();
335 mk.draw(mk.point_iter().iter().copied(), backend, ps)?;
336 }
337 to_f(start_i)
338 }
339 None => return Ok(()),
340 };
341 let spacing = self.spacing.in_pixels(&ps).max(0) as f32;
342 let mut dist = 0.;
343 for curr in points {
344 let end = to_f(curr);
345 while start != end {
347 let (dx, dy) = (end.0 - start.0, end.1 - start.1);
348 let d = dx.hypot(dy);
349 let spacing = if shift == 0. { spacing } else { shift };
350 let left = spacing - dist;
351 if left < d {
353 let t = left / d;
354 start = (start.0 + dx * t, start.1 + dy * t);
355 dist += left;
356 } else {
357 start = end;
358 dist += d;
359 }
360 if spacing <= dist {
362 let mk = (self.func)(to_i(start)).into_dyn();
363 mk.draw(mk.point_iter().iter().copied(), backend, ps)?;
364 shift = 0.;
365 dist = 0.;
366 }
367 }
368 }
369 Ok(())
370 }
371}
372
373#[cfg(test)]
374#[test]
375fn test_dotted_path_element() {
376 use crate::prelude::*;
377 let da = crate::create_mocked_drawing_area(300, 300, |m| {
378 m.drop_check(|b| {
379 assert_eq!(b.num_draw_path_call, 0);
380 assert_eq!(b.draw_count, 7);
381 });
382 });
383 da.draw(&DottedPathElement::new(
384 vec![(100, 100), (105, 105), (150, 150)],
385 5,
386 10,
387 |c| Circle::new(c, 5, Into::<ShapeStyle>::into(RED).filled()),
388 ))
389 .expect("Drawing Failure");
390}
391
392pub struct Rectangle<Coord> {
394 points: [Coord; 2],
395 style: ShapeStyle,
396 margin: (u32, u32, u32, u32),
397}
398
399impl<Coord> Rectangle<Coord> {
400 pub fn new<S: Into<ShapeStyle>>(points: [Coord; 2], style: S) -> Self {
405 Self {
406 points,
407 style: style.into(),
408 margin: (0, 0, 0, 0),
409 }
410 }
411
412 pub fn set_margin(&mut self, t: u32, b: u32, l: u32, r: u32) -> &mut Self {
418 self.margin = (t, b, l, r);
419 self
420 }
421
422 pub fn get_points(&self) -> (&Coord, &Coord) {
425 (&self.points[0], &self.points[1])
426 }
427
428 pub fn set_style<S: Into<ShapeStyle>>(&mut self, style: S) -> &mut Self {
432 self.style = style.into();
433 self
434 }
435}
436
437impl<'a, Coord> PointCollection<'a, Coord> for &'a Rectangle<Coord> {
438 type Point = &'a Coord;
439 type IntoIter = &'a [Coord];
440 fn point_iter(self) -> &'a [Coord] {
441 &self.points
442 }
443}
444
445impl<Coord, DB: DrawingBackend> Drawable<DB> for Rectangle<Coord> {
446 fn draw<I: Iterator<Item = BackendCoord>>(
447 &self,
448 mut points: I,
449 backend: &mut DB,
450 _: (u32, u32),
451 ) -> Result<(), DrawingErrorKind<DB::ErrorType>> {
452 match (points.next(), points.next()) {
453 (Some(a), Some(b)) => {
454 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)));
455 a.1 += self.margin.0 as i32;
456 b.1 -= self.margin.1 as i32;
457 a.0 += self.margin.2 as i32;
458 b.0 -= self.margin.3 as i32;
459 backend.draw_rect(a, b, &self.style, self.style.filled)
460 }
461 _ => Ok(()),
462 }
463 }
464}
465
466#[cfg(test)]
467#[test]
468fn test_rect_element() {
469 use crate::prelude::*;
470 {
471 let da = crate::create_mocked_drawing_area(300, 300, |m| {
472 m.check_draw_rect(|c, s, f, u, d| {
473 assert_eq!(c, BLUE.to_rgba());
474 assert!(!f);
475 assert_eq!(s, 5);
476 assert_eq!([u, d], [(100, 101), (105, 107)]);
477 });
478 m.drop_check(|b| {
479 assert_eq!(b.num_draw_rect_call, 1);
480 assert_eq!(b.draw_count, 1);
481 });
482 });
483 da.draw(&Rectangle::new(
484 [(100, 101), (105, 107)],
485 Color::stroke_width(&BLUE, 5),
486 ))
487 .expect("Drawing Failure");
488 }
489
490 {
491 let da = crate::create_mocked_drawing_area(300, 300, |m| {
492 m.check_draw_rect(|c, _, f, u, d| {
493 assert_eq!(c, BLUE.to_rgba());
494 assert!(f);
495 assert_eq!([u, d], [(100, 101), (105, 107)]);
496 });
497 m.drop_check(|b| {
498 assert_eq!(b.num_draw_rect_call, 1);
499 assert_eq!(b.draw_count, 1);
500 });
501 });
502 da.draw(&Rectangle::new([(100, 101), (105, 107)], BLUE.filled()))
503 .expect("Drawing Failure");
504 }
505}
506
507pub struct Circle<Coord, Size: SizeDesc> {
509 center: Coord,
510 size: Size,
511 style: ShapeStyle,
512}
513
514impl<Coord, Size: SizeDesc> Circle<Coord, Size> {
515 pub fn new<S: Into<ShapeStyle>>(coord: Coord, size: Size, style: S) -> Self {
521 Self {
522 center: coord,
523 size,
524 style: style.into(),
525 }
526 }
527}
528
529impl<'a, Coord, Size: SizeDesc> PointCollection<'a, Coord> for &'a Circle<Coord, Size> {
530 type Point = &'a Coord;
531 type IntoIter = std::iter::Once<&'a Coord>;
532 fn point_iter(self) -> std::iter::Once<&'a Coord> {
533 std::iter::once(&self.center)
534 }
535}
536
537impl<Coord, DB: DrawingBackend, Size: SizeDesc> Drawable<DB> for Circle<Coord, Size> {
538 fn draw<I: Iterator<Item = BackendCoord>>(
539 &self,
540 mut points: I,
541 backend: &mut DB,
542 ps: (u32, u32),
543 ) -> Result<(), DrawingErrorKind<DB::ErrorType>> {
544 if let Some((x, y)) = points.next() {
545 let size = self.size.in_pixels(&ps).max(0) as u32;
546 return backend.draw_circle((x, y), size, &self.style, self.style.filled);
547 }
548 Ok(())
549 }
550}
551
552#[cfg(test)]
553#[test]
554fn test_circle_element() {
555 use crate::prelude::*;
556 let da = crate::create_mocked_drawing_area(300, 300, |m| {
557 m.check_draw_circle(|c, _, f, s, r| {
558 assert_eq!(c, BLUE.to_rgba());
559 assert!(!f);
560 assert_eq!(s, (150, 151));
561 assert_eq!(r, 20);
562 });
563 m.drop_check(|b| {
564 assert_eq!(b.num_draw_circle_call, 1);
565 assert_eq!(b.draw_count, 1);
566 });
567 });
568 da.draw(&Circle::new((150, 151), 20, BLUE))
569 .expect("Drawing Failure");
570}
571
572pub struct Polygon<Coord> {
574 points: Vec<Coord>,
575 style: ShapeStyle,
576}
577impl<Coord> Polygon<Coord> {
578 pub fn new<P: Into<Vec<Coord>>, S: Into<ShapeStyle>>(points: P, style: S) -> Self {
583 Self {
584 points: points.into(),
585 style: style.into(),
586 }
587 }
588}
589
590impl<'a, Coord> PointCollection<'a, Coord> for &'a Polygon<Coord> {
591 type Point = &'a Coord;
592 type IntoIter = &'a [Coord];
593 fn point_iter(self) -> &'a [Coord] {
594 &self.points
595 }
596}
597
598impl<Coord, DB: DrawingBackend> Drawable<DB> for Polygon<Coord> {
599 fn draw<I: Iterator<Item = BackendCoord>>(
600 &self,
601 points: I,
602 backend: &mut DB,
603 _: (u32, u32),
604 ) -> Result<(), DrawingErrorKind<DB::ErrorType>> {
605 backend.fill_polygon(points, &self.style.color.to_backend_color())
606 }
607}
608
609#[cfg(test)]
610#[test]
611fn test_polygon_element() {
612 use crate::prelude::*;
613 let points = vec![(100, 100), (50, 500), (300, 400), (200, 300), (550, 200)];
614 let expected_points = points.clone();
615
616 let da = crate::create_mocked_drawing_area(800, 800, |m| {
617 m.check_fill_polygon(move |c, p| {
618 assert_eq!(c, BLUE.to_rgba());
619 assert_eq!(expected_points.len(), p.len());
620 assert_eq!(expected_points, p);
621 });
622 m.drop_check(|b| {
623 assert_eq!(b.num_fill_polygon_call, 1);
624 assert_eq!(b.draw_count, 1);
625 });
626 });
627
628 da.draw(&Polygon::new(points.clone(), BLUE))
629 .expect("Drawing Failure");
630}