• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::collections::{hash_map::IntoIter as HashMapIter, HashMap};
2 use std::marker::PhantomData;
3 use std::ops::AddAssign;
4 
5 use crate::chart::ChartContext;
6 use crate::coord::cartesian::Cartesian2d;
7 use crate::coord::ranged1d::{DiscreteRanged, Ranged};
8 use crate::element::Rectangle;
9 use crate::style::{Color, ShapeStyle, GREEN};
10 use plotters_backend::DrawingBackend;
11 
12 pub trait HistogramType {}
13 pub struct Vertical;
14 pub struct Horizontal;
15 
16 impl HistogramType for Vertical {}
17 impl HistogramType for Horizontal {}
18 
19 /// The series that aggregate data into a histogram
20 pub struct Histogram<'a, BR, A, Tag = Vertical>
21 where
22     BR: DiscreteRanged,
23     A: AddAssign<A> + Default,
24     Tag: HistogramType,
25 {
26     style: Box<dyn Fn(&BR::ValueType, &A) -> ShapeStyle + 'a>,
27     margin: u32,
28     iter: HashMapIter<usize, A>,
29     baseline: Box<dyn Fn(&BR::ValueType) -> A + 'a>,
30     br: BR,
31     _p: PhantomData<Tag>,
32 }
33 
34 impl<'a, BR, A, Tag> Histogram<'a, BR, A, Tag>
35 where
36     BR: DiscreteRanged + Clone,
37     A: AddAssign<A> + Default + 'a,
38     Tag: HistogramType,
39 {
empty(br: &BR) -> Self40     fn empty(br: &BR) -> Self {
41         Self {
42             style: Box::new(|_, _| GREEN.filled()),
43             margin: 5,
44             iter: HashMap::new().into_iter(),
45             baseline: Box::new(|_| A::default()),
46             br: br.clone(),
47             _p: PhantomData,
48         }
49     }
50     /// Set the style of the histogram
style<S: Into<ShapeStyle>>(mut self, style: S) -> Self51     pub fn style<S: Into<ShapeStyle>>(mut self, style: S) -> Self {
52         let style = style.into();
53         self.style = Box::new(move |_, _| style.clone());
54         self
55     }
56 
57     /// Set the style of histogram using a lambda function
style_func( mut self, style_func: impl Fn(&BR::ValueType, &A) -> ShapeStyle + 'a, ) -> Self58     pub fn style_func(
59         mut self,
60         style_func: impl Fn(&BR::ValueType, &A) -> ShapeStyle + 'a,
61     ) -> Self {
62         self.style = Box::new(style_func);
63         self
64     }
65 
66     /// Set the baseline of the histogram
baseline(mut self, baseline: A) -> Self where A: Clone,67     pub fn baseline(mut self, baseline: A) -> Self
68     where
69         A: Clone,
70     {
71         self.baseline = Box::new(move |_| baseline.clone());
72         self
73     }
74 
75     /// Set a function that defines variant baseline
baseline_func(mut self, func: impl Fn(&BR::ValueType) -> A + 'a) -> Self76     pub fn baseline_func(mut self, func: impl Fn(&BR::ValueType) -> A + 'a) -> Self {
77         self.baseline = Box::new(func);
78         self
79     }
80 
81     /// Set the margin for each bar
margin(mut self, value: u32) -> Self82     pub fn margin(mut self, value: u32) -> Self {
83         self.margin = value;
84         self
85     }
86 
87     /// Set the data iterator
data<TB: Into<BR::ValueType>, I: IntoIterator<Item = (TB, A)>>( mut self, iter: I, ) -> Self88     pub fn data<TB: Into<BR::ValueType>, I: IntoIterator<Item = (TB, A)>>(
89         mut self,
90         iter: I,
91     ) -> Self {
92         let mut buffer = HashMap::<usize, A>::new();
93         for (x, y) in iter.into_iter() {
94             if let Some(x) = self.br.index_of(&x.into()) {
95                 *buffer.entry(x).or_insert_with(Default::default) += y;
96             }
97         }
98         self.iter = buffer.into_iter();
99         self
100     }
101 }
102 
103 impl<'a, BR, A> Histogram<'a, BR, A, Vertical>
104 where
105     BR: DiscreteRanged + Clone,
106     A: AddAssign<A> + Default + 'a,
107 {
vertical<ACoord, DB: DrawingBackend + 'a>( parent: &ChartContext<DB, Cartesian2d<BR, ACoord>>, ) -> Self where ACoord: Ranged<ValueType = A>,108     pub fn vertical<ACoord, DB: DrawingBackend + 'a>(
109         parent: &ChartContext<DB, Cartesian2d<BR, ACoord>>,
110     ) -> Self
111     where
112         ACoord: Ranged<ValueType = A>,
113     {
114         let dp = parent.as_coord_spec().x_spec();
115 
116         Self::empty(dp)
117     }
118 }
119 
120 impl<'a, BR, A> Histogram<'a, BR, A, Horizontal>
121 where
122     BR: DiscreteRanged + Clone,
123     A: AddAssign<A> + Default + 'a,
124 {
horizontal<ACoord, DB: DrawingBackend>( parent: &ChartContext<DB, Cartesian2d<ACoord, BR>>, ) -> Self where ACoord: Ranged<ValueType = A>,125     pub fn horizontal<ACoord, DB: DrawingBackend>(
126         parent: &ChartContext<DB, Cartesian2d<ACoord, BR>>,
127     ) -> Self
128     where
129         ACoord: Ranged<ValueType = A>,
130     {
131         let dp = parent.as_coord_spec().y_spec();
132         Self::empty(dp)
133     }
134 }
135 
136 impl<'a, BR, A> Iterator for Histogram<'a, BR, A, Vertical>
137 where
138     BR: DiscreteRanged,
139     A: AddAssign<A> + Default,
140 {
141     type Item = Rectangle<(BR::ValueType, A)>;
next(&mut self) -> Option<Self::Item>142     fn next(&mut self) -> Option<Self::Item> {
143         while let Some((x, y)) = self.iter.next() {
144             if let Some((x, Some(nx))) = self
145                 .br
146                 .from_index(x)
147                 .map(|v| (v, self.br.from_index(x + 1)))
148             {
149                 let base = (self.baseline)(&x);
150                 let style = (self.style)(&x, &y);
151                 let mut rect = Rectangle::new([(x, y), (nx, base)], style);
152                 rect.set_margin(0, 0, self.margin, self.margin);
153                 return Some(rect);
154             }
155         }
156         None
157     }
158 }
159 
160 impl<'a, BR, A> Iterator for Histogram<'a, BR, A, Horizontal>
161 where
162     BR: DiscreteRanged,
163     A: AddAssign<A> + Default,
164 {
165     type Item = Rectangle<(A, BR::ValueType)>;
next(&mut self) -> Option<Self::Item>166     fn next(&mut self) -> Option<Self::Item> {
167         while let Some((y, x)) = self.iter.next() {
168             if let Some((y, Some(ny))) = self
169                 .br
170                 .from_index(y)
171                 .map(|v| (v, self.br.from_index(y + 1)))
172             {
173                 let base = (self.baseline)(&y);
174                 let style = (self.style)(&y, &x);
175                 let mut rect = Rectangle::new([(x, y), (base, ny)], style);
176                 rect.set_margin(0, 0, self.margin, self.margin);
177                 return Some(rect);
178             }
179         }
180         None
181     }
182 }
183