plotters/coord/ranged1d/combinators/
nested.rs

1use crate::coord::ranged1d::{
2    AsRangedCoord, DiscreteRanged, KeyPointHint, NoDefaultFormatting, Ranged, ValueFormatter,
3};
4use std::ops::Range;
5
6/// Describe a value for a nested coordinate
7#[derive(PartialEq, Eq, Clone, Debug)]
8pub enum NestedValue<C, V> {
9    /// Category value
10    Category(C),
11    /// One exact nested value
12    Value(C, V),
13}
14
15impl<C, V> NestedValue<C, V> {
16    /// Get the category of current nest value
17    pub fn category(&self) -> &C {
18        match self {
19            NestedValue::Category(cat) => cat,
20            NestedValue::Value(cat, _) => cat,
21        }
22    }
23    /// Get the nested value from this value
24    pub fn nested_value(&self) -> Option<&V> {
25        match self {
26            NestedValue::Category(_) => None,
27            NestedValue::Value(_, val) => Some(val),
28        }
29    }
30}
31
32impl<C, V> From<(C, V)> for NestedValue<C, V> {
33    fn from((cat, val): (C, V)) -> NestedValue<C, V> {
34        NestedValue::Value(cat, val)
35    }
36}
37
38impl<C, V> From<C> for NestedValue<C, V> {
39    fn from(cat: C) -> NestedValue<C, V> {
40        NestedValue::Category(cat)
41    }
42}
43
44/// A nested coordinate spec which is a discrete coordinate on the top level and
45/// for each value in discrete value, there is a secondary coordinate system.
46/// And the value is defined as a tuple of primary coordinate value and secondary
47/// coordinate value
48pub struct NestedRange<Primary: DiscreteRanged, Secondary: Ranged> {
49    primary: Primary,
50    secondary: Vec<Secondary>,
51}
52
53impl<PT, ST, P, S> ValueFormatter<NestedValue<PT, ST>> for NestedRange<P, S>
54where
55    P: Ranged<ValueType = PT> + DiscreteRanged,
56    S: Ranged<ValueType = ST>,
57    P: ValueFormatter<PT>,
58    S: ValueFormatter<ST>,
59{
60    fn format(value: &NestedValue<PT, ST>) -> String {
61        match value {
62            NestedValue::Category(cat) => P::format(cat),
63            NestedValue::Value(_, val) => S::format(val),
64        }
65    }
66}
67
68impl<P: DiscreteRanged, S: Ranged> Ranged for NestedRange<P, S> {
69    type FormatOption = NoDefaultFormatting;
70    type ValueType = NestedValue<P::ValueType, S::ValueType>;
71
72    fn range(&self) -> Range<Self::ValueType> {
73        let primary_range = self.primary.range();
74
75        let secondary_left = self.secondary[0].range().start;
76        let secondary_right = self.secondary[self.primary.size() - 1].range().end;
77
78        NestedValue::Value(primary_range.start, secondary_left)
79            ..NestedValue::Value(primary_range.end, secondary_right)
80    }
81
82    fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 {
83        let idx = self.primary.index_of(value.category()).unwrap_or(0);
84        let total = self.primary.size();
85
86        let bucket_size = (limit.1 - limit.0) / total as i32;
87        let mut residual = (limit.1 - limit.0) % total as i32;
88
89        if residual < 0 {
90            residual += total as i32;
91        }
92
93        let s_left = limit.0 + bucket_size * idx as i32 + residual.min(idx as i32);
94        let s_right = s_left + bucket_size + if (residual as usize) < idx { 1 } else { 0 };
95
96        if let Some(secondary_value) = value.nested_value() {
97            self.secondary[idx].map(secondary_value, (s_left, s_right))
98        } else {
99            (s_left + s_right) / 2
100        }
101    }
102
103    fn key_points<Hint: KeyPointHint>(&self, hint: Hint) -> Vec<Self::ValueType> {
104        if !hint.weight().allow_light_points() || hint.max_num_points() < self.primary.size() * 2 {
105            self.primary
106                .key_points(hint)
107                .into_iter()
108                .map(NestedValue::Category)
109                .collect()
110        } else {
111            let secondary_size =
112                (hint.max_num_points() - self.primary.size()) / self.primary.size();
113            self.primary
114                .values()
115                .enumerate()
116                .map(|(idx, val)| {
117                    std::iter::once(NestedValue::Category(val)).chain(
118                        self.secondary[idx]
119                            .key_points(secondary_size)
120                            .into_iter()
121                            .map(move |v| {
122                                NestedValue::Value(self.primary.from_index(idx).unwrap(), v)
123                            }),
124                    )
125                })
126                .flatten()
127                .collect()
128        }
129    }
130}
131
132impl<P: DiscreteRanged, S: DiscreteRanged> DiscreteRanged for NestedRange<P, S> {
133    fn size(&self) -> usize {
134        self.secondary.iter().map(|x| x.size()).sum::<usize>()
135    }
136
137    fn index_of(&self, value: &Self::ValueType) -> Option<usize> {
138        let p_idx = self.primary.index_of(value.category())?;
139        let s_idx = self.secondary[p_idx].index_of(value.nested_value()?)?;
140        Some(
141            s_idx
142                + self.secondary[..p_idx]
143                    .iter()
144                    .map(|x| x.size())
145                    .sum::<usize>(),
146        )
147    }
148
149    fn from_index(&self, mut index: usize) -> Option<Self::ValueType> {
150        for (p_idx, snd) in self.secondary.iter().enumerate() {
151            if snd.size() > index {
152                return Some(NestedValue::Value(
153                    self.primary.from_index(p_idx).unwrap(),
154                    snd.from_index(index).unwrap(),
155                ));
156            }
157            index -= snd.size();
158        }
159        None
160    }
161}
162
163pub trait BuildNestedCoord: AsRangedCoord
164where
165    Self::CoordDescType: DiscreteRanged,
166{
167    fn nested_coord<S: AsRangedCoord>(
168        self,
169        builder: impl Fn(<Self::CoordDescType as Ranged>::ValueType) -> S,
170    ) -> NestedRange<Self::CoordDescType, S::CoordDescType> {
171        let primary: Self::CoordDescType = self.into();
172        assert!(primary.size() > 0);
173
174        let secondary = primary
175            .values()
176            .map(|value| builder(value).into())
177            .collect();
178
179        NestedRange { primary, secondary }
180    }
181}
182
183impl<T: AsRangedCoord> BuildNestedCoord for T where T::CoordDescType: DiscreteRanged {}
184
185#[cfg(test)]
186mod test {
187    use super::*;
188
189    #[test]
190    fn test_nested_coord() {
191        let coord = (0..10).nested_coord(|x| 0..(x + 1));
192
193        let range = coord.range();
194
195        assert_eq!(NestedValue::Value(0, 0)..NestedValue::Value(10, 11), range);
196        assert_eq!(coord.map(&NestedValue::Category(0), (0, 1100)), 50);
197        assert_eq!(coord.map(&NestedValue::Value(0, 0), (0, 1100)), 0);
198        assert_eq!(coord.map(&NestedValue::Value(5, 4), (0, 1100)), 567);
199
200        assert_eq!(coord.size(), (2 + 12) * 11 / 2);
201        assert_eq!(coord.index_of(&NestedValue::Value(5, 4)), Some(24));
202        assert_eq!(coord.from_index(24), Some(NestedValue::Value(5, 4)));
203    }
204}