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