1 use super::{CoordTranslate, ReverseCoordTranslate};
2 use crate::drawing::backend::{BackendCoord, DrawingBackend, DrawingErrorKind};
3 use crate::style::ShapeStyle;
4 
5 use std::ops::Range;
6 
7 /// The trait that indicates we have a ordered and ranged value
8 /// Which is used to describe the axis
9 pub 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
map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i3214     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
key_points(&self, max_points: usize) -> Vec<Self::ValueType>17     fn key_points(&self, max_points: usize) -> Vec<Self::ValueType>;
18 
19     /// Get the range of this value
range(&self) -> Range<Self::ValueType>20     fn range(&self) -> Range<Self::ValueType>;
21 
22     /// This function provides the on-axis part of its range
23     #[allow(clippy::range_plus_one)]
axis_pixel_range(&self, limit: (i32, i32)) -> Range<i32>24     fn axis_pixel_range(&self, limit: (i32, i32)) -> Range<i32> {
25         if limit.0 < limit.1 {
26             limit.0..limit.1
27         } else {
28             (limit.1 + 1)..(limit.0 + 1)
29         }
30     }
31 }
32 
33 /// The trait indicates the ranged value can be map reversely, which means
34 /// an pixel-based coordinate is given, it's possible to figure out the underlying
35 /// logic value.
36 pub trait ReversibleRanged: Ranged {
unmap(&self, input: i32, limit: (i32, i32)) -> Option<Self::ValueType>37     fn unmap(&self, input: i32, limit: (i32, i32)) -> Option<Self::ValueType>;
38 }
39 
40 /// The coordinate described by two ranged value
41 pub struct RangedCoord<X: Ranged, Y: Ranged> {
42     logic_x: X,
43     logic_y: Y,
44     back_x: (i32, i32),
45     back_y: (i32, i32),
46 }
47 
48 impl<X: Ranged + Clone, Y: Ranged + Clone> Clone for RangedCoord<X, Y> {
clone(&self) -> Self49     fn clone(&self) -> Self {
50         Self {
51             logic_x: self.logic_x.clone(),
52             logic_y: self.logic_y.clone(),
53             back_x: self.back_x,
54             back_y: self.back_y,
55         }
56     }
57 }
58 
59 impl<X: Ranged, Y: Ranged> RangedCoord<X, Y> {
60     /// Create a new ranged value coordinate system
new<IntoX: Into<X>, IntoY: Into<Y>>( logic_x: IntoX, logic_y: IntoY, actual: (Range<i32>, Range<i32>), ) -> Self61     pub fn new<IntoX: Into<X>, IntoY: Into<Y>>(
62         logic_x: IntoX,
63         logic_y: IntoY,
64         actual: (Range<i32>, Range<i32>),
65     ) -> Self {
66         Self {
67             logic_x: logic_x.into(),
68             logic_y: logic_y.into(),
69             back_x: (actual.0.start, actual.0.end),
70             back_y: (actual.1.start, actual.1.end),
71         }
72     }
73 
74     /// Draw the mesh for the coordinate system
draw_mesh<E, DrawMesh: FnMut(MeshLine<X, Y>) -> Result<(), E>>( &self, h_limit: usize, v_limit: usize, mut draw_mesh: DrawMesh, ) -> Result<(), E>75     pub fn draw_mesh<E, DrawMesh: FnMut(MeshLine<X, Y>) -> Result<(), E>>(
76         &self,
77         h_limit: usize,
78         v_limit: usize,
79         mut draw_mesh: DrawMesh,
80     ) -> Result<(), E> {
81         let (xkp, ykp) = (
82             self.logic_x.key_points(v_limit),
83             self.logic_y.key_points(h_limit),
84         );
85 
86         for logic_x in xkp {
87             let x = self.logic_x.map(&logic_x, self.back_x);
88             draw_mesh(MeshLine::XMesh(
89                 (x, self.back_y.0),
90                 (x, self.back_y.1),
91                 &logic_x,
92             ))?;
93         }
94 
95         for logic_y in ykp {
96             let y = self.logic_y.map(&logic_y, self.back_y);
97             draw_mesh(MeshLine::YMesh(
98                 (self.back_x.0, y),
99                 (self.back_x.1, y),
100                 &logic_y,
101             ))?;
102         }
103 
104         Ok(())
105     }
106 
107     /// Get the range of X axis
get_x_range(&self) -> Range<X::ValueType>108     pub fn get_x_range(&self) -> Range<X::ValueType> {
109         self.logic_x.range()
110     }
111 
112     /// Get the range of Y axis
get_y_range(&self) -> Range<Y::ValueType>113     pub fn get_y_range(&self) -> Range<Y::ValueType> {
114         self.logic_y.range()
115     }
116 
get_x_axis_pixel_range(&self) -> Range<i32>117     pub fn get_x_axis_pixel_range(&self) -> Range<i32> {
118         self.logic_x.axis_pixel_range(self.back_x)
119     }
120 
get_y_axis_pixel_range(&self) -> Range<i32>121     pub fn get_y_axis_pixel_range(&self) -> Range<i32> {
122         self.logic_y.axis_pixel_range(self.back_y)
123     }
124 
x_spec(&self) -> &X125     pub fn x_spec(&self) -> &X {
126         &self.logic_x
127     }
128 
y_spec(&self) -> &Y129     pub fn y_spec(&self) -> &Y {
130         &self.logic_y
131     }
132 }
133 
134 impl<X: Ranged, Y: Ranged> CoordTranslate for RangedCoord<X, Y> {
135     type From = (X::ValueType, Y::ValueType);
136 
translate(&self, from: &Self::From) -> BackendCoord137     fn translate(&self, from: &Self::From) -> BackendCoord {
138         (
139             self.logic_x.map(&from.0, self.back_x),
140             self.logic_y.map(&from.1, self.back_y),
141         )
142     }
143 }
144 
145 impl<X: ReversibleRanged, Y: ReversibleRanged> ReverseCoordTranslate for RangedCoord<X, Y> {
reverse_translate(&self, input: BackendCoord) -> Option<Self::From>146     fn reverse_translate(&self, input: BackendCoord) -> Option<Self::From> {
147         Some((
148             self.logic_x.unmap(input.0, self.back_x)?,
149             self.logic_y.unmap(input.1, self.back_y)?,
150         ))
151     }
152 }
153 
154 /// Represent a coordinate mesh for the two ranged value coordinate system
155 pub enum MeshLine<'a, X: Ranged, Y: Ranged> {
156     XMesh(BackendCoord, BackendCoord, &'a X::ValueType),
157     YMesh(BackendCoord, BackendCoord, &'a Y::ValueType),
158 }
159 
160 impl<'a, X: Ranged, Y: Ranged> MeshLine<'a, X, Y> {
161     /// Draw a single mesh line onto the backend
draw<DB: DrawingBackend>( &self, backend: &mut DB, style: &ShapeStyle, ) -> Result<(), DrawingErrorKind<DB::ErrorType>>162     pub fn draw<DB: DrawingBackend>(
163         &self,
164         backend: &mut DB,
165         style: &ShapeStyle,
166     ) -> Result<(), DrawingErrorKind<DB::ErrorType>> {
167         let (&left, &right) = match self {
168             MeshLine::XMesh(a, b, _) => (a, b),
169             MeshLine::YMesh(a, b, _) => (a, b),
170         };
171         backend.draw_line(left, right, &style.color)
172     }
173 }
174 
175 /// The trait indicates the coordinate is discrete, so that we can draw histogram on it
176 pub trait DiscreteRanged
177 where
178     Self: Ranged,
179 {
180     type RangeParameter;
181 
get_range_parameter(&self) -> Self::RangeParameter182     fn get_range_parameter(&self) -> Self::RangeParameter;
183 
184     /// Get the smallest value that is larger than the `this` value
next_value(this: &Self::ValueType, param: &Self::RangeParameter) -> Self::ValueType185     fn next_value(this: &Self::ValueType, param: &Self::RangeParameter) -> Self::ValueType;
186 
187     /// Get the largest value that is smaller than `this` value
previous_value(this: &Self::ValueType, param: &Self::RangeParameter) -> Self::ValueType188     fn previous_value(this: &Self::ValueType, param: &Self::RangeParameter) -> Self::ValueType;
189 }
190 
191 /// The trait for the type that can be converted into a ranged coordinate axis
192 pub trait AsRangedCoord: Sized {
193     type CoordDescType: Ranged<ValueType = Self::Value> + From<Self>;
194     type Value;
195 }
196 
197 impl<T> AsRangedCoord for T
198 where
199     T: Ranged,
200     Range<T::ValueType>: Into<T>,
201 {
202     type CoordDescType = T;
203     type Value = T::ValueType;
204 }
205 
206 /// The axis decorator that makes key-point in the center of the value range
207 /// This is useful when we draw a histogram, since we want the axis value label
208 /// to be shown in the middle of the range rather than exactly the location where
209 /// the value mapped to.
210 pub struct CentricDiscreteRange<D: DiscreteRanged>(D)
211 where
212     <D as Ranged>::ValueType: Eq;
213 
214 /// The trait for types that can decorated by `CentricDiscreteRange` decorator
215 pub trait IntoCentric: AsRangedCoord
216 where
217     Self::CoordDescType: DiscreteRanged,
218     <Self::CoordDescType as Ranged>::ValueType: Eq,
219 {
220     /// Convert current ranged value into a centric ranged value
into_centric(self) -> CentricDiscreteRange<Self::CoordDescType>221     fn into_centric(self) -> CentricDiscreteRange<Self::CoordDescType> {
222         CentricDiscreteRange(self.into())
223     }
224 }
225 
226 impl<T: AsRangedCoord> IntoCentric for T
227 where
228     T::CoordDescType: DiscreteRanged,
229     <Self::CoordDescType as Ranged>::ValueType: Eq,
230 {
231 }
232 
233 impl<D: DiscreteRanged + Clone> Clone for CentricDiscreteRange<D>
234 where
235     <D as Ranged>::ValueType: Eq,
236 {
clone(&self) -> Self237     fn clone(&self) -> Self {
238         Self(self.0.clone())
239     }
240 }
241 
242 impl<D: DiscreteRanged> Ranged for CentricDiscreteRange<D>
243 where
244     <D as Ranged>::ValueType: Eq,
245 {
246     type ValueType = <D as Ranged>::ValueType;
247 
map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32248     fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 {
249         let prev = <D as DiscreteRanged>::previous_value(&value, &self.0.get_range_parameter());
250         (self.0.map(&prev, limit) + self.0.map(value, limit)) / 2
251     }
252 
key_points(&self, max_points: usize) -> Vec<Self::ValueType>253     fn key_points(&self, max_points: usize) -> Vec<Self::ValueType> {
254         self.0.key_points(max_points)
255     }
256 
range(&self) -> Range<Self::ValueType>257     fn range(&self) -> Range<Self::ValueType> {
258         self.0.range()
259     }
260 }
261 
262 impl<D: DiscreteRanged> DiscreteRanged for CentricDiscreteRange<D>
263 where
264     <D as Ranged>::ValueType: Eq,
265 {
266     type RangeParameter = <D as DiscreteRanged>::RangeParameter;
get_range_parameter(&self) -> Self::RangeParameter267     fn get_range_parameter(&self) -> Self::RangeParameter {
268         self.0.get_range_parameter()
269     }
next_value(this: &Self::ValueType, param: &Self::RangeParameter) -> Self::ValueType270     fn next_value(this: &Self::ValueType, param: &Self::RangeParameter) -> Self::ValueType {
271         <D as DiscreteRanged>::next_value(this, param)
272     }
273 
previous_value(this: &Self::ValueType, param: &Self::RangeParameter) -> Self::ValueType274     fn previous_value(this: &Self::ValueType, param: &Self::RangeParameter) -> Self::ValueType {
275         <D as DiscreteRanged>::previous_value(this, param)
276     }
277 }
278 
279 impl<D: DiscreteRanged> AsRangedCoord for CentricDiscreteRange<D>
280 where
281     <D as Ranged>::ValueType: Eq,
282 {
283     type CoordDescType = Self;
284     type Value = <Self as Ranged>::ValueType;
285 }
286 
287 /// This axis decorator will make the axis partially display on the axis.
288 /// At some time, we want the axis only covers some part of the value.
289 /// This decorator will have an additional display range defined.
290 pub struct PartialAxis<R: Ranged>(R, Range<R::ValueType>);
291 
292 /// The trait for the types that can be converted into a partial axis
293 pub trait IntoPartialAxis: AsRangedCoord {
294     /// Make the partial axis
295     ///
296     /// - `axis_range`: The range of the axis to be displayed
297     /// - **returns**: The converted range specification
partial_axis( self, axis_range: Range<<Self::CoordDescType as Ranged>::ValueType>, ) -> PartialAxis<Self::CoordDescType>298     fn partial_axis(
299         self,
300         axis_range: Range<<Self::CoordDescType as Ranged>::ValueType>,
301     ) -> PartialAxis<Self::CoordDescType> {
302         PartialAxis(self.into(), axis_range)
303     }
304 }
305 
306 impl<R: AsRangedCoord> IntoPartialAxis for R {}
307 
308 impl<R: Ranged + Clone> Clone for PartialAxis<R>
309 where
310     <R as Ranged>::ValueType: Clone,
311 {
clone(&self) -> Self312     fn clone(&self) -> Self {
313         PartialAxis(self.0.clone(), self.1.clone())
314     }
315 }
316 
317 impl<R: Ranged> Ranged for PartialAxis<R>
318 where
319     R::ValueType: Clone,
320 {
321     type ValueType = R::ValueType;
322 
map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32323     fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 {
324         self.0.map(value, limit)
325     }
326 
key_points(&self, max_points: usize) -> Vec<Self::ValueType>327     fn key_points(&self, max_points: usize) -> Vec<Self::ValueType> {
328         self.0.key_points(max_points)
329     }
330 
range(&self) -> Range<Self::ValueType>331     fn range(&self) -> Range<Self::ValueType> {
332         self.0.range()
333     }
334 
axis_pixel_range(&self, limit: (i32, i32)) -> Range<i32>335     fn axis_pixel_range(&self, limit: (i32, i32)) -> Range<i32> {
336         let left = self.map(&self.1.start, limit);
337         let right = self.map(&self.1.end, limit);
338 
339         left.min(right)..left.max(right)
340     }
341 }
342 
343 impl<R: DiscreteRanged> DiscreteRanged for PartialAxis<R>
344 where
345     R: Ranged,
346     <R as Ranged>::ValueType: Eq + Clone,
347 {
348     type RangeParameter = <R as DiscreteRanged>::RangeParameter;
get_range_parameter(&self) -> Self::RangeParameter349     fn get_range_parameter(&self) -> Self::RangeParameter {
350         self.0.get_range_parameter()
351     }
next_value(this: &Self::ValueType, param: &Self::RangeParameter) -> Self::ValueType352     fn next_value(this: &Self::ValueType, param: &Self::RangeParameter) -> Self::ValueType {
353         <R as DiscreteRanged>::next_value(this, param)
354     }
355 
previous_value(this: &Self::ValueType, param: &Self::RangeParameter) -> Self::ValueType356     fn previous_value(this: &Self::ValueType, param: &Self::RangeParameter) -> Self::ValueType {
357         <R as DiscreteRanged>::previous_value(this, param)
358     }
359 }
360 
361 impl<R: Ranged> AsRangedCoord for PartialAxis<R>
362 where
363     <R as Ranged>::ValueType: Clone,
364 {
365     type CoordDescType = Self;
366     type Value = <Self as Ranged>::ValueType;
367 }
368 
369 /// Make a partial axis based on the percentage of visible portion.
370 /// We can use `into_partial_axis` to create a partial axis range specification.
371 /// But sometimes, we want to directly specify the percentage visible to the user.
372 ///
373 /// - `axis_range`: The range specification
374 /// - `part`: The visible part of the axis. Each value is from [0.0, 1.0]
375 /// - **returns**: The partial axis created from the input, or `None` when not possible
make_partial_axis<T>( axis_range: Range<T>, part: Range<f64>, ) -> Option<PartialAxis<<Range<T> as AsRangedCoord>::CoordDescType>> where Range<T>: AsRangedCoord, T: num_traits::NumCast + Clone,376 pub fn make_partial_axis<T>(
377     axis_range: Range<T>,
378     part: Range<f64>,
379 ) -> Option<PartialAxis<<Range<T> as AsRangedCoord>::CoordDescType>>
380 where
381     Range<T>: AsRangedCoord,
382     T: num_traits::NumCast + Clone,
383 {
384     let left: f64 = num_traits::cast(axis_range.start.clone())?;
385     let right: f64 = num_traits::cast(axis_range.end.clone())?;
386 
387     let full_range_size = (right - left) / (part.end - part.start);
388 
389     let full_left = left - full_range_size * part.start;
390     let full_right = right + full_range_size * (1.0 - part.end);
391 
392     let full_range: Range<T> = num_traits::cast(full_left)?..num_traits::cast(full_right)?;
393 
394     let axis_range: <Range<T> as AsRangedCoord>::CoordDescType = axis_range.into();
395 
396     Some(PartialAxis(full_range.into(), axis_range.range()))
397 }
398