1 use super::*; 2 use plotters_backend::DrawingBackend; 3 use std::borrow::Borrow; 4 use std::iter::{once, Once}; 5 use std::marker::PhantomData; 6 use std::ops::Add; 7 8 /** 9 An empty composable element. This is the starting point of a composed element. 10 11 # Example 12 13 ``` 14 use plotters::prelude::*; 15 let data = [(1.0, 3.3), (2., 2.1), (3., 1.5), (4., 1.9), (5., 1.0)]; 16 let drawing_area = SVGBackend::new("composable.svg", (300, 200)).into_drawing_area(); 17 drawing_area.fill(&WHITE).unwrap(); 18 let mut chart_builder = ChartBuilder::on(&drawing_area); 19 chart_builder.margin(7).set_left_and_bottom_label_area_size(20); 20 let mut chart_context = chart_builder.build_cartesian_2d(0.0..5.5, 0.0..5.5).unwrap(); 21 chart_context.configure_mesh().draw().unwrap(); 22 chart_context.draw_series(data.map(|(x, y)| { 23 EmptyElement::at((x, y)) // Use the guest coordinate system with EmptyElement 24 + Circle::new((0, 0), 10, BLUE) // Use backend coordinates with the rest 25 + Cross::new((4, 4), 3, RED) 26 + Pixel::new((4, -4), RED) 27 + TriangleMarker::new((-4, -4), 4, RED) 28 })).unwrap(); 29 ``` 30 31 The result is a data series where each point consists of a circle, a cross, a pixel, and a triangle: 32 33  34 35 */ 36 pub struct EmptyElement<Coord, DB: DrawingBackend> { 37 coord: Coord, 38 phantom: PhantomData<DB>, 39 } 40 41 impl<Coord, DB: DrawingBackend> EmptyElement<Coord, DB> { 42 /** 43 An empty composable element. This is the starting point of a composed element. 44 45 See [`EmptyElement`] for more information and examples. 46 */ at(coord: Coord) -> Self47 pub fn at(coord: Coord) -> Self { 48 Self { 49 coord, 50 phantom: PhantomData, 51 } 52 } 53 } 54 55 impl<Coord, Other, DB: DrawingBackend> Add<Other> for EmptyElement<Coord, DB> 56 where 57 Other: Drawable<DB>, 58 for<'a> &'a Other: PointCollection<'a, BackendCoord>, 59 { 60 type Output = BoxedElement<Coord, DB, Other>; add(self, other: Other) -> Self::Output61 fn add(self, other: Other) -> Self::Output { 62 BoxedElement { 63 offset: self.coord, 64 inner: other, 65 phantom: PhantomData, 66 } 67 } 68 } 69 70 impl<'a, Coord, DB: DrawingBackend> PointCollection<'a, Coord> for &'a EmptyElement<Coord, DB> { 71 type Point = &'a Coord; 72 type IntoIter = Once<&'a Coord>; point_iter(self) -> Self::IntoIter73 fn point_iter(self) -> Self::IntoIter { 74 once(&self.coord) 75 } 76 } 77 78 impl<Coord, DB: DrawingBackend> Drawable<DB> for EmptyElement<Coord, DB> { draw<I: Iterator<Item = BackendCoord>>( &self, _pos: I, _backend: &mut DB, _: (u32, u32), ) -> Result<(), DrawingErrorKind<DB::ErrorType>>79 fn draw<I: Iterator<Item = BackendCoord>>( 80 &self, 81 _pos: I, 82 _backend: &mut DB, 83 _: (u32, u32), 84 ) -> Result<(), DrawingErrorKind<DB::ErrorType>> { 85 Ok(()) 86 } 87 } 88 89 /** 90 A container for one drawable element, used for composition. 91 92 This is used internally by Plotters and should probably not be included in user code. 93 See [`EmptyElement`] for more information and examples. 94 */ 95 pub struct BoxedElement<Coord, DB: DrawingBackend, A: Drawable<DB>> { 96 inner: A, 97 offset: Coord, 98 phantom: PhantomData<DB>, 99 } 100 101 impl<'b, Coord, DB: DrawingBackend, A: Drawable<DB>> PointCollection<'b, Coord> 102 for &'b BoxedElement<Coord, DB, A> 103 { 104 type Point = &'b Coord; 105 type IntoIter = Once<&'b Coord>; point_iter(self) -> Self::IntoIter106 fn point_iter(self) -> Self::IntoIter { 107 once(&self.offset) 108 } 109 } 110 111 impl<Coord, DB: DrawingBackend, A> Drawable<DB> for BoxedElement<Coord, DB, A> 112 where 113 for<'a> &'a A: PointCollection<'a, BackendCoord>, 114 A: Drawable<DB>, 115 { draw<I: Iterator<Item = BackendCoord>>( &self, mut pos: I, backend: &mut DB, ps: (u32, u32), ) -> Result<(), DrawingErrorKind<DB::ErrorType>>116 fn draw<I: Iterator<Item = BackendCoord>>( 117 &self, 118 mut pos: I, 119 backend: &mut DB, 120 ps: (u32, u32), 121 ) -> Result<(), DrawingErrorKind<DB::ErrorType>> { 122 if let Some((x0, y0)) = pos.next() { 123 self.inner.draw( 124 self.inner.point_iter().into_iter().map(|p| { 125 let p = p.borrow(); 126 (p.0 + x0, p.1 + y0) 127 }), 128 backend, 129 ps, 130 )?; 131 } 132 Ok(()) 133 } 134 } 135 136 impl<Coord, DB: DrawingBackend, My, Yours> Add<Yours> for BoxedElement<Coord, DB, My> 137 where 138 My: Drawable<DB>, 139 for<'a> &'a My: PointCollection<'a, BackendCoord>, 140 Yours: Drawable<DB>, 141 for<'a> &'a Yours: PointCollection<'a, BackendCoord>, 142 { 143 type Output = ComposedElement<Coord, DB, My, Yours>; add(self, yours: Yours) -> Self::Output144 fn add(self, yours: Yours) -> Self::Output { 145 ComposedElement { 146 offset: self.offset, 147 first: self.inner, 148 second: yours, 149 phantom: PhantomData, 150 } 151 } 152 } 153 154 /** 155 A container for two drawable elements, used for composition. 156 157 This is used internally by Plotters and should probably not be included in user code. 158 See [`EmptyElement`] for more information and examples. 159 */ 160 pub struct ComposedElement<Coord, DB: DrawingBackend, A, B> 161 where 162 A: Drawable<DB>, 163 B: Drawable<DB>, 164 { 165 first: A, 166 second: B, 167 offset: Coord, 168 phantom: PhantomData<DB>, 169 } 170 171 impl<'b, Coord, DB: DrawingBackend, A, B> PointCollection<'b, Coord> 172 for &'b ComposedElement<Coord, DB, A, B> 173 where 174 A: Drawable<DB>, 175 B: Drawable<DB>, 176 { 177 type Point = &'b Coord; 178 type IntoIter = Once<&'b Coord>; point_iter(self) -> Self::IntoIter179 fn point_iter(self) -> Self::IntoIter { 180 once(&self.offset) 181 } 182 } 183 184 impl<Coord, DB: DrawingBackend, A, B> Drawable<DB> for ComposedElement<Coord, DB, A, B> 185 where 186 for<'a> &'a A: PointCollection<'a, BackendCoord>, 187 for<'b> &'b B: PointCollection<'b, BackendCoord>, 188 A: Drawable<DB>, 189 B: Drawable<DB>, 190 { draw<I: Iterator<Item = BackendCoord>>( &self, mut pos: I, backend: &mut DB, ps: (u32, u32), ) -> Result<(), DrawingErrorKind<DB::ErrorType>>191 fn draw<I: Iterator<Item = BackendCoord>>( 192 &self, 193 mut pos: I, 194 backend: &mut DB, 195 ps: (u32, u32), 196 ) -> Result<(), DrawingErrorKind<DB::ErrorType>> { 197 if let Some((x0, y0)) = pos.next() { 198 self.first.draw( 199 self.first.point_iter().into_iter().map(|p| { 200 let p = p.borrow(); 201 (p.0 + x0, p.1 + y0) 202 }), 203 backend, 204 ps, 205 )?; 206 self.second.draw( 207 self.second.point_iter().into_iter().map(|p| { 208 let p = p.borrow(); 209 (p.0 + x0, p.1 + y0) 210 }), 211 backend, 212 ps, 213 )?; 214 } 215 Ok(()) 216 } 217 } 218 219 impl<Coord, DB: DrawingBackend, A, B, C> Add<C> for ComposedElement<Coord, DB, A, B> 220 where 221 A: Drawable<DB>, 222 for<'a> &'a A: PointCollection<'a, BackendCoord>, 223 B: Drawable<DB>, 224 for<'a> &'a B: PointCollection<'a, BackendCoord>, 225 C: Drawable<DB>, 226 for<'a> &'a C: PointCollection<'a, BackendCoord>, 227 { 228 type Output = ComposedElement<Coord, DB, A, ComposedElement<BackendCoord, DB, B, C>>; add(self, rhs: C) -> Self::Output229 fn add(self, rhs: C) -> Self::Output { 230 ComposedElement { 231 offset: self.offset, 232 first: self.first, 233 second: ComposedElement { 234 offset: (0, 0), 235 first: self.second, 236 second: rhs, 237 phantom: PhantomData, 238 }, 239 phantom: PhantomData, 240 } 241 } 242 } 243