diff options
Diffstat (limited to 'src/series/histogram.rs')
-rw-r--r-- | src/series/histogram.rs | 129 |
1 files changed, 49 insertions, 80 deletions
diff --git a/src/series/histogram.rs b/src/series/histogram.rs index 75c2fb2..477e4ad 100644 --- a/src/series/histogram.rs +++ b/src/series/histogram.rs @@ -1,13 +1,13 @@ use std::collections::{hash_map::IntoIter as HashMapIter, HashMap}; -use std::hash::Hash; use std::marker::PhantomData; use std::ops::AddAssign; use crate::chart::ChartContext; -use crate::coord::{DiscreteRanged, Ranged, RangedCoord}; -use crate::drawing::DrawingBackend; +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; @@ -20,32 +20,30 @@ impl HistogramType for Horizontal {} pub struct Histogram<'a, BR, A, Tag = Vertical> where BR: DiscreteRanged, - BR::ValueType: Eq + Hash, A: AddAssign<A> + Default, Tag: HistogramType, { style: Box<dyn Fn(&BR::ValueType, &A) -> ShapeStyle + 'a>, margin: u32, - iter: HashMapIter<BR::ValueType, A>, - baseline: Box<dyn Fn(BR::ValueType) -> A + 'a>, - br_param: BR::RangeParameter, - _p: PhantomData<(BR, Tag)>, + iter: HashMapIter<usize, A>, + baseline: Box<dyn Fn(&BR::ValueType) -> A + 'a>, + br: BR, + _p: PhantomData<Tag>, } impl<'a, BR, A, Tag> Histogram<'a, BR, A, Tag> where - BR: DiscreteRanged, - BR::ValueType: Eq + Hash, + BR: DiscreteRanged + Clone, A: AddAssign<A> + Default + 'a, Tag: HistogramType, { - fn empty(br_param: BR::RangeParameter) -> Self { + fn empty(br: &BR) -> Self { Self { style: Box::new(|_, _| GREEN.filled()), margin: 5, iter: HashMap::new().into_iter(), baseline: Box::new(|_| A::default()), - br_param, + br: br.clone(), _p: PhantomData, } } @@ -75,7 +73,7 @@ where } /// Set a function that defines variant baseline - pub fn baseline_func(mut self, func: impl Fn(BR::ValueType) -> A + 'a) -> Self { + pub fn baseline_func(mut self, func: impl Fn(&BR::ValueType) -> A + 'a) -> Self { self.baseline = Box::new(func); self } @@ -87,68 +85,33 @@ where } /// Set the data iterator - pub fn data<I: IntoIterator<Item = (BR::ValueType, A)>>(mut self, iter: I) -> Self { - let mut buffer = HashMap::<BR::ValueType, A>::new(); + pub fn data<TB: Into<BR::ValueType>, I: IntoIterator<Item = (TB, A)>>( + mut self, + iter: I, + ) -> Self { + let mut buffer = HashMap::<usize, A>::new(); for (x, y) in iter.into_iter() { - *buffer.entry(x).or_insert_with(Default::default) += y; + 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 } } -pub trait UseDefaultParameter: Default { - fn new() -> Self { - Default::default() - } -} - -impl UseDefaultParameter for () {} - impl<'a, BR, A> Histogram<'a, BR, A, Vertical> where - BR: DiscreteRanged, - BR::ValueType: Eq + Hash, + BR: DiscreteRanged + Clone, A: AddAssign<A> + Default + 'a, { - /// Create a new histogram series. - /// - /// - `iter`: The data iterator - /// - `margin`: The margin between bars - /// - `style`: The style of bars - /// - /// Returns the newly created histogram series - #[allow(clippy::redundant_closure)] - pub fn new<S: Into<ShapeStyle>, I: IntoIterator<Item = (BR::ValueType, A)>>( - iter: I, - margin: u32, - style: S, - ) -> Self - where - BR::RangeParameter: UseDefaultParameter, - { - let mut buffer = HashMap::<BR::ValueType, A>::new(); - for (x, y) in iter.into_iter() { - *buffer.entry(x).or_insert_with(Default::default) += y; - } - let style = style.into(); - Self { - style: Box::new(move |_, _| style.clone()), - margin, - iter: buffer.into_iter(), - baseline: Box::new(|_| A::default()), - br_param: BR::RangeParameter::new(), - _p: PhantomData, - } - } - pub fn vertical<ACoord, DB: DrawingBackend + 'a>( - parent: &ChartContext<DB, RangedCoord<BR, ACoord>>, + parent: &ChartContext<DB, Cartesian2d<BR, ACoord>>, ) -> Self where ACoord: Ranged<ValueType = A>, { - let dp = parent.as_coord_spec().x_spec().get_range_parameter(); + let dp = parent.as_coord_spec().x_spec(); Self::empty(dp) } @@ -156,17 +119,16 @@ where impl<'a, BR, A> Histogram<'a, BR, A, Horizontal> where - BR: DiscreteRanged, - BR::ValueType: Eq + Hash, + BR: DiscreteRanged + Clone, A: AddAssign<A> + Default + 'a, { pub fn horizontal<ACoord, DB: DrawingBackend>( - parent: &ChartContext<DB, RangedCoord<ACoord, BR>>, + parent: &ChartContext<DB, Cartesian2d<ACoord, BR>>, ) -> Self where ACoord: Ranged<ValueType = A>, { - let dp = parent.as_coord_spec().y_spec().get_range_parameter(); + let dp = parent.as_coord_spec().y_spec(); Self::empty(dp) } } @@ -174,18 +136,22 @@ where impl<'a, BR, A> Iterator for Histogram<'a, BR, A, Vertical> where BR: DiscreteRanged, - BR::ValueType: Eq + Hash, A: AddAssign<A> + Default, { type Item = Rectangle<(BR::ValueType, A)>; fn next(&mut self) -> Option<Self::Item> { - if let Some((x, y)) = self.iter.next() { - let nx = BR::next_value(&x, &self.br_param); - let base = (self.baseline)(BR::previous_value(&nx, &self.br_param)); - 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); + 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 } @@ -194,19 +160,22 @@ where impl<'a, BR, A> Iterator for Histogram<'a, BR, A, Horizontal> where BR: DiscreteRanged, - BR::ValueType: Eq + Hash, A: AddAssign<A> + Default, { type Item = Rectangle<(A, BR::ValueType)>; fn next(&mut self) -> Option<Self::Item> { - if let Some((y, x)) = self.iter.next() { - let ny = BR::next_value(&y, &self.br_param); - // With this trick we can avoid the clone trait bound - let base = (self.baseline)(BR::previous_value(&ny, &self.br_param)); - let style = (self.style)(&y, &x); - let mut rect = Rectangle::new([(x, y), (base, ny)], style); - rect.set_margin(self.margin, self.margin, 0, 0); - return Some(rect); + 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 } |