use std::collections::{hash_map::IntoIter as HashMapIter, HashMap}; use std::marker::PhantomData; use std::ops::AddAssign; use crate::chart::ChartContext; use crate::coord::cartesian::Cartesian2d; use crate::coord::ranged1d::{DiscreteRanged, Ranged}; use crate::element::Rectangle; use crate::style::{Color, ShapeStyle, GREEN}; use plotters_backend::DrawingBackend; pub trait HistogramType {} pub struct Vertical; pub struct Horizontal; impl HistogramType for Vertical {} impl HistogramType for Horizontal {} /// The series that aggregate data into a histogram pub struct Histogram<'a, BR, A, Tag = Vertical> where BR: DiscreteRanged, A: AddAssign + Default, Tag: HistogramType, { style: Box ShapeStyle + 'a>, margin: u32, iter: HashMapIter, baseline: Box A + 'a>, br: BR, _p: PhantomData, } impl<'a, BR, A, Tag> Histogram<'a, BR, A, Tag> where BR: DiscreteRanged + Clone, A: AddAssign + Default + 'a, Tag: HistogramType, { fn empty(br: &BR) -> Self { Self { style: Box::new(|_, _| GREEN.filled()), margin: 5, iter: HashMap::new().into_iter(), baseline: Box::new(|_| A::default()), br: br.clone(), _p: PhantomData, } } /// Set the style of the histogram pub fn style>(mut self, style: S) -> Self { let style = style.into(); self.style = Box::new(move |_, _| style.clone()); self } /// Set the style of histogram using a lambda function pub fn style_func( mut self, style_func: impl Fn(&BR::ValueType, &A) -> ShapeStyle + 'a, ) -> Self { self.style = Box::new(style_func); self } /// Set the baseline of the histogram pub fn baseline(mut self, baseline: A) -> Self where A: Clone, { self.baseline = Box::new(move |_| baseline.clone()); self } /// Set a function that defines variant baseline pub fn baseline_func(mut self, func: impl Fn(&BR::ValueType) -> A + 'a) -> Self { self.baseline = Box::new(func); self } /// Set the margin for each bar pub fn margin(mut self, value: u32) -> Self { self.margin = value; self } /// Set the data iterator pub fn data, I: IntoIterator>( mut self, iter: I, ) -> Self { let mut buffer = HashMap::::new(); for (x, y) in iter.into_iter() { if let Some(x) = self.br.index_of(&x.into()) { *buffer.entry(x).or_insert_with(Default::default) += y; } } self.iter = buffer.into_iter(); self } } impl<'a, BR, A> Histogram<'a, BR, A, Vertical> where BR: DiscreteRanged + Clone, A: AddAssign + Default + 'a, { pub fn vertical( parent: &ChartContext>, ) -> Self where ACoord: Ranged, { let dp = parent.as_coord_spec().x_spec(); Self::empty(dp) } } impl<'a, BR, A> Histogram<'a, BR, A, Horizontal> where BR: DiscreteRanged + Clone, A: AddAssign + Default + 'a, { pub fn horizontal( parent: &ChartContext>, ) -> Self where ACoord: Ranged, { let dp = parent.as_coord_spec().y_spec(); Self::empty(dp) } } impl<'a, BR, A> Iterator for Histogram<'a, BR, A, Vertical> where BR: DiscreteRanged, A: AddAssign + Default, { type Item = Rectangle<(BR::ValueType, A)>; fn next(&mut self) -> Option { while let Some((x, y)) = self.iter.next() { if let Some((x, Some(nx))) = self .br .from_index(x) .map(|v| (v, self.br.from_index(x + 1))) { let base = (self.baseline)(&x); let style = (self.style)(&x, &y); let mut rect = Rectangle::new([(x, y), (nx, base)], style); rect.set_margin(0, 0, self.margin, self.margin); return Some(rect); } } None } } impl<'a, BR, A> Iterator for Histogram<'a, BR, A, Horizontal> where BR: DiscreteRanged, A: AddAssign + Default, { type Item = Rectangle<(A, BR::ValueType)>; fn next(&mut self) -> Option { while let Some((y, x)) = self.iter.next() { if let Some((y, Some(ny))) = self .br .from_index(y) .map(|v| (v, self.br.from_index(y + 1))) { let base = (self.baseline)(&y); let style = (self.style)(&y, &x); let mut rect = Rectangle::new([(x, y), (base, ny)], style); rect.set_margin(0, 0, self.margin, self.margin); return Some(rect); } } None } }