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 ![](https://cdn.jsdelivr.net/gh/facorread/plotters-doc-data@06d370f/apidoc/composable.svg)
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