1 use std::convert::TryFrom; 2 use std::ops::Range; 3 4 use crate::coord::ranged1d::{ 5 AsRangedCoord, DefaultFormatting, DiscreteRanged, KeyPointHint, NoDefaultFormatting, Ranged, 6 ReversibleRanged, ValueFormatter, 7 }; 8 9 macro_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 38 macro_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 } 46 macro_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 } 61 macro_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 // Corner case: If we have a range that have only one value, 77 // then we just assign everything to the only point 78 if self.1 == self.0 { 79 return (limit.1 - limit.0) / 2; 80 } 81 82 let logic_length = (*v - self.0) as f64 / (self.1 - 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 return limit.0 + (actual_length as f64 * logic_length + 1e-3).floor() as i32; 91 } 92 fn key_points<Hint: KeyPointHint>(&self, hint: Hint) -> Vec<$type> { 93 $key_points((self.0, self.1), hint.max_num_points()) 94 } 95 fn range(&self) -> Range<$type> { 96 return self.0..self.1; 97 } 98 } 99 }; 100 ($type:ty, $name:ident, $key_points:ident, $doc: expr) => { 101 make_numeric_coord!($type, $name, $key_points, $doc, DefaultFormatting); 102 } 103 } 104 105 macro_rules! gen_key_points_comp { 106 (float, $name:ident, $type:ty) => { 107 fn $name(range: ($type, $type), max_points: usize) -> Vec<$type> { 108 if max_points == 0 { 109 return vec![]; 110 } 111 112 let range = (range.0 as f64, range.1 as f64); 113 let mut scale = (10f64).powf((range.1 - range.0).log(10.0).floor()); 114 let mut digits = -(range.1 - range.0).log(10.0).floor() as i32 + 1; 115 fn rem_euclid(a: f64, b: f64) -> f64 { 116 if b > 0.0 { 117 a - (a / b).floor() * b 118 } else { 119 a - (a / b).ceil() * b 120 } 121 } 122 123 // At this point we need to make sure that the loop invariant: 124 // The scale must yield number of points than requested 125 if 1 + ((range.1 - range.0) / scale).floor() as usize > max_points { 126 scale *= 10.0; 127 } 128 129 'outer: loop { 130 let old_scale = scale; 131 for nxt in [2.0, 5.0, 10.0].iter() { 132 let new_left = range.0 + scale / nxt - rem_euclid(range.0, scale / nxt); 133 let new_right = range.1 - rem_euclid(range.1, scale / nxt); 134 135 let npoints = 1 + ((new_right - new_left) / old_scale * nxt) as usize; 136 137 if npoints > max_points { 138 break 'outer; 139 } 140 141 scale = old_scale / nxt; 142 } 143 scale = old_scale / 10.0; 144 if scale < 1.0 { 145 digits += 1; 146 } 147 } 148 149 let mut ret = vec![]; 150 let mut left = range.0 + scale - rem_euclid(range.0, scale); 151 let right = range.1 - rem_euclid(range.1, scale); 152 while left <= right { 153 let size = (10f64).powf(digits as f64 + 1.0); 154 let new_left = (left * size).abs() + 1e-3; 155 if left < 0.0 { 156 left = -new_left.round() / size; 157 } else { 158 left = new_left.round() / size; 159 } 160 ret.push(left as $type); 161 left += scale; 162 } 163 return ret; 164 } 165 }; 166 (integer, $name:ident, $type:ty) => { 167 fn $name(range: ($type, $type), max_points: usize) -> Vec<$type> { 168 let mut scale: $type = 1; 169 let range = (range.0.min(range.1), range.0.max(range.1)); 170 'outer: while (range.1 - range.0 + scale - 1) as usize / (scale as usize) > max_points { 171 let next_scale = scale * 10; 172 for new_scale in [scale * 2, scale * 5, scale * 10].iter() { 173 scale = *new_scale; 174 if (range.1 - range.0 + *new_scale - 1) as usize / (*new_scale as usize) 175 < max_points 176 { 177 break 'outer; 178 } 179 } 180 scale = next_scale; 181 } 182 183 let (mut left, right) = ( 184 range.0 + (scale - range.0 % scale) % scale, 185 range.1 - range.1 % scale, 186 ); 187 188 let mut ret = vec![]; 189 while left <= right { 190 ret.push(left as $type); 191 left += scale; 192 } 193 194 return ret; 195 } 196 }; 197 } 198 199 gen_key_points_comp!(float, compute_f32_key_points, f32); 200 gen_key_points_comp!(float, compute_f64_key_points, f64); 201 gen_key_points_comp!(integer, compute_i32_key_points, i32); 202 gen_key_points_comp!(integer, compute_u32_key_points, u32); 203 gen_key_points_comp!(integer, compute_i64_key_points, i64); 204 gen_key_points_comp!(integer, compute_u64_key_points, u64); 205 gen_key_points_comp!(integer, compute_i128_key_points, i128); 206 gen_key_points_comp!(integer, compute_u128_key_points, u128); 207 gen_key_points_comp!(integer, compute_isize_key_points, isize); 208 gen_key_points_comp!(integer, compute_usize_key_points, usize); 209 210 make_numeric_coord!( 211 f32, 212 RangedCoordf32, 213 compute_f32_key_points, 214 "The ranged coordinate for type f32", 215 NoDefaultFormatting 216 ); 217 impl_reverse_mapping_trait!(f32, RangedCoordf32); 218 impl ValueFormatter<f32> for RangedCoordf32 { format(value: &f32) -> String219 fn format(value: &f32) -> String { 220 crate::data::float::FloatPrettyPrinter { 221 allow_scientific: false, 222 min_decimal: 1, 223 max_decimal: 5, 224 } 225 .print(*value as f64) 226 } 227 } 228 make_numeric_coord!( 229 f64, 230 RangedCoordf64, 231 compute_f64_key_points, 232 "The ranged coordinate for type f64", 233 NoDefaultFormatting 234 ); 235 impl_reverse_mapping_trait!(f64, RangedCoordf64); 236 impl ValueFormatter<f64> for RangedCoordf64 { format(value: &f64) -> String237 fn format(value: &f64) -> String { 238 crate::data::float::FloatPrettyPrinter { 239 allow_scientific: false, 240 min_decimal: 1, 241 max_decimal: 5, 242 } 243 .print(*value) 244 } 245 } 246 make_numeric_coord!( 247 u32, 248 RangedCoordu32, 249 compute_u32_key_points, 250 "The ranged coordinate for type u32" 251 ); 252 make_numeric_coord!( 253 i32, 254 RangedCoordi32, 255 compute_i32_key_points, 256 "The ranged coordinate for type i32" 257 ); 258 make_numeric_coord!( 259 u64, 260 RangedCoordu64, 261 compute_u64_key_points, 262 "The ranged coordinate for type u64" 263 ); 264 make_numeric_coord!( 265 i64, 266 RangedCoordi64, 267 compute_i64_key_points, 268 "The ranged coordinate for type i64" 269 ); 270 make_numeric_coord!( 271 u128, 272 RangedCoordu128, 273 compute_u128_key_points, 274 "The ranged coordinate for type u128" 275 ); 276 make_numeric_coord!( 277 i128, 278 RangedCoordi128, 279 compute_i128_key_points, 280 "The ranged coordinate for type i128" 281 ); 282 make_numeric_coord!( 283 usize, 284 RangedCoordusize, 285 compute_usize_key_points, 286 "The ranged coordinate for type usize" 287 ); 288 make_numeric_coord!( 289 isize, 290 RangedCoordisize, 291 compute_isize_key_points, 292 "The ranged coordinate for type isize" 293 ); 294 295 impl_discrete_trait!(RangedCoordu32); 296 impl_discrete_trait!(RangedCoordi32); 297 impl_discrete_trait!(RangedCoordu64); 298 impl_discrete_trait!(RangedCoordi64); 299 impl_discrete_trait!(RangedCoordu128); 300 impl_discrete_trait!(RangedCoordi128); 301 impl_discrete_trait!(RangedCoordusize); 302 impl_discrete_trait!(RangedCoordisize); 303 304 impl_ranged_type_trait!(f32, RangedCoordf32); 305 impl_ranged_type_trait!(f64, RangedCoordf64); 306 impl_ranged_type_trait!(i32, RangedCoordi32); 307 impl_ranged_type_trait!(u32, RangedCoordu32); 308 impl_ranged_type_trait!(i64, RangedCoordi64); 309 impl_ranged_type_trait!(u64, RangedCoordu64); 310 impl_ranged_type_trait!(i128, RangedCoordi128); 311 impl_ranged_type_trait!(u128, RangedCoordu128); 312 impl_ranged_type_trait!(isize, RangedCoordisize); 313 impl_ranged_type_trait!(usize, RangedCoordusize); 314 315 #[cfg(test)] 316 mod test { 317 use super::*; 318 #[test] test_key_points()319 fn test_key_points() { 320 let kp = compute_i32_key_points((0, 999), 28); 321 322 assert!(kp.len() > 0); 323 assert!(kp.len() <= 28); 324 325 let kp = compute_f64_key_points((-1.2, 1.2), 1); 326 assert!(kp.len() == 1); 327 328 let kp = compute_f64_key_points((-1.2, 1.2), 0); 329 assert!(kp.len() == 0); 330 } 331 332 #[test] test_linear_coord_map()333 fn test_linear_coord_map() { 334 let coord: RangedCoordu32 = (0..20).into(); 335 assert_eq!(coord.key_points(11).len(), 11); 336 assert_eq!(coord.key_points(11)[0], 0); 337 assert_eq!(coord.key_points(11)[10], 20); 338 assert_eq!(coord.map(&5, (0, 100)), 25); 339 340 let coord: RangedCoordf32 = (0f32..20f32).into(); 341 assert_eq!(coord.map(&5.0, (0, 100)), 25); 342 } 343 344 #[test] test_linear_coord_system()345 fn test_linear_coord_system() { 346 let _coord = 347 crate::coord::ranged2d::cartesian::Cartesian2d::<RangedCoordu32, RangedCoordu32>::new( 348 0..10, 349 0..10, 350 (0..1024, 0..768), 351 ); 352 } 353 354 #[test] test_coord_unmap()355 fn test_coord_unmap() { 356 let coord: RangedCoordu32 = (0..20).into(); 357 let pos = coord.map(&5, (1000, 2000)); 358 let value = coord.unmap(pos, (1000, 2000)); 359 assert_eq!(value, Some(5)); 360 } 361 } 362