diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 02:49:50 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 02:49:50 +0000 |
commit | 9835e2ae736235810b4ea1c162ca5e65c547e770 (patch) | |
tree | 3fcebf40ed70e581d776a8a4c65923e8ec20e026 /vendor/plotters/src/element/boxplot.rs | |
parent | Releasing progress-linux version 1.70.0+dfsg2-1~progress7.99u1. (diff) | |
download | rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.tar.xz rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.zip |
Merging upstream version 1.71.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/plotters/src/element/boxplot.rs')
-rw-r--r-- | vendor/plotters/src/element/boxplot.rs | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/vendor/plotters/src/element/boxplot.rs b/vendor/plotters/src/element/boxplot.rs new file mode 100644 index 000000000..2de2bd062 --- /dev/null +++ b/vendor/plotters/src/element/boxplot.rs @@ -0,0 +1,288 @@ +use std::marker::PhantomData; + +use crate::data::Quartiles; +use crate::element::{Drawable, PointCollection}; +use crate::style::{Color, ShapeStyle, BLACK}; +use plotters_backend::{BackendCoord, DrawingBackend, DrawingErrorKind}; + +/// The boxplot orientation trait +pub trait BoxplotOrient<K, V> { + type XType; + type YType; + + fn make_coord(key: K, val: V) -> (Self::XType, Self::YType); + fn with_offset(coord: BackendCoord, offset: f64) -> BackendCoord; +} + +/// The vertical boxplot phantom +pub struct BoxplotOrientV<K, V>(PhantomData<(K, V)>); + +/// The horizontal boxplot phantom +pub struct BoxplotOrientH<K, V>(PhantomData<(K, V)>); + +impl<K, V> BoxplotOrient<K, V> for BoxplotOrientV<K, V> { + type XType = K; + type YType = V; + + fn make_coord(key: K, val: V) -> (K, V) { + (key, val) + } + + fn with_offset(coord: BackendCoord, offset: f64) -> BackendCoord { + (coord.0 + offset as i32, coord.1) + } +} + +impl<K, V> BoxplotOrient<K, V> for BoxplotOrientH<K, V> { + type XType = V; + type YType = K; + + fn make_coord(key: K, val: V) -> (V, K) { + (val, key) + } + + fn with_offset(coord: BackendCoord, offset: f64) -> BackendCoord { + (coord.0, coord.1 + offset as i32) + } +} + +const DEFAULT_WIDTH: u32 = 10; + +/// The boxplot element +pub struct Boxplot<K, O: BoxplotOrient<K, f32>> { + style: ShapeStyle, + width: u32, + whisker_width: f64, + offset: f64, + key: K, + values: [f32; 5], + _p: PhantomData<O>, +} + +impl<K: Clone> Boxplot<K, BoxplotOrientV<K, f32>> { + /// Create a new vertical boxplot element. + /// + /// - `key`: The key (the X axis value) + /// - `quartiles`: The quartiles values for the Y axis + /// - **returns** The newly created boxplot element + /// + /// ```rust + /// use plotters::prelude::*; + /// + /// let quartiles = Quartiles::new(&[7, 15, 36, 39, 40, 41]); + /// let plot = Boxplot::new_vertical("group", &quartiles); + /// ``` + pub fn new_vertical(key: K, quartiles: &Quartiles) -> Self { + Self { + style: Into::<ShapeStyle>::into(&BLACK), + width: DEFAULT_WIDTH, + whisker_width: 1.0, + offset: 0.0, + key, + values: quartiles.values(), + _p: PhantomData, + } + } +} + +impl<K: Clone> Boxplot<K, BoxplotOrientH<K, f32>> { + /// Create a new horizontal boxplot element. + /// + /// - `key`: The key (the Y axis value) + /// - `quartiles`: The quartiles values for the X axis + /// - **returns** The newly created boxplot element + /// + /// ```rust + /// use plotters::prelude::*; + /// + /// let quartiles = Quartiles::new(&[7, 15, 36, 39, 40, 41]); + /// let plot = Boxplot::new_horizontal("group", &quartiles); + /// ``` + pub fn new_horizontal(key: K, quartiles: &Quartiles) -> Self { + Self { + style: Into::<ShapeStyle>::into(&BLACK), + width: DEFAULT_WIDTH, + whisker_width: 1.0, + offset: 0.0, + key, + values: quartiles.values(), + _p: PhantomData, + } + } +} + +impl<K, O: BoxplotOrient<K, f32>> Boxplot<K, O> { + /// Set the style of the boxplot. + /// + /// - `S`: The required style + /// - **returns** The up-to-dated boxplot element + /// + /// ```rust + /// use plotters::prelude::*; + /// + /// let quartiles = Quartiles::new(&[7, 15, 36, 39, 40, 41]); + /// let plot = Boxplot::new_horizontal("group", &quartiles).style(&BLUE); + /// ``` + pub fn style<S: Into<ShapeStyle>>(mut self, style: S) -> Self { + self.style = style.into(); + self + } + + /// Set the bar width. + /// + /// - `width`: The required width + /// - **returns** The up-to-dated boxplot element + /// + /// ```rust + /// use plotters::prelude::*; + /// + /// let quartiles = Quartiles::new(&[7, 15, 36, 39, 40, 41]); + /// let plot = Boxplot::new_horizontal("group", &quartiles).width(10); + /// ``` + pub fn width(mut self, width: u32) -> Self { + self.width = width; + self + } + + /// Set the width of the whiskers as a fraction of the bar width. + /// + /// - `whisker_width`: The required fraction + /// - **returns** The up-to-dated boxplot element + /// + /// ```rust + /// use plotters::prelude::*; + /// + /// let quartiles = Quartiles::new(&[7, 15, 36, 39, 40, 41]); + /// let plot = Boxplot::new_horizontal("group", &quartiles).whisker_width(0.5); + /// ``` + pub fn whisker_width(mut self, whisker_width: f64) -> Self { + self.whisker_width = whisker_width; + self + } + + /// Set the element offset on the key axis. + /// + /// - `offset`: The required offset (on the X axis for vertical, on the Y axis for horizontal) + /// - **returns** The up-to-dated boxplot element + /// + /// ```rust + /// use plotters::prelude::*; + /// + /// let quartiles = Quartiles::new(&[7, 15, 36, 39, 40, 41]); + /// let plot = Boxplot::new_horizontal("group", &quartiles).offset(-5); + /// ``` + pub fn offset<T: Into<f64> + Copy>(mut self, offset: T) -> Self { + self.offset = offset.into(); + self + } +} + +impl<'a, K: Clone, O: BoxplotOrient<K, f32>> PointCollection<'a, (O::XType, O::YType)> + for &'a Boxplot<K, O> +{ + type Point = (O::XType, O::YType); + type IntoIter = Vec<Self::Point>; + fn point_iter(self) -> Self::IntoIter { + self.values + .iter() + .map(|v| O::make_coord(self.key.clone(), *v)) + .collect() + } +} + +impl<K, DB: DrawingBackend, O: BoxplotOrient<K, f32>> Drawable<DB> for Boxplot<K, O> { + fn draw<I: Iterator<Item = BackendCoord>>( + &self, + points: I, + backend: &mut DB, + _: (u32, u32), + ) -> Result<(), DrawingErrorKind<DB::ErrorType>> { + let points: Vec<_> = points.take(5).collect(); + if points.len() == 5 { + let width = f64::from(self.width); + let moved = |coord| O::with_offset(coord, self.offset); + let start_bar = |coord| O::with_offset(moved(coord), -width / 2.0); + let end_bar = |coord| O::with_offset(moved(coord), width / 2.0); + let start_whisker = + |coord| O::with_offset(moved(coord), -width * self.whisker_width / 2.0); + let end_whisker = + |coord| O::with_offset(moved(coord), width * self.whisker_width / 2.0); + + // |---[ | ]----| + // ^________________ + backend.draw_line( + start_whisker(points[0]), + end_whisker(points[0]), + &self.style, + )?; + + // |---[ | ]----| + // _^^^_____________ + + backend.draw_line( + moved(points[0]), + moved(points[1]), + &self.style.color.to_backend_color(), + )?; + + // |---[ | ]----| + // ____^______^_____ + let corner1 = start_bar(points[3]); + let corner2 = end_bar(points[1]); + let upper_left = (corner1.0.min(corner2.0), corner1.1.min(corner2.1)); + let bottom_right = (corner1.0.max(corner2.0), corner1.1.max(corner2.1)); + backend.draw_rect(upper_left, bottom_right, &self.style, false)?; + + // |---[ | ]----| + // ________^________ + backend.draw_line(start_bar(points[2]), end_bar(points[2]), &self.style)?; + + // |---[ | ]----| + // ____________^^^^_ + backend.draw_line(moved(points[3]), moved(points[4]), &self.style)?; + + // |---[ | ]----| + // ________________^ + backend.draw_line( + start_whisker(points[4]), + end_whisker(points[4]), + &self.style, + )?; + } + Ok(()) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::prelude::*; + + #[test] + fn test_draw_v() { + let root = MockedBackend::new(1024, 768).into_drawing_area(); + let chart = ChartBuilder::on(&root) + .build_cartesian_2d(0..2, 0f32..100f32) + .unwrap(); + + let values = Quartiles::new(&[6]); + assert!(chart + .plotting_area() + .draw(&Boxplot::new_vertical(1, &values)) + .is_ok()); + } + + #[test] + fn test_draw_h() { + let root = MockedBackend::new(1024, 768).into_drawing_area(); + let chart = ChartBuilder::on(&root) + .build_cartesian_2d(0f32..100f32, 0..2) + .unwrap(); + + let values = Quartiles::new(&[6]); + assert!(chart + .plotting_area() + .draw(&Boxplot::new_horizontal(1, &values)) + .is_ok()); + } +} |