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 guareentee return None when the value is out of range. 24 /// The only way to confirm the value is in the range is to examing 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 guareentee 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 protential 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 coordiante in two ways: 91 /// - Add an extra dummy element after all the values in origianl discrete coordinate 92 /// - Logically each value `v` from original coordinate system is mapped into an segment `[v, v+1)` where `v+1` denotes the sucessor 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. The segmented coord always emits `CenterOf(value)` key points, thus it allows all the label and tick marks 95 /// of the coordinate rendered in the middle of each segment. 96 /// The coresponding trait [IntoSegmentedCoord](trait.IntoSegmentedCoord.html) is used to apply this decorator to coordinates. 97 #[derive(Clone)] 98 pub struct SegmentedCoord<D: DiscreteRanged>(D); 99 100 /// The trait for types that can decorated by [SegmentedCoord](struct.SegmentedCoord.html) decorator. 101 pub trait IntoSegmentedCoord: AsRangedCoord 102 where 103 Self::CoordDescType: DiscreteRanged, 104 { 105 /// Convert current ranged value into a segmented coordinate into_segmented(self) -> SegmentedCoord<Self::CoordDescType>106 fn into_segmented(self) -> SegmentedCoord<Self::CoordDescType> { 107 SegmentedCoord(self.into()) 108 } 109 } 110 111 impl<R: AsRangedCoord> IntoSegmentedCoord for R where R::CoordDescType: DiscreteRanged {} 112 113 /// The value that used by the segmented coordinate. 114 #[derive(Clone, Debug)] 115 pub enum SegmentValue<T> { 116 /// Means we are referring the exact position of value `T` 117 Exact(T), 118 /// Means we are referring the center of position `T` and the successor of `T` 119 CenterOf(T), 120 /// Referring the last dummy element 121 Last, 122 } 123 124 impl<T, D: DiscreteRanged + Ranged<ValueType = T>> ValueFormatter<SegmentValue<T>> 125 for SegmentedCoord<D> 126 where 127 D: ValueFormatter<T>, 128 { format(value: &SegmentValue<T>) -> String129 fn format(value: &SegmentValue<T>) -> String { 130 match value { 131 SegmentValue::Exact(ref value) => D::format(value), 132 SegmentValue::CenterOf(ref value) => D::format(value), 133 _ => "".to_string(), 134 } 135 } 136 } 137 138 impl<D: DiscreteRanged> Ranged for SegmentedCoord<D> { 139 type FormatOption = NoDefaultFormatting; 140 type ValueType = SegmentValue<D::ValueType>; 141 map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32142 fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 { 143 let margin = ((limit.1 - limit.0) as f32 / self.0.size() as f32).round() as i32; 144 145 match value { 146 SegmentValue::Exact(coord) => self.0.map(coord, (limit.0, limit.1 - margin)), 147 SegmentValue::CenterOf(coord) => { 148 let left = self.0.map(coord, (limit.0, limit.1 - margin)); 149 if let Some(idx) = self.0.index_of(coord) { 150 if idx + 1 < self.0.size() { 151 let right = self.0.map( 152 &self.0.from_index(idx + 1).unwrap(), 153 (limit.0, limit.1 - margin), 154 ); 155 return (left + right) / 2; 156 } 157 } 158 left + margin / 2 159 } 160 SegmentValue::Last => limit.1, 161 } 162 } 163 key_points<HintType: KeyPointHint>(&self, hint: HintType) -> Vec<Self::ValueType>164 fn key_points<HintType: KeyPointHint>(&self, hint: HintType) -> Vec<Self::ValueType> { 165 self.0 166 .key_points(hint) 167 .into_iter() 168 .map(SegmentValue::CenterOf) 169 .collect() 170 } 171 range(&self) -> Range<Self::ValueType>172 fn range(&self) -> Range<Self::ValueType> { 173 let range = self.0.range(); 174 SegmentValue::Exact(range.start)..SegmentValue::Exact(range.end) 175 } 176 } 177 178 impl<D: DiscreteRanged> DiscreteRanged for SegmentedCoord<D> { size(&self) -> usize179 fn size(&self) -> usize { 180 self.0.size() + 1 181 } 182 index_of(&self, value: &Self::ValueType) -> Option<usize>183 fn index_of(&self, value: &Self::ValueType) -> Option<usize> { 184 match value { 185 SegmentValue::Exact(value) => self.0.index_of(value), 186 SegmentValue::CenterOf(value) => self.0.index_of(value), 187 SegmentValue::Last => Some(self.0.size()), 188 } 189 } 190 from_index(&self, idx: usize) -> Option<Self::ValueType>191 fn from_index(&self, idx: usize) -> Option<Self::ValueType> { 192 match idx { 193 idx if idx < self.0.size() => self.0.from_index(idx).map(SegmentValue::Exact), 194 idx if idx == self.0.size() => Some(SegmentValue::Last), 195 _ => None, 196 } 197 } 198 } 199 200 impl<T> From<T> for SegmentValue<T> { from(this: T) -> SegmentValue<T>201 fn from(this: T) -> SegmentValue<T> { 202 SegmentValue::Exact(this) 203 } 204 } 205 206 impl<DC: DiscreteRanged> ReversibleRanged for DC { unmap(&self, input: i32, limit: (i32, i32)) -> Option<Self::ValueType>207 fn unmap(&self, input: i32, limit: (i32, i32)) -> Option<Self::ValueType> { 208 let idx = (f64::from(input - limit.0) * (self.size() as f64) / f64::from(limit.1 - limit.0)) 209 .floor() as usize; 210 self.from_index(idx) 211 } 212 } 213 214 /// The iterator that can be used to iterate all the values defined by a discrete coordinate 215 pub struct DiscreteValueIter<'a, T: DiscreteRanged>(&'a T, usize, usize); 216 217 impl<'a, T: DiscreteRanged> Iterator for DiscreteValueIter<'a, T> { 218 type Item = T::ValueType; next(&mut self) -> Option<T::ValueType>219 fn next(&mut self) -> Option<T::ValueType> { 220 if self.1 >= self.2 { 221 return None; 222 } 223 let idx = self.1; 224 self.1 += 1; 225 self.0.from_index(idx) 226 } 227 } 228 229 #[cfg(test)] 230 mod test { 231 use super::*; 232 #[test] test_value_iter()233 fn test_value_iter() { 234 let range: crate::coord::ranged1d::types::RangedCoordi32 = (-10..10).into(); 235 236 let values: Vec<_> = range.values().collect(); 237 238 assert_eq!(21, values.len()); 239 240 for (expected, value) in (-10..=10).zip(values) { 241 assert_eq!(expected, value); 242 } 243 assert_eq!(range.next(&5), Some(6)); 244 assert_eq!(range.next(&10), None); 245 assert_eq!(range.previous(&-10), None); 246 assert_eq!(range.previous(&10), Some(9)); 247 } 248 249 #[test] test_centric_coord()250 fn test_centric_coord() { 251 let coord = (0..10).into_segmented(); 252 253 assert_eq!(coord.size(), 12); 254 for i in 0..=11 { 255 match coord.from_index(i as usize) { 256 Some(SegmentValue::Exact(value)) => assert_eq!(i, value), 257 Some(SegmentValue::Last) => assert_eq!(i, 11), 258 _ => panic!(), 259 } 260 } 261 262 for (kps, idx) in coord.key_points(20).into_iter().zip(0..) { 263 match kps { 264 SegmentValue::CenterOf(value) if value <= 10 => assert_eq!(value, idx), 265 _ => panic!(), 266 } 267 } 268 269 assert_eq!(coord.map(&SegmentValue::CenterOf(0), (0, 24)), 1); 270 assert_eq!(coord.map(&SegmentValue::Exact(0), (0, 24)), 0); 271 assert_eq!(coord.map(&SegmentValue::Exact(1), (0, 24)), 2); 272 } 273 } 274