//! Useful mathematical operations in graphical contexts. pub mod rect; pub mod vec2; use std::cmp::Ordering; use nalgebra::RealField; use num_traits::Pow; pub use self::rect::*; pub use self::vec2::*; /// Round a floating point number to the nearest step given by the step /// argument. For instance, if the step is 0.5, then all numbers from 0.0 to /// 0.24999... will be 0., while all numbers from 0.25 to 0.74999... will be 0.5 /// and so on. pub fn round(num: T, step: T) -> T where T: RealField, { // Only positive steps will be accepted. assert!(step > T::zero()); let lower_bound = (num / step).floor() * step; let upper_bound = lower_bound + step; // Compare the distances and prefer the smaller. If they are the same, prefer // the upper bound. if (num - lower_bound) < (upper_bound - num) { lower_bound } else { upper_bound } } /// Like round, but instead of rounding to a certain fraction, rounds to the nth /// decimal place instead of taking a granularity. pub fn round_nth_decimal(num: T, decimal_place: u16) -> T where T: RealField + Pow, { round(num, nalgebra::convert::(0.1).pow(decimal_place)) } /// Works like `std::cmp::max`, however also allows partial comparisons. It is /// specifically designed so functions that should be able to use f32 and f64 /// work, eventhough these do not implement Ord. The downside of this function /// however is, that its behaviour is undefined when `f32::NaN` for instance /// were to be passed. pub(crate) fn partial_max(a: T, b: T) -> T where T: PartialOrd, { match a.partial_cmp(&b) { Some(Ordering::Greater) => a, _ => b, } } /// Like `partial_max`, but for minimum values. Comes with the same downside, /// too. pub(crate) fn partial_min(a: T, b: T) -> T where T: PartialOrd, { match a.partial_cmp(&b) { Some(Ordering::Less) => a, _ => b, } } #[cfg(test)] mod test { #[test] fn partial_max() { assert_eq!(super::partial_max(0., 0.), 0.); assert_eq!(super::partial_max(-1., 1.), 1.); assert_eq!(super::partial_max(-2., -1.), -1.); assert_eq!(super::partial_max(2., 1.), 2.); } #[test] fn partial_min() { assert_eq!(super::partial_min(0., 0.), 0.); assert_eq!(super::partial_min(-1., 1.), -1.); assert_eq!(super::partial_min(-2., -1.), -2.); assert_eq!(super::partial_min(2., 1.), 1.); } }