]> git.nega.tv - josh/narcissus/commitdiff
Rework maths library
authorJoshua Simmons <josh@nega.tv>
Tue, 6 Sep 2022 21:43:38 +0000 (23:43 +0200)
committerJoshua Simmons <josh@nega.tv>
Tue, 6 Sep 2022 21:43:38 +0000 (23:43 +0200)
Split into multiple files.
Add `Affine2` and `Affine3` transformation types.
Add `Point2` and `Point3` types.
Use macro instead of repeating basic `Point` and `Vec` implementation.
Change `Mat4 * Point` to use homogeneous coordinates where `w=1`.
Change `Mat4 * Vec` to use homogeneous coordinates where `w=0`.

12 files changed:
narcissus-maths/src/affine2.rs [new file with mode: 0644]
narcissus-maths/src/affine3.rs [new file with mode: 0644]
narcissus-maths/src/lib.rs
narcissus-maths/src/mat2.rs [new file with mode: 0644]
narcissus-maths/src/mat3.rs [new file with mode: 0644]
narcissus-maths/src/mat4.rs
narcissus-maths/src/point2.rs [new file with mode: 0644]
narcissus-maths/src/point3.rs [new file with mode: 0644]
narcissus-maths/src/quat.rs
narcissus-maths/src/vec2.rs
narcissus-maths/src/vec3.rs
narcissus-maths/src/vec4.rs

diff --git a/narcissus-maths/src/affine2.rs b/narcissus-maths/src/affine2.rs
new file mode 100644 (file)
index 0000000..42a0a2c
--- /dev/null
@@ -0,0 +1,8 @@
+use crate::{Mat2, Vec2};
+
+#[derive(Clone, Copy, PartialEq)]
+#[repr(C)]
+pub struct Affine2 {
+    matrix: Mat2,
+    translate: Vec2,
+}
diff --git a/narcissus-maths/src/affine3.rs b/narcissus-maths/src/affine3.rs
new file mode 100644 (file)
index 0000000..f13a653
--- /dev/null
@@ -0,0 +1,8 @@
+use crate::{Mat3, Vec3};
+
+#[derive(Clone, Copy, PartialEq)]
+#[repr(C)]
+pub struct Affine3 {
+    matrix: Mat3,
+    translate: Vec3,
+}
index 4c499a057a9164df8394b1477787d4bc95c02cdd..43312466c0ed404ee472d9720dca3ffd4be9fb7b 100644 (file)
+mod affine2;
+mod affine3;
+mod mat2;
+mod mat3;
 mod mat4;
+mod point2;
+mod point3;
 mod quat;
 mod vec2;
 mod vec3;
 mod vec4;
 
+pub use affine2::Affine2;
+pub use affine3::Affine3;
+pub use mat2::Mat2;
+pub use mat3::Mat3;
 pub use mat4::Mat4;
+pub use point2::Point2;
+pub use point3::Point3;
 pub use quat::Quat;
 pub use vec2::Vec2;
 pub use vec3::Vec3;
 pub use vec4::Vec4;
