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/plotters-rs/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 #[derive(Clone)] 36 pub struct DualCoordChartState<CT1: CoordTranslate, CT2: CoordTranslate> { 37 primary: ChartState<CT1>, 38 secondary: ChartState<CT2>, 39 } 40 41 impl<DB: DrawingBackend, CT1: CoordTranslate, CT2: CoordTranslate> 42 DualCoordChartContext<'_, DB, CT1, CT2> 43 { 44 /// 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>45 pub fn into_chart_state(self) -> DualCoordChartState<CT1, CT2> { 46 DualCoordChartState { 47 primary: self.primary.into(), 48 secondary: self.secondary.into(), 49 } 50 } 51 52 /// Convert the chart context into a sharable chart state. into_shared_chart_state(self) -> DualCoordChartState<Arc<CT1>, Arc<CT2>>53 pub fn into_shared_chart_state(self) -> DualCoordChartState<Arc<CT1>, Arc<CT2>> { 54 DualCoordChartState { 55 primary: self.primary.into_shared_chart_state(), 56 secondary: self.secondary.into_shared_chart_state(), 57 } 58 } 59 60 /// Copy the coordinate specs and make a chart state to_chart_state(&self) -> DualCoordChartState<CT1, CT2> where CT1: Clone, CT2: Clone,61 pub fn to_chart_state(&self) -> DualCoordChartState<CT1, CT2> 62 where 63 CT1: Clone, 64 CT2: Clone, 65 { 66 DualCoordChartState { 67 primary: self.primary.to_chart_state(), 68 secondary: self.secondary.to_chart_state(), 69 } 70 } 71 } 72 73 impl<CT1: CoordTranslate, CT2: CoordTranslate> DualCoordChartState<CT1, CT2> { 74 /// Restore the chart state on the given drawing area restore<DB: DrawingBackend>( self, area: &DrawingArea<DB, Shift>, ) -> DualCoordChartContext<'_, DB, CT1, CT2>75 pub fn restore<DB: DrawingBackend>( 76 self, 77 area: &DrawingArea<DB, Shift>, 78 ) -> DualCoordChartContext<'_, DB, CT1, CT2> { 79 let primary = self.primary.restore(area); 80 let secondary = self 81 .secondary 82 .restore(&primary.plotting_area().strip_coord_spec()); 83 DualCoordChartContext { primary, secondary } 84 } 85 } 86 87 impl<DB: DrawingBackend, CT1: CoordTranslate, CT2: CoordTranslate> 88 From<DualCoordChartContext<'_, DB, CT1, CT2>> for DualCoordChartState<CT1, CT2> 89 { from(chart: DualCoordChartContext<'_, DB, CT1, CT2>) -> DualCoordChartState<CT1, CT2>90 fn from(chart: DualCoordChartContext<'_, DB, CT1, CT2>) -> DualCoordChartState<CT1, CT2> { 91 chart.into_chart_state() 92 } 93 } 94 95 impl<'b, DB: DrawingBackend, CT1: CoordTranslate + Clone, CT2: CoordTranslate + Clone> 96 From<&'b DualCoordChartContext<'_, DB, CT1, CT2>> for DualCoordChartState<CT1, CT2> 97 { from(chart: &'b DualCoordChartContext<'_, DB, CT1, CT2>) -> DualCoordChartState<CT1, CT2>98 fn from(chart: &'b DualCoordChartContext<'_, DB, CT1, CT2>) -> DualCoordChartState<CT1, CT2> { 99 chart.to_chart_state() 100 } 101 } 102 103 impl<'a, DB: DrawingBackend, CT1: CoordTranslate, CT2: CoordTranslate> 104 DualCoordChartContext<'a, DB, CT1, CT2> 105 { new(mut primary: ChartContext<'a, DB, CT1>, secondary_coord: CT2) -> Self106 pub(super) fn new(mut primary: ChartContext<'a, DB, CT1>, secondary_coord: CT2) -> Self { 107 let secondary_drawing_area = primary 108 .drawing_area 109 .strip_coord_spec() 110 .apply_coord_spec(secondary_coord); 111 let mut secondary_x_label_area = [None, None]; 112 let mut secondary_y_label_area = [None, None]; 113 114 std::mem::swap(&mut primary.x_label_area[0], &mut secondary_x_label_area[0]); 115 std::mem::swap(&mut primary.y_label_area[1], &mut secondary_y_label_area[1]); 116 117 Self { 118 primary, 119 secondary: ChartContext { 120 x_label_area: secondary_x_label_area, 121 y_label_area: secondary_y_label_area, 122 drawing_area: secondary_drawing_area, 123 series_anno: vec![], 124 drawing_area_pos: (0, 0), 125 }, 126 } 127 } 128 129 /// Get a reference to the drawing area that uses the secondary coordinate system secondary_plotting_area(&self) -> &DrawingArea<DB, CT2>130 pub fn secondary_plotting_area(&self) -> &DrawingArea<DB, CT2> { 131 &self.secondary.drawing_area 132 } 133 134 /// Borrow a mutable reference to the chart context that uses the secondary 135 /// coordinate system borrow_secondary(&self) -> &ChartContext<'a, DB, CT2>136 pub fn borrow_secondary(&self) -> &ChartContext<'a, DB, CT2> { 137 &self.secondary 138 } 139 } 140 141 impl<DB: DrawingBackend, CT1: CoordTranslate, CT2: ReverseCoordTranslate> 142 DualCoordChartContext<'_, DB, CT1, CT2> 143 { 144 /// Convert the chart context into the secondary coordinate translation function into_secondary_coord_trans(self) -> impl Fn(BackendCoord) -> Option<CT2::From>145 pub fn into_secondary_coord_trans(self) -> impl Fn(BackendCoord) -> Option<CT2::From> { 146 let coord_spec = self.secondary.drawing_area.into_coord_spec(); 147 move |coord| coord_spec.reverse_translate(coord) 148 } 149 } 150 151 impl<DB: DrawingBackend, CT1: ReverseCoordTranslate, CT2: ReverseCoordTranslate> 152 DualCoordChartContext<'_, DB, CT1, CT2> 153 { 154 /// Convert the chart context into a pair of closures that maps the pixel coordinate into the 155 /// 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>, )156 pub fn into_coord_trans_pair( 157 self, 158 ) -> ( 159 impl Fn(BackendCoord) -> Option<CT1::From>, 160 impl Fn(BackendCoord) -> Option<CT2::From>, 161 ) { 162 let coord_spec_1 = self.primary.drawing_area.into_coord_spec(); 163 let coord_spec_2 = self.secondary.drawing_area.into_coord_spec(); 164 ( 165 move |coord| coord_spec_1.reverse_translate(coord), 166 move |coord| coord_spec_2.reverse_translate(coord), 167 ) 168 } 169 } 170 171 impl< 172 'a, 173 DB: DrawingBackend, 174 CT1: CoordTranslate, 175 XT, 176 YT, 177 SX: Ranged<ValueType = XT>, 178 SY: Ranged<ValueType = YT>, 179 > DualCoordChartContext<'a, DB, CT1, Cartesian2d<SX, SY>> 180 where 181 SX: ValueFormatter<XT>, 182 SY: ValueFormatter<YT>, 183 { 184 /// Start configure the style for the secondary axes configure_secondary_axes<'b>(&'b mut self) -> SecondaryMeshStyle<'a, 'b, SX, SY, DB>185 pub fn configure_secondary_axes<'b>(&'b mut self) -> SecondaryMeshStyle<'a, 'b, SX, SY, DB> { 186 SecondaryMeshStyle::new(&mut self.secondary) 187 } 188 } 189 190 impl<'a, DB: DrawingBackend, X: Ranged, Y: Ranged, SX: Ranged, SY: Ranged> 191 DualCoordChartContext<'a, DB, Cartesian2d<X, Y>, Cartesian2d<SX, SY>> 192 { 193 /// Draw a series use the secondary coordinate system. 194 /// - `series`: The series to draw 195 /// - `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>,196 pub fn draw_secondary_series<E, R, S>( 197 &mut self, 198 series: S, 199 ) -> Result<&mut SeriesAnno<'a, DB>, DrawingAreaErrorKind<DB::ErrorType>> 200 where 201 for<'b> &'b E: PointCollection<'b, (SX::ValueType, SY::ValueType)>, 202 E: Drawable<DB>, 203 R: Borrow<E>, 204 S: IntoIterator<Item = R>, 205 { 206 self.secondary.draw_series_impl(series)?; 207 Ok(self.primary.alloc_series_anno()) 208 } 209 } 210 211 impl<'a, DB: DrawingBackend, CT1: CoordTranslate, CT2: CoordTranslate> 212 Borrow<ChartContext<'a, DB, CT1>> for DualCoordChartContext<'a, DB, CT1, CT2> 213 { borrow(&self) -> &ChartContext<'a, DB, CT1>214 fn borrow(&self) -> &ChartContext<'a, DB, CT1> { 215 &self.primary 216 } 217 } 218 219 impl<'a, DB: DrawingBackend, CT1: CoordTranslate, CT2: CoordTranslate> 220 BorrowMut<ChartContext<'a, DB, CT1>> for DualCoordChartContext<'a, DB, CT1, CT2> 221 { borrow_mut(&mut self) -> &mut ChartContext<'a, DB, CT1>222 fn borrow_mut(&mut self) -> &mut ChartContext<'a, DB, CT1> { 223 &mut self.primary 224 } 225 } 226 227 impl<'a, DB: DrawingBackend, CT1: CoordTranslate, CT2: CoordTranslate> Deref 228 for DualCoordChartContext<'a, DB, CT1, CT2> 229 { 230 type Target = ChartContext<'a, DB, CT1>; deref(&self) -> &Self::Target231 fn deref(&self) -> &Self::Target { 232 self.borrow() 233 } 234 } 235 236 impl<'a, DB: DrawingBackend, CT1: CoordTranslate, CT2: CoordTranslate> DerefMut 237 for DualCoordChartContext<'a, DB, CT1, CT2> 238 { deref_mut(&mut self) -> &mut Self::Target239 fn deref_mut(&mut self) -> &mut Self::Target { 240 self.borrow_mut() 241 } 242 } 243