sable_core/math/
mod.rs

1//! Math primitives for the Sable engine.
2//!
3//! This module provides vector, matrix, and quaternion types optimized for
4//! game development and 3D graphics.
5//!
6//! # Types
7//!
8//! - [`Vec2`], [`Vec3`], [`Vec4`] — 2D, 3D, and 4D vectors
9//! - [`Mat3`], [`Mat4`] — 3x3 and 4x4 matrices
10//! - [`Quat`] — Quaternion for rotations
11//!
12//! # Precision
13//!
14//! By default, all types use `f32`. Enable the `f64` feature for double precision.
15
16mod mat3;
17mod mat4;
18mod quat;
19mod vec2;
20mod vec3;
21mod vec4;
22
23pub use mat3::Mat3;
24pub use mat4::Mat4;
25pub use quat::Quat;
26pub use vec2::Vec2;
27pub use vec3::Vec3;
28pub use vec4::Vec4;
29
30/// The floating-point type used for math operations.
31#[cfg(not(feature = "f64"))]
32pub type Float = f32;
33
34/// The floating-point type used for math operations.
35#[cfg(feature = "f64")]
36pub type Float = f64;
37
38/// A small epsilon value for floating-point comparisons.
39pub const EPSILON: Float = 1e-6;
40
41/// Pi constant.
42pub const PI: Float = std::f32::consts::PI as Float;
43
44/// Two times Pi.
45pub const TAU: Float = std::f32::consts::TAU as Float;
46
47/// Converts degrees to radians.
48#[inline]
49#[must_use]
50pub fn radians(degrees: Float) -> Float {
51    degrees * (PI / 180.0)
52}
53
54/// Converts radians to degrees.
55#[inline]
56#[must_use]
57pub fn degrees(radians: Float) -> Float {
58    radians * (180.0 / PI)
59}
60
61/// Linear interpolation between two values.
62#[inline]
63#[must_use]
64pub fn lerp(a: Float, b: Float, t: Float) -> Float {
65    a + (b - a) * t
66}
67
68/// Clamps a value between min and max.
69#[inline]
70#[must_use]
71pub fn clamp(value: Float, min: Float, max: Float) -> Float {
72    value.max(min).min(max)
73}
74
75/// Checks if two floats are approximately equal.
76#[inline]
77#[must_use]
78pub fn approx_eq(a: Float, b: Float) -> bool {
79    (a - b).abs() < EPSILON
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    #[test]
87    fn test_radians() {
88        assert!(approx_eq(radians(0.0), 0.0));
89        assert!(approx_eq(radians(90.0), PI / 2.0));
90        assert!(approx_eq(radians(180.0), PI));
91        assert!(approx_eq(radians(360.0), TAU));
92    }
93
94    #[test]
95    fn test_degrees() {
96        assert!(approx_eq(degrees(0.0), 0.0));
97        assert!(approx_eq(degrees(PI / 2.0), 90.0));
98        assert!(approx_eq(degrees(PI), 180.0));
99        assert!(approx_eq(degrees(TAU), 360.0));
100    }
101
102    #[test]
103    fn test_lerp() {
104        assert!(approx_eq(lerp(0.0, 10.0, 0.0), 0.0));
105        assert!(approx_eq(lerp(0.0, 10.0, 0.5), 5.0));
106        assert!(approx_eq(lerp(0.0, 10.0, 1.0), 10.0));
107        assert!(approx_eq(lerp(-5.0, 5.0, 0.5), 0.0));
108    }
109
110    #[test]
111    fn test_clamp() {
112        assert!(approx_eq(clamp(5.0, 0.0, 10.0), 5.0));
113        assert!(approx_eq(clamp(-5.0, 0.0, 10.0), 0.0));
114        assert!(approx_eq(clamp(15.0, 0.0, 10.0), 10.0));
115    }
116
117    #[test]
118    fn test_approx_eq() {
119        assert!(approx_eq(1.0, 1.0));
120        assert!(approx_eq(1.0, 1.0 + EPSILON / 2.0));
121        assert!(!approx_eq(1.0, 1.0 + EPSILON * 2.0));
122    }
123}