| // The customized coordinate combinators. |
| // This file contains a set of coorindate combinators that allows you determine the |
| // keypoint by your own code. |
| use std::ops::Range; |
| |
| use crate::coord::ranged1d::{AsRangedCoord, DiscreteRanged, KeyPointHint, Ranged}; |
| |
| /// The coordinate decorator that binds a key point vector. |
| /// Normally, all the ranged coordinate implements its own keypoint algorithm |
| /// to determine how to render the tick mark and mesh grid. |
| /// This decorator allows customized tick mark specifiied by vector. |
| /// See [BindKeyPoints::with_key_points](trait.BindKeyPoints.html#tymethod.with_key_points) |
| /// for details. |
| /// Note: For any coordinate spec wrapped by this decorator, the maxium number of labels configured by |
| /// MeshStyle will be ignored and the key point function will always returns the entire vector |
| pub struct WithKeyPoints<Inner: Ranged> { |
| inner: Inner, |
| bold_points: Vec<Inner::ValueType>, |
| light_points: Vec<Inner::ValueType>, |
| } |
| |
| impl<I: Ranged> WithKeyPoints<I> { |
| /// Specify the light key points, which is used to render the light mesh line |
| pub fn with_light_points<T: IntoIterator<Item = I::ValueType>>(mut self, iter: T) -> Self { |
| self.light_points.clear(); |
| self.light_points.extend(iter); |
| self |
| } |
| |
| /// Get a reference to the bold points |
| pub fn bold_points(&self) -> &[I::ValueType] { |
| self.bold_points.as_ref() |
| } |
| |
| /// Get a mut reference to the bold points |
| pub fn bold_points_mut(&mut self) -> &mut [I::ValueType] { |
| self.bold_points.as_mut() |
| } |
| |
| /// Get a reference to light key points |
| pub fn light_points(&self) -> &[I::ValueType] { |
| self.light_points.as_ref() |
| } |
| |
| /// Get a mut reference to the light key points |
| pub fn light_points_mut(&mut self) -> &mut [I::ValueType] { |
| self.light_points.as_mut() |
| } |
| } |
| |
| impl<R: Ranged> Ranged for WithKeyPoints<R> |
| where |
| R::ValueType: Clone, |
| { |
| type ValueType = R::ValueType; |
| type FormatOption = R::FormatOption; |
| |
| fn range(&self) -> Range<Self::ValueType> { |
| self.inner.range() |
| } |
| |
| fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 { |
| self.inner.map(value, limit) |
| } |
| |
| fn key_points<Hint: KeyPointHint>(&self, hint: Hint) -> Vec<Self::ValueType> { |
| if hint.weight().allow_light_points() { |
| self.light_points.clone() |
| } else { |
| self.bold_points.clone() |
| } |
| } |
| |
| fn axis_pixel_range(&self, limit: (i32, i32)) -> Range<i32> { |
| self.inner.axis_pixel_range(limit) |
| } |
| } |
| |
| impl<R: DiscreteRanged> DiscreteRanged for WithKeyPoints<R> |
| where |
| R::ValueType: Clone, |
| { |
| fn size(&self) -> usize { |
| self.inner.size() |
| } |
| fn index_of(&self, value: &Self::ValueType) -> Option<usize> { |
| self.inner.index_of(value) |
| } |
| fn from_index(&self, index: usize) -> Option<Self::ValueType> { |
| self.inner.from_index(index) |
| } |
| } |
| |
| pub trait BindKeyPoints |
| where |
| Self: AsRangedCoord, |
| { |
| /// Bind a existing coordinate spec with a given key points vector. See [WithKeyPoints](struct.WithKeyPoints.html ) for more details. |
| /// Example: |
| /// ``` |
| ///use plotters::prelude::*; |
| ///use plotters_bitmap::BitMapBackend; |
| ///let mut buffer = vec![0;1024*768*3]; |
| /// let root = BitMapBackend::with_buffer(&mut buffer, (1024, 768)).into_drawing_area(); |
| /// let mut chart = ChartBuilder::on(&root) |
| /// .build_ranged( |
| /// (0..100).with_key_points(vec![1,20,50,90]), // <= This line will make the plot shows 4 tick marks at 1, 20, 50, 90 |
| /// 0..10 |
| /// ).unwrap(); |
| /// chart.configure_mesh().draw().unwrap(); |
| ///``` |
| fn with_key_points(self, points: Vec<Self::Value>) -> WithKeyPoints<Self::CoordDescType> { |
| WithKeyPoints { |
| inner: self.into(), |
| bold_points: points, |
| light_points: vec![], |
| } |
| } |
| } |
| |
| impl<T: AsRangedCoord> BindKeyPoints for T {} |
| |
| /// The coordinate decorator that allows customized keypoint algorithms. |
| /// Normally, all the coordinate spec implements its own key point algorith |
| /// But this decorator allows you override the pre-defined key point algorithm. |
| /// |
| /// To use this decorator, see [BindKeyPointMethod::with_key_point_func](trait.BindKeyPointMethod.html#tymethod.with_key_point_func) |
| pub struct WithKeyPointMethod<R: Ranged> { |
| inner: R, |
| bold_func: Box<dyn Fn(usize) -> Vec<R::ValueType>>, |
| light_func: Box<dyn Fn(usize) -> Vec<R::ValueType>>, |
| } |
| |
| pub trait BindKeyPointMethod |
| where |
| Self: AsRangedCoord, |
| { |
| /// Bind a existing coordinate spec with a given key points algorithm. See [WithKeyPointMethod](struct.WithKeyMethod.html ) for more details. |
| /// Example: |
| /// ``` |
| ///use plotters::prelude::*; |
| ///use plotters_bitmap::BitMapBackend; |
| ///let mut buffer = vec![0;1024*768*3]; |
| /// let root = BitMapBackend::with_buffer(&mut buffer, (1024, 768)).into_drawing_area(); |
| /// let mut chart = ChartBuilder::on(&root) |
| /// .build_ranged( |
| /// (0..100).with_key_point_func(|n| (0..100 / n as i32).map(|x| x * 100 / n as i32).collect()), |
| /// 0..10 |
| /// ).unwrap(); |
| /// chart.configure_mesh().draw().unwrap(); |
| ///``` |
| fn with_key_point_func<F: Fn(usize) -> Vec<Self::Value> + 'static>( |
| self, |
| func: F, |
| ) -> WithKeyPointMethod<Self::CoordDescType> { |
| WithKeyPointMethod { |
| inner: self.into(), |
| bold_func: Box::new(func), |
| light_func: Box::new(|_| vec![]), |
| } |
| } |
| } |
| |
| impl<T: AsRangedCoord> BindKeyPointMethod for T {} |
| |
| impl<R: Ranged> WithKeyPointMethod<R> { |
| /// Define the light key point algorithm, by default this returns an empty set |
| pub fn with_light_point_func<F: Fn(usize) -> Vec<R::ValueType> + 'static>( |
| mut self, |
| func: F, |
| ) -> Self { |
| self.light_func = Box::new(func); |
| self |
| } |
| } |
| |
| impl<R: Ranged> Ranged for WithKeyPointMethod<R> { |
| type ValueType = R::ValueType; |
| type FormatOption = R::FormatOption; |
| |
| fn range(&self) -> Range<Self::ValueType> { |
| self.inner.range() |
| } |
| |
| fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 { |
| self.inner.map(value, limit) |
| } |
| |
| fn key_points<Hint: KeyPointHint>(&self, hint: Hint) -> Vec<Self::ValueType> { |
| if hint.weight().allow_light_points() { |
| (self.light_func)(hint.max_num_points()) |
| } else { |
| (self.bold_func)(hint.max_num_points()) |
| } |
| } |
| |
| fn axis_pixel_range(&self, limit: (i32, i32)) -> Range<i32> { |
| self.inner.axis_pixel_range(limit) |
| } |
| } |
| |
| impl<R: DiscreteRanged> DiscreteRanged for WithKeyPointMethod<R> { |
| fn size(&self) -> usize { |
| self.inner.size() |
| } |
| fn index_of(&self, value: &Self::ValueType) -> Option<usize> { |
| self.inner.index_of(value) |
| } |
| fn from_index(&self, index: usize) -> Option<Self::ValueType> { |
| self.inner.from_index(index) |
| } |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use super::*; |
| use crate::coord::ranged1d::{BoldPoints, LightPoints}; |
| #[test] |
| fn test_with_key_points() { |
| let range = (0..100).with_key_points(vec![1, 2, 3]); |
| assert_eq!(range.map(&3, (0, 1000)), 30); |
| assert_eq!(range.range(), 0..100); |
| assert_eq!(range.key_points(BoldPoints(100)), vec![1, 2, 3]); |
| assert_eq!(range.key_points(LightPoints::new(100, 100)), vec![]); |
| let range = range.with_light_points(5..10); |
| assert_eq!(range.key_points(BoldPoints(10)), vec![1, 2, 3]); |
| assert_eq!( |
| range.key_points(LightPoints::new(10, 10)), |
| (5..10).collect::<Vec<_>>() |
| ); |
| |
| assert_eq!(range.size(), 101); |
| assert_eq!(range.index_of(&10), Some(10)); |
| assert_eq!(range.from_index(10), Some(10)); |
| |
| assert_eq!(range.axis_pixel_range((0, 1000)), 0..1000); |
| |
| let mut range = range; |
| |
| assert_eq!(range.light_points().len(), 5); |
| assert_eq!(range.light_points_mut().len(), 5); |
| assert_eq!(range.bold_points().len(), 3); |
| assert_eq!(range.bold_points_mut().len(), 3); |
| } |
| |
| #[test] |
| fn test_with_key_point_method() { |
| let range = (0..100).with_key_point_func(|_| vec![1, 2, 3]); |
| assert_eq!(range.map(&3, (0, 1000)), 30); |
| assert_eq!(range.range(), 0..100); |
| assert_eq!(range.key_points(BoldPoints(100)), vec![1, 2, 3]); |
| assert_eq!(range.key_points(LightPoints::new(100, 100)), vec![]); |
| let range = range.with_light_point_func(|_| (5..10).collect()); |
| assert_eq!(range.key_points(BoldPoints(10)), vec![1, 2, 3]); |
| assert_eq!( |
| range.key_points(LightPoints::new(10, 10)), |
| (5..10).collect::<Vec<_>>() |
| ); |
| |
| assert_eq!(range.size(), 101); |
| assert_eq!(range.index_of(&10), Some(10)); |
| assert_eq!(range.from_index(10), Some(10)); |
| |
| assert_eq!(range.axis_pixel_range((0, 1000)), 0..1000); |
| } |
| } |