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 maximum 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
94/// Bind a existing coordinate spec with a given key points vector. See [WithKeyPoints](struct.WithKeyPoints.html ) for more details.
95pub trait BindKeyPoints
96where
97    Self: AsRangedCoord,
98{
99    /// Bind a existing coordinate spec with a given key points vector. See [WithKeyPoints](struct.WithKeyPoints.html ) for more details.
100    /// Example:
101    /// ```
102    ///use plotters::prelude::*;
103    ///use plotters_bitmap::BitMapBackend;
104    ///let mut buffer = vec![0;1024*768*3];
105    /// let root = BitMapBackend::with_buffer(&mut buffer, (1024, 768)).into_drawing_area();
106    /// let mut chart = ChartBuilder::on(&root)
107    ///    .build_cartesian_2d(
108    ///        (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
109    ///        0..10
110    /// ).unwrap();
111    /// chart.configure_mesh().draw().unwrap();
112    ///```
113    fn with_key_points(self, points: Vec<Self::Value>) -> WithKeyPoints<Self::CoordDescType> {
114        WithKeyPoints {
115            inner: self.into(),
116            bold_points: points,
117            light_points: vec![],
118        }
119    }
120}
121
122impl<T: AsRangedCoord> BindKeyPoints for T {}
123
124/// The coordinate decorator that allows customized keypoint algorithms.
125/// Normally, all the coordinate spec implements its own key point algorithm
126/// But this decorator allows you override the pre-defined key point algorithm.
127///
128/// To use this decorator, see [BindKeyPointMethod::with_key_point_func](trait.BindKeyPointMethod.html#tymethod.with_key_point_func)
129pub struct WithKeyPointMethod<R: Ranged> {
130    inner: R,
131    bold_func: Box<dyn Fn(usize) -> Vec<R::ValueType>>,
132    light_func: Box<dyn Fn(usize) -> Vec<R::ValueType>>,
133}
134
135/// Bind an existing coordinate spec with a given key points algorithm. See [WithKeyPointMethod](struct.WithKeyMethod.html ) for more details.
136pub trait BindKeyPointMethod
137where
138    Self: AsRangedCoord,
139{
140    /// Bind a existing coordinate spec with a given key points algorithm. See [WithKeyPointMethod](struct.WithKeyMethod.html ) for more details.
141    /// Example:
142    /// ```
143    ///use plotters::prelude::*;
144    ///use plotters_bitmap::BitMapBackend;
145    ///let mut buffer = vec![0;1024*768*3];
146    /// let root = BitMapBackend::with_buffer(&mut buffer, (1024, 768)).into_drawing_area();
147    /// let mut chart = ChartBuilder::on(&root)
148    ///    .build_cartesian_2d(
149    ///        (0..100).with_key_point_func(|n| (0..100 / n as i32).map(|x| x * 100 / n as i32).collect()),
150    ///        0..10
151    /// ).unwrap();
152    /// chart.configure_mesh().draw().unwrap();
153    ///```
154    fn with_key_point_func<F: Fn(usize) -> Vec<Self::Value> + 'static>(
155        self,
156        func: F,
157    ) -> WithKeyPointMethod<Self::CoordDescType> {
158        WithKeyPointMethod {
159            inner: self.into(),
160            bold_func: Box::new(func),
161            light_func: Box::new(|_| Vec::new()),
162        }
163    }
164}
165
166impl<T: AsRangedCoord> BindKeyPointMethod for T {}
167
168impl<R: Ranged> WithKeyPointMethod<R> {
169    /// Define the light key point algorithm, by default this returns an empty set
170    pub fn with_light_point_func<F: Fn(usize) -> Vec<R::ValueType> + 'static>(
171        mut self,
172        func: F,
173    ) -> Self {
174        self.light_func = Box::new(func);
175        self
176    }
177}
178
179impl<R: Ranged> Ranged for WithKeyPointMethod<R> {
180    type ValueType = R::ValueType;
181    type FormatOption = R::FormatOption;
182
183    fn range(&self) -> Range<Self::ValueType> {
184        self.inner.range()
185    }
186
187    fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 {
188        self.inner.map(value, limit)
189    }
190
191    fn key_points<Hint: KeyPointHint>(&self, hint: Hint) -> Vec<Self::ValueType> {
192        if hint.weight().allow_light_points() {
193            (self.light_func)(hint.max_num_points())
194        } else {
195            (self.bold_func)(hint.max_num_points())
196        }
197    }
198
199    fn axis_pixel_range(&self, limit: (i32, i32)) -> Range<i32> {
200        self.inner.axis_pixel_range(limit)
201    }
202}
203
204impl<R: DiscreteRanged> DiscreteRanged for WithKeyPointMethod<R> {
205    fn size(&self) -> usize {
206        self.inner.size()
207    }
208    fn index_of(&self, value: &Self::ValueType) -> Option<usize> {
209        self.inner.index_of(value)
210    }
211    fn from_index(&self, index: usize) -> Option<Self::ValueType> {
212        self.inner.from_index(index)
213    }
214}
215
216#[cfg(test)]
217mod test {
218    use super::*;
219    use crate::coord::ranged1d::{BoldPoints, LightPoints};
220    #[test]
221    fn test_with_key_points() {
222        let range = (0..100).with_key_points(vec![1, 2, 3]);
223        assert_eq!(range.map(&3, (0, 1000)), 30);
224        assert_eq!(range.range(), 0..100);
225        assert_eq!(range.key_points(BoldPoints(100)), vec![1, 2, 3]);
226        assert_eq!(range.key_points(LightPoints::new(100, 100)), vec![]);
227        let range = range.with_light_points(5..10);
228        assert_eq!(range.key_points(BoldPoints(10)), vec![1, 2, 3]);
229        assert_eq!(
230            range.key_points(LightPoints::new(10, 10)),
231            (5..10).collect::<Vec<_>>()
232        );
233
234        assert_eq!(range.size(), 101);
235        assert_eq!(range.index_of(&10), Some(10));
236        assert_eq!(range.from_index(10), Some(10));
237
238        assert_eq!(range.axis_pixel_range((0, 1000)), 0..1000);
239
240        let mut range = range;
241
242        assert_eq!(range.light_points().len(), 5);
243        assert_eq!(range.light_points_mut().len(), 5);
244        assert_eq!(range.bold_points().len(), 3);
245        assert_eq!(range.bold_points_mut().len(), 3);
246    }
247
248    #[test]
249    fn test_with_key_point_method() {
250        let range = (0..100).with_key_point_func(|_| vec![1, 2, 3]);
251        assert_eq!(range.map(&3, (0, 1000)), 30);
252        assert_eq!(range.range(), 0..100);
253        assert_eq!(range.key_points(BoldPoints(100)), vec![1, 2, 3]);
254        assert_eq!(range.key_points(LightPoints::new(100, 100)), vec![]);
255        let range = range.with_light_point_func(|_| (5..10).collect());
256        assert_eq!(range.key_points(BoldPoints(10)), vec![1, 2, 3]);
257        assert_eq!(
258            range.key_points(LightPoints::new(10, 10)),
259            (5..10).collect::<Vec<_>>()
260        );
261
262        assert_eq!(range.size(), 101);
263        assert_eq!(range.index_of(&10), Some(10));
264        assert_eq!(range.from_index(10), Some(10));
265
266        assert_eq!(range.axis_pixel_range((0, 1000)), 0..1000);
267    }
268}