plotters/coord/ranged1d/combinators/
ckps.rs

1// The customized coordinate combinators.
2// This file contains a set of coorindate combinators that allows you determine the
3// keypoint by your own code.
4use std::ops::Range;
5
6use crate::coord::ranged1d::{AsRangedCoord, DiscreteRanged, KeyPointHint, Ranged};
7
8/// The coordinate decorator that binds a key point vector.
9/// Normally, all the ranged coordinate implements its own keypoint algorithm
10/// to determine how to render the tick mark and mesh grid.
11/// This decorator allows customized tick mark specifiied by vector.
12/// See [BindKeyPoints::with_key_points](trait.BindKeyPoints.html#tymethod.with_key_points)
13/// for details.
14/// Note: For any coordinate spec wrapped by this decorator, the maxium number of labels configured by
15/// MeshStyle will be ignored and the key point function will always returns the entire vector
16pub struct WithKeyPoints<Inner: Ranged> {
17    inner: Inner,
18    bold_points: Vec<Inner::ValueType>,
19    light_points: Vec<Inner::ValueType>,
20}
21
22impl<I: Ranged> WithKeyPoints<I> {
23    /// Specify the light key points, which is used to render the light mesh line
24    pub fn with_light_points<T: IntoIterator<Item = I::ValueType>>(mut self, iter: T) -> Self {
25        self.light_points.clear();
26        self.light_points.extend(iter);
27        self
28    }
29
30    /// Get a reference to the bold points
31    pub fn bold_points(&self) -> &[I::ValueType] {
32        self.bold_points.as_ref()
33    }
34
35    /// Get a mut reference to the bold points
36    pub fn bold_points_mut(&mut self) -> &mut [I::ValueType] {
37        self.bold_points.as_mut()
38    }
39
40    /// Get a reference to light key points
41    pub fn light_points(&self) -> &[I::ValueType] {
42        self.light_points.as_ref()
43    }
44
45    /// Get a mut reference to the light key points
46    pub fn light_points_mut(&mut self) -> &mut [I::ValueType] {
47        self.light_points.as_mut()
48    }
49}
50
51impl<R: Ranged> Ranged for WithKeyPoints<R>
52where
53    R::ValueType: Clone,
54{
55    type ValueType = R::ValueType;
56    type FormatOption = R::FormatOption;
57
58    fn range(&self) -> Range<Self::ValueType> {
59        self.inner.range()
60    }
61
62    fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 {
63        self.inner.map(value, limit)
64    }
65
66    fn key_points<Hint: KeyPointHint>(&self, hint: Hint) -> Vec<Self::ValueType> {
67        if hint.weight().allow_light_points() {
68            self.light_points.clone()
69        } else {
70            self.bold_points.clone()
71        }
72    }
73
74    fn axis_pixel_range(&self, limit: (i32, i32)) -> Range<i32> {
75        self.inner.axis_pixel_range(limit)
76    }
77}
78
79impl<R: DiscreteRanged> DiscreteRanged for WithKeyPoints<R>
80where
81    R::ValueType: Clone,
82{
83    fn size(&self) -> usize {
84        self.inner.size()
85    }
86    fn index_of(&self, value: &Self::ValueType) -> Option<usize> {
87        self.inner.index_of(value)
88    }
89    fn from_index(&self, index: usize) -> Option<Self::ValueType> {
90        self.inner.from_index(index)
91    }
92}
93
94pub trait BindKeyPoints
95where
96    Self: AsRangedCoord,
97{
98    /// Bind a existing coordinate spec with a given key points vector. See [WithKeyPoints](struct.WithKeyPoints.html ) for more details.
99    /// Example:
100    /// ```
101    ///use plotters::prelude::*;
102    ///use plotters_bitmap::BitMapBackend;
103    ///let mut buffer = vec![0;1024*768*3];
104    /// let root = BitMapBackend::with_buffer(&mut buffer, (1024, 768)).into_drawing_area();
105    /// let mut chart = ChartBuilder::on(&root)
106    ///    .build_cartesian_2d(
107    ///        (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
108    ///        0..10
109    /// ).unwrap();
110    /// chart.configure_mesh().draw().unwrap();
111    ///```
112    fn with_key_points(self, points: Vec<Self::Value>) -> WithKeyPoints<Self::CoordDescType> {
113        WithKeyPoints {
114            inner: self.into(),
115            bold_points: points,
116            light_points: vec![],
117        }
118    }
119}
120
121impl<T: AsRangedCoord> BindKeyPoints for T {}
122
123/// The coordinate decorator that allows customized keypoint algorithms.
124/// Normally, all the coordinate spec implements its own key point algorith
125/// But this decorator allows you override the pre-defined key point algorithm.
126///
127/// To use this decorator, see [BindKeyPointMethod::with_key_point_func](trait.BindKeyPointMethod.html#tymethod.with_key_point_func)
128pub struct WithKeyPointMethod<R: Ranged> {
129    inner: R,
130    bold_func: Box<dyn Fn(usize) -> Vec<R::ValueType>>,
131    light_func: Box<dyn Fn(usize) -> Vec<R::ValueType>>,
132}
133
134pub trait BindKeyPointMethod
135where
136    Self: AsRangedCoord,
137{
138    /// Bind a existing coordinate spec with a given key points algorithm. See [WithKeyPointMethod](struct.WithKeyMethod.html ) for more details.
139    /// Example:
140    /// ```
141    ///use plotters::prelude::*;
142    ///use plotters_bitmap::BitMapBackend;
143    ///let mut buffer = vec![0;1024*768*3];
144    /// let root = BitMapBackend::with_buffer(&mut buffer, (1024, 768)).into_drawing_area();
145    /// let mut chart = ChartBuilder::on(&root)
146    ///    .build_cartesian_2d(
147    ///        (0..100).with_key_point_func(|n| (0..100 / n as i32).map(|x| x * 100 / n as i32).collect()),
148    ///        0..10
149    /// ).unwrap();
150    /// chart.configure_mesh().draw().unwrap();
151    ///```
152    fn with_key_point_func<F: Fn(usize) -> Vec<Self::Value> + 'static>(
153        self,
154        func: F,
155    ) -> WithKeyPointMethod<Self::CoordDescType> {
156        WithKeyPointMethod {
157            inner: self.into(),
158            bold_func: Box::new(func),
159            light_func: Box::new(|_| vec![]),
160        }
161    }
162}
163
164impl<T: AsRangedCoord> BindKeyPointMethod for T {}
165
166impl<R: Ranged> WithKeyPointMethod<R> {
167    /// Define the light key point algorithm, by default this returns an empty set
168    pub fn with_light_point_func<F: Fn(usize) -> Vec<R::ValueType> + 'static>(
169        mut self,
170        func: F,
171    ) -> Self {
172        self.light_func = Box::new(func);
173        self
174    }
175}
176
177impl<R: Ranged> Ranged for WithKeyPointMethod<R> {
178    type ValueType = R::ValueType;
179    type FormatOption = R::FormatOption;
180
181    fn range(&self) -> Range<Self::ValueType> {
182        self.inner.range()
183    }
184
185    fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 {
186        self.inner.map(value, limit)
187    }
188
189    fn key_points<Hint: KeyPointHint>(&self, hint: Hint) -> Vec<Self::ValueType> {
190        if hint.weight().allow_light_points() {
191            (self.light_func)(hint.max_num_points())
192        } else {
193            (self.bold_func)(hint.max_num_points())
194        }
195    }
196
197    fn axis_pixel_range(&self, limit: (i32, i32)) -> Range<i32> {
198        self.inner.axis_pixel_range(limit)
199    }
200}
201
202impl<R: DiscreteRanged> DiscreteRanged for WithKeyPointMethod<R> {
203    fn size(&self) -> usize {
204        self.inner.size()
205    }
206    fn index_of(&self, value: &Self::ValueType) -> Option<usize> {
207        self.inner.index_of(value)
208    }
209    fn from_index(&self, index: usize) -> Option<Self::ValueType> {
210        self.inner.from_index(index)
211    }
212}
213
214#[cfg(test)]
215mod test {
216    use super::*;
217    use crate::coord::ranged1d::{BoldPoints, LightPoints};
218    #[test]
219    fn test_with_key_points() {
220        let range = (0..100).with_key_points(vec![1, 2, 3]);
221        assert_eq!(range.map(&3, (0, 1000)), 30);
222        assert_eq!(range.range(), 0..100);
223        assert_eq!(range.key_points(BoldPoints(100)), vec![1, 2, 3]);
224        assert_eq!(range.key_points(LightPoints::new(100, 100)), vec![]);
225        let range = range.with_light_points(5..10);
226        assert_eq!(range.key_points(BoldPoints(10)), vec![1, 2, 3]);
227        assert_eq!(
228            range.key_points(LightPoints::new(10, 10)),
229            (5..10).collect::<Vec<_>>()
230        );
231
232        assert_eq!(range.size(), 101);
233        assert_eq!(range.index_of(&10), Some(10));
234        assert_eq!(range.from_index(10), Some(10));
235
236        assert_eq!(range.axis_pixel_range((0, 1000)), 0..1000);
237
238        let mut range = range;
239
240        assert_eq!(range.light_points().len(), 5);
241        assert_eq!(range.light_points_mut().len(), 5);
242        assert_eq!(range.bold_points().len(), 3);
243        assert_eq!(range.bold_points_mut().len(), 3);
244    }
245
246    #[test]
247    fn test_with_key_point_method() {
248        let range = (0..100).with_key_point_func(|_| vec![1, 2, 3]);
249        assert_eq!(range.map(&3, (0, 1000)), 30);
250        assert_eq!(range.range(), 0..100);
251        assert_eq!(range.key_points(BoldPoints(100)), vec![1, 2, 3]);
252        assert_eq!(range.key_points(LightPoints::new(100, 100)), vec![]);
253        let range = range.with_light_point_func(|_| (5..10).collect());
254        assert_eq!(range.key_points(BoldPoints(10)), vec![1, 2, 3]);
255        assert_eq!(
256            range.key_points(LightPoints::new(10, 10)),
257            (5..10).collect::<Vec<_>>()
258        );
259
260        assert_eq!(range.size(), 101);
261        assert_eq!(range.index_of(&10), Some(10));
262        assert_eq!(range.from_index(10), Some(10));
263
264        assert_eq!(range.axis_pixel_range((0, 1000)), 0..1000);
265    }
266}