vec.rs

4.3 kB · rust · 154 lines

1use mrlycore::trig;23fn sini(i: i64) -> f32 {4    trig::sin_idx(i.rem_euclid(trig::N as i64) as usize)5}67fn cosi(i: i64) -> f32 {8    trig::cos_idx(i.rem_euclid(trig::N as i64) as usize)9}1011/// A three-component vector of f32.12#[derive(Clone, Copy, Debug, PartialEq)]13pub struct Vec3 {14    /// The x component.15    pub x: f32,16    /// The y component.17    pub y: f32,18    /// The z component.19    pub z: f32,20}2122impl std::ops::Add for Vec3 {23    type Output = Vec3;24    fn add(self, o: Vec3) -> Vec3 {25        Vec3::new(self.x + o.x, self.y + o.y, self.z + o.z)26    }27}2829impl std::ops::Sub for Vec3 {30    type Output = Vec3;31    fn sub(self, o: Vec3) -> Vec3 {32        Vec3::new(self.x - o.x, self.y - o.y, self.z - o.z)33    }34}3536impl Vec3 {37    /// Builds a vector from its components.38    pub fn new(x: f32, y: f32, z: f32) -> Vec3 {39        Vec3 { x, y, z }40    }41    /// Multiplies every component by the scalar.42    pub fn scale(self, s: f32) -> Vec3 {43        Vec3::new(self.x * s, self.y * s, self.z * s)44    }45    /// Returns the dot product of the two vectors.46    pub fn dot(self, o: Vec3) -> f32 {47        self.x * o.x + self.y * o.y + self.z * o.z48    }49    /// Returns the cross product, perpendicular to both vectors.50    ///51    /// ```52    /// use mrlymath::space::Vec3;53    /// let n = Vec3::new(1.0, 0.0, 0.0).cross(Vec3::new(0.0, 1.0, 0.0));54    /// assert_eq!(n, Vec3::new(0.0, 0.0, 1.0));55    /// ```56    pub fn cross(self, o: Vec3) -> Vec3 {57        Vec3::new(58            self.y * o.z - self.z * o.y,59            self.z * o.x - self.x * o.z,60            self.x * o.y - self.y * o.x,61        )62    }63}6465/// A three-by-three rotation matrix of f32.66#[derive(Clone, Copy, Debug, PartialEq)]67pub struct Mat3 {68    /// The rows of the matrix.69    pub m: [[f32; 3]; 3],70}7172impl std::ops::Mul for Mat3 {73    type Output = Mat3;74    fn mul(self, o: Mat3) -> Mat3 {75        let mut m = [[0.0f32; 3]; 3];76        for (r, row) in m.iter_mut().enumerate() {77            for (c, cell) in row.iter_mut().enumerate() {78                *cell =79                    self.m[r][0] * o.m[0][c] + self.m[r][1] * o.m[1][c] + self.m[r][2] * o.m[2][c];80            }81        }82        Mat3 { m }83    }84}8586impl Mat3 {87    /// Builds the identity matrix.88    pub fn identity() -> Mat3 {89        Mat3 {90            m: [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]],91        }92    }93    /// Builds the rotation about the y axis by i angle steps, wrapping in either direction.94    pub fn yaw(i: i64) -> Mat3 {95        let (c, s) = (cosi(i), sini(i));96        Mat3 {97            m: [[c, 0.0, s], [0.0, 1.0, 0.0], [-s, 0.0, c]],98        }99    }100    /// Builds the rotation about the x axis by i angle steps, wrapping in either direction.101    pub fn pitch(i: i64) -> Mat3 {102        let (c, s) = (cosi(i), sini(i));103        Mat3 {104            m: [[1.0, 0.0, 0.0], [0.0, c, -s], [0.0, s, c]],105        }106    }107    /// Transforms the vector by the matrix.108    pub fn apply(self, v: Vec3) -> Vec3 {109        Vec3::new(110            self.m[0][0] * v.x + self.m[0][1] * v.y + self.m[0][2] * v.z,111            self.m[1][0] * v.x + self.m[1][1] * v.y + self.m[1][2] * v.z,112            self.m[2][0] * v.x + self.m[2][1] * v.y + self.m[2][2] * v.z,113        )114    }115}116117#[cfg(test)]118mod tests {119    use super::*;120121    #[test]122    fn quarter_turns_are_exact() {123        let q = (trig::N / 4) as i64;124        let v = Mat3::yaw(q).apply(Vec3::new(1.0, 0.0, 0.0));125        assert!(v.x.abs() < 1e-6 && (v.z + 1.0).abs() < 1e-6);126        let v = Mat3::pitch(q).apply(Vec3::new(0.0, 1.0, 0.0));127        assert!(v.y.abs() < 1e-6 && (v.z - 1.0).abs() < 1e-6);128    }129    #[test]130    fn rotation_preserves_length() {131        let v = Vec3::new(0.3, -0.7, 0.64);132        for i in [0i64, 17, 100, -40, 255] {133            let r = (Mat3::yaw(i) * Mat3::pitch(i * 3)).apply(v);134            assert!((r.dot(r) - v.dot(v)).abs() < 1e-5);135        }136    }137    #[test]138    fn negative_indices_wrap() {139        assert_eq!(Mat3::yaw(-3).m, Mat3::yaw(253).m);140    }141    #[test]142    fn cross_is_perpendicular() {143        let a = Vec3::new(1.0, 2.0, 3.0);144        let b = Vec3::new(-2.0, 0.5, 1.0);145        let c = a.cross(b);146        assert!(c.dot(a).abs() < 1e-6);147        assert!(c.dot(b).abs() < 1e-6);148    }149    #[test]150    fn identity_leaves_vectors() {151        let v = Vec3::new(0.1, 0.2, 0.3);152        assert_eq!(Mat3::identity().apply(v), v);153    }154}