1 use crate::coord::ranged1d::{ 2 AsRangedCoord, DiscreteRanged, KeyPointHint, NoDefaultFormatting, Ranged, ValueFormatter, 3 }; 4 use std::ops::Range; 5 6 /// Grouping the value in the coordinate specification. 7 /// 8 /// This combinator doesn't change the coordinate mapping behavior. But it changes how 9 /// the key point is generated, this coordinate specification will enforce that only the first value in each group 10 /// can be emitted as the bold key points. 11 /// 12 /// This is useful, for example, when we have an X axis is a integer and denotes days. 13 /// And we are expecting the tick mark denotes weeks, in this way we can make the range 14 /// spec grouping by 7 elements. 15 /// With the help of the GroupBy decorator, this can be archived quite easily: 16 ///```rust 17 ///use plotters::prelude::*; 18 ///let mut buf = vec![0;1024*768*3]; 19 ///let area = BitMapBackend::with_buffer(buf.as_mut(), (1024, 768)).into_drawing_area(); 20 ///let chart = ChartBuilder::on(&area) 21 /// .build_cartesian_2d((0..100).group_by(7), 0..100) 22 /// .unwrap(); 23 ///``` 24 /// 25 /// To apply this combinator, call [ToGroupByRange::group_by](trait.ToGroupByRange.html#tymethod.group_by) method on any discrete coordinate spec. 26 #[derive(Clone)] 27 pub struct GroupBy<T: DiscreteRanged>(T, usize); 28 29 /// The trait that provides method `Self::group_by` function which creates a 30 /// `GroupBy` decorated ranged value. 31 pub trait ToGroupByRange: AsRangedCoord + Sized 32 where 33 Self::CoordDescType: DiscreteRanged, 34 { 35 /// Make a grouping ranged value, see the documentation for `GroupBy` for details. 36 /// 37 /// - `value`: The number of values we want to group it 38 /// - **return**: The newly created grouping range specification group_by(self, value: usize) -> GroupBy<<Self as AsRangedCoord>::CoordDescType>39 fn group_by(self, value: usize) -> GroupBy<<Self as AsRangedCoord>::CoordDescType> { 40 GroupBy(self.into(), value) 41 } 42 } 43 44 impl<T: AsRangedCoord + Sized> ToGroupByRange for T where T::CoordDescType: DiscreteRanged {} 45 46 impl<T: DiscreteRanged> DiscreteRanged for GroupBy<T> { size(&self) -> usize47 fn size(&self) -> usize { 48 (self.0.size() + self.1 - 1) / self.1 49 } index_of(&self, value: &Self::ValueType) -> Option<usize>50 fn index_of(&self, value: &Self::ValueType) -> Option<usize> { 51 self.0.index_of(value).map(|idx| idx / self.1) 52 } from_index(&self, index: usize) -> Option<Self::ValueType>53 fn from_index(&self, index: usize) -> Option<Self::ValueType> { 54 self.0.from_index(index * self.1) 55 } 56 } 57 58 impl<T, R: DiscreteRanged<ValueType = T> + ValueFormatter<T>> ValueFormatter<T> for GroupBy<R> { format(value: &T) -> String59 fn format(value: &T) -> String { 60 R::format(value) 61 } 62 } 63 64 impl<T: DiscreteRanged> Ranged for GroupBy<T> { 65 type FormatOption = NoDefaultFormatting; 66 type ValueType = T::ValueType; map(&self, value: &T::ValueType, limit: (i32, i32)) -> i3267 fn map(&self, value: &T::ValueType, limit: (i32, i32)) -> i32 { 68 self.0.map(value, limit) 69 } range(&self) -> Range<T::ValueType>70 fn range(&self) -> Range<T::ValueType> { 71 self.0.range() 72 } 73 // TODO: See issue issue #88 key_points<HintType: KeyPointHint>(&self, hint: HintType) -> Vec<T::ValueType>74 fn key_points<HintType: KeyPointHint>(&self, hint: HintType) -> Vec<T::ValueType> { 75 let range = 0..(self.0.size() + self.1) / self.1; 76 //let logic_range: RangedCoordusize = range.into(); 77 78 let interval = 79 ((range.end - range.start + hint.bold_points() - 1) / hint.bold_points()).max(1); 80 let count = (range.end - range.start) / interval; 81 82 let idx_iter = (0..hint.bold_points()).map(|x| x * interval); 83 84 if hint.weight().allow_light_points() && count < hint.bold_points() * 2 { 85 let outter_ticks = idx_iter; 86 let outter_tick_size = interval * self.1; 87 let inner_ticks_per_group = hint.max_num_points() / outter_ticks.len(); 88 let inner_ticks = 89 (outter_tick_size + inner_ticks_per_group - 1) / inner_ticks_per_group; 90 let inner_ticks: Vec<_> = (0..(outter_tick_size / inner_ticks)) 91 .map(move |x| x * inner_ticks) 92 .collect(); 93 let size = self.0.size(); 94 return outter_ticks 95 .flat_map(|base| inner_ticks.iter().map(move |&ofs| base * self.1 + ofs)) 96 .take_while(|&idx| idx < size) 97 .map(|x| self.0.from_index(x).unwrap()) 98 .collect(); 99 } 100 101 idx_iter 102 .map(|x| self.0.from_index(x * self.1).unwrap()) 103 .collect() 104 } 105 } 106 107 #[cfg(test)] 108 mod test { 109 use super::*; 110 #[test] test_group_by()111 fn test_group_by() { 112 let coord = (0..100).group_by(10); 113 assert_eq!(coord.size(), 11); 114 for (idx, val) in (0..).zip(coord.values()) { 115 assert_eq!(val, idx * 10); 116 assert_eq!(coord.from_index(idx as usize), Some(val)); 117 } 118 } 119 } 120