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. |
4 | use std::ops::Range; |
5 | |
6 | use 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 |
16 | pub struct WithKeyPoints<Inner: Ranged> { |
17 | inner: Inner, |
18 | bold_points: Vec<Inner::ValueType>, |
19 | light_points: Vec<Inner::ValueType>, |
20 | } |
21 | |
22 | impl<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 | |
51 | impl<R: Ranged> Ranged for WithKeyPoints<R> |
52 | where |
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 | |
79 | impl<R: DiscreteRanged> DiscreteRanged for WithKeyPoints<R> |
80 | where |
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. |
95 | pub trait BindKeyPoints |
96 | where |
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 | |
122 | impl<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 algorith |
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) |
129 | pub 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. |
136 | pub trait BindKeyPointMethod |
137 | where |
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 | |
166 | impl<T: AsRangedCoord> BindKeyPointMethod for T {} |
167 | |
168 | impl<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 | |
179 | impl<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 | |
204 | impl<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)] |
217 | mod 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 | } |
269 | |