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