1 use crate::coord::ranged1d::{
2     AsRangedCoord, DefaultFormatting, DiscreteRanged, KeyPointHint, Ranged,
3 };
4 use std::ops::Range;
5 
6 /// This axis decorator will make the axis partially display on the axis.
7 /// At some time, we want the axis only covers some part of the value.
8 /// This decorator will have an additional display range defined.
9 #[derive(Clone)]
10 pub struct PartialAxis<R: Ranged>(R, Range<R::ValueType>);
11 
12 /// The trait for the types that can be converted into a partial axis
13 pub trait IntoPartialAxis: AsRangedCoord {
14     /// Make the partial axis
15     ///
16     /// - `axis_range`: The range of the axis to be displayed
17     /// - **returns**: The converted range specification
partial_axis( self, axis_range: Range<<Self::CoordDescType as Ranged>::ValueType>, ) -> PartialAxis<Self::CoordDescType>18     fn partial_axis(
19         self,
20         axis_range: Range<<Self::CoordDescType as Ranged>::ValueType>,
21     ) -> PartialAxis<Self::CoordDescType> {
22         PartialAxis(self.into(), axis_range)
23     }
24 }
25 
26 impl<R: AsRangedCoord> IntoPartialAxis for R {}
27 
28 impl<R: Ranged> Ranged for PartialAxis<R>
29 where
30     R::ValueType: Clone,
31 {
32     type FormatOption = DefaultFormatting;
33     type ValueType = R::ValueType;
34 
map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i3235     fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 {
36         self.0.map(value, limit)
37     }
38 
key_points<Hint: KeyPointHint>(&self, hint: Hint) -> Vec<Self::ValueType>39     fn key_points<Hint: KeyPointHint>(&self, hint: Hint) -> Vec<Self::ValueType> {
40         self.0.key_points(hint)
41     }
42 
range(&self) -> Range<Self::ValueType>43     fn range(&self) -> Range<Self::ValueType> {
44         self.0.range()
45     }
46 
axis_pixel_range(&self, limit: (i32, i32)) -> Range<i32>47     fn axis_pixel_range(&self, limit: (i32, i32)) -> Range<i32> {
48         let left = self.map(&self.1.start, limit);
49         let right = self.map(&self.1.end, limit);
50 
51         left.min(right)..left.max(right)
52     }
53 }
54 
55 impl<R: DiscreteRanged> DiscreteRanged for PartialAxis<R>
56 where
57     R: Ranged,
58     <R as Ranged>::ValueType: Eq + Clone,
59 {
size(&self) -> usize60     fn size(&self) -> usize {
61         self.0.size()
62     }
63 
index_of(&self, value: &R::ValueType) -> Option<usize>64     fn index_of(&self, value: &R::ValueType) -> Option<usize> {
65         self.0.index_of(value)
66     }
67 
from_index(&self, index: usize) -> Option<Self::ValueType>68     fn from_index(&self, index: usize) -> Option<Self::ValueType> {
69         self.0.from_index(index)
70     }
71 }
72 
73 /// Make a partial axis based on the percentage of visible portion.
74 /// We can use `into_partial_axis` to create a partial axis range specification.
75 /// But sometimes, we want to directly specify the percentage visible to the user.
76 ///
77 /// - `axis_range`: The range specification
78 /// - `part`: The visible part of the axis. Each value is from [0.0, 1.0]
79 /// - **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,80 pub fn make_partial_axis<T>(
81     axis_range: Range<T>,
82     part: Range<f64>,
83 ) -> Option<PartialAxis<<Range<T> as AsRangedCoord>::CoordDescType>>
84 where
85     Range<T>: AsRangedCoord,
86     T: num_traits::NumCast + Clone,
87 {
88     let left: f64 = num_traits::cast(axis_range.start.clone())?;
89     let right: f64 = num_traits::cast(axis_range.end.clone())?;
90 
91     let full_range_size = (right - left) / (part.end - part.start);
92 
93     let full_left = left - full_range_size * part.start;
94     let full_right = right + full_range_size * (1.0 - part.end);
95 
96     let full_range: Range<T> = num_traits::cast(full_left)?..num_traits::cast(full_right)?;
97 
98     let axis_range: <Range<T> as AsRangedCoord>::CoordDescType = axis_range.into();
99 
100     Some(PartialAxis(full_range.into(), axis_range.range()))
101 }
102 
103 #[cfg(test)]
104 mod test {
105     use super::*;
106     #[test]
test_make_partial_axis()107     fn test_make_partial_axis() {
108         let r = make_partial_axis(20..80, 0.2..0.8).unwrap();
109         assert_eq!(r.size(), 101);
110         assert_eq!(r.range(), 0..100);
111         assert_eq!(r.axis_pixel_range((0, 100)), 20..80);
112     }
113 }
114