• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /// The dual coordinate system support
2 use std::borrow::{Borrow, BorrowMut};
3 use std::ops::{Deref, DerefMut};
4 use std::sync::Arc;
5 
6 use super::mesh::SecondaryMeshStyle;
7 use super::{ChartContext, ChartState, SeriesAnno};
8 
9 use crate::coord::cartesian::Cartesian2d;
10 use crate::coord::ranged1d::{Ranged, ValueFormatter};
11 use crate::coord::{CoordTranslate, ReverseCoordTranslate, Shift};
12 
13 use crate::drawing::DrawingArea;
14 use crate::drawing::DrawingAreaErrorKind;
15 use crate::element::{Drawable, PointCollection};
16 
17 use plotters_backend::{BackendCoord, DrawingBackend};
18 
19 /// The chart context that has two coordinate system attached.
20 /// This situation is quite common, for example, we with two different coodinate system.
21 /// For instance this example <img src="https://plotters-rs.github.io/plotters-doc-data/twoscale.png"></img>
22 /// This is done by attaching  a second coordinate system to ChartContext by method [ChartContext::set_secondary_coord](struct.ChartContext.html#method.set_secondary_coord).
23 /// For instance of dual coordinate charts, see [this example](https://github.com/38/plotters/blob/master/examples/two-scales.rs#L15).
24 /// Note: `DualCoordChartContext` is always deref to the chart context.
25 /// - If you want to configure the secondary axis, method [DualCoordChartContext::configure_secondary_axes](struct.DualCoordChartContext.html#method.configure_secondary_axes)
26 /// - If you want to draw a series using secondary coordinate system, use [DualCoordChartContext::draw_secondary_series](struct.DualCoordChartContext.html#method.draw_secondary_series). And method [ChartContext::draw_series](struct.ChartContext.html#method.draw_series) will always use primary coordinate spec.
27 pub struct DualCoordChartContext<'a, DB: DrawingBackend, CT1: CoordTranslate, CT2: CoordTranslate> {
28     pub(super) primary: ChartContext<'a, DB, CT1>,
29     pub(super) secondary: ChartContext<'a, DB, CT2>,
30 }
31 
32 /// The chart state for a dual coord chart, see the detailed description for `ChartState` for more
33 /// information about the purpose of a chart state.
34 /// Similar to [ChartState](struct.ChartState.html), but used for the dual coordinate charts.
35 pub struct DualCoordChartState<CT1: CoordTranslate, CT2: CoordTranslate> {
36     primary: ChartState<CT1>,
37     secondary: ChartState<CT2>,
38 }
39 
40 impl<DB: DrawingBackend, CT1: CoordTranslate, CT2: CoordTranslate>
41     DualCoordChartContext<'_, DB, CT1, CT2>
42 {
43     /// Convert the chart context into a chart state, similar to [ChartContext::into_chart_state](struct.ChartContext.html#method.into_chart_state)
into_chart_state(self) -> DualCoordChartState<CT1, CT2>44     pub fn into_chart_state(self) -> DualCoordChartState<CT1, CT2> {
45         DualCoordChartState {
46             primary: self.primary.into(),
47             secondary: self.secondary.into(),
48         }
49     }
50 
51     /// Convert the chart context into a sharable chart state.
into_shared_chart_state(self) -> DualCoordChartState<Arc<CT1>, Arc<CT2>>52     pub fn into_shared_chart_state(self) -> DualCoordChartState<Arc<CT1>, Arc<CT2>> {
53         DualCoordChartState {
54             primary: self.primary.into_shared_chart_state(),
55             secondary: self.secondary.into_shared_chart_state(),
56         }
57     }
58 
59     /// Copy the coordinate specs and make a chart state
to_chart_state(&self) -> DualCoordChartState<CT1, CT2> where CT1: Clone, CT2: Clone,60     pub fn to_chart_state(&self) -> DualCoordChartState<CT1, CT2>
61     where
62         CT1: Clone,
63         CT2: Clone,
64     {
65         DualCoordChartState {
66             primary: self.primary.to_chart_state(),
67             secondary: self.secondary.to_chart_state(),
68         }
69     }
70 }
71 
72 impl<CT1: CoordTranslate, CT2: CoordTranslate> DualCoordChartState<CT1, CT2> {
73     /// Restore the chart state on the given drawing area
restore<DB: DrawingBackend>( self, area: &DrawingArea<DB, Shift>, ) -> DualCoordChartContext<'_, DB, CT1, CT2>74     pub fn restore<DB: DrawingBackend>(
75         self,
76         area: &DrawingArea<DB, Shift>,
77     ) -> DualCoordChartContext<'_, DB, CT1, CT2> {
78         let primary = self.primary.restore(area);
79         let secondary = self
80             .secondary
81             .restore(&primary.plotting_area().strip_coord_spec());
82         DualCoordChartContext { primary, secondary }
83     }
84 }
85 
86 impl<DB: DrawingBackend, CT1: CoordTranslate, CT2: CoordTranslate>
87     From<DualCoordChartContext<'_, DB, CT1, CT2>> for DualCoordChartState<CT1, CT2>
88 {
from(chart: DualCoordChartContext<'_, DB, CT1, CT2>) -> DualCoordChartState<CT1, CT2>89     fn from(chart: DualCoordChartContext<'_, DB, CT1, CT2>) -> DualCoordChartState<CT1, CT2> {
90         chart.into_chart_state()
91     }
92 }
93 
94 impl<'b, DB: DrawingBackend, CT1: CoordTranslate + Clone, CT2: CoordTranslate + Clone>
95     From<&'b DualCoordChartContext<'_, DB, CT1, CT2>> for DualCoordChartState<CT1, CT2>
96 {
from(chart: &'b DualCoordChartContext<'_, DB, CT1, CT2>) -> DualCoordChartState<CT1, CT2>97     fn from(chart: &'b DualCoordChartContext<'_, DB, CT1, CT2>) -> DualCoordChartState<CT1, CT2> {
98         chart.to_chart_state()
99     }
100 }
101 
102 impl<'a, DB: DrawingBackend, CT1: CoordTranslate, CT2: CoordTranslate>
103     DualCoordChartContext<'a, DB, CT1, CT2>
104 {
new(mut primary: ChartContext<'a, DB, CT1>, secondary_coord: CT2) -> Self105     pub(super) fn new(mut primary: ChartContext<'a, DB, CT1>, secondary_coord: CT2) -> Self {
106         let secondary_drawing_area = primary
107             .drawing_area
108             .strip_coord_spec()
109             .apply_coord_spec(secondary_coord);
110         let mut secondary_x_label_area = [None, None];
111         let mut secondary_y_label_area = [None, None];
112 
113         std::mem::swap(&mut primary.x_label_area[0], &mut secondary_x_label_area[0]);
114         std::mem::swap(&mut primary.y_label_area[1], &mut secondary_y_label_area[1]);
115 
116         Self {
117             primary,
118             secondary: ChartContext {
119                 x_label_area: secondary_x_label_area,
120                 y_label_area: secondary_y_label_area,
121                 drawing_area: secondary_drawing_area,
122                 series_anno: vec![],
123                 drawing_area_pos: (0, 0),
124             },
125         }
126     }
127 
128     /// Get a reference to the drawing area that uses the secondary coordinate system
secondary_plotting_area(&self) -> &DrawingArea<DB, CT2>129     pub fn secondary_plotting_area(&self) -> &DrawingArea<DB, CT2> {
130         &self.secondary.drawing_area
131     }
132 
133     /// Borrow a mutable reference to the chart context that uses the secondary
134     /// coordinate system
borrow_secondary(&self) -> &ChartContext<'a, DB, CT2>135     pub fn borrow_secondary(&self) -> &ChartContext<'a, DB, CT2> {
136         &self.secondary
137     }
138 }
139 
140 impl<DB: DrawingBackend, CT1: CoordTranslate, CT2: ReverseCoordTranslate>
141     DualCoordChartContext<'_, DB, CT1, CT2>
142 {
143     /// Convert the chart context into the secondary coordinate translation function
into_secondary_coord_trans(self) -> impl Fn(BackendCoord) -> Option<CT2::From>144     pub fn into_secondary_coord_trans(self) -> impl Fn(BackendCoord) -> Option<CT2::From> {
145         let coord_spec = self.secondary.drawing_area.into_coord_spec();
146         move |coord| coord_spec.reverse_translate(coord)
147     }
148 }
149 
150 impl<DB: DrawingBackend, CT1: ReverseCoordTranslate, CT2: ReverseCoordTranslate>
151     DualCoordChartContext<'_, DB, CT1, CT2>
152 {
153     /// Convert the chart context into a pair of closures that maps the pixel coordinate into the
154     /// logical coordinate for both primary coordinate system and secondary coordinate system.
into_coord_trans_pair( self, ) -> ( impl Fn(BackendCoord) -> Option<CT1::From>, impl Fn(BackendCoord) -> Option<CT2::From>, )155     pub fn into_coord_trans_pair(
156         self,
157     ) -> (
158         impl Fn(BackendCoord) -> Option<CT1::From>,
159         impl Fn(BackendCoord) -> Option<CT2::From>,
160     ) {
161         let coord_spec_1 = self.primary.drawing_area.into_coord_spec();
162         let coord_spec_2 = self.secondary.drawing_area.into_coord_spec();
163         (
164             move |coord| coord_spec_1.reverse_translate(coord),
165             move |coord| coord_spec_2.reverse_translate(coord),
166         )
167     }
168 }
169 
170 impl<
171         'a,
172         DB: DrawingBackend,
173         CT1: CoordTranslate,
174         XT,
175         YT,
176         SX: Ranged<ValueType = XT>,
177         SY: Ranged<ValueType = YT>,
178     > DualCoordChartContext<'a, DB, CT1, Cartesian2d<SX, SY>>
179 where
180     SX: ValueFormatter<XT>,
181     SY: ValueFormatter<YT>,
182 {
183     /// Start configure the style for the secondary axes
configure_secondary_axes<'b>(&'b mut self) -> SecondaryMeshStyle<'a, 'b, SX, SY, DB>184     pub fn configure_secondary_axes<'b>(&'b mut self) -> SecondaryMeshStyle<'a, 'b, SX, SY, DB> {
185         SecondaryMeshStyle::new(&mut self.secondary)
186     }
187 }
188 
189 impl<'a, DB: DrawingBackend, X: Ranged, Y: Ranged, SX: Ranged, SY: Ranged>
190     DualCoordChartContext<'a, DB, Cartesian2d<X, Y>, Cartesian2d<SX, SY>>
191 {
192     /// Draw a series use the secondary coordinate system.
193     /// - `series`: The series to draw
194     /// - `Returns` the series annotation object or error code
draw_secondary_series<E, R, S>( &mut self, series: S, ) -> Result<&mut SeriesAnno<'a, DB>, DrawingAreaErrorKind<DB::ErrorType>> where for<'b> &'b E: PointCollection<'b, (SX::ValueType, SY::ValueType)>, E: Drawable<DB>, R: Borrow<E>, S: IntoIterator<Item = R>,195     pub fn draw_secondary_series<E, R, S>(
196         &mut self,
197         series: S,
198     ) -> Result<&mut SeriesAnno<'a, DB>, DrawingAreaErrorKind<DB::ErrorType>>
199     where
200         for<'b> &'b E: PointCollection<'b, (SX::ValueType, SY::ValueType)>,
201         E: Drawable<DB>,
202         R: Borrow<E>,
203         S: IntoIterator<Item = R>,
204     {
205         self.secondary.draw_series_impl(series)?;
206         Ok(self.primary.alloc_series_anno())
207     }
208 }
209 
210 impl<'a, DB: DrawingBackend, CT1: CoordTranslate, CT2: CoordTranslate>
211     Borrow<ChartContext<'a, DB, CT1>> for DualCoordChartContext<'a, DB, CT1, CT2>
212 {
borrow(&self) -> &ChartContext<'a, DB, CT1>213     fn borrow(&self) -> &ChartContext<'a, DB, CT1> {
214         &self.primary
215     }
216 }
217 
218 impl<'a, DB: DrawingBackend, CT1: CoordTranslate, CT2: CoordTranslate>
219     BorrowMut<ChartContext<'a, DB, CT1>> for DualCoordChartContext<'a, DB, CT1, CT2>
220 {
borrow_mut(&mut self) -> &mut ChartContext<'a, DB, CT1>221     fn borrow_mut(&mut self) -> &mut ChartContext<'a, DB, CT1> {
222         &mut self.primary
223     }
224 }
225 
226 impl<'a, DB: DrawingBackend, CT1: CoordTranslate, CT2: CoordTranslate> Deref
227     for DualCoordChartContext<'a, DB, CT1, CT2>
228 {
229     type Target = ChartContext<'a, DB, CT1>;
deref(&self) -> &Self::Target230     fn deref(&self) -> &Self::Target {
231         self.borrow()
232     }
233 }
234 
235 impl<'a, DB: DrawingBackend, CT1: CoordTranslate, CT2: CoordTranslate> DerefMut
236     for DualCoordChartContext<'a, DB, CT1, CT2>
237 {
deref_mut(&mut self) -> &mut Self::Target238     fn deref_mut(&mut self) -> &mut Self::Target {
239         self.borrow_mut()
240     }
241 }
242