use std::ops;
#[cfg(test)]
mod test {
use super::*;
impl approx::AbsDiffEq for Vector4 {
type Epsilon = f32;
fn default_epsilon() -> f32 {
f32::default_epsilon()
}
fn abs_diff_eq(&self, other: &Vector4, epsilon: Self::Epsilon) -> bool {
f32::abs_diff_eq(&self.x, &other.x, epsilon)
&& f32::abs_diff_eq(&self.y, &other.y, epsilon)
&& f32::abs_diff_eq(&self.z, &other.z, epsilon)
&& f32::abs_diff_eq(&self.w, &other.w, epsilon)
}
}
impl approx::RelativeEq for Vector4 {
fn default_max_relative() -> f32 {
f32::default_max_relative()
}
fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
f32::relative_eq(&self.x, &other.x, epsilon, max_relative)
&& f32::relative_eq(&self.y, &other.y, epsilon, max_relative)
&& f32::relative_eq(&self.z, &other.z, epsilon, max_relative)
&& f32::relative_eq(&self.w, &other.w, epsilon, max_relative)
}
}
impl approx::UlpsEq for Vector4 {
fn default_max_ulps() -> u32 {
f32::default_max_ulps()
}
fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool {
f32::ulps_eq(&self.x, &other.x, epsilon, max_ulps)
&& f32::ulps_eq(&self.y, &other.y, epsilon, max_ulps)
&& f32::ulps_eq(&self.z, &other.z, epsilon, max_ulps)
&& f32::ulps_eq(&self.w, &other.w, epsilon, max_ulps)
}
}
impl approx::AbsDiffEq for Matrix4 {
type Epsilon = f32;
fn default_epsilon() -> f32 {
f32::default_epsilon()
}
fn abs_diff_eq(&self, other: &Matrix4, epsilon: Self::Epsilon) -> bool {
Vector4::abs_diff_eq(&self.x, &other.x, epsilon)
&& Vector4::abs_diff_eq(&self.y, &other.y, epsilon)
&& Vector4::abs_diff_eq(&self.z, &other.z, epsilon)
&& Vector4::abs_diff_eq(&self.w, &other.w, epsilon)
}
}
impl approx::RelativeEq for Matrix4 {
fn default_max_relative() -> f32 {
f32::default_max_relative()
}
fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
Vector4::relative_eq(&self.x, &other.x, epsilon, max_relative)
&& Vector4::relative_eq(&self.y, &other.y, epsilon, max_relative)
&& Vector4::relative_eq(&self.z, &other.z, epsilon, max_relative)
&& Vector4::relative_eq(&self.w, &other.w, epsilon, max_relative)
}
}
impl approx::UlpsEq for Matrix4 {
fn default_max_ulps() -> u32 {
f32::default_max_ulps()
}
fn ulps_eq(&self, other: &Self, epsilon: f32, max_ulps: u32) -> bool {
Vector4::ulps_eq(&self.x, &other.x, epsilon, max_ulps)
&& Vector4::ulps_eq(&self.y, &other.y, epsilon, max_ulps)
&& Vector4::ulps_eq(&self.z, &other.z, epsilon, max_ulps)
&& Vector4::ulps_eq(&self.w, &other.w, epsilon, max_ulps)
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(C)]
pub struct Vector3 {
pub x: f32,
pub y: f32,
pub z: f32,
}
impl Vector3 {
pub fn new(x: f32, y: f32, z: f32) -> Self {
Vector3 { x, y, z }
}
pub fn magnitude(&self) -> f32 {
(self.x * self.x + self.y * self.y + self.z * self.z).sqrt()
}
pub fn multiply(&mut self, s: f32) {
self.x *= s;
self.y *= s;
self.z *= s;
}
#[cfg(test)]
pub fn normalize(self) -> Vector3 {
self * (1.0 / self.magnitude())
}
}
impl ops::Mul<f32> for Vector3 {
type Output = Vector3;
fn mul(mut self, rhs: f32) -> Self::Output {
self.multiply(rhs);
self
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(C)]
pub struct Vector4 {
pub x: f32,
pub y: f32,
pub z: f32,
pub w: f32,
}
impl Vector4 {
pub fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
Vector4 { x, y, z, w }
}
pub fn multiply(&mut self, s: f32) {
self.x *= s;
self.y *= s;
self.z *= s;
self.w *= s;
}
pub fn as_array(&self) -> [f32; 4] {
[self.x, self.y, self.z, self.w]
}
#[cfg(test)]
pub fn from_array([x, y, z, w]: [f32; 4]) -> Self {
Self { x, y, z, w }
}
}
impl ops::Add for Vector4 {
type Output = Self;
fn add(self, other: Self) -> Self {
Self {
x: self.x + other.x,
y: self.y + other.y,
z: self.z + other.z,
w: self.w + other.w,
}
}
}
impl ops::Mul<f32> for Vector4 {
type Output = Vector4;
fn mul(mut self, rhs: f32) -> Self::Output {
self.multiply(rhs);
self
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(C)]
pub struct Matrix3 {
pub x: Vector3,
pub y: Vector3,
pub z: Vector3,
}
impl Matrix3 {
#[rustfmt::skip]
#[allow(clippy::too_many_arguments)]
pub fn new(
c0r0: f32, c0r1: f32, c0r2: f32,
c1r0: f32, c1r1: f32, c1r2: f32,
c2r0: f32, c2r1: f32, c2r2: f32,
) -> Matrix3 {
Matrix3 {
x: Vector3::new(c0r0, c0r1, c0r2),
y: Vector3::new(c1r0, c1r1, c1r2),
z: Vector3::new(c2r0, c2r1, c2r2),
}
}
pub fn determinant(&self) -> f32 {
self.x.x * (self.y.y * self.z.z - self.z.y * self.y.z)
- self.y.x * (self.x.y * self.z.z - self.z.y * self.x.z)
+ self.z.x * (self.x.y * self.y.z - self.y.y * self.x.z)
}
pub fn trace(&self) -> f32 {
self.x.x + self.y.y + self.z.z
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(C)]
pub struct Matrix4 {
pub x: Vector4,
pub y: Vector4,
pub z: Vector4,
pub w: Vector4,
}
impl Matrix4 {
#[rustfmt::skip]
#[allow(clippy::too_many_arguments)]
pub fn new(
c0r0: f32, c0r1: f32, c0r2: f32, c0r3: f32,
c1r0: f32, c1r1: f32, c1r2: f32, c1r3: f32,
c2r0: f32, c2r1: f32, c2r2: f32, c2r3: f32,
c3r0: f32, c3r1: f32, c3r2: f32, c3r3: f32,
) -> Matrix4 {
Matrix4 {
x: Vector4::new(c0r0, c0r1, c0r2, c0r3),
y: Vector4::new(c1r0, c1r1, c1r2, c1r3),
z: Vector4::new(c2r0, c2r1, c2r2, c2r3),
w: Vector4::new(c3r0, c3r1, c3r2, c3r3),
}
}
#[cfg(test)]
pub fn from_array([x, y, z, w]: [[f32; 4]; 4]) -> Matrix4 {
Matrix4 {
x: Vector4::from_array(x),
y: Vector4::from_array(y),
z: Vector4::from_array(z),
w: Vector4::from_array(w),
}
}
#[rustfmt::skip]
pub fn from_translation(v: Vector3) -> Matrix4 {
Matrix4::new(
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
v.x, v.y, v.z, 1.0,
)
}
#[rustfmt::skip]
pub fn from_nonuniform_scale(x: f32, y: f32, z: f32) -> Matrix4 {
Matrix4::new(
x, 0.0, 0.0, 0.0,
0.0, y, 0.0, 0.0,
0.0, 0.0, z, 0.0,
0.0, 0.0, 0.0, 1.0,
)
}
pub fn from_quaternion(q: Quaternion) -> Matrix4 {
let x2 = q.v.x + q.v.x;
let y2 = q.v.y + q.v.y;
let z2 = q.v.z + q.v.z;
let xx2 = x2 * q.v.x;
let xy2 = x2 * q.v.y;
let xz2 = x2 * q.v.z;
let yy2 = y2 * q.v.y;
let yz2 = y2 * q.v.z;
let zz2 = z2 * q.v.z;
let sy2 = y2 * q.s;
let sz2 = z2 * q.s;
let sx2 = x2 * q.s;
Matrix4 {
x: Vector4::new(1.0 - yy2 - zz2, xy2 + sz2, xz2 - sy2, 0.0),
y: Vector4::new(xy2 - sz2, 1.0 - xx2 - zz2, yz2 + sx2, 0.0),
z: Vector4::new(xz2 + sy2, yz2 - sx2, 1.0 - xx2 - yy2, 0.0),
w: Vector4::new(0.0, 0.0, 0.0, 1.0),
}
}
pub fn as_array(&self) -> [[f32; 4]; 4] {
[
self.x.as_array(),
self.y.as_array(),
self.z.as_array(),
self.w.as_array(),
]
}
}
impl ops::Mul<Matrix4> for Matrix4 {
type Output = Matrix4;
fn mul(self, rhs: Matrix4) -> Self::Output {
let a = self.x;
let b = self.y;
let c = self.z;
let d = self.w;
Matrix4 {
x: a * rhs.x.x + b * rhs.x.y + c * rhs.x.z + d * rhs.x.w,
y: a * rhs.y.x + b * rhs.y.y + c * rhs.y.z + d * rhs.y.w,
z: a * rhs.z.x + b * rhs.z.y + c * rhs.z.z + d * rhs.z.w,
w: a * rhs.w.x + b * rhs.w.y + c * rhs.w.z + d * rhs.w.w,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(C)]
pub struct Quaternion {
pub s: f32,
pub v: Vector3,
}
impl Quaternion {
pub fn new(w: f32, xi: f32, yj: f32, zk: f32) -> Quaternion {
Quaternion {
s: w,
v: Vector3::new(xi, yj, zk),
}
}
#[cfg(test)]
pub fn from_axis_angle(axis: Vector3, radians: f32) -> Quaternion {
Quaternion {
s: (0.5 * radians).cos(),
v: axis * (0.5 * radians).sin(),
}
}
pub fn from_matrix(m: Matrix3) -> Quaternion {
let trace = m.trace();
if trace >= 0.0 {
let s = (1.0 + trace).sqrt();
let w = 0.5 * s;
let s = 0.5 / s;
let x = (m.y.z - m.z.y) * s;
let y = (m.z.x - m.x.z) * s;
let z = (m.x.y - m.y.x) * s;
Quaternion::new(w, x, y, z)
} else if (m.x.x > m.y.y) && (m.x.x > m.z.z) {
let s = ((m.x.x - m.y.y - m.z.z) + 1.0).sqrt();
let x = 0.5 * s;
let s = 0.5 / s;
let y = (m.y.x + m.x.y) * s;
let z = (m.x.z + m.z.x) * s;
let w = (m.y.z - m.z.y) * s;
Quaternion::new(w, x, y, z)
} else if m.y.y > m.z.z {
let s = ((m.y.y - m.x.x - m.z.z) + 1.0).sqrt();
let y = 0.5 * s;
let s = 0.5 / s;
let z = (m.z.y + m.y.z) * s;
let x = (m.y.x + m.x.y) * s;
let w = (m.z.x - m.x.z) * s;
Quaternion::new(w, x, y, z)
} else {
let s = ((m.z.z - m.x.x - m.y.y) + 1.0).sqrt();
let z = 0.5 * s;
let s = 0.5 / s;
let x = (m.x.z + m.z.x) * s;
let y = (m.z.y + m.y.z) * s;
let w = (m.x.y - m.y.x) * s;
Quaternion::new(w, x, y, z)
}
}
}