plotters/coord/ranged1d/
discrete.rs

1use crate::coord::ranged1d::{
2    AsRangedCoord, KeyPointHint, NoDefaultFormatting, Ranged, ReversibleRanged, ValueFormatter,
3};
4use 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.
11pub trait DiscreteRanged
12where
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
19    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
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)]
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
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
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
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)]
99pub struct SegmentedCoord<D: DiscreteRanged>(D);
100
101/// The trait for types that can decorated by [SegmentedCoord](struct.SegmentedCoord.html) decorator.
102pub trait IntoSegmentedCoord: AsRangedCoord
103where
104    Self::CoordDescType: DiscreteRanged,
105{
106    /// Convert current ranged value into a segmented coordinate
107    fn into_segmented(self) -> SegmentedCoord<Self::CoordDescType> {
108        SegmentedCoord(self.into())
109    }
110}
111
112impl<R: AsRangedCoord> IntoSegmentedCoord for R where R::CoordDescType: DiscreteRanged {}
113
114/// The value that used by the segmented coordinate.
115#[derive(Clone, Debug)]
116pub 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
125impl<T, D: DiscreteRanged + Ranged<ValueType = T>> ValueFormatter<SegmentValue<T>>
126    for SegmentedCoord<D>
127where
128    D: ValueFormatter<T>,
129{
130    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
139impl<D: DiscreteRanged> Ranged for SegmentedCoord<D> {
140    type FormatOption = NoDefaultFormatting;
141    type ValueType = SegmentValue<D::ValueType>;
142
143    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
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
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
179impl<D: DiscreteRanged> DiscreteRanged for SegmentedCoord<D> {
180    fn size(&self) -> usize {
181        self.0.size() + 1
182    }
183
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
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
201impl<T> From<T> for SegmentValue<T> {
202    fn from(this: T) -> SegmentValue<T> {
203        SegmentValue::Exact(this)
204    }
205}
206
207impl<DC: DiscreteRanged> ReversibleRanged for DC {
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
216pub struct DiscreteValueIter<'a, T: DiscreteRanged>(&'a T, usize, usize);
217
218impl<'a, T: DiscreteRanged> Iterator for DiscreteValueIter<'a, T> {
219    type Item = 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)]
231mod test {
232    use super::*;
233    #[test]
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]
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}