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