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`.
--- /dev/null
+use crate::{Mat2, Vec2};
+
+#[derive(Clone, Copy, PartialEq)]
+#[repr(C)]
+pub struct Affine2 {
+ matrix: Mat2,
+ translate: Vec2,
+}
--- /dev/null
+use crate::{Mat3, Vec3};
+
+#[derive(Clone, Copy, PartialEq)]
+#[repr(C)]
+pub struct Affine3 {
+ matrix: Mat3,
+ translate: Vec3,
+}
+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)
+ }
+ }
+ };
+}
--- /dev/null
+#[derive(Clone, Copy, PartialEq)]
+#[repr(C)]
+pub struct Mat2(pub [f32; 4]);
--- /dev/null
+#[derive(Clone, Copy, PartialEq)]
+#[repr(C)]
+pub struct Mat3(pub [f32; 9]);
-use crate::{Vec3, Vec4};
+use crate::{Point2, Point3, Vec2, Vec3, Vec4};
#[derive(Clone, Copy, PartialEq)]
#[repr(C)]
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],
]);
#[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) }
}
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],
}
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],
#[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]],
}
}
+ #[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),
)
}
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(
_mm_hadd_ps(_mm_mul_ps(rows[2], vec), _mm_mul_ps(rows[3], vec)),
);
- Vec4::from_m128(values)
+ values.into()
}
#[must_use]
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]
};
#[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])),
)
}
}
}
+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::*;
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() {
}
}
+ #[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!(
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),
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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
+ );
+ }
+}
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,
+ };
+}
+use crate::{impl_shared, impl_vector};
+
#[derive(Clone, Copy, PartialEq, PartialOrd, Default, Debug)]
#[repr(C)]
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
}
}
+use crate::{impl_shared, impl_vector};
+
#[derive(Clone, Copy, PartialEq, PartialOrd, Default, Debug)]
#[repr(C)]
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()
}
}
+use crate::{impl_shared, impl_vector};
+
#[derive(Clone, Copy, PartialEq, PartialOrd, Default, Debug)]
#[repr(C)]
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;
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()
}
}
}
#[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() }
}
}
#[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() }
}
}
#[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;
}
}
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);
}
}