Skip to main content

algosul_math/
num.rs

1use std::ops::Range;
2
3use num_traits::{float::FloatCore, Bounded, Num};
4pub trait NumPercent<N>
5{
6  /// # Safety
7  /// + `range.start != range.end`
8  fn percent_in<U: Num + From<N> + Clone>(self, range: Range<U>) -> U;
9  /// # Safety
10  /// + `range.start != range.end`
11  fn try_percent_in<U: Num + TryFrom<N> + Clone>(
12    self, range: Range<U>,
13  ) -> Result<U, U::Error>;
14}
15pub trait NumLerp<N: FloatCore>
16{
17  fn lerp_in(self, range: Range<N>) -> N;
18  fn lerp(range: Range<N>, alpha: N) -> N;
19}
20pub trait NumRemap<N>
21{
22  fn remap<U: FloatCore + NumLerp<U> + From<N>>(
23    self, from: Range<U>, to: Range<U>,
24  ) -> U;
25  fn try_remap<U: FloatCore + NumLerp<U> + TryFrom<N>>(
26    self, from: Range<U>, to: Range<U>,
27  ) -> Result<U, U::Error>;
28  fn remap01<U: FloatCore + NumLerp<U> + From<N>>(self) -> U
29  where
30    N: Bounded,
31    Self: Sized,
32  {
33    self
34      .remap(N::min_value().into()..N::max_value().into(), U::zero()..U::one())
35  }
36  fn try_remap01<U: FloatCore + NumLerp<U> + TryFrom<N>>(
37    self,
38  ) -> Result<U, U::Error>
39  where
40    N: Bounded,
41    Self: Sized,
42  {
43    Ok(self.try_remap(
44      N::min_value().try_into()?..N::max_value().try_into()?,
45      U::zero()..U::one(),
46    ))?
47  }
48}
49pub trait NumsRemap<N, const L: usize>
50{
51  fn remap<U: FloatCore + NumRemap<U> + From<N>>(
52    self, from: Range<U>, to: Range<U>,
53  ) -> [U; L];
54  fn try_remap<U: FloatCore + NumRemap<U> + TryFrom<N>>(
55    self, from: Range<U>, to: Range<U>,
56  ) -> Result<[U; L], U::Error>;
57  fn remap01<U: FloatCore + NumLerp<U> + From<N>>(self) -> [U; L]
58  where
59    N: Bounded,
60    Self: Sized,
61  {
62    self
63      .remap(N::min_value().into()..N::max_value().into(), U::zero()..U::one())
64  }
65  fn try_remap01<U: FloatCore + NumLerp<U> + TryFrom<N>>(
66    self,
67  ) -> Result<[U; L], U::Error>
68  where
69    N: Bounded,
70    Self: Sized,
71  {
72    Ok(self.try_remap(
73      N::min_value().try_into()?..N::max_value().try_into()?,
74      U::zero()..U::one(),
75    ))?
76  }
77}
78impl<N: Num> NumPercent<N> for N
79{
80  fn percent_in<U: Num + From<N> + Clone>(self, range: Range<U>) -> U
81  {
82    (<U as From<N>>::from(self) - range.start.clone())
83      / (range.end - range.start)
84  }
85
86  fn try_percent_in<U: Num + TryFrom<N> + Clone>(
87    self, range: Range<U>,
88  ) -> Result<U, U::Error>
89  {
90    Ok(
91      (<U as TryFrom<N>>::try_from(self)? - range.start.clone())
92        / (range.end - range.start),
93    )
94  }
95}
96impl<N: FloatCore> NumLerp<N> for N
97{
98  fn lerp_in(self, range: Range<N>) -> N
99  {
100    Self::lerp(range, self)
101  }
102
103  fn lerp(range: Range<N>, alpha: N) -> N
104  {
105    ((N::one() - alpha) * range.start) + (alpha * range.end)
106  }
107}
108impl<N: Num + NumPercent<N>> NumRemap<N> for N
109{
110  fn remap<U: FloatCore + NumLerp<U> + From<N>>(
111    self, from: Range<U>, to: Range<U>,
112  ) -> U
113  {
114    if from.start == from.end
115    {
116      to.start
117    }
118    else
119    {
120      let percent = self.percent_in::<U>(from);
121      percent.lerp_in(to)
122    }
123  }
124
125  fn try_remap<U: FloatCore + NumLerp<U> + TryFrom<N>>(
126    self, from: Range<U>, to: Range<U>,
127  ) -> Result<U, U::Error>
128  {
129    Ok(
130      if from.start == from.end
131      {
132        to.start
133      }
134      else
135      {
136        let percent = self.try_percent_in::<U>(from)?;
137        percent.lerp_in(to)
138      },
139    )
140  }
141}
142impl<N: Num, const L: usize> NumsRemap<N, L> for [N; L]
143{
144  fn remap<U: FloatCore + NumRemap<U> + From<N>>(
145    self, from: Range<U>, to: Range<U>,
146  ) -> [U; L]
147  {
148    let mut arr: [std::mem::MaybeUninit<U>; L] =
149      unsafe { std::mem::MaybeUninit::uninit().assume_init() };
150    for (elem, n) in arr.iter_mut().zip(self.into_iter())
151    {
152      *elem = std::mem::MaybeUninit::new(
153        <U as From<N>>::from(n).remap(from.clone(), to.clone()),
154      );
155    }
156    unsafe { std::mem::transmute_copy::<_, [U; L]>(&arr) }
157  }
158
159  fn try_remap<U: FloatCore + NumRemap<U> + TryFrom<N>>(
160    self, from: Range<U>, to: Range<U>,
161  ) -> Result<[U; L], U::Error>
162  {
163    let mut arr: [std::mem::MaybeUninit<U>; L] =
164      unsafe { std::mem::MaybeUninit::uninit().assume_init() };
165    for (elem, n) in arr.iter_mut().zip(self.into_iter())
166    {
167      *elem = std::mem::MaybeUninit::new(
168        <U as TryFrom<N>>::try_from(n)?.remap(from.clone(), to.clone()),
169      );
170    }
171    Ok(unsafe { std::mem::transmute_copy::<_, [U; L]>(&arr) })
172  }
173}