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
10use crate::coord::ranged1d::{KeyPointHint, Ranged, ReversibleRanged};
11use crate::coord::{CoordTranslate, ReverseCoordTranslate};
12
13use crate::style::ShapeStyle;
14use plotters_backend::{BackendCoord, DrawingBackend, DrawingErrorKind};
15
16use std::ops::Range;
17
18/// A 2D Cartesian coordinate system described by two 1D ranged coordinate specs.
19#[derive(Clone)]
20pub 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
27impl<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
31 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
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
83 pub fn get_x_range(&self) -> Range<X::ValueType> {
84 self.logic_x.range()
85 }
86
87 /// Get the range of Y axis
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
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
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
103 pub fn x_spec(&self) -> &X {
104 &self.logic_x
105 }
106
107 /// Get the 1D coordinate spec for Y axis
108 pub fn y_spec(&self) -> &Y {
109 &self.logic_y
110 }
111}
112
113impl<X: Ranged, Y: Ranged> CoordTranslate for Cartesian2d<X, Y> {
114 type From = (X::ValueType, Y::ValueType);
115
116 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
124impl<X: ReversibleRanged, Y: ReversibleRanged> ReverseCoordTranslate for Cartesian2d<X, Y> {
125 fn reverse_translate(&self, input: BackendCoord) -> Option<Self::From> {
126 Some((
127 self.logic_x.unmap(input:input.0, self.back_x)?,
128 self.logic_y.unmap(input:input.1, self.back_y)?,
129 ))
130 }
131}
132
133/// Represent a coordinate mesh for the two ranged value coordinate system
134pub enum MeshLine<'a, X: Ranged, Y: Ranged> {
135 /// Used to plot the horizontal lines of the mesh
136 XMesh(BackendCoord, BackendCoord, &'a X::ValueType),
137 /// Used to plot the vertical lines of the mesh
138 YMesh(BackendCoord, BackendCoord, &'a Y::ValueType),
139}
140
141impl<'a, X: Ranged, Y: Ranged> MeshLine<'a, X, Y> {
142 /// Draw a single mesh line onto the backend
143 pub fn draw<DB: DrawingBackend>(
144 &self,
145 backend: &mut DB,
146 style: &ShapeStyle,
147 ) -> Result<(), DrawingErrorKind<DB::ErrorType>> {
148 let (&left: (i32, i32), &right: (i32, i32)) = match self {
149 MeshLine::XMesh(a: &(i32, i32), b: &(i32, i32), _) => (a, b),
150 MeshLine::YMesh(a: &(i32, i32), b: &(i32, i32), _) => (a, b),
151 };
152 backend.draw_line(from:left, to:right, style)
153 }
154}
155