1use core::ops::Add;
5use num_traits::{Float, NumCast, One};
6
7pub use num_traits;
8
9const ONE_TURN_DEGREES_F32: f32 = 360.0;
10const ONE_TURN_DEGREES_F64: f64 = 360.0;
11
12pub trait ConvertAngle {
16 fn deg_to_rad(self) -> Self;
17 fn rad_to_deg(self) -> Self;
18 fn turns_to_rad(self) -> Self;
19 fn rad_to_turns(self) -> Self;
20}
21
22pub trait Vec2Angle {
24 fn angle(self) -> f32;
28}
29
30pub trait Vec2Rotate {
31 fn rotate(self, radians: f32) -> Self;
33}
34
35pub trait Mat4LookTo {
39 fn look_to_rh(eye: glam::Vec3, dir: glam::Vec3, up: glam::Vec3) -> glam::Mat4 {
40 let f = dir.normalize();
41 let s = f.cross(up).normalize();
42 let u = s.cross(f);
43 glam::Mat4::from_cols(
44 glam::vec4(s.x, u.x, -f.x, 0.0),
45 glam::vec4(s.y, u.y, -f.y, 0.0),
46 glam::vec4(s.z, u.z, -f.z, 0.0),
47 glam::vec4(-eye.dot(s), -eye.dot(u), eye.dot(f), 1.0),
48 )
49 }
50
51 fn look_to_lh(eye: glam::Vec3, dir: glam::Vec3, up: glam::Vec3) -> glam::Mat4 {
52 Self::look_to_rh(eye, -dir, up)
53 }
54}
55
56impl ConvertAngle for f32 {
57 fn deg_to_rad(self) -> Self {
58 self * core::f32::consts::TAU / ONE_TURN_DEGREES_F32
59 }
60 fn rad_to_deg(self) -> Self {
61 self * ONE_TURN_DEGREES_F32 / core::f32::consts::TAU
62 }
63 fn turns_to_rad(self) -> Self {
64 self * core::f32::consts::TAU
65 }
66 fn rad_to_turns(self) -> Self {
67 self / core::f32::consts::TAU
68 }
69}
70
71impl ConvertAngle for f64 {
72 fn deg_to_rad(self) -> Self {
73 self * core::f64::consts::TAU / ONE_TURN_DEGREES_F64
74 }
75 fn rad_to_deg(self) -> Self {
76 self * ONE_TURN_DEGREES_F64 / core::f64::consts::TAU
77 }
78 fn turns_to_rad(self) -> Self {
79 self * core::f64::consts::TAU
80 }
81 fn rad_to_turns(self) -> Self {
82 self / core::f64::consts::TAU
83 }
84}
85
86impl Vec2Angle for glam::Vec2 {
87 fn angle(self) -> f32 {
88 glam::Vec2::X.angle_between(self)
89 }
90}
91
92impl Vec2Rotate for glam::Vec2 {
93 fn rotate(self, radians: f32) -> Self {
94 let rad_cos = radians.cos();
95 let rad_sin = radians.sin();
96 let x = self.x * rad_cos - self.y * rad_sin;
97 let y = self.x * rad_sin + self.y * rad_cos;
98 glam::vec2(x, y)
99 }
100}
101
102impl Mat4LookTo for glam::Mat4 {}
103
104pub fn map_range<X, Y>(val: X, in_min: X, in_max: X, out_min: Y, out_max: Y) -> Y
130where
131 X: NumCast,
132 Y: NumCast,
133{
134 macro_rules! unwrap_or_panic {
135 ($result:expr, $arg:expr) => {
136 $result.unwrap_or_else(|| panic!("[map_range] failed to cast {} arg to `f64`", $arg))
137 };
138 }
139
140 let val_f: f64 = unwrap_or_panic!(NumCast::from(val), "first");
141 let in_min_f: f64 = unwrap_or_panic!(NumCast::from(in_min), "second");
142 let in_max_f: f64 = unwrap_or_panic!(NumCast::from(in_max), "third");
143 let out_min_f: f64 = unwrap_or_panic!(NumCast::from(out_min), "fourth");
144 let out_max_f: f64 = unwrap_or_panic!(NumCast::from(out_max), "fifth");
145
146 NumCast::from((val_f - in_min_f) / (in_max_f - in_min_f) * (out_max_f - out_min_f) + out_min_f)
147 .unwrap_or_else(|| panic!("[map_range] failed to cast result to target type"))
148}
149
150pub fn partial_max<T>(a: T, b: T) -> T
152where
153 T: PartialOrd,
154{
155 if a >= b {
156 a
157 } else {
158 b
159 }
160}
161
162pub fn partial_min<T>(a: T, b: T) -> T
164where
165 T: PartialOrd,
166{
167 if a <= b {
168 a
169 } else {
170 b
171 }
172}
173
174pub fn clamp<T>(n: T, start: T, end: T) -> T
176where
177 T: PartialOrd,
178{
179 if start <= end {
180 if n < start {
181 start
182 } else if n > end {
183 end
184 } else {
185 n
186 }
187 } else {
188 if n < end {
189 end
190 } else if n > start {
191 start
192 } else {
193 n
194 }
195 }
196}
197
198pub fn two<S>() -> S
199where
200 S: Add<Output = S> + One,
201{
202 S::one() + S::one()
203}
204
205#[inline]
207pub fn fmod<F>(numer: F, denom: F) -> F
208where
209 F: Float,
210{
211 let rquot: F = (numer / denom).floor();
212 numer - rquot * denom
213}
214
215pub fn deg_to_rad<S>(s: S) -> S
217where
218 S: ConvertAngle,
219{
220 s.deg_to_rad()
221}
222
223pub fn rad_to_deg<S>(s: S) -> S
225where
226 S: ConvertAngle,
227{
228 s.rad_to_deg()
229}
230
231pub fn turns_to_rad<S>(s: S) -> S
233where
234 S: ConvertAngle,
235{
236 s.turns_to_rad()
237}
238
239pub fn rad_to_turns<S>(s: S) -> S
241where
242 S: ConvertAngle,
243{
244 s.rad_to_turns()
245}