plotters/coord/ranged1d/types/
numeric.rs1use std::convert::TryFrom;
2use std::ops::Range;
3
4use crate::coord::ranged1d::{
5 AsRangedCoord, DefaultFormatting, DiscreteRanged, KeyPointHint, NoDefaultFormatting, Ranged,
6 ReversibleRanged, ValueFormatter,
7};
8
9macro_rules! impl_discrete_trait {
10 ($name:ident) => {
11 impl DiscreteRanged for $name {
12 fn size(&self) -> usize {
13 if &self.1 < &self.0 {
14 return 0;
15 }
16 let values = self.1 - self.0;
17 (values + 1) as usize
18 }
19
20 fn index_of(&self, value: &Self::ValueType) -> Option<usize> {
21 if value < &self.0 {
22 return None;
23 }
24 let ret = value - self.0;
25 Some(ret as usize)
26 }
27
28 fn from_index(&self, index: usize) -> Option<Self::ValueType> {
29 if let Ok(index) = Self::ValueType::try_from(index) {
30 return Some(self.0 + index);
31 }
32 None
33 }
34 }
35 };
36}
37
38macro_rules! impl_ranged_type_trait {
39 ($value:ty, $coord:ident) => {
40 impl AsRangedCoord for Range<$value> {
41 type CoordDescType = $coord;
42 type Value = $value;
43 }
44 };
45}
46macro_rules! impl_reverse_mapping_trait {
47 ($type:ty, $name: ident) => {
48 impl ReversibleRanged for $name {
49 fn unmap(&self, p: i32, (min, max): (i32, i32)) -> Option<$type> {
50 if p < min.min(max) || p > max.max(min) || min == max {
51 return None;
52 }
53
54 let logical_offset = f64::from(p - min) / f64::from(max - min);
55
56 return Some(((self.1 - self.0) as f64 * logical_offset + self.0 as f64) as $type);
57 }
58 }
59 };
60}
61macro_rules! make_numeric_coord {
62 ($type:ty, $name:ident, $key_points:ident, $doc: expr, $fmt: ident) => {
63 #[doc = $doc]
64 #[derive(Clone)]
65 pub struct $name($type, $type);
66 impl From<Range<$type>> for $name {
67 fn from(range: Range<$type>) -> Self {
68 return $name(range.start, range.end);
69 }
70 }
71 impl Ranged for $name {
72 type FormatOption = $fmt;
73 type ValueType = $type;
74 #[allow(clippy::float_cmp)]
75 fn map(&self, v: &$type, limit: (i32, i32)) -> i32 {
76 if self.1 == self.0 {
79 return (limit.1 - limit.0) / 2;
80 }
81
82 let logic_length = (*v as f64 - self.0 as f64) / (self.1 as f64 - self.0 as f64);
83
84 let actual_length = limit.1 - limit.0;
85
86 if actual_length == 0 {
87 return limit.1;
88 }
89
90 if logic_length.is_infinite() {
91 if logic_length.is_sign_positive() {
92 return limit.1;
93 } else {
94 return limit.0;
95 }
96 }
97
98 if actual_length > 0 {
99 return limit.0 + (actual_length as f64 * logic_length + 1e-3).floor() as i32;
100 } else {
101 return limit.0 + (actual_length as f64 * logic_length - 1e-3).ceil() as i32;
102 }
103 }
104 fn key_points<Hint: KeyPointHint>(&self, hint: Hint) -> Vec<$type> {
105 $key_points((self.0, self.1), hint.max_num_points())
106 }
107 fn range(&self) -> Range<$type> {
108 return self.0..self.1;
109 }
110 }
111 };
112 ($type:ty, $name:ident, $key_points:ident, $doc: expr) => {
113 make_numeric_coord!($type, $name, $key_points, $doc, DefaultFormatting);
114 };
115}
116
117macro_rules! gen_key_points_comp {
118 (float, $name:ident, $type:ty) => {
119 fn $name(range: ($type, $type), max_points: usize) -> Vec<$type> {
120 if max_points == 0 {
121 return vec![];
122 }
123
124 let range = (range.0.min(range.1) as f64, range.1.max(range.0) as f64);
125
126 assert!(!(range.0.is_nan() || range.1.is_nan()));
127
128 if (range.0 - range.1).abs() < f64::EPSILON {
129 return vec![range.0 as $type];
130 }
131
132 let mut scale = (10f64).powf((range.1 - range.0).log(10.0).floor());
133 let mut value_granularity = scale / 10.0;
138 fn rem_euclid(a: f64, b: f64) -> f64 {
139 let ret = if b > 0.0 {
140 a - (a / b).floor() * b
141 } else {
142 a - (a / b).ceil() * b
143 };
144 if (ret - b).abs() < f64::EPSILON {
145 0.0
146 } else {
147 ret
148 }
149 }
150
151 if 1 + ((range.1 - range.0) / scale).floor() as usize > max_points {
154 scale *= 10.0;
155 value_granularity *= 10.0;
156 }
157
158 'outer: loop {
159 let old_scale = scale;
160 for nxt in [2.0, 5.0, 10.0].iter() {
161 let mut new_left = range.0 - rem_euclid(range.0, old_scale / nxt);
162 if new_left < range.0 {
163 new_left += old_scale / nxt;
164 }
165 let new_right = range.1 - rem_euclid(range.1, old_scale / nxt);
166
167 let npoints = 1.0 + ((new_right - new_left) / old_scale * nxt);
168
169 if npoints.round() as usize > max_points {
170 break 'outer;
171 }
172
173 scale = old_scale / nxt;
174 }
175 scale = old_scale / 10.0;
176 value_granularity /= 10.0;
177 }
178
179 let mut ret = vec![];
180 let left = {
185 let mut value = range.0 - rem_euclid(range.0, scale);
186 if value < range.0 {
187 value += scale;
188 }
189 value
190 };
191 let left_base = (left / value_granularity).floor() * value_granularity;
192 let mut left_relative = left - left_base;
193 let right = range.1 - rem_euclid(range.1, scale);
194 while (right - left_relative - left_base) >= -f64::EPSILON {
195 let new_left_relative =
196 (left_relative / value_granularity).round() * value_granularity;
197 if new_left_relative < 0.0 {
198 left_relative += value_granularity;
199 }
200 ret.push((left_relative + left_base) as $type);
201 left_relative += scale;
202 }
203 return ret;
204 }
205 };
206 (integer, $name:ident, $type:ty) => {
207 fn $name(range: ($type, $type), max_points: usize) -> Vec<$type> {
208 let mut scale: $type = 1;
209 let range = (range.0.min(range.1), range.0.max(range.1));
210 let range_size = range.1 as f64 - range.0 as f64;
211 'outer: while (range_size / scale as f64).ceil() > max_points as f64 {
212 let next_scale = scale * 10;
213 for new_scale in [scale * 2, scale * 5, scale * 10].iter() {
214 scale = *new_scale;
215 if (range_size / *new_scale as f64).ceil() < max_points as f64 {
216 break 'outer;
217 }
218 }
219 scale = next_scale;
220 }
221
222 let (mut left, right) = (
223 range.0 + (scale - range.0 % scale) % scale,
224 range.1 - range.1 % scale,
225 );
226
227 let mut ret = vec![];
228 while left <= right {
229 ret.push(left as $type);
230 if left < right {
231 left += scale;
232 } else {
233 break;
234 }
235 }
236
237 return ret;
238 }
239 };
240}
241
242gen_key_points_comp!(float, compute_f32_key_points, f32);
243gen_key_points_comp!(float, compute_f64_key_points, f64);
244gen_key_points_comp!(integer, compute_i32_key_points, i32);
245gen_key_points_comp!(integer, compute_u32_key_points, u32);
246gen_key_points_comp!(integer, compute_i64_key_points, i64);
247gen_key_points_comp!(integer, compute_u64_key_points, u64);
248gen_key_points_comp!(integer, compute_i128_key_points, i128);
249gen_key_points_comp!(integer, compute_u128_key_points, u128);
250gen_key_points_comp!(integer, compute_isize_key_points, isize);
251gen_key_points_comp!(integer, compute_usize_key_points, usize);
252
253make_numeric_coord!(
254 f32,
255 RangedCoordf32,
256 compute_f32_key_points,
257 "The ranged coordinate for type f32",
258 NoDefaultFormatting
259);
260impl_reverse_mapping_trait!(f32, RangedCoordf32);
261impl ValueFormatter<f32> for RangedCoordf32 {
262 fn format(value: &f32) -> String {
263 crate::data::float::FloatPrettyPrinter {
264 allow_scientific: false,
265 min_decimal: 1,
266 max_decimal: 5,
267 }
268 .print(*value as f64)
269 }
270}
271make_numeric_coord!(
272 f64,
273 RangedCoordf64,
274 compute_f64_key_points,
275 "The ranged coordinate for type f64",
276 NoDefaultFormatting
277);
278impl_reverse_mapping_trait!(f64, RangedCoordf64);
279impl ValueFormatter<f64> for RangedCoordf64 {
280 fn format(value: &f64) -> String {
281 crate::data::float::FloatPrettyPrinter {
282 allow_scientific: false,
283 min_decimal: 1,
284 max_decimal: 5,
285 }
286 .print(*value)
287 }
288}
289make_numeric_coord!(
290 u32,
291 RangedCoordu32,
292 compute_u32_key_points,
293 "The ranged coordinate for type u32"
294);
295make_numeric_coord!(
296 i32,
297 RangedCoordi32,
298 compute_i32_key_points,
299 "The ranged coordinate for type i32"
300);
301make_numeric_coord!(
302 u64,
303 RangedCoordu64,
304 compute_u64_key_points,
305 "The ranged coordinate for type u64"
306);
307make_numeric_coord!(
308 i64,
309 RangedCoordi64,
310 compute_i64_key_points,
311 "The ranged coordinate for type i64"
312);
313make_numeric_coord!(
314 u128,
315 RangedCoordu128,
316 compute_u128_key_points,
317 "The ranged coordinate for type u128"
318);
319make_numeric_coord!(
320 i128,
321 RangedCoordi128,
322 compute_i128_key_points,
323 "The ranged coordinate for type i128"
324);
325make_numeric_coord!(
326 usize,
327 RangedCoordusize,
328 compute_usize_key_points,
329 "The ranged coordinate for type usize"
330);
331make_numeric_coord!(
332 isize,
333 RangedCoordisize,
334 compute_isize_key_points,
335 "The ranged coordinate for type isize"
336);
337
338impl_discrete_trait!(RangedCoordu32);
339impl_discrete_trait!(RangedCoordi32);
340impl_discrete_trait!(RangedCoordu64);
341impl_discrete_trait!(RangedCoordi64);
342impl_discrete_trait!(RangedCoordu128);
343impl_discrete_trait!(RangedCoordi128);
344impl_discrete_trait!(RangedCoordusize);
345impl_discrete_trait!(RangedCoordisize);
346
347impl_ranged_type_trait!(f32, RangedCoordf32);
348impl_ranged_type_trait!(f64, RangedCoordf64);
349impl_ranged_type_trait!(i32, RangedCoordi32);
350impl_ranged_type_trait!(u32, RangedCoordu32);
351impl_ranged_type_trait!(i64, RangedCoordi64);
352impl_ranged_type_trait!(u64, RangedCoordu64);
353impl_ranged_type_trait!(i128, RangedCoordi128);
354impl_ranged_type_trait!(u128, RangedCoordu128);
355impl_ranged_type_trait!(isize, RangedCoordisize);
356impl_ranged_type_trait!(usize, RangedCoordusize);
357
358#[cfg(test)]
359mod test {
360 use super::*;
361 #[test]
362 fn test_key_points() {
363 let kp = compute_i32_key_points((0, 999), 28);
364
365 assert!(!kp.is_empty());
366 assert!(kp.len() <= 28);
367
368 let kp = compute_f64_key_points((-1.2, 1.2), 1);
369 assert!(kp.len() == 1);
370
371 let kp = compute_f64_key_points((-1.2, 1.2), 0);
372 assert!(kp.is_empty());
373 }
374
375 #[test]
376 fn test_linear_coord_map() {
377 let coord: RangedCoordu32 = (0..20).into();
378 assert_eq!(coord.key_points(11).len(), 11);
379 assert_eq!(coord.key_points(11)[0], 0);
380 assert_eq!(coord.key_points(11)[10], 20);
381 assert_eq!(coord.map(&5, (0, 100)), 25);
382
383 let coord: RangedCoordf32 = (0f32..20f32).into();
384 assert_eq!(coord.map(&5.0, (0, 100)), 25);
385 }
386
387 #[test]
388 fn test_linear_coord_system() {
389 let _coord =
390 crate::coord::ranged2d::cartesian::Cartesian2d::<RangedCoordu32, RangedCoordu32>::new(
391 0..10,
392 0..10,
393 (0..1024, 0..768),
394 );
395 }
396
397 #[test]
398 fn test_coord_unmap() {
399 let coord: RangedCoordu32 = (0..20).into();
400 let pos = coord.map(&5, (1000, 2000));
401 let value = coord.unmap(pos, (1000, 2000));
402 assert_eq!(value, Some(5));
403 }
404
405 #[test]
406 fn regression_test_issue_253_zero_sized_coord_not_hang() {
407 let coord: RangedCoordf32 = (0.0..0.0).into();
408 let _points = coord.key_points(10);
409 }
410
411 #[test]
412 fn test_small_coord() {
413 let coord: RangedCoordf64 = (0.0..1e-25).into();
414 let points = coord.key_points(10);
415 assert!(!points.is_empty());
416 }
417
418 #[test]
419 fn regression_test_issue_255_reverse_f32_coord_no_hang() {
420 let coord: RangedCoordf32 = (10.0..0.0).into();
421 let _points = coord.key_points(10);
422 }
423
424 #[test]
425 fn regression_test_issue_358_key_points_no_hang() {
426 let coord: RangedCoordf64 = (-200.0..801.0).into();
427 let points = coord.key_points(500);
428 assert!(points.len() <= 500);
429 }
430
431 #[test]
432 fn regression_test_issue_358_key_points_no_hang_2() {
433 let coord: RangedCoordf64 = (10000000000001f64..10000000000002f64).into();
434 let points = coord.key_points(500);
435 assert!(points.len() <= 500);
436 }
437
438 #[test]
439 fn test_coord_follows_hint() {
440 let coord: RangedCoordf64 = (1.0..2.0).into();
441 let points = coord.key_points(6);
442 assert_eq!(points.len(), 6);
443 assert_eq!(points[0], 1.0);
444 let coord: RangedCoordf64 = (1.0..125.0).into();
445 let points = coord.key_points(12);
446 assert_eq!(points.len(), 12);
447 let coord: RangedCoordf64 = (0.9995..1.0005).into();
448 let points = coord.key_points(11);
449 assert_eq!(points.len(), 11);
450 let coord: RangedCoordf64 = (0.9995..1.0005).into();
451 let points = coord.key_points(2);
452 assert!(points.len() <= 2);
453 }
454
455 #[test]
456 fn regression_test_issue_304_intmax_keypoint_no_panic() {
457 let coord: RangedCoordu32 = (0..u32::MAX).into();
458 let p = coord.key_points(10);
459 assert!(!p.is_empty() && p.len() <= 10);
460 }
461}