aboutsummaryrefslogtreecommitdiff
path: root/src/coord/ranged1d/combinators/ckps.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/coord/ranged1d/combinators/ckps.rs')
-rw-r--r--src/coord/ranged1d/combinators/ckps.rs266
1 files changed, 266 insertions, 0 deletions
diff --git a/src/coord/ranged1d/combinators/ckps.rs b/src/coord/ranged1d/combinators/ckps.rs
new file mode 100644
index 0000000..bca1d87
--- /dev/null
+++ b/src/coord/ranged1d/combinators/ckps.rs
@@ -0,0 +1,266 @@
+// 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);
+ }
+}