plotters_unsable/coord/
ranged.rs

1use super::{CoordTranslate, ReverseCoordTranslate};
2use crate::drawing::backend::{BackendCoord, DrawingBackend, DrawingErrorKind};
3use crate::style::ShapeStyle;
4
5use std::ops::Range;
6
7/// The trait that indicates we have a ordered and ranged value
8/// Which is used to describe the axis
9pub trait Ranged {
10    /// The type of this value
11    type ValueType;
12
13    /// This function maps the value to i32, which is the drawing coordinate
14    fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32;
15
16    /// This function gives the key points that we can draw a grid based on this
17    fn key_points(&self, max_points: usize) -> Vec<Self::ValueType>;
18
19    /// Get the range of this value
20    fn range(&self) -> Range<Self::ValueType>;
21}
22
23/// The trait indicates the ranged value can be map reversely, which means
24/// an pixel-based cooridinate is given, it's possible to figureout the underlying
25/// logic value.
26pub trait ReversableRanged: Ranged {
27    fn unmap(&self, input: i32, limit: (i32, i32)) -> Option<Self::ValueType>;
28}
29
30/// The coordinate described by two ranged value
31pub struct RangedCoord<X: Ranged, Y: Ranged> {
32    logic_x: X,
33    logic_y: Y,
34    back_x: (i32, i32),
35    back_y: (i32, i32),
36}
37
38impl<X: Ranged, Y: Ranged> RangedCoord<X, Y> {
39    /// Create a new ranged value coordinate system
40    pub fn new<IntoX: Into<X>, IntoY: Into<Y>>(
41        logic_x: IntoX,
42        logic_y: IntoY,
43        actual: (Range<i32>, Range<i32>),
44    ) -> Self {
45        Self {
46            logic_x: logic_x.into(),
47            logic_y: logic_y.into(),
48            back_x: (actual.0.start, actual.0.end),
49            back_y: (actual.1.start, actual.1.end),
50        }
51    }
52
53    /// Draw the mesh for the coordinate system
54    pub fn draw_mesh<E, DrawMesh: FnMut(MeshLine<X, Y>) -> Result<(), E>>(
55        &self,
56        h_limit: usize,
57        v_limit: usize,
58        mut draw_mesh: DrawMesh,
59    ) -> Result<(), E> {
60        let (xkp, ykp) = (
61            self.logic_x.key_points(v_limit),
62            self.logic_y.key_points(h_limit),
63        );
64
65        for logic_x in xkp {
66            let x = self.logic_x.map(&logic_x, self.back_x);
67            draw_mesh(MeshLine::XMesh(
68                (x, self.back_y.0),
69                (x, self.back_y.1),
70                &logic_x,
71            ))?;
72        }
73
74        for logic_y in ykp {
75            let y = self.logic_y.map(&logic_y, self.back_y);
76            draw_mesh(MeshLine::YMesh(
77                (self.back_x.0, y),
78                (self.back_x.1, y),
79                &logic_y,
80            ))?;
81        }
82
83        Ok(())
84    }
85
86    /// Get the range of X axis
87    pub fn get_x_range(&self) -> Range<X::ValueType> {
88        self.logic_x.range()
89    }
90
91    /// Get the range of Y axis
92    pub fn get_y_range(&self) -> Range<Y::ValueType> {
93        self.logic_y.range()
94    }
95}
96
97impl<X: Ranged, Y: Ranged> CoordTranslate for RangedCoord<X, Y> {
98    type From = (X::ValueType, Y::ValueType);
99
100    fn translate(&self, from: &Self::From) -> BackendCoord {
101        (
102            self.logic_x.map(&from.0, self.back_x),
103            self.logic_y.map(&from.1, self.back_y),
104        )
105    }
106}
107
108impl<X: ReversableRanged, Y: ReversableRanged> ReverseCoordTranslate for RangedCoord<X, Y> {
109    fn reverse_translate(&self, input: BackendCoord) -> Option<Self::From> {
110        Some((
111            self.logic_x.unmap(input.0, self.back_x)?,
112            self.logic_y.unmap(input.1, self.back_y)?,
113        ))
114    }
115}
116
117/// Represent a coordinate mesh for the two ranged value coordinate system
118pub enum MeshLine<'a, X: Ranged, Y: Ranged> {
119    XMesh(BackendCoord, BackendCoord, &'a X::ValueType),
120    YMesh(BackendCoord, BackendCoord, &'a Y::ValueType),
121}
122
123impl<'a, X: Ranged, Y: Ranged> MeshLine<'a, X, Y> {
124    /// Draw a single mesh line onto the backend
125    pub fn draw<DB: DrawingBackend>(
126        &self,
127        backend: &mut DB,
128        style: &ShapeStyle,
129    ) -> Result<(), DrawingErrorKind<DB::ErrorType>> {
130        let (&left, &right) = match self {
131            MeshLine::XMesh(a, b, _) => (a, b),
132            MeshLine::YMesh(a, b, _) => (a, b),
133        };
134        backend.draw_line(left, right, &Box::new(style.color))
135    }
136}
137
138/// The trait indicates the coordinate is descrete, so that we can draw histogram on it
139pub trait DescreteRanged
140where
141    Self: Ranged,
142    Self::ValueType: Eq,
143{
144    /// Get the smallest value that is larger than the `this` value
145    fn next_value(this: &Self::ValueType) -> Self::ValueType;
146}
147
148/// The trait for the type that can be converted into a ranged coordinate axis
149pub trait AsRangedCoord: Sized {
150    type CoordDescType: Ranged + From<Self>;
151    type Value;
152}
153
154impl<T> AsRangedCoord for T
155where
156    T: Ranged,
157    Range<T::ValueType>: Into<T>,
158{
159    type CoordDescType = T;
160    type Value = T::ValueType;
161}