fart_utils/
lib.rs

1//! Small math utility functions for `fart`.
2
3#![deny(missing_docs)]
4
5use num_traits::{Num, NumCast};
6use std::cmp;
7use std::fmt::Debug;
8use std::ops::{Range, RangeInclusive};
9
10/// Clamp a value to within some range.
11///
12/// # Example
13///
14/// ```
15/// # use fart_utils::clamp;
16/// let x = clamp(5.0, 0.0..=10.0);
17/// assert_eq!(x, 5.0);
18///
19/// let y = clamp(11.0, 0.0..=10.0);
20/// assert_eq!(y, 10.0);
21///
22/// let z = clamp(-5.0, 0.0..=10.0);
23/// assert_eq!(z, 0.0);
24/// ```
25///
26/// # Panics
27///
28/// Panics if `range.start() > range.end()`.
29pub fn clamp<N>(value: N, range: RangeInclusive<N>) -> N
30where
31    N: PartialOrd,
32{
33    let (low, high) = range.into_inner();
34    assert!(low <= high);
35    if value < low {
36        low
37    } else if value > high {
38        high
39    } else {
40        value
41    }
42}
43
44/// Map a value from one range to another range.
45///
46/// # Example
47///
48/// ```
49/// # use fart_utils::map_range;
50/// let x = map_range(5, 0..10, 0..100);
51/// assert_eq!(x, 50);
52///
53/// let y = map_range(3, 2..5, 0..3);
54/// assert_eq!(y, 1);
55/// ```
56///
57/// # Panics
58///
59/// Panics if the given value is outside the input range, if `in_low >= in_high`,
60/// or if `out_low >= out_high`, or if number conversions fail.
61pub fn map_range<N, M>(
62    value: N,
63    Range {
64        start: in_low,
65        end: in_high,
66    }: Range<N>,
67    Range {
68        start: out_low,
69        end: out_high,
70    }: Range<M>,
71) -> M
72where
73    N: Num + NumCast + Copy + PartialOrd + Debug,
74    M: Num + NumCast + Copy + PartialOrd + Debug,
75{
76    assert!(in_low < in_high, "{:?} < {:?}", in_low, in_high);
77    assert!(out_low < out_high, "{:?} < {:?}", out_low, out_high);
78    assert!(value >= in_low, "{:?} >= {:?}", value, in_low);
79    assert!(value <= in_high, "{:?} <= {:?}", value, in_high);
80
81    let value: M = NumCast::from(value).unwrap();
82    let in_low: M = NumCast::from(in_low).unwrap();
83    let in_high: M = NumCast::from(in_high).unwrap();
84
85    let dividend = out_high - out_low;
86    let divisor = in_high - in_low;
87    assert!(!divisor.is_zero());
88
89    let slope = dividend / divisor;
90    out_low + (slope * (value - in_low))
91}
92
93/// Turn a `T: PartialOrd` and/or `T: PartialEq` into `Ord` and/or `Eq`.
94#[derive(Copy, Clone, Debug, Default, PartialOrd, PartialEq, Hash)]
95pub struct NoMorePartial<T>(pub T);
96
97impl<T: PartialOrd> cmp::Ord for NoMorePartial<T> {
98    #[inline]
99    fn cmp(&self, other: &Self) -> cmp::Ordering {
100        self.partial_cmp(other).unwrap()
101    }
102}
103
104impl<T: PartialEq> cmp::Eq for NoMorePartial<T> {}