plotters/coord/ranged2d/
cartesian.rs

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.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
134pub 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    /// Draw a single mesh line onto the backend
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}