| 1 | use super::*; |
| 2 | use super::{Drawable, PointCollection}; |
| 3 | use crate::style::{Color, ShapeStyle, SizeDesc}; |
| 4 | use plotters_backend::{BackendCoord, DrawingBackend, DrawingErrorKind}; |
| 5 | |
| 6 | /** |
| 7 | A common trait for elements that can be interpreted as points: A cross, a circle, a triangle marker... |
| 8 | |
| 9 | This is used internally by Plotters and should probably not be included in user code. |
| 10 | See [`EmptyElement`] for more information and examples. |
| 11 | */ |
| 12 | pub trait PointElement<Coord, Size: SizeDesc> { |
| 13 | /** |
| 14 | Point creator. |
| 15 | |
| 16 | This is used internally by Plotters and should probably not be included in user code. |
| 17 | See [`EmptyElement`] for more information and examples. |
| 18 | */ |
| 19 | fn make_point(pos: Coord, size: Size, style: ShapeStyle) -> Self; |
| 20 | } |
| 21 | |
| 22 | /** |
| 23 | A cross marker for visualizing data series. |
| 24 | |
| 25 | See [`EmptyElement`] for more information and examples. |
| 26 | */ |
| 27 | pub struct Cross<Coord, Size: SizeDesc> { |
| 28 | center: Coord, |
| 29 | size: Size, |
| 30 | style: ShapeStyle, |
| 31 | } |
| 32 | |
| 33 | impl<Coord, Size: SizeDesc> Cross<Coord, Size> { |
| 34 | /** |
| 35 | Creates a cross marker. |
| 36 | |
| 37 | See [`EmptyElement`] for more information and examples. |
| 38 | */ |
| 39 | pub fn new<T: Into<ShapeStyle>>(coord: Coord, size: Size, style: T) -> Self { |
| 40 | Self { |
| 41 | center: coord, |
| 42 | size, |
| 43 | style: style.into(), |
| 44 | } |
| 45 | } |
| 46 | } |
| 47 | |
| 48 | impl<'a, Coord: 'a, Size: SizeDesc> PointCollection<'a, Coord> for &'a Cross<Coord, Size> { |
| 49 | type Point = &'a Coord; |
| 50 | type IntoIter = std::iter::Once<&'a Coord>; |
| 51 | fn point_iter(self) -> std::iter::Once<&'a Coord> { |
| 52 | std::iter::once(&self.center) |
| 53 | } |
| 54 | } |
| 55 | |
| 56 | impl<Coord, DB: DrawingBackend, Size: SizeDesc> Drawable<DB> for Cross<Coord, Size> { |
| 57 | fn draw<I: Iterator<Item = BackendCoord>>( |
| 58 | &self, |
| 59 | mut points: I, |
| 60 | backend: &mut DB, |
| 61 | ps: (u32, u32), |
| 62 | ) -> Result<(), DrawingErrorKind<DB::ErrorType>> { |
| 63 | if let Some((x, y)) = points.next() { |
| 64 | let size = self.size.in_pixels(&ps); |
| 65 | let (x0, y0) = (x - size, y - size); |
| 66 | let (x1, y1) = (x + size, y + size); |
| 67 | backend.draw_line((x0, y0), (x1, y1), &self.style)?; |
| 68 | backend.draw_line((x0, y1), (x1, y0), &self.style)?; |
| 69 | } |
| 70 | Ok(()) |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | /** |
| 75 | A triangle marker for visualizing data series. |
| 76 | |
| 77 | See [`EmptyElement`] for more information and examples. |
| 78 | */ |
| 79 | pub struct TriangleMarker<Coord, Size: SizeDesc> { |
| 80 | center: Coord, |
| 81 | size: Size, |
| 82 | style: ShapeStyle, |
| 83 | } |
| 84 | |
| 85 | impl<Coord, Size: SizeDesc> TriangleMarker<Coord, Size> { |
| 86 | /** |
| 87 | Creates a triangle marker. |
| 88 | |
| 89 | See [`EmptyElement`] for more information and examples. |
| 90 | */ |
| 91 | pub fn new<T: Into<ShapeStyle>>(coord: Coord, size: Size, style: T) -> Self { |
| 92 | Self { |
| 93 | center: coord, |
| 94 | size, |
| 95 | style: style.into(), |
| 96 | } |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | impl<'a, Coord: 'a, Size: SizeDesc> PointCollection<'a, Coord> for &'a TriangleMarker<Coord, Size> { |
| 101 | type Point = &'a Coord; |
| 102 | type IntoIter = std::iter::Once<&'a Coord>; |
| 103 | fn point_iter(self) -> std::iter::Once<&'a Coord> { |
| 104 | std::iter::once(&self.center) |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | impl<Coord, DB: DrawingBackend, Size: SizeDesc> Drawable<DB> for TriangleMarker<Coord, Size> { |
| 109 | fn draw<I: Iterator<Item = BackendCoord>>( |
| 110 | &self, |
| 111 | mut points: I, |
| 112 | backend: &mut DB, |
| 113 | ps: (u32, u32), |
| 114 | ) -> Result<(), DrawingErrorKind<DB::ErrorType>> { |
| 115 | if let Some((x, y)) = points.next() { |
| 116 | let size = self.size.in_pixels(&ps); |
| 117 | let points = [-90, -210, -330] |
| 118 | .iter() |
| 119 | .map(|deg| f64::from(*deg) * std::f64::consts::PI / 180.0) |
| 120 | .map(|rad| { |
| 121 | ( |
| 122 | (rad.cos() * f64::from(size) + f64::from(x)).ceil() as i32, |
| 123 | (rad.sin() * f64::from(size) + f64::from(y)).ceil() as i32, |
| 124 | ) |
| 125 | }); |
| 126 | backend.fill_polygon(points, &self.style.color.to_backend_color())?; |
| 127 | } |
| 128 | Ok(()) |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | impl<Coord, Size: SizeDesc> PointElement<Coord, Size> for Cross<Coord, Size> { |
| 133 | fn make_point(pos: Coord, size: Size, style: ShapeStyle) -> Self { |
| 134 | Self::new(pos, size, style) |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | impl<Coord, Size: SizeDesc> PointElement<Coord, Size> for TriangleMarker<Coord, Size> { |
| 139 | fn make_point(pos: Coord, size: Size, style: ShapeStyle) -> Self { |
| 140 | Self::new(pos, size, style) |
| 141 | } |
| 142 | } |
| 143 | |
| 144 | impl<Coord, Size: SizeDesc> PointElement<Coord, Size> for Circle<Coord, Size> { |
| 145 | fn make_point(pos: Coord, size: Size, style: ShapeStyle) -> Self { |
| 146 | Self::new(pos, size, style) |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | impl<Coord, Size: SizeDesc> PointElement<Coord, Size> for Pixel<Coord> { |
| 151 | fn make_point(pos: Coord, _: Size, style: ShapeStyle) -> Self { |
| 152 | Self::new(pos, style) |
| 153 | } |
| 154 | } |
| 155 | |