• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::coord::ranged1d::{
2     AsRangedCoord, KeyPointHint, NoDefaultFormatting, Ranged, ReversibleRanged, ValueFormatter,
3 };
4 use std::ops::Range;
5 
6 /// The trait indicates the coordinate is discrete
7 /// This means we can bidirectionally map the range value to 0 to N
8 /// in which N is the number of distinct values of the range.
9 ///
10 /// This is useful since for a histgoram, this is an abstraction of bucket.
11 pub trait DiscreteRanged
12 where
13     Self: Ranged,
14 {
15     /// Get the number of element in the range
16     /// Note: we assume that all the ranged discrete coordinate has finite value
17     ///
18     /// - **returns** The number of values in the range
size(&self) -> usize19     fn size(&self) -> usize;
20 
21     /// Map a value to the index
22     ///
23     /// Note: This function doesn't guarantee return None when the value is out of range.
24     /// The only way to confirm the value is in the range is to examining the return value isn't
25     /// larger than self.size.
26     ///
27     /// - `value`: The value to map
28     /// - **returns** The index of the value
index_of(&self, value: &Self::ValueType) -> Option<usize>29     fn index_of(&self, value: &Self::ValueType) -> Option<usize>;
30 
31     /// Reverse map the index to the value
32     ///
33     /// Note: This function doesn't guarantee returning None when the index is out of range.
34     ///
35     /// - `value`: The index to map
36     /// - **returns** The value
37     // TODO: This doesn't follows rust's naming convention - however, this is a potential breaking
38     // change, so postpone the fix to the next major release
39     #[allow(clippy::wrong_self_convention)]
from_index(&self, index: usize) -> Option<Self::ValueType>40     fn from_index(&self, index: usize) -> Option<Self::ValueType>;
41 
42     /// Return a iterator that iterates over the all possible values
43     ///
44     /// - **returns** The value iterator
values(&self) -> DiscreteValueIter<'_, Self> where Self: Sized,45     fn values(&self) -> DiscreteValueIter<'_, Self>
46     where
47         Self: Sized,
48     {
49         DiscreteValueIter(self, 0, self.size())
50     }
51 
52     /// Returns the previous value in this range
53     ///
54     /// Normally, it's based on the `from_index` and `index_of` function. But for
55     /// some of the coord spec, it's possible that we value faster implementation.
56     /// If this is the case, we can impelemnet the type specific impl for the `previous`
57     /// and `next`.
58     ///
59     /// - `value`: The current value
60     /// - **returns**: The value piror to current value
previous(&self, value: &Self::ValueType) -> Option<Self::ValueType>61     fn previous(&self, value: &Self::ValueType) -> Option<Self::ValueType> {
62         if let Some(idx) = self.index_of(value) {
63             if idx > 0 {
64                 return self.from_index(idx - 1);
65             }
66         }
67         None
68     }
69 
70     /// Returns the next value in this range
71     ///
72     /// Normally, it's based on the `from_index` and `index_of` function. But for
73     /// some of the coord spec, it's possible that we value faster implementation.
74     /// If this is the case, we can impelemnet the type specific impl for the `previous`
75     /// and `next`.
76     ///
77     /// - `value`: The current value
78     /// - **returns**: The value next to current value
next(&self, value: &Self::ValueType) -> Option<Self::ValueType>79     fn next(&self, value: &Self::ValueType) -> Option<Self::ValueType> {
80         if let Some(idx) = self.index_of(value) {
81             if idx + 1 < self.size() {
82                 return self.from_index(idx + 1);
83             }
84         }
85         None
86     }
87 }
88 
89 /// A `SegmentedCoord` is a decorator on any discrete coordinate specification.
90 /// This decorator will convert the discrete coordinate in two ways:
91 /// - Add an extra dummy element after all the values in original discrete coordinate
92 /// - Logically each value `v` from original coordinate system is mapped into an segment `[v, v+1)` where `v+1` denotes the successor of the `v`
93 /// - Introduce two types of values `SegmentValue::Exact(value)` which denotes the left end of value's segment and `SegmentValue::CenterOf(value)` which refers the center of the segment.
94 ///   This is used in histogram types, which uses a discrete coordinate as the buckets.
95 ///   The segmented coord always emits `CenterOf(value)` key points, thus it allows all the label and tick marks
96 ///   of the coordinate rendered in the middle of each segment.
97 ///   The corresponding trait [IntoSegmentedCoord](trait.IntoSegmentedCoord.html) is used to apply this decorator to coordinates.
98 #[derive(Clone)]
99 pub struct SegmentedCoord<D: DiscreteRanged>(D);
100 
101 /// The trait for types that can decorated by [SegmentedCoord](struct.SegmentedCoord.html) decorator.
102 pub trait IntoSegmentedCoord: AsRangedCoord
103 where
104     Self::CoordDescType: DiscreteRanged,
105 {
106     /// Convert current ranged value into a segmented coordinate
into_segmented(self) -> SegmentedCoord<Self::CoordDescType>107     fn into_segmented(self) -> SegmentedCoord<Self::CoordDescType> {
108         SegmentedCoord(self.into())
109     }
110 }
111 
112 impl<R: AsRangedCoord> IntoSegmentedCoord for R where R::CoordDescType: DiscreteRanged {}
113 
114 /// The value that used by the segmented coordinate.
115 #[derive(Clone, Debug)]
116 pub enum SegmentValue<T> {
117     /// Means we are referring the exact position of value `T`
118     Exact(T),
119     /// Means we are referring the center of position `T` and the successor of `T`
120     CenterOf(T),
121     /// Referring the last dummy element
122     Last,
123 }
124 
125 impl<T, D: DiscreteRanged + Ranged<ValueType = T>> ValueFormatter<SegmentValue<T>>
126     for SegmentedCoord<D>
127 where
128     D: ValueFormatter<T>,
129 {
format(value: &SegmentValue<T>) -> String130     fn format(value: &SegmentValue<T>) -> String {
131         match value {
132             SegmentValue::Exact(ref value) => D::format(value),
133             SegmentValue::CenterOf(ref value) => D::format(value),
134             _ => "".to_string(),
135         }
136     }
137 }
138 
139 impl<D: DiscreteRanged> Ranged for SegmentedCoord<D> {
140     type FormatOption = NoDefaultFormatting;
141     type ValueType = SegmentValue<D::ValueType>;
142 
map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32143     fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 {
144         let margin = ((limit.1 - limit.0) as f32 / self.0.size() as f32).round() as i32;
145 
146         match value {
147             SegmentValue::Exact(coord) => self.0.map(coord, (limit.0, limit.1 - margin)),
148             SegmentValue::CenterOf(coord) => {
149                 let left = self.0.map(coord, (limit.0, limit.1 - margin));
150                 if let Some(idx) = self.0.index_of(coord) {
151                     if idx + 1 < self.0.size() {
152                         let right = self.0.map(
153                             &self.0.from_index(idx + 1).unwrap(),
154                             (limit.0, limit.1 - margin),
155                         );
156                         return (left + right) / 2;
157                     }
158                 }
159                 left + margin / 2
160             }
161             SegmentValue::Last => limit.1,
162         }
163     }
164 
key_points<HintType: KeyPointHint>(&self, hint: HintType) -> Vec<Self::ValueType>165     fn key_points<HintType: KeyPointHint>(&self, hint: HintType) -> Vec<Self::ValueType> {
166         self.0
167             .key_points(hint)
168             .into_iter()
169             .map(SegmentValue::CenterOf)
170             .collect()
171     }
172 
range(&self) -> Range<Self::ValueType>173     fn range(&self) -> Range<Self::ValueType> {
174         let range = self.0.range();
175         SegmentValue::Exact(range.start)..SegmentValue::Exact(range.end)
176     }
177 }
178 
179 impl<D: DiscreteRanged> DiscreteRanged for SegmentedCoord<D> {
size(&self) -> usize180     fn size(&self) -> usize {
181         self.0.size() + 1
182     }
183 
index_of(&self, value: &Self::ValueType) -> Option<usize>184     fn index_of(&self, value: &Self::ValueType) -> Option<usize> {
185         match value {
186             SegmentValue::Exact(value) => self.0.index_of(value),
187             SegmentValue::CenterOf(value) => self.0.index_of(value),
188             SegmentValue::Last => Some(self.0.size()),
189         }
190     }
191 
from_index(&self, idx: usize) -> Option<Self::ValueType>192     fn from_index(&self, idx: usize) -> Option<Self::ValueType> {
193         match idx {
194             idx if idx < self.0.size() => self.0.from_index(idx).map(SegmentValue::Exact),
195             idx if idx == self.0.size() => Some(SegmentValue::Last),
196             _ => None,
197         }
198     }
199 }
200 
201 impl<T> From<T> for SegmentValue<T> {
from(this: T) -> SegmentValue<T>202     fn from(this: T) -> SegmentValue<T> {
203         SegmentValue::Exact(this)
204     }
205 }
206 
207 impl<DC: DiscreteRanged> ReversibleRanged for DC {
unmap(&self, input: i32, limit: (i32, i32)) -> Option<Self::ValueType>208     fn unmap(&self, input: i32, limit: (i32, i32)) -> Option<Self::ValueType> {
209         let idx = (f64::from(input - limit.0) * (self.size() as f64) / f64::from(limit.1 - limit.0))
210             .floor() as usize;
211         self.from_index(idx)
212     }
213 }
214 
215 /// The iterator that can be used to iterate all the values defined by a discrete coordinate
216 pub struct DiscreteValueIter<'a, T: DiscreteRanged>(&'a T, usize, usize);
217 
218 impl<'a, T: DiscreteRanged> Iterator for DiscreteValueIter<'a, T> {
219     type Item = T::ValueType;
next(&mut self) -> Option<T::ValueType>220     fn next(&mut self) -> Option<T::ValueType> {
221         if self.1 >= self.2 {
222             return None;
223         }
224         let idx = self.1;
225         self.1 += 1;
226         self.0.from_index(idx)
227     }
228 }
229 
230 #[cfg(test)]
231 mod test {
232     use super::*;
233     #[test]
test_value_iter()234     fn test_value_iter() {
235         let range: crate::coord::ranged1d::types::RangedCoordi32 = (-10..10).into();
236 
237         let values: Vec<_> = range.values().collect();
238 
239         assert_eq!(21, values.len());
240 
241         for (expected, value) in (-10..=10).zip(values) {
242             assert_eq!(expected, value);
243         }
244         assert_eq!(range.next(&5), Some(6));
245         assert_eq!(range.next(&10), None);
246         assert_eq!(range.previous(&-10), None);
247         assert_eq!(range.previous(&10), Some(9));
248     }
249 
250     #[test]
test_centric_coord()251     fn test_centric_coord() {
252         let coord = (0..10).into_segmented();
253 
254         assert_eq!(coord.size(), 12);
255         for i in 0..=11 {
256             match coord.from_index(i as usize) {
257                 Some(SegmentValue::Exact(value)) => assert_eq!(i, value),
258                 Some(SegmentValue::Last) => assert_eq!(i, 11),
259                 _ => panic!(),
260             }
261         }
262 
263         for (kps, idx) in coord.key_points(20).into_iter().zip(0..) {
264             match kps {
265                 SegmentValue::CenterOf(value) if value <= 10 => assert_eq!(value, idx),
266                 _ => panic!(),
267             }
268         }
269 
270         assert_eq!(coord.map(&SegmentValue::CenterOf(0), (0, 24)), 1);
271         assert_eq!(coord.map(&SegmentValue::Exact(0), (0, 24)), 0);
272         assert_eq!(coord.map(&SegmentValue::Exact(1), (0, 24)), 2);
273     }
274 }
275