1 /*! 2 The 2-dimensional cartesian coordinate system. 3 4 This module provides the 2D cartesian coordinate system, which is composed by two independent 5 ranged 1D coordinate sepcification. 6 7 This types of coordinate system is used by the chart constructed with [ChartBuilder::build_cartesian_2d](../../chart/ChartBuilder.html#method.build_cartesian_2d). 8 */ 9 10 use crate::coord::ranged1d::{KeyPointHint, Ranged, ReversibleRanged}; 11 use crate::coord::{CoordTranslate, ReverseCoordTranslate}; 12 13 use crate::style::ShapeStyle; 14 use plotters_backend::{BackendCoord, DrawingBackend, DrawingErrorKind}; 15 16 use std::ops::Range; 17 18 /// A 2D Cartesian coordinate system described by two 1D ranged coordinate specs. 19 #[derive(Clone)] 20 pub struct Cartesian2d<X: Ranged, Y: Ranged> { 21 logic_x: X, 22 logic_y: Y, 23 back_x: (i32, i32), 24 back_y: (i32, i32), 25 } 26 27 impl<X: Ranged, Y: Ranged> Cartesian2d<X, Y> { 28 /// Create a new 2D cartesian coordinate system 29 /// - `logic_x` and `logic_y` : The description for the 1D coordinate system 30 /// - `actual`: The pixel range on the screen for this coordinate system new<IntoX: Into<X>, IntoY: Into<Y>>( logic_x: IntoX, logic_y: IntoY, actual: (Range<i32>, Range<i32>), ) -> Self31 pub fn new<IntoX: Into<X>, IntoY: Into<Y>>( 32 logic_x: IntoX, 33 logic_y: IntoY, 34 actual: (Range<i32>, Range<i32>), 35 ) -> Self { 36 Self { 37 logic_x: logic_x.into(), 38 logic_y: logic_y.into(), 39 back_x: (actual.0.start, actual.0.end), 40 back_y: (actual.1.start, actual.1.end), 41 } 42 } 43 44 /// Draw the mesh for the coordinate system draw_mesh< E, DrawMesh: FnMut(MeshLine<X, Y>) -> Result<(), E>, XH: KeyPointHint, YH: KeyPointHint, >( &self, h_limit: YH, v_limit: XH, mut draw_mesh: DrawMesh, ) -> Result<(), E>45 pub fn draw_mesh< 46 E, 47 DrawMesh: FnMut(MeshLine<X, Y>) -> Result<(), E>, 48 XH: KeyPointHint, 49 YH: KeyPointHint, 50 >( 51 &self, 52 h_limit: YH, 53 v_limit: XH, 54 mut draw_mesh: DrawMesh, 55 ) -> Result<(), E> { 56 let (xkp, ykp) = ( 57 self.logic_x.key_points(v_limit), 58 self.logic_y.key_points(h_limit), 59 ); 60 61 for logic_x in xkp { 62 let x = self.logic_x.map(&logic_x, self.back_x); 63 draw_mesh(MeshLine::XMesh( 64 (x, self.back_y.0), 65 (x, self.back_y.1), 66 &logic_x, 67 ))?; 68 } 69 70 for logic_y in ykp { 71 let y = self.logic_y.map(&logic_y, self.back_y); 72 draw_mesh(MeshLine::YMesh( 73 (self.back_x.0, y), 74 (self.back_x.1, y), 75 &logic_y, 76 ))?; 77 } 78 79 Ok(()) 80 } 81 82 /// Get the range of X axis get_x_range(&self) -> Range<X::ValueType>83 pub fn get_x_range(&self) -> Range<X::ValueType> { 84 self.logic_x.range() 85 } 86 87 /// Get the range of Y axis get_y_range(&self) -> Range<Y::ValueType>88 pub fn get_y_range(&self) -> Range<Y::ValueType> { 89 self.logic_y.range() 90 } 91 92 /// Get the horizental backend coordinate range where X axis should be drawn get_x_axis_pixel_range(&self) -> Range<i32>93 pub fn get_x_axis_pixel_range(&self) -> Range<i32> { 94 self.logic_x.axis_pixel_range(self.back_x) 95 } 96 97 /// Get the vertical backend coordinate range where Y axis should be drawn get_y_axis_pixel_range(&self) -> Range<i32>98 pub fn get_y_axis_pixel_range(&self) -> Range<i32> { 99 self.logic_y.axis_pixel_range(self.back_y) 100 } 101 102 /// Get the 1D coordinate spec for X axis x_spec(&self) -> &X103 pub fn x_spec(&self) -> &X { 104 &self.logic_x 105 } 106 107 /// Get the 1D coordinate spec for Y axis y_spec(&self) -> &Y108 pub fn y_spec(&self) -> &Y { 109 &self.logic_y 110 } 111 } 112 113 impl<X: Ranged, Y: Ranged> CoordTranslate for Cartesian2d<X, Y> { 114 type From = (X::ValueType, Y::ValueType); 115 translate(&self, from: &Self::From) -> BackendCoord116 fn translate(&self, from: &Self::From) -> BackendCoord { 117 ( 118 self.logic_x.map(&from.0, self.back_x), 119 self.logic_y.map(&from.1, self.back_y), 120 ) 121 } 122 } 123 124 impl<X: ReversibleRanged, Y: ReversibleRanged> ReverseCoordTranslate for Cartesian2d<X, Y> { reverse_translate(&self, input: BackendCoord) -> Option<Self::From>125 fn reverse_translate(&self, input: BackendCoord) -> Option<Self::From> { 126 Some(( 127 self.logic_x.unmap(input.0, self.back_x)?, 128 self.logic_y.unmap(input.1, self.back_y)?, 129 )) 130 } 131 } 132 133 /// Represent a coordinate mesh for the two ranged value coordinate system 134 pub enum MeshLine<'a, X: Ranged, Y: Ranged> { 135 XMesh(BackendCoord, BackendCoord, &'a X::ValueType), 136 YMesh(BackendCoord, BackendCoord, &'a Y::ValueType), 137 } 138 139 impl<'a, X: Ranged, Y: Ranged> MeshLine<'a, X, Y> { 140 /// Draw a single mesh line onto the backend draw<DB: DrawingBackend>( &self, backend: &mut DB, style: &ShapeStyle, ) -> Result<(), DrawingErrorKind<DB::ErrorType>>141 pub fn draw<DB: DrawingBackend>( 142 &self, 143 backend: &mut DB, 144 style: &ShapeStyle, 145 ) -> Result<(), DrawingErrorKind<DB::ErrorType>> { 146 let (&left, &right) = match self { 147 MeshLine::XMesh(a, b, _) => (a, b), 148 MeshLine::YMesh(a, b, _) => (a, b), 149 }; 150 backend.draw_line(left, right, style) 151 } 152 } 153