1use std::ops::Range;
2
3use super::{AsRangedCoord, DescreteRanged, Ranged, ReversableRanged};
4
5macro_rules! impl_descrete_trait {
6 ($name:ident) => {
7 impl DescreteRanged for $name {
8 fn next_value(this: &Self::ValueType) -> Self::ValueType {
9 return *this + 1;
10 }
11 }
12 };
13}
14
15macro_rules! impl_ranged_type_trait {
16 ($value:ty, $coord:ident) => {
17 impl AsRangedCoord for Range<$value> {
18 type CoordDescType = $coord;
19 type Value = $value;
20 }
21 };
22}
23
24macro_rules! make_numeric_coord {
25 ($type:ty, $name:ident, $key_points:ident, $doc: expr) => {
26 #[doc = $doc]
27 pub struct $name($type, $type);
28 impl From<Range<$type>> for $name {
29 fn from(range: Range<$type>) -> Self {
30 return Self(range.start, range.end);
31 }
32 }
33 impl Ranged for $name {
34 type ValueType = $type;
35 fn map(&self, v: &$type, limit: (i32, i32)) -> i32 {
36 let logic_length = (*v - self.0) as f64 / (self.1 - self.0) as f64;
37 let actual_length = limit.1 - limit.0;
38
39 if actual_length == 0 {
40 return limit.1;
41 }
42
43 return limit.0 + (actual_length as f64 * logic_length + 1e-3).floor() as i32;
44 }
45 fn key_points(&self, max_points: usize) -> Vec<$type> {
46 $key_points((self.0, self.1), max_points)
47 }
48 fn range(&self) -> Range<$type> {
49 return self.0..self.1;
50 }
51 }
52
53 impl ReversableRanged for $name {
54 fn unmap(&self, p:i32, (min,max): (i32, i32)) -> Option<$type> {
55 if p < min.min(max) || p > max.max(min) {
56 return None;
57 }
58
59 let logical_offset = (p - min) as f64 / (max - min) as f64;
60
61 return Some(((self.1 - self.0) as f64 * logical_offset + self.0 as f64) as $type);
62 }
63 }
64 };
65}
66
67macro_rules! gen_key_points_comp {
68 (float, $name:ident, $type:ty) => {
69 fn $name(range: ($type, $type), max_points: usize) -> Vec<$type> {
70 let range = (range.0 as f64, range.1 as f64);
71 let mut scale = (10f64).powf((range.1 - range.0).log(10.0).floor());
72 let mut digits = -(range.1 - range.0).log(10.0).floor() as i32 + 1;
73 fn rem_euclid(a: f64, b: f64) -> f64 {
74 if b > 0.0 {
75 a - (a / b).floor() * b
76 } else {
77 a - (a / b).ceil() * b
78 }
79 }
80 'outer: loop {
81 let old_scale = scale;
82 for nxt in [2.0, 5.0, 10.0].iter() {
83 let new_left = range.0 + scale / nxt - rem_euclid(range.0, scale / nxt);
84 let new_right = range.1 - rem_euclid(range.1, scale / nxt);
85
86 let npoints = 1 + ((new_right - new_left) / old_scale * nxt) as usize;
87
88 if npoints > max_points {
89 break 'outer;
90 }
91
92 scale = old_scale / nxt;
93 }
94 scale = old_scale / 10.0;
95 if scale < 1.0 {
96 digits += 1;
97 }
98 }
99
100 let mut ret = vec![];
101 let mut left = range.0 + scale - rem_euclid(range.0, scale);
102 let right = range.1 - rem_euclid(range.1, scale);
103 while left <= right {
104 let size = (10f64).powf(digits as f64 + 1.0);
105 let new_left = (left * size).abs() + 1e-3;
106 if left < 0.0 {
107 left = -new_left.round() / size;
108 } else {
109 left = new_left.round() / size;
110 }
111 ret.push(left as $type);
112 left += scale;
113 }
114 return ret;
115 }
116 };
117 (integer, $name:ident, $type:ty) => {
118 fn $name(range: ($type, $type), max_points: usize) -> Vec<$type> {
119 let mut scale: $type = 1;
120 'outter: while (range.1 - range.0 + scale - 1) as usize / (scale as usize) > max_points
121 {
122 let next_scale = scale * 10;
123 for new_scale in [scale * 2, scale * 5, scale * 10].iter() {
124 scale = *new_scale;
125 if (range.1 - range.0 + *new_scale - 1) as usize / (*new_scale as usize)
126 < max_points
127 {
128 break 'outter;
129 }
130 }
131 scale = next_scale;
132 }
133
134 let (mut left, right) = (
135 range.0 + (scale - range.0 % scale) % scale,
136 range.1 - range.1 % scale,
137 );
138
139 let mut ret = vec![];
140 while left <= right {
141 ret.push(left as $type);
142 left += scale;
143 }
144
145 return ret;
146 }
147 };
148}
149
150gen_key_points_comp!(float, compute_f32_key_points, f32);
151gen_key_points_comp!(float, compute_f64_key_points, f64);
152gen_key_points_comp!(integer, compute_i32_key_points, i32);
153gen_key_points_comp!(integer, compute_u32_key_points, u32);
154gen_key_points_comp!(integer, compute_i64_key_points, i64);
155gen_key_points_comp!(integer, compute_u64_key_points, u64);
156
157make_numeric_coord!(
158 f32,
159 RangedCoordf32,
160 compute_f32_key_points,
161 "The ranged coordinate for type f32"
162);
163make_numeric_coord!(
164 f64,
165 RangedCoordf64,
166 compute_f64_key_points,
167 "The ranged coordinate for type f64"
168);
169make_numeric_coord!(
170 u32,
171 RangedCoordu32,
172 compute_u32_key_points,
173 "The ranged coordinate for type u32"
174);
175make_numeric_coord!(
176 i32,
177 RangedCoordi32,
178 compute_i32_key_points,
179 "The ranged coordinate for type i32"
180);
181make_numeric_coord!(
182 u64,
183 RangedCoordu64,
184 compute_u64_key_points,
185 "The ranged coordinate for type u64"
186);
187make_numeric_coord!(
188 i64,
189 RangedCoordi64,
190 compute_i64_key_points,
191 "The ranged coordinate for type i64"
192);
193
194impl_descrete_trait!(RangedCoordu32);
195impl_descrete_trait!(RangedCoordi32);
196impl_descrete_trait!(RangedCoordu64);
197impl_descrete_trait!(RangedCoordi64);
198
199impl_ranged_type_trait!(f32, RangedCoordf32);
200impl_ranged_type_trait!(f64, RangedCoordf64);
201impl_ranged_type_trait!(i32, RangedCoordi32);
202impl_ranged_type_trait!(i64, RangedCoordi64);
203impl_ranged_type_trait!(u32, RangedCoordu32);
204impl_ranged_type_trait!(u64, RangedCoordu64);
205
206#[cfg(test)]
212mod test {
213 use super::*;
214 use crate::coord::*;
215 #[test]
216 fn test_key_points() {
217 let kp = compute_i32_key_points((0, 999), 28);
218
219 assert!(kp.len() > 0);
220 assert!(kp.len() <= 28);
221 }
222
223 #[test]
224 fn test_linear_coord_map() {
225 let coord: RangedCoordu32 = (0..20).into();
226 assert_eq!(coord.key_points(11).len(), 11);
227 assert_eq!(coord.key_points(11)[0], 0);
228 assert_eq!(coord.key_points(11)[10], 20);
229 assert_eq!(coord.map(&5, (0, 100)), 25);
230
231 let coord: RangedCoordf32 = (0f32..20f32).into();
232 assert_eq!(coord.map(&5.0, (0, 100)), 25);
233 }
234
235 #[test]
236 fn test_linear_coord_system() {
237 let _coord =
238 RangedCoord::<RangedCoordu32, RangedCoordu32>::new(0..10, 0..10, (0..1024, 0..768));
239 }
240}