+
+#[macro_export]
+macro_rules! impl_shared {
+    ($name:ty, $t:ty, $n:expr) => {
+        impl $name {
+            pub const ZERO: Self = Self::splat(0.0);
+            pub const ONE: Self = Self::splat(1.0);
+            pub const NAN: Self = Self::splat(0.0 / 0.0);
+
+            #[inline(always)]
+            pub const fn splat(value: $t) -> Self {
+                // we have to transmute here because we can't make `into()` const.
+                unsafe { std::mem::transmute([value; $n]) }
+            }
+
+            #[inline(always)]
+            pub fn ceil(self) -> Self {
+                self.map(|x| x.ceil())
+            }
+
+            #[inline(always)]
+            pub fn floor(self) -> Self {
+                self.map(|x| x.floor())
+            }
+
+            #[inline(always)]
+            pub fn round(self) -> Self {
+                self.map(|x| x.round())
+            }
+        }
+
+        impl From<[$t; $n]> for $name {
+            #[inline(always)]
+            fn from(x: [$t; $n]) -> Self {
+                unsafe { std::mem::transmute(x) }
+            }
+        }
+
+        impl From<$name> for [$t; $n] {
+            #[inline(always)]
+            fn from(x: $name) -> [$t; $n] {
+                unsafe { std::mem::transmute(x) }
+            }
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! impl_affine {
+    ($name:ty, $t:ty, $n:expr) => {
+        impl $name {
+            #[inline]
+            pub fn distance(a: Self, b: Self) -> $t {
+                (a - b).length()
+            }
+
+            #[inline]
+            pub fn distance_sq(a: Self, b: Self) -> $t {
+                (a - b).length_sq()
+            }
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! impl_vector {
+    ($name:ty, $t:ty, $n:expr) => {
+        impl $name {
+            #[inline]
+            pub fn length(self) -> $t {
+                self.length_sq().sqrt()
+            }
+
+            #[inline]
+            pub fn length_sq(self) -> $t {
+                Self::dot(self, self)
+            }
+        }
+    };
+}
diff --git a/narcissus-maths/src/mat2.rs b/narcissus-maths/src/mat2.rs
new file mode 100644 (file)
index 0000000..8b21e7b
--- /dev/null
@@ -0,0 +1,3 @@
+#[derive(Clone, Copy, PartialEq)]
+#[repr(C)]
+pub struct Mat2(pub [f32; 4]);
diff --git a/narcissus-maths/src/mat3.rs b/narcissus-maths/src/mat3.rs
new file mode 100644 (file)
index 0000000..93a772a
--- /dev/null
@@ -0,0 +1,3 @@
+#[derive(Clone, Copy, PartialEq)]
+#[repr(C)]
+pub struct Mat3(pub [f32; 9]);
index 9c7311b0dbb026ca6ca8283780b5b02f7d8791c1..074eb2acca549e35738ea7cf060b3d8096aa589c 100644 (file)
@@ -1,4 +1,4 @@
-use crate::{Vec3, Vec4};
+use crate::{Point2, Point3, Vec2, Vec3, Vec4};
 
 #[derive(Clone, Copy, PartialEq)]
 #[repr(C)]
@@ -6,35 +6,27 @@ pub struct Mat4(pub [f32; 16]);
 
 impl std::fmt::Debug for Mat4 {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.write_str("Mat4 [")?;
         if f.alternate() {
-            writeln!(f)?;
-            for row in self.as_rows_array() {
-                f.write_str("\t")?;
-                for value in row {
-                    f.write_fmt(format_args!("{value}, "))?;
-                }
-                f.write_str("\n")?;
+            writeln!(f, "Mat4 [")?;
+            for row in self.as_rows() {
+                writeln!(f, "\t{:?}", row)?;
             }
+            writeln!(f, "]")
         } else {
-            for value in &self.0[..15] {
-                f.write_fmt(format_args!("{value}, "))?;
-            }
-            f.write_fmt(format_args!("{}", self.0[15]))?;
+            writeln!(f, "Mat4 {:?}", self.as_rows())
         }
-        f.write_str("]")
     }
 }
 
 impl Mat4 {
-    pub const ZERO: Mat4 = Mat4::from_rows_array([
+    pub const ZERO: Mat4 = Mat4::from_rows([
         [0.0, 0.0, 0.0, 0.0],
         [0.0, 0.0, 0.0, 0.0],
         [0.0, 0.0, 0.0, 0.0],
         [0.0, 0.0, 0.0, 0.0],
     ]);
 
-    pub const IDENTITY: Mat4 = Mat4::from_rows_array([
+    pub const IDENTITY: Mat4 = Mat4::from_rows([
         [1.0, 0.0, 0.0, 0.0],
         [0.0, 1.0, 0.0, 0.0],
         [0.0, 0.0, 1.0, 0.0],
@@ -42,17 +34,17 @@ impl Mat4 {
     ]);
 
     #[inline(always)]
-    pub fn as_rows_array(&self) -> &[[f32; 4]; 4] {
+    pub fn as_rows(&self) -> &[[f32; 4]; 4] {
         unsafe { std::mem::transmute(&self.0) }
     }
 
     #[inline(always)]
-    pub fn as_rows_array_mut(&mut self) -> &mut [[f32; 4]; 4] {
+    pub fn as_rows_mut(&mut self) -> &mut [[f32; 4]; 4] {
         unsafe { std::mem::transmute(&mut self.0) }
     }
 
     #[inline(always)]
-    pub const fn from_rows_array(rows: [[f32; 4]; 4]) -> Self {
+    pub const fn from_rows(rows: [[f32; 4]; 4]) -> Self {
         unsafe { std::mem::transmute(rows) }
     }
 
@@ -85,8 +77,26 @@ impl Mat4 {
         result
     }
 
+    pub const fn from_diagonal(diagonal: Vec4) -> Mat4 {
+        Mat4::from_rows([
+            [diagonal.x, 0.0, 0.0, 0.0],
+            [0.0, diagonal.y, 0.0, 0.0],
+            [0.0, 0.0, diagonal.z, 0.0],
+            [0.0, 0.0, 0.0, diagonal.w],
+        ])
+    }
+
+    pub const fn from_rotation(scale: Vec3) -> Mat4 {
+        Mat4::from_rows([
+            [scale.x, 0.0, 0.0, 0.0],
+            [0.0, scale.y, 0.0, 0.0],
+            [0.0, 0.0, scale.z, 0.0],
+            [0.0, 0.0, 0.0, 1.0],
+        ])
+    }
+
     pub const fn from_scale(scale: Vec3) -> Mat4 {
-        Mat4::from_rows_array([
+        Mat4::from_rows([
             [scale.x, 0.0, 0.0, 0.0],
             [0.0, scale.y, 0.0, 0.0],
             [0.0, 0.0, scale.z, 0.0],
@@ -95,7 +105,7 @@ impl Mat4 {
     }
 
     pub const fn from_translation(translation: Vec3) -> Mat4 {
-        Mat4::from_rows_array([
+        Mat4::from_rows([
             [1.0, 0.0, 0.0, translation.x],
             [0.0, 1.0, 0.0, translation.y],
             [0.0, 0.0, 1.0, translation.z],
@@ -107,7 +117,7 @@ impl Mat4 {
     #[inline(always)]
     fn transpose_base(self) -> Mat4 {
         let m = &self.0;
-        Mat4::from_rows_array([
+        Mat4::from_rows([
             [m[0x0], m[0x4], m[0x8], m[0xc]],
             [m[0x1], m[0x5], m[0x9], m[0xd]],
             [m[0x2], m[0x6], m[0xa], m[0xe]],
@@ -137,22 +147,46 @@ impl Mat4 {
         }
     }
 
+    #[must_use]
+    #[inline]
+    pub fn mul_vec2(&self, vec: Vec2) -> Vec2 {
+        let vec = Vec4::new(vec.x, vec.y, 0.0, 0.0);
+        let vec = self.mul_vec4(vec);
+        Vec2::new(vec.x, vec.y)
+    }
+
+    #[must_use]
+    #[inline]
+    pub fn mul_point2(&self, point: Point2) -> Point2 {
+        let vec = Vec4::new(point.x, point.y, 0.0, 1.0);
+        let vec = self.mul_vec4(vec);
+        Point2::new(vec.x, vec.y)
+    }
+
     #[must_use]
     #[inline]
     pub fn mul_vec3(&self, vec: Vec3) -> Vec3 {
-        let vec = Vec4::new(vec.x, vec.y, vec.z, 1.0);
+        let vec = Vec4::new(vec.x, vec.y, vec.z, 0.0);
+        let vec = self.mul_vec4(vec);
+        [vec.x, vec.y, vec.z].into()
+    }
+
+    #[must_use]
+    #[inline]
+    pub fn mul_point3(&self, point: Point3) -> Point3 {
+        let vec = Vec4::new(point.x, point.y, point.z, 1.0);
         let vec = self.mul_vec4(vec);
-        Vec3::new(vec.x, vec.y, vec.z)
+        Point3::new(vec.x, vec.y, vec.z)
     }
 
     #[inline(always)]
     fn mul_vec4_base(&self, vec: Vec4) -> Vec4 {
-        let rows = self.as_rows_array();
+        let rows = self.as_rows();
         Vec4::new(
-            Vec4::dot(Vec4::from_array(rows[0]), vec),
-            Vec4::dot(Vec4::from_array(rows[1]), vec),
-            Vec4::dot(Vec4::from_array(rows[2]), vec),
-            Vec4::dot(Vec4::from_array(rows[3]), vec),
+            Vec4::dot(rows[0].into(), vec),
+            Vec4::dot(rows[1].into(), vec),
+            Vec4::dot(rows[2].into(), vec),
+            Vec4::dot(rows[3].into(), vec),
         )
     }
 
@@ -162,7 +196,7 @@ impl Mat4 {
     unsafe fn mul_vec4_sse41(&self, vec: Vec4) -> Vec4 {
         use std::arch::x86_64::{_mm_hadd_ps, _mm_mul_ps};
 
-        let vec = vec.as_m128();
+        let vec = vec.into();
         let rows = self.as_m128_array();
 
         let values = _mm_hadd_ps(
@@ -170,7 +204,7 @@ impl Mat4 {
             _mm_hadd_ps(_mm_mul_ps(rows[2], vec), _mm_mul_ps(rows[3], vec)),
         );
 
-        Vec4::from_m128(values)
+        values.into()
     }
 
     #[must_use]
@@ -192,9 +226,9 @@ impl Mat4 {
     fn mul_mat4_base(self: &Mat4, rhs: Mat4) -> Mat4 {
         let mut result = Mat4::IDENTITY;
         {
-            let result = result.as_rows_array_mut();
-            let lhs = self.as_rows_array();
-            let rhs = rhs.as_rows_array();
+            let result = result.as_rows_mut();
+            let lhs = self.as_rows();
+            let rhs = rhs.as_rows();
             for i in 0..4 {
                 for j in 0..4 {
                     result[i][j] = lhs[i][0] * rhs[0][j]
@@ -244,19 +278,19 @@ impl Mat4 {
         };
 
         #[inline(always)]
-        unsafe fn two_linear_combine(a: __m256, mat: &[__m128; 4]) -> __m256 {
-            let r = _mm256_mul_ps(_mm256_shuffle_ps(a, a, 0x00), _mm256_broadcast_ps(&mat[0]));
+        unsafe fn two_linear_combine(a: __m256, m: &[__m128; 4]) -> __m256 {
+            let r = _mm256_mul_ps(_mm256_shuffle_ps(a, a, 0x00), _mm256_broadcast_ps(&m[0]));
             let r = _mm256_add_ps(
                 r,
-                _mm256_mul_ps(_mm256_shuffle_ps(a, a, 0x55), _mm256_broadcast_ps(&mat[1])),
+                _mm256_mul_ps(_mm256_shuffle_ps(a, a, 0x55), _mm256_broadcast_ps(&m[1])),
             );
             let r = _mm256_add_ps(
                 r,
-                _mm256_mul_ps(_mm256_shuffle_ps(a, a, 0xaa), _mm256_broadcast_ps(&mat[2])),
+                _mm256_mul_ps(_mm256_shuffle_ps(a, a, 0xaa), _mm256_broadcast_ps(&m[2])),
             );
             _mm256_add_ps(
                 r,
-                _mm256_mul_ps(_mm256_shuffle_ps(a, a, 0xff), _mm256_broadcast_ps(&mat[3])),
+                _mm256_mul_ps(_mm256_shuffle_ps(a, a, 0xff), _mm256_broadcast_ps(&m[3])),
             )
         }
 
@@ -321,6 +355,33 @@ impl std::ops::Mul<Vec3> for Mat4 {
     }
 }
 
+impl std::ops::Mul<Point3> for Mat4 {
+    type Output = Point3;
+
+    #[inline(always)]
+    fn mul(self, rhs: Point3) -> Self::Output {
+        self.mul_point3(rhs)
+    }
+}
+
+impl std::ops::Mul<Vec2> for Mat4 {
+    type Output = Vec2;
+
+    #[inline(always)]
+    fn mul(self, rhs: Vec2) -> Self::Output {
+        self.mul_vec2(rhs)
+    }
+}
+
+impl std::ops::Mul<Point2> for Mat4 {
+    type Output = Point2;
+
+    #[inline(always)]
+    fn mul(self, rhs: Point2) -> Self::Output {
+        self.mul_point2(rhs)
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -328,20 +389,24 @@ mod tests {
     const IDENTITY: Mat4 = Mat4::IDENTITY;
     const SCALE: Mat4 = Mat4::from_scale(Vec3::splat(2.0));
     const TRANSLATE: Mat4 = Mat4::from_translation(Vec3::new(1.0, 2.0, 3.0));
-    const M: Mat4 = Mat4::from_rows_array([
+    const M: Mat4 = Mat4::from_rows([
         [1.0, 2.0, 3.0, 4.0],
         [5.0, 6.0, 7.0, 8.0],
         [9.0, 10.0, 11.0, 12.0],
         [13.0, 14.0, 15.0, 16.0],
     ]);
-    const T: Mat4 = Mat4::from_rows_array([
+    const T: Mat4 = Mat4::from_rows([
         [1.0, 5.0, 9.0, 13.0],
         [2.0, 6.0, 10.0, 14.0],
         [3.0, 7.0, 11.0, 15.0],
         [4.0, 8.0, 12.0, 16.0],
     ]);
+
+    const V2: Vec2 = Vec2::new(1.0, 2.0);
     const V3: Vec3 = Vec3::new(1.0, 2.0, 3.0);
-    const V: Vec4 = Vec4::new(1.0, 2.0, 3.0, 4.0);
+    const V4: Vec4 = Vec4::new(1.0, 2.0, 3.0, 4.0);
+    const P2: Point2 = Point2::new(1.0, 2.0);
+    const P3: Point3 = Point3::new(1.0, 2.0, 3.0);
 
     #[test]
     fn transpose() {
@@ -373,19 +438,46 @@ mod tests {
         }
     }
 
+    #[test]
+    fn mul_vec2() {
+        assert_eq!(IDENTITY * Vec2::ZERO, Vec2::ZERO);
+        assert_eq!(IDENTITY * V2, V2);
+        assert_eq!(SCALE * Vec2::ZERO, Vec2::ZERO);
+        assert_eq!(SCALE * Vec2::ONE, Vec2::splat(2.0));
+        assert_eq!(TRANSLATE * Vec2::ZERO, Vec2::ZERO);
+    }
+
+    #[test]
+    fn mul_point2() {
+        assert_eq!(IDENTITY * Point2::ZERO, Point2::ZERO);
+        assert_eq!(IDENTITY * P2, P2);
+        assert_eq!(SCALE * Point2::ZERO, Point2::ZERO);
+        assert_eq!(SCALE * Point2::ONE, Point2::splat(2.0));
+        assert_eq!(TRANSLATE * Point2::ZERO, P2);
+    }
+
     #[test]
     fn mul_vec3() {
         assert_eq!(IDENTITY * Vec3::ZERO, Vec3::ZERO);
         assert_eq!(IDENTITY * V3, V3);
         assert_eq!(SCALE * Vec3::ZERO, Vec3::ZERO);
         assert_eq!(SCALE * Vec3::ONE, Vec3::splat(2.0));
-        assert_eq!(TRANSLATE * Vec3::ZERO, V3);
+        assert_eq!(TRANSLATE * Vec3::ZERO, Vec3::ZERO);
+    }
+
+    #[test]
+    fn mul_point3() {
+        assert_eq!(IDENTITY * Point3::ZERO, Point3::ZERO);
+        assert_eq!(IDENTITY * P3, P3);
+        assert_eq!(SCALE * Point3::ZERO, Point3::ZERO);
+        assert_eq!(SCALE * Point3::ONE, Point3::splat(2.0));
+        assert_eq!(TRANSLATE * Point3::ZERO, P3);
     }
 
     #[test]
     fn mul_vec4() {
         assert_eq!(IDENTITY * Vec4::ZERO, Vec4::ZERO);
-        assert_eq!(IDENTITY * V, V);
+        assert_eq!(IDENTITY * V4, V4);
         assert_eq!(SCALE * Vec4::ZERO, Vec4::ZERO);
         assert_eq!(SCALE * Vec4::ONE, Vec4::new(2.0, 2.0, 2.0, 1.0));
         assert_eq!(
@@ -396,7 +488,7 @@ mod tests {
         if std::is_x86_feature_detected!("sse4.1") {
             unsafe {
                 assert_eq!(IDENTITY.mul_vec4_sse41(Vec4::ZERO), Vec4::ZERO);
-                assert_eq!(IDENTITY.mul_vec4_sse41(V), V);
+                assert_eq!(IDENTITY.mul_vec4_sse41(V4), V4);
                 assert_eq!(SCALE.mul_vec4_sse41(Vec4::ZERO), Vec4::ZERO);
                 assert_eq!(
                     SCALE.mul_vec4_sse41(Vec4::ONE),
diff --git a/narcissus-maths/src/point2.rs b/narcissus-maths/src/point2.rs
new file mode 100644 (file)
index 0000000..109d197
--- /dev/null
@@ -0,0 +1,84 @@
+use crate::{impl_affine, impl_shared, Vec2};
+
+/// Type representing a point in a 2d affine space.
+#[derive(Clone, Copy, PartialEq, PartialOrd, Default, Debug)]
+#[repr(C)]
+pub struct Point2 {
+    pub x: f32,
+    pub y: f32,
+}
+
+impl_shared!(Point2, f32, 2);
+impl_affine!(Point2, f32, 2);
+
+impl Point2 {
+    pub const X: Self = Self::new(1.0, 0.0);
+    pub const Y: Self = Self::new(0.0, 1.0);
+
+    /// Creates a new `Point2` with the given `x` and `y` coordinates.
+    #[inline(always)]
+    pub const fn new(x: f32, y: f32) -> Self {
+        Self { x, y }
+    }
+
+    /// Returns a new `Point2` with the function `f` applied to each coordinate in order.
+    #[inline(always)]
+    pub fn map<F>(self, mut f: F) -> Self
+    where
+        F: FnMut(f32) -> f32,
+    {
+        Self {
+            x: f(self.x),
+            y: f(self.y),
+        }
+    }
+}
+
+impl std::ops::Sub for Point2 {
+    type Output = Vec2;
+    #[inline]
+    fn sub(self, rhs: Self) -> Self::Output {
+        Self::Output {
+            x: self.x - rhs.x,
+            y: self.y - rhs.y,
+        }
+    }
+}
+
+impl std::ops::Add<Vec2> for Point2 {
+    type Output = Point2;
+    #[inline]
+    fn add(self, rhs: Vec2) -> Self::Output {
+        Self::Output {
+            x: self.x + rhs.x,
+            y: self.y + rhs.y,
+        }
+    }
+}
+
+impl std::ops::Sub<Vec2> for Point2 {
+    type Output = Point2;
+    #[inline]
+    fn sub(self, rhs: Vec2) -> Self::Output {
+        Self::Output {
+            x: self.x - rhs.x,
+            y: self.y - rhs.y,
+        }
+    }
+}
+
+impl std::ops::AddAssign<Vec2> for Point2 {
+    #[inline]
+    fn add_assign(&mut self, rhs: Vec2) {
+        self.x += rhs.x;
+        self.y += rhs.y;
+    }
+}
+
+impl std::ops::SubAssign<Vec2> for Point2 {
+    #[inline]
+    fn sub_assign(&mut self, rhs: Vec2) {
+        self.x -= rhs.x;
+        self.y -= rhs.y;
+    }
+}
diff --git a/narcissus-maths/src/point3.rs b/narcissus-maths/src/point3.rs
new file mode 100644 (file)
index 0000000..721e6c0
--- /dev/null
@@ -0,0 +1,109 @@
+use crate::{impl_affine, impl_shared, Vec3};
+
+/// Type representing a point in a 3d affine space.
+#[derive(Clone, Copy, PartialEq, PartialOrd, Default, Debug)]
+#[repr(C)]
+pub struct Point3 {
+    pub x: f32,
+    pub y: f32,
+    pub z: f32,
+}
+
+impl_shared!(Point3, f32, 3);
+impl_affine!(Point3, f32, 3);
+
+impl Point3 {
+    pub const X: Self = Self::new(1.0, 0.0, 0.0);
+    pub const Y: Self = Self::new(0.0, 1.0, 0.0);
+    pub const Z: Self = Self::new(0.0, 0.0, 1.0);
+
+    /// Creates a new point in 3d space with the given `x`, `y` and `z` coordinates.
+    #[inline(always)]
+    pub const fn new(x: f32, y: f32, z: f32) -> Self {
+        Self { x, y, z }
+    }
+
+    /// Returns a new point in 3d space with the function `f` applied to each coordinate in order.
+    #[inline(always)]
+    pub fn map<F>(self, mut f: F) -> Self
+    where
+        F: FnMut(f32) -> f32,
+    {
+        Self {
+            x: f(self.x),
+            y: f(self.y),
+            z: f(self.z),
+        }
+    }
+}
+
+impl std::ops::Sub for Point3 {
+    type Output = Vec3;
+    #[inline]
+    fn sub(self, rhs: Self) -> Self::Output {
+        Self::Output {
+            x: self.x - rhs.x,
+            y: self.y - rhs.y,
+            z: self.z - rhs.z,
+        }
+    }
+}
+
+impl std::ops::Add<Vec3> for Point3 {
+    type Output = Point3;
+    #[inline]
+    fn add(self, rhs: Vec3) -> Self::Output {
+        Self::Output {
+            x: self.x + rhs.x,
+            y: self.y + rhs.y,
+            z: self.z + rhs.z,
+        }
+    }
+}
+
+impl std::ops::Sub<Vec3> for Point3 {
+    type Output = Point3;
+    #[inline]
+    fn sub(self, rhs: Vec3) -> Self::Output {
+        Self::Output {
+            x: self.x - rhs.x,
+            y: self.y - rhs.y,
+            z: self.z - rhs.z,
+        }
+    }
+}
+
+impl std::ops::AddAssign<Vec3> for Point3 {
+    #[inline]
+    fn add_assign(&mut self, rhs: Vec3) {
+        self.x += rhs.x;
+        self.y += rhs.y;
+        self.z += rhs.z;
+    }
+}
+
+impl std::ops::SubAssign<Vec3> for Point3 {
+    #[inline]
+    fn sub_assign(&mut self, rhs: Vec3) {
+        self.x -= rhs.x;
+        self.y -= rhs.y;
+        self.z -= rhs.z;
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    #[test]
+    pub fn distance() {
+        // nice triangle
+        assert_eq!(
+            Point3::distance_sq(Point3::ZERO, Point3::new(5.0, 12.0, 0.0)),
+            169.0
+        );
+        assert_eq!(
+            Point3::distance(Point3::ZERO, Point3::new(5.0, 12.0, 0.0)),
+            13.0
+        );
+    }
+}
index a7d2b9cd6bd629ed81c10178fcfaa07f557873fa..df374dc1eed09e9667a49d9703d253e482d333b0 100644 (file)
@@ -6,3 +6,26 @@ pub struct Quat {
     pub c: f32,
     pub d: f32,
 }
+
+impl Quat {
+    pub const ZERO: Self = Self {
+        a: 0.0,
+        b: 0.0,
+        c: 0.0,
+        d: 0.0,
+    };
+
+    pub const IDENTITY: Self = Self {
+        a: 0.0,
+        b: 0.0,
+        c: 0.0,
+        d: 1.0,
+    };
+
+    pub const NAN: Self = Self {
+        a: std::f32::NAN,
+        b: std::f32::NAN,
+        c: std::f32::NAN,
+        d: std::f32::NAN,
+    };
+}
index 3828a61776e5fe346479dd7ba5527488914ddaa6..8056dbebc4f245347f77db831af56554058c29e9 100644 (file)
@@ -1,3 +1,5 @@
+use crate::{impl_shared, impl_vector};
+
 #[derive(Clone, Copy, PartialEq, PartialOrd, Default, Debug)]
 #[repr(C)]
 pub struct Vec2 {
@@ -5,80 +7,34 @@ pub struct Vec2 {
     pub y: f32,
 }
 
-impl Vec2 {
-    pub const ZERO: Self = Self::splat(0.0);
-    pub const ONE: Self = Self::splat(1.0);
+impl_shared!(Vec2, f32, 2);
+impl_vector!(Vec2, f32, 2);
 
+impl Vec2 {
     pub const X: Self = Self::new(1.0, 0.0);
     pub const Y: Self = Self::new(0.0, 1.0);
 
+    /// Creates a new 2d vector with the given `x` and `y` components.
     #[inline(always)]
     pub const fn new(x: f32, y: f32) -> Self {
         Self { x, y }
     }
 
+    /// Returns a new 2d vector with the function `f` applied to each component in order.
     #[inline(always)]
-    pub const fn splat(value: f32) -> Self {
-        Self { x: value, y: value }
-    }
-
-    #[inline(always)]
-    pub fn as_array(self) -> [f32; 2] {
-        unsafe { std::mem::transmute(self) }
-    }
-
-    #[inline(always)]
-    pub fn from_array(values: [f32; 2]) -> Self {
-        unsafe { std::mem::transmute(values) }
-    }
-
-    #[inline]
-    pub fn distance(a: Self, b: Self) -> f32 {
-        (a - b).length()
-    }
-
-    #[inline]
-    pub fn distance_sq(a: Self, b: Self) -> f32 {
-        (a - b).length_sq()
-    }
-
-    #[inline]
-    pub fn dot(a: Self, b: Self) -> f32 {
-        a.x * b.x + a.y * b.y
-    }
-
-    #[inline]
-    pub fn length(self) -> f32 {
-        self.length_sq().sqrt()
-    }
-
-    #[inline]
-    pub fn length_sq(self) -> f32 {
-        Self::dot(self, self)
-    }
-
-    #[inline]
-    pub fn ceil(self) -> Self {
+    pub fn map<F>(self, mut f: F) -> Self
+    where
+        F: FnMut(f32) -> f32,
+    {
         Self {
-            x: self.x.ceil(),
-            y: self.y.ceil(),
+            x: f(self.x),
+            y: f(self.y),
         }
     }
 
     #[inline]
-    pub fn floor(self) -> Self {
-        Self {
-            x: self.x.floor(),
-            y: self.y.floor(),
-        }
-    }
-
-    #[inline]
-    pub fn round(self) -> Self {
-        Self {
-            x: self.x.round(),
-            y: self.y.round(),
-        }
+    pub fn dot(a: Self, b: Self) -> f32 {
+        a.x * b.x + a.y * b.y
     }
 }
 
index dc5fb604ee3860113951039d433a019e1d10c16f..c5a3665f31b1e51b6298d65ffbd70bb6f67ef729 100644 (file)
@@ -1,3 +1,5 @@
+use crate::{impl_shared, impl_vector};
+
 #[derive(Clone, Copy, PartialEq, PartialOrd, Default, Debug)]
 #[repr(C)]
 pub struct Vec3 {
@@ -6,88 +8,42 @@ pub struct Vec3 {
     pub z: f32,
 }
 
-impl Vec3 {
-    pub const ZERO: Self = Self::splat(0.0);
-    pub const ONE: Self = Self::splat(1.0);
-
-    pub const X: Self = Self::new(1.0, 0.0, 0.0);
-    pub const Y: Self = Self::new(0.0, 1.0, 0.0);
-    pub const Z: Self = Self::new(0.0, 0.0, 1.0);
+impl_shared!(Vec3, f32, 3);
+impl_vector!(Vec3, f32, 3);
 
+impl Vec3 {
+    /// Creates a new 3d vector with the given `x`, `y` and `z` components.
     #[inline(always)]
     pub const fn new(x: f32, y: f32, z: f32) -> Self {
         Self { x, y, z }
     }
 
+    /// Returns a new 3d vector with the function `f` applied to each component in order.
     #[inline(always)]
-    pub const fn splat(value: f32) -> Self {
+    pub fn map<F>(self, mut f: F) -> Self
+    where
+        F: FnMut(f32) -> f32,
+    {
         Self {
-            x: value,
-            y: value,
-            z: value,
+            x: f(self.x),
+            y: f(self.y),
+            z: f(self.z),
         }
     }
 
-    #[inline(always)]
-    pub fn as_array(self) -> [f32; 3] {
-        unsafe { std::mem::transmute(self) }
-    }
-
-    #[inline(always)]
-    pub fn from_array(values: [f32; 3]) -> Self {
-        unsafe { std::mem::transmute(values) }
-    }
-
-    #[inline]
-    pub fn distance(a: Self, b: Self) -> f32 {
-        (a - b).length()
-    }
-
-    #[inline]
-    pub fn distance_sq(a: Self, b: Self) -> f32 {
-        (a - b).length_sq()
-    }
-
     #[inline]
     pub fn dot(a: Self, b: Self) -> f32 {
         a.x * b.x + a.y * b.y + a.z * b.z
     }
 
     #[inline]
-    pub fn length(self) -> f32 {
-        self.length_sq().sqrt()
-    }
-
-    #[inline]
-    pub fn length_sq(self) -> f32 {
-        Self::dot(self, self)
-    }
-
-    #[inline]
-    pub fn ceil(self) -> Self {
-        Self {
-            x: self.x.ceil(),
-            y: self.y.ceil(),
-            z: self.z.ceil(),
-        }
-    }
-
-    #[inline]
-    pub fn floor(self) -> Self {
-        Self {
-            x: self.x.floor(),
-            y: self.y.floor(),
-            z: self.z.floor(),
-        }
-    }
-
-    #[inline]
-    pub fn round(self) -> Self {
-        Self {
-            x: self.x.round(),
-            y: self.y.round(),
-            z: self.z.round(),
-        }
+    pub fn cross(a: Self, b: Self) -> Vec3 {
+        [
+            a.y * b.z - a.z * b.y,
+            -(a.x * b.z - a.z * b.x),
+            a.x * b.y - a.y * b.x,
+        ]
+        .into()
     }
 }
 
index 4953317650ecc992dc54ce4d53398691b8115b65..f0f23921ee1b94f2c68e1763c147d2fcc5a5a13b 100644 (file)
@@ -1,3 +1,5 @@
+use crate::{impl_shared, impl_vector};
+
 #[derive(Clone, Copy, PartialEq, PartialOrd, Default, Debug)]
 #[repr(C)]
 pub struct Vec4 {
@@ -7,111 +9,75 @@ pub struct Vec4 {
     pub w: f32,
 }
 
-impl Vec4 {
-    pub const ZERO: Self = Self::splat(0.0);
-    pub const ONE: Self = Self::splat(1.0);
+impl_shared!(Vec4, f32, 4);
+impl_vector!(Vec4, f32, 4);
 
+impl Vec4 {
     pub const X: Self = Self::new(1.0, 0.0, 0.0, 0.0);
     pub const Y: Self = Self::new(0.0, 1.0, 0.0, 0.0);
     pub const Z: Self = Self::new(0.0, 0.0, 1.0, 0.0);
     pub const W: Self = Self::new(0.0, 0.0, 0.0, 1.0);
 
+    /// Creates a new 4d vector with the given `x`, `y`, `z` and `w` components.
     #[inline(always)]
     pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
         Self { x, y, z, w }
     }
 
+    /// Returns a new 4d vector with the function `f` applied to each component in order.
     #[inline(always)]
-    pub const fn splat(value: f32) -> Self {
+    pub fn map<F>(self, mut f: F) -> Self
+    where
+        F: FnMut(f32) -> f32,
+    {
         Self {
-            x: value,
-            y: value,
-            z: value,
-            w: value,
+            x: f(self.x),
+            y: f(self.y),
+            z: f(self.z),
+            w: f(self.w),
         }
     }
 
-    #[inline(always)]
-    pub fn as_array(self) -> [f32; 4] {
-        unsafe { std::mem::transmute(self) }
-    }
-
-    #[inline(always)]
-    pub fn from_array(values: [f32; 4]) -> Self {
-        unsafe { std::mem::transmute(values) }
-    }
-
-    #[cfg(target_feature = "sse2")]
-    #[inline(always)]
-    pub(crate) fn as_m128(self) -> std::arch::x86_64::__m128 {
-        unsafe { std::arch::x86_64::_mm_loadu_ps(&self.x) }
+    #[inline]
+    pub fn dot(a: Self, b: Self) -> f32 {
+        a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w
     }
+}
 
-    #[cfg(target_feature = "sse2")]
+#[cfg(target_feature = "sse2")]
+impl From<std::arch::x86_64::__m128> for Vec4 {
     #[inline(always)]
-    pub(crate) fn from_m128(values: std::arch::x86_64::__m128) -> Self {
+    fn from(values: std::arch::x86_64::__m128) -> Self {
         use std::arch::x86_64::_mm_storeu_ps;
         let mut result = Vec4::ZERO;
         unsafe { _mm_storeu_ps(&mut result.x, values) }
         result
     }
+}
 
-    #[inline]
-    pub fn distance(a: Self, b: Self) -> f32 {
-        (a - b).length()
-    }
-
-    #[inline]
-    pub fn distance_sq(a: Self, b: Self) -> f32 {
-        (a - b).length_sq()
-    }
-
-    #[inline]
-    pub fn dot(a: Self, b: Self) -> f32 {
-        a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w
-    }
-
-    #[inline]
-    pub fn length(self) -> f32 {
-        self.length_sq().sqrt()
-    }
-
-    #[inline]
-    pub fn length_sq(self) -> f32 {
-        Self::dot(self, self)
-    }
-
-    #[inline]
-    pub fn ceil(self) -> Self {
-        Self {
-            x: self.x.ceil(),
-            y: self.y.ceil(),
-            z: self.z.ceil(),
-            w: self.w.ceil(),
-        }
-    }
-
-    #[inline]
-    pub fn floor(self) -> Self {
-        Self {
-            x: self.x.floor(),
-            y: self.y.floor(),
-            z: self.z.floor(),
-            w: self.w.floor(),
-        }
-    }
-
-    #[inline]
-    pub fn round(self) -> Self {
-        Self {
-            x: self.x.round(),
-            y: self.y.round(),
-            z: self.z.round(),
-            w: self.w.round(),
-        }
+#[cfg(target_feature = "sse2")]
+impl From<Vec4> for std::arch::x86_64::__m128 {
+    #[inline(always)]
+    fn from(x: Vec4) -> Self {
+        unsafe { std::arch::x86_64::_mm_loadu_ps(&x.x) }
     }
 }
 
+//
+// #[inline(always)]
+// pub(crate) fn as_m128(self) -> std::arch::x86_64::__m128 {
+//
+// }
+
+// #[cfg(target_feature = "sse2")]
+// #[inline(always)]
+// pub(crate) fn from_m128(values: std::arch::x86_64::__m128) -> Self {
+//     use std::arch::x86_64::_mm_storeu_ps;
+//     let mut result = Vec4::ZERO;
+//     unsafe { _mm_storeu_ps(&mut result.x, values) }
+//     result
+// }
+
 impl std::ops::Add for Vec4 {
     type Output = Vec4;
 
@@ -131,7 +97,7 @@ impl std::ops::Add for Vec4 {
     fn add(self, rhs: Self) -> Self::Output {
         unsafe {
             use std::arch::x86_64::_mm_add_ps;
-            Vec4::from_m128(_mm_add_ps(self.as_m128(), rhs.as_m128()))
+            _mm_add_ps(self.into(), rhs.into()).into()
         }
     }
 }
@@ -153,10 +119,7 @@ impl std::ops::Sub for Vec4 {
     #[cfg(target_feature = "sse2")]
     #[inline(always)]
     fn sub(self, rhs: Self) -> Self::Output {
-        unsafe {
-            use std::arch::x86_64::_mm_sub_ps;
-            Vec4::from_m128(_mm_sub_ps(self.as_m128(), rhs.as_m128()))
-        }
+        unsafe { std::arch::x86_64::_mm_sub_ps(self.into(), rhs.into()).into() }
     }
 }
 
@@ -177,10 +140,7 @@ impl std::ops::Mul for Vec4 {
     #[cfg(target_feature = "sse2")]
     #[inline(always)]
     fn mul(self, rhs: Self) -> Self::Output {
-        unsafe {
-            use std::arch::x86_64::_mm_mul_ps;
-            Vec4::from_m128(_mm_mul_ps(self.as_m128(), rhs.as_m128()))
-        }
+        unsafe { std::arch::x86_64::_mm_mul_ps(self.into(), rhs.into()).into() }
     }
 }
 
@@ -201,87 +161,35 @@ impl std::ops::Div for Vec4 {
     #[cfg(target_feature = "sse2")]
     #[inline(always)]
     fn div(self, rhs: Self) -> Self::Output {
-        unsafe {
-            use std::arch::x86_64::_mm_div_ps;
-            Vec4::from_m128(_mm_div_ps(self.as_m128(), rhs.as_m128()))
-        }
+        unsafe { std::arch::x86_64::_mm_div_ps(self.into(), rhs.into()).into() }
     }
 }
 
 impl std::ops::AddAssign for Vec4 {
-    #[cfg(not(target_feature = "sse2"))]
     #[inline(always)]
     fn add_assign(&mut self, rhs: Self) {
-        self.x += rhs.x;
-        self.y += rhs.y;
-        self.z += rhs.z;
-        self.w += rhs.w;
-    }
-
-    #[cfg(target_feature = "sse2")]
-    #[inline(always)]
-    fn add_assign(&mut self, rhs: Self) {
-        use std::arch::x86_64::_mm_add_ps;
-        unsafe {
-            *self = Vec4::from_m128(_mm_add_ps(self.as_m128(), rhs.as_m128()));
-        }
+        *self = *self + rhs;
     }
 }
 
 impl std::ops::SubAssign for Vec4 {
-    #[cfg(not(target_feature = "sse2"))]
-    #[inline(always)]
-    fn sub_assign(&mut self, rhs: Self) {
-        self.x -= rhs.x;
-        self.y -= rhs.y;
-        self.z -= rhs.z;
-        self.w -= rhs.w;
-    }
-
-    #[cfg(target_feature = "sse2")]
     #[inline(always)]
     fn sub_assign(&mut self, rhs: Self) {
-        unsafe {
-            *self = Vec4::from_m128(std::arch::x86_64::_mm_sub_ps(self.as_m128(), rhs.as_m128()));
-        }
+        *self = *self - rhs;
     }
 }
 
 impl std::ops::MulAssign for Vec4 {
-    #[cfg(not(target_feature = "sse2"))]
-    #[inline(always)]
-    fn mul_assign(&mut self, rhs: Self) {
-        self.x *= rhs.x;
-        self.y *= rhs.y;
-        self.z *= rhs.z;
-        self.w *= rhs.w;
-    }
-
-    #[cfg(target_feature = "sse2")]
     #[inline(always)]
     fn mul_assign(&mut self, rhs: Self) {
-        unsafe {
-            *self = Vec4::from_m128(std::arch::x86_64::_mm_mul_ps(self.as_m128(), rhs.as_m128()));
-        }
+        *self = *self * rhs;
     }
 }
 
 impl std::ops::DivAssign for Vec4 {
-    #[cfg(not(target_feature = "sse2"))]
     #[inline(always)]
     fn div_assign(&mut self, rhs: Self) {
-        self.x /= rhs.x;
-        self.y /= rhs.y;
-        self.z /= rhs.z;
-        self.w /= rhs.w;
-    }
-
-    #[cfg(target_feature = "sse2")]
-    #[inline(always)]
-    fn div_assign(&mut self, rhs: Self) {
-        unsafe {
-            *self = Vec4::from_m128(std::arch::x86_64::_mm_div_ps(self.as_m128(), rhs.as_m128()));
-        }
+        *self = *self / rhs;
     }
 }
 
@@ -310,8 +218,5 @@ mod tests {
 
         assert_eq!(Vec4::new(2.0, 2.0, 2.0, 2.0).length_sq(), 16.0);
         assert_eq!(Vec4::new(2.0, 2.0, 2.0, 2.0).length(), 4.0);
-
-        assert_eq!(Vec4::distance_sq(Vec4::splat(-1.0), Vec4::splat(1.0)), 16.0);
-        assert_eq!(Vec4::distance(Vec4::splat(-1.0), Vec4::splat(1.0)), 4.0);
     }
 }