1use 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#[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 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 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 pub fn get_x_range(&self) -> Range<X::ValueType> {
84 self.logic_x.range()
85 }
86
87 pub fn get_y_range(&self) -> Range<Y::ValueType> {
89 self.logic_y.range()
90 }
91
92 pub fn get_x_axis_pixel_range(&self) -> Range<i32> {
94 self.logic_x.axis_pixel_range(self.back_x)
95 }
96
97 pub fn get_y_axis_pixel_range(&self) -> Range<i32> {
99 self.logic_y.axis_pixel_range(self.back_y)
100 }
101
102 pub fn x_spec(&self) -> &X {
104 &self.logic_x
105 }
106
107 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.0, self.back_x)?,
128 self.logic_y.unmap(input.1, self.back_y)?,
129 ))
130 }
131}
132
133pub enum MeshLine<'a, X: Ranged, Y: Ranged> {
135 XMesh(BackendCoord, BackendCoord, &'a X::ValueType),
136 YMesh(BackendCoord, BackendCoord, &'a Y::ValueType),
137}
138
139impl<'a, X: Ranged, Y: Ranged> MeshLine<'a, X, Y> {
140 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}