1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
//! A small collection of numeric functions. use std::mem; /// Clamps `num` to the range [`a`, `b`]. /// /// If `num` is in the range [`a`, `b`], returns `num`. /// If `num` is outside the range, returns the nearest end of the range. /// /// The ends of the range are interchangeable, so that /// `clamp(num, a, b) == clamp(num, b, a)` pub fn clamp(num: f32, a: f32, b: f32) -> f32 { let min = a.min(b); let max = a.max(b); num.max(min).min(max) } /// Linearly interpolates from `start` to `end` by `t`. /// /// `t` < 0 or `t` > 1 gives results outside [`start`, `end`]. pub fn lerp(start: f32, end: f32, t: f32) -> f32 { start + (end - start) * t } /// Linearly inpterpolates from `start` to `end` by `t`. /// /// `t` is clamped to the range [0, 1]. pub fn lerp_clamped(start: f32, end: f32, t: f32) -> f32 { lerp(t.max(0.0).min(1.0), start, end) } /// Maps a value relative to some range to /// the corresponding value relative to another range. /// /// Computes how far `x` is (percentage-wise) from the start to the end /// of the range [`from_start`, `from_end`], and returns the value with /// the same percentage in the range [`to_start`, `to_end`]. /// /// Still works if `x` is outside the range. pub fn range_map(x: f32, from_start: f32, from_end: f32, to_start: f32, to_end: f32) -> f32 { let offset = x - from_start; let from_span = from_end - from_start; let to_span = to_end - to_start; (offset / from_span) * to_span + to_start } /// Calculates `num` modulo `denom`. /// /// Equivalent to num % denom, except that the result /// is corrected to never be negative. No guarantees made /// about the result if `denom <= 0`, however. /// /// This gives the property that (discounting rounding errors) /// `modulo(n, d) == modulo(n + d, d)` for all `n` and positive `d`. /// /// Returns NaN if `denom` is zero. #[inline] pub fn modulo(num: f32, denom: f32) -> f32 { let rem = num % denom; if rem < 0.0 { rem + denom } else { rem } } /// Quickly, but roughly, approximates 1/sqrt(x). /// /// Uses a technique popularized, but not invented, by John Carmack /// in Quake 3 Arena, generally known as the fast inverse square root. /// Has a maximum error of about 0.175%. /// /// Although `1.0/x.sqrt()` likely won't be optimized by the compiler, /// any hardware implementation of 1/sqrt(x) will be both faster /// and more accurate, and should as such be preferred. /// /// Returns nonsense for negative numbers, NaN and infinities. #[inline] pub fn inv_sqrt(x: f32) -> f32 { const MAGIC_NUMBER: u32 = 0x5f375a86; let bits: u32 = unsafe { mem::transmute(x) }; let hacked_bits = MAGIC_NUMBER.wrapping_sub(bits >> 1); let y: f32 = unsafe { mem::transmute(hacked_bits) }; y*(1.5 - 0.5*x*y*y) }