// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use num_traits::{cast, NumCast}; #[cfg(feature = "rand")] use rand::{ distributions::{Distribution, Standard}, Rng, }; use std::fmt; use std::iter; use std::mem; use std::ops::*; use std::ptr; use structure::*; use angle::Rad; use approx; use euler::Euler; use num::BaseFloat; use point::{Point2, Point3}; use quaternion::Quaternion; use transform::{Transform, Transform2, Transform3}; use vector::{Vector2, Vector3, Vector4}; #[cfg(feature = "mint")] use mint; /// A 2 x 2, column major matrix /// /// This type is marked as `#[repr(C)]`. #[repr(C)] #[derive(Copy, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Matrix2 { /// The first column of the matrix. pub x: Vector2, /// The second column of the matrix. pub y: Vector2, } /// A 3 x 3, column major matrix /// /// This type is marked as `#[repr(C)]`. #[repr(C)] #[derive(Copy, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Matrix3 { /// The first column of the matrix. pub x: Vector3, /// The second column of the matrix. pub y: Vector3, /// The third column of the matrix. pub z: Vector3, } /// A 4 x 4, column major matrix /// /// This type is marked as `#[repr(C)]`. #[repr(C)] #[derive(Copy, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Matrix4 { /// The first column of the matrix. pub x: Vector4, /// The second column of the matrix. pub y: Vector4, /// The third column of the matrix. pub z: Vector4, /// The fourth column of the matrix. pub w: Vector4, } impl Matrix2 { /// Create a new matrix, providing values for each index. #[inline] pub const fn new(c0r0: S, c0r1: S, c1r0: S, c1r1: S) -> Matrix2 { Matrix2::from_cols(Vector2::new(c0r0, c0r1), Vector2::new(c1r0, c1r1)) } /// Create a new matrix, providing columns. #[inline] pub const fn from_cols(c0: Vector2, c1: Vector2) -> Matrix2 { Matrix2 { x: c0, y: c1 } } } impl Matrix2 { /// Create a transformation matrix that will cause `unit_x()` to point at /// `dir`. `unit_y()` will be perpendicular to `dir`, and the closest to `up`. pub fn look_at(dir: Vector2, up: Vector2) -> Matrix2 { Matrix2::look_at_stable(dir, up.x * dir.y >= up.y * dir.x) } /// Crate a transformation that will cause `unit_x()` to point at /// `dir`. This is similar to `look_at`, but does not take an `up` vector. /// This will not cause `unit_y()` to flip when `dir` crosses over the `up` vector. pub fn look_at_stable(dir: Vector2, flip: bool) -> Matrix2 { let basis1 = dir.normalize(); let basis2 = if flip { Vector2::new(basis1.y, -basis1.x) } else { Vector2::new(-basis1.y, basis1.x) }; Matrix2::from_cols(basis1, basis2) } #[inline] pub fn from_angle>>(theta: A) -> Matrix2 { let (s, c) = Rad::sin_cos(theta.into()); Matrix2::new(c, s, -s, c) } /// Are all entries in the matrix finite. pub fn is_finite(&self) -> bool { self.x.is_finite() && self.y.is_finite() } } impl Matrix3 { /// Create a new matrix, providing values for each index. #[inline] #[cfg_attr(rustfmt, rustfmt_skip)] pub const fn new( c0r0:S, c0r1:S, c0r2:S, c1r0:S, c1r1:S, c1r2:S, c2r0:S, c2r1:S, c2r2:S, ) -> Matrix3 { Matrix3::from_cols( Vector3::new(c0r0, c0r1, c0r2), Vector3::new(c1r0, c1r1, c1r2), Vector3::new(c2r0, c2r1, c2r2), ) } /// Create a new matrix, providing columns. #[inline] pub const fn from_cols(c0: Vector3, c1: Vector3, c2: Vector3) -> Matrix3 { Matrix3 { x: c0, y: c1, z: c2, } } } impl Matrix3 { /// Create a homogeneous transformation matrix from a translation vector. #[inline] pub fn from_translation(v: Vector2) -> Matrix3 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix3::new( S::one(), S::zero(), S::zero(), S::zero(), S::one(), S::zero(), v.x, v.y, S::one(), ) } /// Create a homogeneous transformation matrix from a scale value. #[inline] pub fn from_scale(value: S) -> Matrix3 { Matrix3::from_nonuniform_scale(value, value) } /// Create a homogeneous transformation matrix from a set of scale values. #[inline] pub fn from_nonuniform_scale(x: S, y: S) -> Matrix3 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix3::new( x, S::zero(), S::zero(), S::zero(), y, S::zero(), S::zero(), S::zero(), S::one(), ) } /// Create a rotation matrix that will cause a vector to point at /// `dir`, using `up` for orientation. pub fn look_at(dir: Vector3, up: Vector3) -> Matrix3 { let dir = dir.normalize(); let side = up.cross(dir).normalize(); let up = dir.cross(side).normalize(); Matrix3::from_cols(side, up, dir).transpose() } /// Create a rotation matrix from a rotation around the `x` axis (pitch). pub fn from_angle_x>>(theta: A) -> Matrix3 { // http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations let (s, c) = Rad::sin_cos(theta.into()); #[cfg_attr(rustfmt, rustfmt_skip)] Matrix3::new( S::one(), S::zero(), S::zero(), S::zero(), c, s, S::zero(), -s, c, ) } /// Create a rotation matrix from a rotation around the `y` axis (yaw). pub fn from_angle_y>>(theta: A) -> Matrix3 { // http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations let (s, c) = Rad::sin_cos(theta.into()); #[cfg_attr(rustfmt, rustfmt_skip)] Matrix3::new( c, S::zero(), -s, S::zero(), S::one(), S::zero(), s, S::zero(), c, ) } /// Create a rotation matrix from a rotation around the `z` axis (roll). pub fn from_angle_z>>(theta: A) -> Matrix3 { // http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations let (s, c) = Rad::sin_cos(theta.into()); #[cfg_attr(rustfmt, rustfmt_skip)] Matrix3::new( c, s, S::zero(), -s, c, S::zero(), S::zero(), S::zero(), S::one(), ) } /// Create a rotation matrix from an angle around an arbitrary axis. /// /// The specified axis **must be normalized**, or it represents an invalid rotation. pub fn from_axis_angle>>(axis: Vector3, angle: A) -> Matrix3 { let (s, c) = Rad::sin_cos(angle.into()); let _1subc = S::one() - c; #[cfg_attr(rustfmt, rustfmt_skip)] Matrix3::new( _1subc * axis.x * axis.x + c, _1subc * axis.x * axis.y + s * axis.z, _1subc * axis.x * axis.z - s * axis.y, _1subc * axis.x * axis.y - s * axis.z, _1subc * axis.y * axis.y + c, _1subc * axis.y * axis.z + s * axis.x, _1subc * axis.x * axis.z + s * axis.y, _1subc * axis.y * axis.z - s * axis.x, _1subc * axis.z * axis.z + c, ) } /// Are all entries in the matrix finite. pub fn is_finite(&self) -> bool { self.x.is_finite() && self.y.is_finite() && self.z.is_finite() } } impl Matrix4 { /// Create a new matrix, providing values for each index. #[inline] #[cfg_attr(rustfmt, rustfmt_skip)] pub const fn new( c0r0: S, c0r1: S, c0r2: S, c0r3: S, c1r0: S, c1r1: S, c1r2: S, c1r3: S, c2r0: S, c2r1: S, c2r2: S, c2r3: S, c3r0: S, c3r1: S, c3r2: S, c3r3: S, ) -> Matrix4 { Matrix4::from_cols( Vector4::new(c0r0, c0r1, c0r2, c0r3), Vector4::new(c1r0, c1r1, c1r2, c1r3), Vector4::new(c2r0, c2r1, c2r2, c2r3), Vector4::new(c3r0, c3r1, c3r2, c3r3), ) } /// Create a new matrix, providing columns. #[inline] pub const fn from_cols( c0: Vector4, c1: Vector4, c2: Vector4, c3: Vector4, ) -> Matrix4 { Matrix4 { x: c0, y: c1, z: c2, w: c3, } } } impl Matrix4 { /// Create a homogeneous transformation matrix from a translation vector. #[inline] pub fn from_translation(v: Vector3) -> Matrix4 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( S::one(), S::zero(), S::zero(), S::zero(), S::zero(), S::one(), S::zero(), S::zero(), S::zero(), S::zero(), S::one(), S::zero(), v.x, v.y, v.z, S::one(), ) } /// Create a homogeneous transformation matrix from a scale value. #[inline] pub fn from_scale(value: S) -> Matrix4 { Matrix4::from_nonuniform_scale(value, value, value) } /// Create a homogeneous transformation matrix from a set of scale values. #[inline] pub fn from_nonuniform_scale(x: S, y: S, z: S) -> Matrix4 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( x, S::zero(), S::zero(), S::zero(), S::zero(), y, S::zero(), S::zero(), S::zero(), S::zero(), z, S::zero(), S::zero(), S::zero(), S::zero(), S::one(), ) } /// Create a homogeneous transformation matrix that will cause a vector to point at /// `dir`, using `up` for orientation. pub fn look_at_dir(eye: Point3, dir: Vector3, up: Vector3) -> Matrix4 { let f = dir.normalize(); let s = f.cross(up).normalize(); let u = s.cross(f); #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( s.x.clone(), u.x.clone(), -f.x.clone(), S::zero(), s.y.clone(), u.y.clone(), -f.y.clone(), S::zero(), s.z.clone(), u.z.clone(), -f.z.clone(), S::zero(), -eye.dot(s), -eye.dot(u), eye.dot(f), S::one(), ) } /// Create a homogeneous transformation matrix that will cause a vector to point at /// `center`, using `up` for orientation. pub fn look_at(eye: Point3, center: Point3, up: Vector3) -> Matrix4 { Matrix4::look_at_dir(eye, center - eye, up) } /// Create a homogeneous transformation matrix from a rotation around the `x` axis (pitch). pub fn from_angle_x>>(theta: A) -> Matrix4 { // http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations let (s, c) = Rad::sin_cos(theta.into()); #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( S::one(), S::zero(), S::zero(), S::zero(), S::zero(), c, s, S::zero(), S::zero(), -s, c, S::zero(), S::zero(), S::zero(), S::zero(), S::one(), ) } /// Create a homogeneous transformation matrix from a rotation around the `y` axis (yaw). pub fn from_angle_y>>(theta: A) -> Matrix4 { // http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations let (s, c) = Rad::sin_cos(theta.into()); #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( c, S::zero(), -s, S::zero(), S::zero(), S::one(), S::zero(), S::zero(), s, S::zero(), c, S::zero(), S::zero(), S::zero(), S::zero(), S::one(), ) } /// Create a homogeneous transformation matrix from a rotation around the `z` axis (roll). pub fn from_angle_z>>(theta: A) -> Matrix4 { // http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations let (s, c) = Rad::sin_cos(theta.into()); #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( c, s, S::zero(), S::zero(), -s, c, S::zero(), S::zero(), S::zero(), S::zero(), S::one(), S::zero(), S::zero(), S::zero(), S::zero(), S::one(), ) } /// Create a homogeneous transformation matrix from an angle around an arbitrary axis. /// /// The specified axis **must be normalized**, or it represents an invalid rotation. pub fn from_axis_angle>>(axis: Vector3, angle: A) -> Matrix4 { let (s, c) = Rad::sin_cos(angle.into()); let _1subc = S::one() - c; #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( _1subc * axis.x * axis.x + c, _1subc * axis.x * axis.y + s * axis.z, _1subc * axis.x * axis.z - s * axis.y, S::zero(), _1subc * axis.x * axis.y - s * axis.z, _1subc * axis.y * axis.y + c, _1subc * axis.y * axis.z + s * axis.x, S::zero(), _1subc * axis.x * axis.z + s * axis.y, _1subc * axis.y * axis.z - s * axis.x, _1subc * axis.z * axis.z + c, S::zero(), S::zero(), S::zero(), S::zero(), S::one(), ) } /// Are all entries in the matrix finite. pub fn is_finite(&self) -> bool { self.w.is_finite() && self.x.is_finite() && self.y.is_finite() && self.z.is_finite() } } impl Zero for Matrix2 { #[inline] fn zero() -> Matrix2 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix2::new( S::zero(), S::zero(), S::zero(), S::zero(), ) } #[inline] fn is_zero(&self) -> bool { ulps_eq!(self, &Self::zero()) } } impl Zero for Matrix3 { #[inline] fn zero() -> Matrix3 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix3::new( S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), ) } #[inline] fn is_zero(&self) -> bool { ulps_eq!(self, &Self::zero()) } } impl Zero for Matrix4 { #[inline] fn zero() -> Matrix4 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), S::zero(), ) } #[inline] fn is_zero(&self) -> bool { ulps_eq!(self, &Self::zero()) } } impl One for Matrix2 { #[inline] fn one() -> Matrix2 { Matrix2::from_value(S::one()) } } impl One for Matrix3 { #[inline] fn one() -> Matrix3 { Matrix3::from_value(S::one()) } } impl One for Matrix4 { #[inline] fn one() -> Matrix4 { Matrix4::from_value(S::one()) } } impl VectorSpace for Matrix2 { type Scalar = S; } impl VectorSpace for Matrix3 { type Scalar = S; } impl VectorSpace for Matrix4 { type Scalar = S; } impl Matrix for Matrix2 { type Column = Vector2; type Row = Vector2; type Transpose = Matrix2; #[inline] fn row(&self, r: usize) -> Vector2 { Vector2::new(self[0][r], self[1][r]) } #[inline] fn swap_rows(&mut self, a: usize, b: usize) { self[0].swap_elements(a, b); self[1].swap_elements(a, b); } #[inline] fn swap_columns(&mut self, a: usize, b: usize) { unsafe { ptr::swap(&mut self[a], &mut self[b]) }; } #[inline] fn swap_elements(&mut self, a: (usize, usize), b: (usize, usize)) { let (ac, ar) = a; let (bc, br) = b; unsafe { ptr::swap(&mut self[ac][ar], &mut self[bc][br]) }; } fn transpose(&self) -> Matrix2 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix2::new( self[0][0], self[1][0], self[0][1], self[1][1], ) } } impl SquareMatrix for Matrix2 { type ColumnRow = Vector2; #[inline] fn from_value(value: S) -> Matrix2 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix2::new( value, S::zero(), S::zero(), value, ) } #[inline] fn from_diagonal(value: Vector2) -> Matrix2 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix2::new( value.x, S::zero(), S::zero(), value.y, ) } #[inline] fn transpose_self(&mut self) { self.swap_elements((0, 1), (1, 0)); } #[inline] fn determinant(&self) -> S { self[0][0] * self[1][1] - self[1][0] * self[0][1] } #[inline] fn diagonal(&self) -> Vector2 { Vector2::new(self[0][0], self[1][1]) } #[inline] fn invert(&self) -> Option> { let det = self.determinant(); if det == S::zero() { None } else { #[cfg_attr(rustfmt, rustfmt_skip)] Some(Matrix2::new( self[1][1] / det, -self[0][1] / det, -self[1][0] / det, self[0][0] / det, )) } } #[inline] fn is_diagonal(&self) -> bool { ulps_eq!(self[0][1], &S::zero()) && ulps_eq!(self[1][0], &S::zero()) } #[inline] fn is_symmetric(&self) -> bool { ulps_eq!(self[0][1], &self[1][0]) && ulps_eq!(self[1][0], &self[0][1]) } } impl Matrix for Matrix3 { type Column = Vector3; type Row = Vector3; type Transpose = Matrix3; #[inline] fn row(&self, r: usize) -> Vector3 { Vector3::new(self[0][r], self[1][r], self[2][r]) } #[inline] fn swap_rows(&mut self, a: usize, b: usize) { self[0].swap_elements(a, b); self[1].swap_elements(a, b); self[2].swap_elements(a, b); } #[inline] fn swap_columns(&mut self, a: usize, b: usize) { unsafe { ptr::swap(&mut self[a], &mut self[b]) }; } #[inline] fn swap_elements(&mut self, a: (usize, usize), b: (usize, usize)) { let (ac, ar) = a; let (bc, br) = b; unsafe { ptr::swap(&mut self[ac][ar], &mut self[bc][br]) }; } fn transpose(&self) -> Matrix3 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix3::new( self[0][0], self[1][0], self[2][0], self[0][1], self[1][1], self[2][1], self[0][2], self[1][2], self[2][2], ) } } impl SquareMatrix for Matrix3 { type ColumnRow = Vector3; #[inline] fn from_value(value: S) -> Matrix3 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix3::new( value, S::zero(), S::zero(), S::zero(), value, S::zero(), S::zero(), S::zero(), value, ) } #[inline] fn from_diagonal(value: Vector3) -> Matrix3 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix3::new( value.x, S::zero(), S::zero(), S::zero(), value.y, S::zero(), S::zero(), S::zero(), value.z, ) } #[inline] fn transpose_self(&mut self) { self.swap_elements((0, 1), (1, 0)); self.swap_elements((0, 2), (2, 0)); self.swap_elements((1, 2), (2, 1)); } fn determinant(&self) -> S { self[0][0] * (self[1][1] * self[2][2] - self[2][1] * self[1][2]) - self[1][0] * (self[0][1] * self[2][2] - self[2][1] * self[0][2]) + self[2][0] * (self[0][1] * self[1][2] - self[1][1] * self[0][2]) } #[inline] fn diagonal(&self) -> Vector3 { Vector3::new(self[0][0], self[1][1], self[2][2]) } fn invert(&self) -> Option> { let det = self.determinant(); if det == S::zero() { None } else { Some( Matrix3::from_cols( self[1].cross(self[2]) / det, self[2].cross(self[0]) / det, self[0].cross(self[1]) / det, ) .transpose(), ) } } fn is_diagonal(&self) -> bool { ulps_eq!(self[0][1], &S::zero()) && ulps_eq!(self[0][2], &S::zero()) && ulps_eq!(self[1][0], &S::zero()) && ulps_eq!(self[1][2], &S::zero()) && ulps_eq!(self[2][0], &S::zero()) && ulps_eq!(self[2][1], &S::zero()) } fn is_symmetric(&self) -> bool { ulps_eq!(self[0][1], &self[1][0]) && ulps_eq!(self[0][2], &self[2][0]) && ulps_eq!(self[1][0], &self[0][1]) && ulps_eq!(self[1][2], &self[2][1]) && ulps_eq!(self[2][0], &self[0][2]) && ulps_eq!(self[2][1], &self[1][2]) } } impl Matrix for Matrix4 { type Column = Vector4; type Row = Vector4; type Transpose = Matrix4; #[inline] fn row(&self, r: usize) -> Vector4 { Vector4::new(self[0][r], self[1][r], self[2][r], self[3][r]) } #[inline] fn swap_rows(&mut self, a: usize, b: usize) { self[0].swap_elements(a, b); self[1].swap_elements(a, b); self[2].swap_elements(a, b); self[3].swap_elements(a, b); } #[inline] fn swap_columns(&mut self, a: usize, b: usize) { unsafe { ptr::swap(&mut self[a], &mut self[b]) }; } #[inline] fn swap_elements(&mut self, a: (usize, usize), b: (usize, usize)) { let (ac, ar) = a; let (bc, br) = b; unsafe { ptr::swap(&mut self[ac][ar], &mut self[bc][br]) }; } fn transpose(&self) -> Matrix4 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( self[0][0], self[1][0], self[2][0], self[3][0], self[0][1], self[1][1], self[2][1], self[3][1], self[0][2], self[1][2], self[2][2], self[3][2], self[0][3], self[1][3], self[2][3], self[3][3], ) } } impl SquareMatrix for Matrix4 { type ColumnRow = Vector4; #[inline] fn from_value(value: S) -> Matrix4 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( value, S::zero(), S::zero(), S::zero(), S::zero(), value, S::zero(), S::zero(), S::zero(), S::zero(), value, S::zero(), S::zero(), S::zero(), S::zero(), value, ) } #[inline] fn from_diagonal(value: Vector4) -> Matrix4 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( value.x, S::zero(), S::zero(), S::zero(), S::zero(), value.y, S::zero(), S::zero(), S::zero(), S::zero(), value.z, S::zero(), S::zero(), S::zero(), S::zero(), value.w, ) } fn transpose_self(&mut self) { self.swap_elements((0, 1), (1, 0)); self.swap_elements((0, 2), (2, 0)); self.swap_elements((0, 3), (3, 0)); self.swap_elements((1, 2), (2, 1)); self.swap_elements((1, 3), (3, 1)); self.swap_elements((2, 3), (3, 2)); } fn determinant(&self) -> S { let tmp = unsafe { det_sub_proc_unsafe(self, 1, 2, 3) }; tmp.dot(Vector4::new(self[0][0], self[1][0], self[2][0], self[3][0])) } #[inline] fn diagonal(&self) -> Vector4 { Vector4::new(self[0][0], self[1][1], self[2][2], self[3][3]) } // The new implementation results in negative optimization when used // without SIMD. so we opt them in with configuration. // A better option would be using specialization. But currently somewhat // specialization is too buggy, and it won't apply here. I'm getting // weird error msgs. Help wanted. #[cfg(not(feature = "simd"))] fn invert(&self) -> Option> { let det = self.determinant(); if det == S::zero() { None } else { let inv_det = S::one() / det; let t = self.transpose(); let cf = |i, j| { let mat = match i { 0 => { Matrix3::from_cols(t.y.truncate_n(j), t.z.truncate_n(j), t.w.truncate_n(j)) } 1 => { Matrix3::from_cols(t.x.truncate_n(j), t.z.truncate_n(j), t.w.truncate_n(j)) } 2 => { Matrix3::from_cols(t.x.truncate_n(j), t.y.truncate_n(j), t.w.truncate_n(j)) } 3 => { Matrix3::from_cols(t.x.truncate_n(j), t.y.truncate_n(j), t.z.truncate_n(j)) } _ => panic!("out of range"), }; let sign = if (i + j) & 1 == 1 { -S::one() } else { S::one() }; mat.determinant() * sign * inv_det }; #[cfg_attr(rustfmt, rustfmt_skip)] Some(Matrix4::new( cf(0, 0), cf(0, 1), cf(0, 2), cf(0, 3), cf(1, 0), cf(1, 1), cf(1, 2), cf(1, 3), cf(2, 0), cf(2, 1), cf(2, 2), cf(2, 3), cf(3, 0), cf(3, 1), cf(3, 2), cf(3, 3), )) } } #[cfg(feature = "simd")] fn invert(&self) -> Option> { let tmp0 = unsafe { det_sub_proc_unsafe(self, 1, 2, 3) }; let det = tmp0.dot(Vector4::new(self[0][0], self[1][0], self[2][0], self[3][0])); if det == S::zero() { None } else { let inv_det = S::one() / det; let tmp0 = tmp0 * inv_det; let tmp1 = unsafe { det_sub_proc_unsafe(self, 0, 3, 2) * inv_det }; let tmp2 = unsafe { det_sub_proc_unsafe(self, 0, 1, 3) * inv_det }; let tmp3 = unsafe { det_sub_proc_unsafe(self, 0, 2, 1) * inv_det }; Some(Matrix4::from_cols(tmp0, tmp1, tmp2, tmp3)) } } fn is_diagonal(&self) -> bool { ulps_eq!(self[0][1], &S::zero()) && ulps_eq!(self[0][2], &S::zero()) && ulps_eq!(self[0][3], &S::zero()) && ulps_eq!(self[1][0], &S::zero()) && ulps_eq!(self[1][2], &S::zero()) && ulps_eq!(self[1][3], &S::zero()) && ulps_eq!(self[2][0], &S::zero()) && ulps_eq!(self[2][1], &S::zero()) && ulps_eq!(self[2][3], &S::zero()) && ulps_eq!(self[3][0], &S::zero()) && ulps_eq!(self[3][1], &S::zero()) && ulps_eq!(self[3][2], &S::zero()) } fn is_symmetric(&self) -> bool { ulps_eq!(self[0][1], &self[1][0]) && ulps_eq!(self[0][2], &self[2][0]) && ulps_eq!(self[0][3], &self[3][0]) && ulps_eq!(self[1][0], &self[0][1]) && ulps_eq!(self[1][2], &self[2][1]) && ulps_eq!(self[1][3], &self[3][1]) && ulps_eq!(self[2][0], &self[0][2]) && ulps_eq!(self[2][1], &self[1][2]) && ulps_eq!(self[2][3], &self[3][2]) && ulps_eq!(self[3][0], &self[0][3]) && ulps_eq!(self[3][1], &self[1][3]) && ulps_eq!(self[3][2], &self[2][3]) } } impl approx::AbsDiffEq for Matrix2 { type Epsilon = S::Epsilon; #[inline] fn default_epsilon() -> S::Epsilon { cast(1.0e-6f64).unwrap() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: S::Epsilon) -> bool { Vector2::abs_diff_eq(&self[0], &other[0], epsilon) && Vector2::abs_diff_eq(&self[1], &other[1], epsilon) } } impl approx::RelativeEq for Matrix2 { #[inline] fn default_max_relative() -> S::Epsilon { S::default_max_relative() } #[inline] fn relative_eq(&self, other: &Self, epsilon: S::Epsilon, max_relative: S::Epsilon) -> bool { Vector2::relative_eq(&self[0], &other[0], epsilon, max_relative) && Vector2::relative_eq(&self[1], &other[1], epsilon, max_relative) } } impl approx::UlpsEq for Matrix2 { #[inline] fn default_max_ulps() -> u32 { S::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: S::Epsilon, max_ulps: u32) -> bool { Vector2::ulps_eq(&self[0], &other[0], epsilon, max_ulps) && Vector2::ulps_eq(&self[1], &other[1], epsilon, max_ulps) } } impl approx::AbsDiffEq for Matrix3 { type Epsilon = S::Epsilon; #[inline] fn default_epsilon() -> S::Epsilon { cast(1.0e-6f64).unwrap() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: S::Epsilon) -> bool { Vector3::abs_diff_eq(&self[0], &other[0], epsilon) && Vector3::abs_diff_eq(&self[1], &other[1], epsilon) && Vector3::abs_diff_eq(&self[2], &other[2], epsilon) } } impl approx::RelativeEq for Matrix3 { #[inline] fn default_max_relative() -> S::Epsilon { S::default_max_relative() } #[inline] fn relative_eq(&self, other: &Self, epsilon: S::Epsilon, max_relative: S::Epsilon) -> bool { Vector3::relative_eq(&self[0], &other[0], epsilon, max_relative) && Vector3::relative_eq(&self[1], &other[1], epsilon, max_relative) && Vector3::relative_eq(&self[2], &other[2], epsilon, max_relative) } } impl approx::UlpsEq for Matrix3 { #[inline] fn default_max_ulps() -> u32 { S::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: S::Epsilon, max_ulps: u32) -> bool { Vector3::ulps_eq(&self[0], &other[0], epsilon, max_ulps) && Vector3::ulps_eq(&self[1], &other[1], epsilon, max_ulps) && Vector3::ulps_eq(&self[2], &other[2], epsilon, max_ulps) } } impl approx::AbsDiffEq for Matrix4 { type Epsilon = S::Epsilon; #[inline] fn default_epsilon() -> S::Epsilon { cast(1.0e-6f64).unwrap() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: S::Epsilon) -> bool { Vector4::abs_diff_eq(&self[0], &other[0], epsilon) && Vector4::abs_diff_eq(&self[1], &other[1], epsilon) && Vector4::abs_diff_eq(&self[2], &other[2], epsilon) && Vector4::abs_diff_eq(&self[3], &other[3], epsilon) } } impl approx::RelativeEq for Matrix4 { #[inline] fn default_max_relative() -> S::Epsilon { S::default_max_relative() } #[inline] fn relative_eq(&self, other: &Self, epsilon: S::Epsilon, max_relative: S::Epsilon) -> bool { Vector4::relative_eq(&self[0], &other[0], epsilon, max_relative) && Vector4::relative_eq(&self[1], &other[1], epsilon, max_relative) && Vector4::relative_eq(&self[2], &other[2], epsilon, max_relative) && Vector4::relative_eq(&self[3], &other[3], epsilon, max_relative) } } impl approx::UlpsEq for Matrix4 { #[inline] fn default_max_ulps() -> u32 { S::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: S::Epsilon, max_ulps: u32) -> bool { Vector4::ulps_eq(&self[0], &other[0], epsilon, max_ulps) && Vector4::ulps_eq(&self[1], &other[1], epsilon, max_ulps) && Vector4::ulps_eq(&self[2], &other[2], epsilon, max_ulps) && Vector4::ulps_eq(&self[3], &other[3], epsilon, max_ulps) } } impl Transform> for Matrix3 { fn look_at(eye: Point2, center: Point2, up: Vector2) -> Matrix3 { let dir = center - eye; Matrix3::from(Matrix2::look_at(dir, up)) } fn transform_vector(&self, vec: Vector2) -> Vector2 { (self * vec.extend(S::zero())).truncate() } fn transform_point(&self, point: Point2) -> Point2 { Point2::from_vec((self * Point3::new(point.x, point.y, S::one()).to_vec()).truncate()) } fn concat(&self, other: &Matrix3) -> Matrix3 { self * other } fn inverse_transform(&self) -> Option> { SquareMatrix::invert(self) } } impl Transform> for Matrix3 { fn look_at(eye: Point3, center: Point3, up: Vector3) -> Matrix3 { let dir = center - eye; Matrix3::look_at(dir, up) } fn transform_vector(&self, vec: Vector3) -> Vector3 { self * vec } fn transform_point(&self, point: Point3) -> Point3 { Point3::from_vec(self * point.to_vec()) } fn concat(&self, other: &Matrix3) -> Matrix3 { self * other } fn inverse_transform(&self) -> Option> { SquareMatrix::invert(self) } } impl Transform> for Matrix4 { fn look_at(eye: Point3, center: Point3, up: Vector3) -> Matrix4 { Matrix4::look_at(eye, center, up) } fn transform_vector(&self, vec: Vector3) -> Vector3 { (self * vec.extend(S::zero())).truncate() } fn transform_point(&self, point: Point3) -> Point3 { Point3::from_homogeneous(self * point.to_homogeneous()) } fn concat(&self, other: &Matrix4) -> Matrix4 { self * other } fn inverse_transform(&self) -> Option> { SquareMatrix::invert(self) } } impl Transform2 for Matrix3 { type Scalar = S; } impl Transform3 for Matrix3 { type Scalar = S; } impl Transform3 for Matrix4 { type Scalar = S; } macro_rules! impl_matrix { ($MatrixN:ident, $VectorN:ident { $($field:ident : $row_index:expr),+ }) => { impl_operator!( Neg for $MatrixN { fn neg(matrix) -> $MatrixN { $MatrixN { $($field: -matrix.$field),+ } } }); impl_operator!( Mul for $MatrixN { fn mul(matrix, scalar) -> $MatrixN { $MatrixN { $($field: matrix.$field * scalar),+ } } }); impl_operator!( Div for $MatrixN { fn div(matrix, scalar) -> $MatrixN { $MatrixN { $($field: matrix.$field / scalar),+ } } }); impl_operator!( Rem for $MatrixN { fn rem(matrix, scalar) -> $MatrixN { $MatrixN { $($field: matrix.$field % scalar),+ } } }); impl_assignment_operator!( MulAssign for $MatrixN { fn mul_assign(&mut self, scalar) { $(self.$field *= scalar);+ } }); impl_assignment_operator!( DivAssign for $MatrixN { fn div_assign(&mut self, scalar) { $(self.$field /= scalar);+ } }); impl_assignment_operator!( RemAssign for $MatrixN { fn rem_assign(&mut self, scalar) { $(self.$field %= scalar);+ } }); impl_operator!( Add<$MatrixN > for $MatrixN { fn add(lhs, rhs) -> $MatrixN { $MatrixN { $($field: lhs.$field + rhs.$field),+ } } }); impl_operator!( Sub<$MatrixN > for $MatrixN { fn sub(lhs, rhs) -> $MatrixN { $MatrixN { $($field: lhs.$field - rhs.$field),+ } } }); impl> AddAssign<$MatrixN> for $MatrixN { fn add_assign(&mut self, other: $MatrixN) { $(self.$field += other.$field);+ } } impl> SubAssign<$MatrixN> for $MatrixN { fn sub_assign(&mut self, other: $MatrixN) { $(self.$field -= other.$field);+ } } impl iter::Sum<$MatrixN> for $MatrixN { #[inline] fn sum>>(iter: I) -> $MatrixN { iter.fold($MatrixN::zero(), Add::add) } } impl<'a, S: 'a + BaseFloat> iter::Sum<&'a $MatrixN> for $MatrixN { #[inline] fn sum>>(iter: I) -> $MatrixN { iter.fold($MatrixN::zero(), Add::add) } } impl iter::Product for $MatrixN { #[inline] fn product>>(iter: I) -> $MatrixN { iter.fold($MatrixN::identity(), Mul::mul) } } impl<'a, S: 'a + BaseFloat> iter::Product<&'a $MatrixN> for $MatrixN { #[inline] fn product>>(iter: I) -> $MatrixN { iter.fold($MatrixN::identity(), Mul::mul) } } impl_scalar_ops!($MatrixN { $($field),+ }); impl_scalar_ops!($MatrixN { $($field),+ }); impl_scalar_ops!($MatrixN { $($field),+ }); impl_scalar_ops!($MatrixN { $($field),+ }); impl_scalar_ops!($MatrixN { $($field),+ }); impl_scalar_ops!($MatrixN { $($field),+ }); impl_scalar_ops!($MatrixN { $($field),+ }); impl_scalar_ops!($MatrixN { $($field),+ }); impl_scalar_ops!($MatrixN { $($field),+ }); impl_scalar_ops!($MatrixN { $($field),+ }); impl_scalar_ops!($MatrixN { $($field),+ }); impl_scalar_ops!($MatrixN { $($field),+ }); impl $MatrixN { /// Component-wise casting to another type #[inline] pub fn cast(&self) -> Option<$MatrixN> { $( let $field = match self.$field.cast() { Some(field) => field, None => return None }; )+ Some($MatrixN { $($field),+ }) } } } } macro_rules! impl_scalar_ops { ($MatrixN:ident<$S:ident> { $($field:ident),+ }) => { impl_operator!(Mul<$MatrixN<$S>> for $S { fn mul(scalar, matrix) -> $MatrixN<$S> { $MatrixN { $($field: scalar * matrix.$field),+ } } }); impl_operator!(Div<$MatrixN<$S>> for $S { fn div(scalar, matrix) -> $MatrixN<$S> { $MatrixN { $($field: scalar / matrix.$field),+ } } }); impl_operator!(Rem<$MatrixN<$S>> for $S { fn rem(scalar, matrix) -> $MatrixN<$S> { $MatrixN { $($field: scalar % matrix.$field),+ } } }); }; } impl_matrix!(Matrix2, Vector2 { x: 0, y: 1 }); impl_matrix!(Matrix3, Vector3 { x: 0, y: 1, z: 2 }); #[cfg_attr(rustfmt, rustfmt_skip)] impl_matrix!(Matrix4, Vector4 { x: 0, y: 1, z: 2, w: 3 }); macro_rules! impl_mv_operator { ($MatrixN:ident, $VectorN:ident { $($field:ident : $row_index:expr),+ }) => { impl_operator!( Mul<$VectorN > for $MatrixN { fn mul(matrix, vector) -> $VectorN {$VectorN::new($(matrix.row($row_index).dot(vector.clone())),+)} }); } } impl_mv_operator!(Matrix2, Vector2 { x: 0, y: 1 }); impl_mv_operator!(Matrix3, Vector3 { x: 0, y: 1, z: 2 }); #[cfg(not(feature = "simd"))] #[cfg_attr(rustfmt, rustfmt_skip)] impl_mv_operator!(Matrix4, Vector4 { x: 0, y: 1, z: 2, w: 3 }); #[cfg(feature = "simd")] impl_operator!( Mul > for Matrix4 { fn mul(matrix, vector) -> Vector4 { matrix[0] * vector[0] + matrix[1] * vector[1] + matrix[2] * vector[2] + matrix[3] * vector[3] } }); impl_operator!( Mul > for Matrix2 { fn mul(lhs, rhs) -> Matrix2 { Matrix2::new(lhs.row(0).dot(rhs[0]), lhs.row(1).dot(rhs[0]), lhs.row(0).dot(rhs[1]), lhs.row(1).dot(rhs[1])) } }); impl_operator!( Mul > for Matrix3 { fn mul(lhs, rhs) -> Matrix3 { Matrix3::new(lhs.row(0).dot(rhs[0]), lhs.row(1).dot(rhs[0]), lhs.row(2).dot(rhs[0]), lhs.row(0).dot(rhs[1]), lhs.row(1).dot(rhs[1]), lhs.row(2).dot(rhs[1]), lhs.row(0).dot(rhs[2]), lhs.row(1).dot(rhs[2]), lhs.row(2).dot(rhs[2])) } }); // Using self.row(0).dot(other[0]) like the other matrix multiplies // causes the LLVM to miss identical loads and multiplies. This optimization // causes the code to be auto vectorized properly increasing the performance // around ~4 times. // Update: this should now be a bit more efficient impl_operator!( Mul > for Matrix4 { fn mul(lhs, rhs) -> Matrix4 { { let a = lhs[0]; let b = lhs[1]; let c = lhs[2]; let d = lhs[3]; #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::from_cols( a*rhs[0][0] + b*rhs[0][1] + c*rhs[0][2] + d*rhs[0][3], a*rhs[1][0] + b*rhs[1][1] + c*rhs[1][2] + d*rhs[1][3], a*rhs[2][0] + b*rhs[2][1] + c*rhs[2][2] + d*rhs[2][3], a*rhs[3][0] + b*rhs[3][1] + c*rhs[3][2] + d*rhs[3][3], ) } } }); macro_rules! index_operators { ($MatrixN:ident<$S:ident>, $n:expr, $Output:ty, $I:ty) => { impl<$S> Index<$I> for $MatrixN<$S> { type Output = $Output; #[inline] fn index<'a>(&'a self, i: $I) -> &'a $Output { let v: &[[$S; $n]; $n] = self.as_ref(); From::from(&v[i]) } } impl<$S> IndexMut<$I> for $MatrixN<$S> { #[inline] fn index_mut<'a>(&'a mut self, i: $I) -> &'a mut $Output { let v: &mut [[$S; $n]; $n] = self.as_mut(); From::from(&mut v[i]) } } }; } index_operators!(Matrix2, 2, Vector2, usize); index_operators!(Matrix3, 3, Vector3, usize); index_operators!(Matrix4, 4, Vector4, usize); // index_operators!(Matrix2, 2, [Vector2], Range); // index_operators!(Matrix3, 3, [Vector3], Range); // index_operators!(Matrix4, 4, [Vector4], Range); // index_operators!(Matrix2, 2, [Vector2], RangeTo); // index_operators!(Matrix3, 3, [Vector3], RangeTo); // index_operators!(Matrix4, 4, [Vector4], RangeTo); // index_operators!(Matrix2, 2, [Vector2], RangeFrom); // index_operators!(Matrix3, 3, [Vector3], RangeFrom); // index_operators!(Matrix4, 4, [Vector4], RangeFrom); // index_operators!(Matrix2, 2, [Vector2], RangeFull); // index_operators!(Matrix3, 3, [Vector3], RangeFull); // index_operators!(Matrix4, 4, [Vector4], RangeFull); impl From> for Matrix3 where A: Angle + Into::Unitless>>, { fn from(src: Euler) -> Matrix3 { // Page A-2: http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf let (sx, cx) = Rad::sin_cos(src.x.into()); let (sy, cy) = Rad::sin_cos(src.y.into()); let (sz, cz) = Rad::sin_cos(src.z.into()); #[cfg_attr(rustfmt, rustfmt_skip)] Matrix3::new( cy * cz, cx * sz + sx * sy * cz, sx * sz - cx * sy * cz, -cy * sz, cx * cz - sx * sy * sz, sx * cz + cx * sy * sz, sy, -sx * cy, cx * cy, ) } } impl From> for Matrix4 where A: Angle + Into::Unitless>>, { fn from(src: Euler) -> Matrix4 { // Page A-2: http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf let (sx, cx) = Rad::sin_cos(src.x.into()); let (sy, cy) = Rad::sin_cos(src.y.into()); let (sz, cz) = Rad::sin_cos(src.z.into()); #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( cy * cz, cx * sz + sx * sy * cz, sx * sz - cx * sy * cz, A::Unitless::zero(), -cy * sz, cx * cz - sx * sy * sz, sx * cz + cx * sy * sz, A::Unitless::zero(), sy, -sx * cy, cx * cy, A::Unitless::zero(), A::Unitless::zero(), A::Unitless::zero(), A::Unitless::zero(), A::Unitless::one(), ) } } macro_rules! fixed_array_conversions { ($MatrixN:ident <$S:ident> { $($field:ident : $index:expr),+ }, $n:expr) => { impl<$S> Into<[[$S; $n]; $n]> for $MatrixN<$S> { #[inline] fn into(self) -> [[$S; $n]; $n] { match self { $MatrixN { $($field),+ } => [$($field.into()),+] } } } impl<$S> AsRef<[[$S; $n]; $n]> for $MatrixN<$S> { #[inline] fn as_ref(&self) -> &[[$S; $n]; $n] { unsafe { mem::transmute(self) } } } impl<$S> AsMut<[[$S; $n]; $n]> for $MatrixN<$S> { #[inline] fn as_mut(&mut self) -> &mut [[$S; $n]; $n] { unsafe { mem::transmute(self) } } } impl<$S: Copy> From<[[$S; $n]; $n]> for $MatrixN<$S> { #[inline] fn from(m: [[$S; $n]; $n]) -> $MatrixN<$S> { // We need to use a copy here because we can't pattern match on arrays yet $MatrixN { $($field: From::from(m[$index])),+ } } } impl<'a, $S> From<&'a [[$S; $n]; $n]> for &'a $MatrixN<$S> { #[inline] fn from(m: &'a [[$S; $n]; $n]) -> &'a $MatrixN<$S> { unsafe { mem::transmute(m) } } } impl<'a, $S> From<&'a mut [[$S; $n]; $n]> for &'a mut $MatrixN<$S> { #[inline] fn from(m: &'a mut [[$S; $n]; $n]) -> &'a mut $MatrixN<$S> { unsafe { mem::transmute(m) } } } // impl<$S> Into<[$S; ($n * $n)]> for $MatrixN<$S> { // #[inline] // fn into(self) -> [[$S; $n]; $n] { // // TODO: Not sure how to implement this... // unimplemented!() // } // } impl<$S> AsRef<[$S; ($n * $n)]> for $MatrixN<$S> { #[inline] fn as_ref(&self) -> &[$S; ($n * $n)] { unsafe { mem::transmute(self) } } } impl<$S> AsMut<[$S; ($n * $n)]> for $MatrixN<$S> { #[inline] fn as_mut(&mut self) -> &mut [$S; ($n * $n)] { unsafe { mem::transmute(self) } } } // impl<$S> From<[$S; ($n * $n)]> for $MatrixN<$S> { // #[inline] // fn from(m: [$S; ($n * $n)]) -> $MatrixN<$S> { // // TODO: Not sure how to implement this... // unimplemented!() // } // } impl<'a, $S> From<&'a [$S; ($n * $n)]> for &'a $MatrixN<$S> { #[inline] fn from(m: &'a [$S; ($n * $n)]) -> &'a $MatrixN<$S> { unsafe { mem::transmute(m) } } } impl<'a, $S> From<&'a mut [$S; ($n * $n)]> for &'a mut $MatrixN<$S> { #[inline] fn from(m: &'a mut [$S; ($n * $n)]) -> &'a mut $MatrixN<$S> { unsafe { mem::transmute(m) } } } } } fixed_array_conversions!(Matrix2 { x:0, y:1 }, 2); fixed_array_conversions!(Matrix3 { x:0, y:1, z:2 }, 3); fixed_array_conversions!(Matrix4 { x:0, y:1, z:2, w:3 }, 4); #[cfg(feature = "mint")] macro_rules! mint_conversions { ($MatrixN:ident { $($field:ident),+ }, $MintN:ident) => { impl Into> for $MatrixN { #[inline] fn into(self) -> mint::$MintN { mint::$MintN { $($field: self.$field.into()),+ } } } impl From> for $MatrixN { #[inline] fn from(m: mint::$MintN) -> Self { $MatrixN { $($field: m.$field.into()),+ } } } } } #[cfg(feature = "mint")] mint_conversions!(Matrix2 { x, y }, ColumnMatrix2); #[cfg(feature = "mint")] mint_conversions!(Matrix3 { x, y, z }, ColumnMatrix3); #[cfg(feature = "mint")] mint_conversions!(Matrix4 { x, y, z, w }, ColumnMatrix4); impl From> for Matrix3 { /// Clone the elements of a 2-dimensional matrix into the top-left corner /// of a 3-dimensional identity matrix. fn from(m: Matrix2) -> Matrix3 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix3::new( m[0][0], m[0][1], S::zero(), m[1][0], m[1][1], S::zero(), S::zero(), S::zero(), S::one(), ) } } impl From> for Matrix4 { /// Clone the elements of a 2-dimensional matrix into the top-left corner /// of a 4-dimensional identity matrix. fn from(m: Matrix2) -> Matrix4 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( m[0][0], m[0][1], S::zero(), S::zero(), m[1][0], m[1][1], S::zero(), S::zero(), S::zero(), S::zero(), S::one(), S::zero(), S::zero(), S::zero(), S::zero(), S::one(), ) } } impl From> for Matrix4 { /// Clone the elements of a 3-dimensional matrix into the top-left corner /// of a 4-dimensional identity matrix. fn from(m: Matrix3) -> Matrix4 { #[cfg_attr(rustfmt, rustfmt_skip)] Matrix4::new( m[0][0], m[0][1], m[0][2], S::zero(), m[1][0], m[1][1], m[1][2], S::zero(), m[2][0], m[2][1], m[2][2], S::zero(), S::zero(), S::zero(), S::zero(), S::one(), ) } } impl From> for Quaternion { /// Convert the matrix to a quaternion fn from(mat: Matrix3) -> Quaternion { // http://www.cs.ucr.edu/~vbz/resources/quatut.pdf let trace = mat.trace(); let half: S = cast(0.5f64).unwrap(); if trace >= S::zero() { let s = (S::one() + trace).sqrt(); let w = half * s; let s = half / s; let x = (mat[1][2] - mat[2][1]) * s; let y = (mat[2][0] - mat[0][2]) * s; let z = (mat[0][1] - mat[1][0]) * s; Quaternion::new(w, x, y, z) } else if (mat[0][0] > mat[1][1]) && (mat[0][0] > mat[2][2]) { let s = ((mat[0][0] - mat[1][1] - mat[2][2]) + S::one()).sqrt(); let x = half * s; let s = half / s; let y = (mat[1][0] + mat[0][1]) * s; let z = (mat[0][2] + mat[2][0]) * s; let w = (mat[1][2] - mat[2][1]) * s; Quaternion::new(w, x, y, z) } else if mat[1][1] > mat[2][2] { let s = ((mat[1][1] - mat[0][0] - mat[2][2]) + S::one()).sqrt(); let y = half * s; let s = half / s; let z = (mat[2][1] + mat[1][2]) * s; let x = (mat[1][0] + mat[0][1]) * s; let w = (mat[2][0] - mat[0][2]) * s; Quaternion::new(w, x, y, z) } else { let s = ((mat[2][2] - mat[0][0] - mat[1][1]) + S::one()).sqrt(); let z = half * s; let s = half / s; let x = (mat[0][2] + mat[2][0]) * s; let y = (mat[2][1] + mat[1][2]) * s; let w = (mat[0][1] - mat[1][0]) * s; Quaternion::new(w, x, y, z) } } } impl fmt::Debug for Matrix2 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Matrix2 ")?; <[[S; 2]; 2] as fmt::Debug>::fmt(self.as_ref(), f) } } impl fmt::Debug for Matrix3 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Matrix3 ")?; <[[S; 3]; 3] as fmt::Debug>::fmt(self.as_ref(), f) } } impl fmt::Debug for Matrix4 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Matrix4 ")?; <[[S; 4]; 4] as fmt::Debug>::fmt(self.as_ref(), f) } } #[cfg(feature = "rand")] impl Distribution> for Standard where Standard: Distribution>, S: BaseFloat, { #[inline] fn sample(&self, rng: &mut R) -> Matrix2 { Matrix2 { x: self.sample(rng), y: self.sample(rng), } } } #[cfg(feature = "rand")] impl Distribution> for Standard where Standard: Distribution>, S: BaseFloat, { #[inline] fn sample(&self, rng: &mut R) -> Matrix3 { Matrix3 { x: rng.gen(), y: rng.gen(), z: rng.gen(), } } } #[cfg(feature = "rand")] impl Distribution> for Standard where Standard: Distribution>, S: BaseFloat, { #[inline] fn sample(&self, rng: &mut R) -> Matrix4 { Matrix4 { x: rng.gen(), y: rng.gen(), z: rng.gen(), w: rng.gen(), } } } // Sub procedure for SIMD when dealing with determinant and inversion #[inline] unsafe fn det_sub_proc_unsafe( m: &Matrix4, x: usize, y: usize, z: usize, ) -> Vector4 { let s: &[S; 16] = m.as_ref(); let a = Vector4::new( *s.get_unchecked(4 + x), *s.get_unchecked(12 + x), *s.get_unchecked(x), *s.get_unchecked(8 + x), ); let b = Vector4::new( *s.get_unchecked(8 + y), *s.get_unchecked(8 + y), *s.get_unchecked(4 + y), *s.get_unchecked(4 + y), ); let c = Vector4::new( *s.get_unchecked(12 + z), *s.get_unchecked(z), *s.get_unchecked(12 + z), *s.get_unchecked(z), ); let d = Vector4::new( *s.get_unchecked(8 + x), *s.get_unchecked(8 + x), *s.get_unchecked(4 + x), *s.get_unchecked(4 + x), ); let e = Vector4::new( *s.get_unchecked(12 + y), *s.get_unchecked(y), *s.get_unchecked(12 + y), *s.get_unchecked(y), ); let f = Vector4::new( *s.get_unchecked(4 + z), *s.get_unchecked(12 + z), *s.get_unchecked(z), *s.get_unchecked(8 + z), ); let g = Vector4::new( *s.get_unchecked(12 + x), *s.get_unchecked(x), *s.get_unchecked(12 + x), *s.get_unchecked(x), ); let h = Vector4::new( *s.get_unchecked(4 + y), *s.get_unchecked(12 + y), *s.get_unchecked(y), *s.get_unchecked(8 + y), ); let i = Vector4::new( *s.get_unchecked(8 + z), *s.get_unchecked(8 + z), *s.get_unchecked(4 + z), *s.get_unchecked(4 + z), ); let mut tmp = a.mul_element_wise(b.mul_element_wise(c)); tmp += d.mul_element_wise(e.mul_element_wise(f)); tmp += g.mul_element_wise(h.mul_element_wise(i)); tmp -= a.mul_element_wise(e.mul_element_wise(i)); tmp -= d.mul_element_wise(h.mul_element_wise(c)); tmp -= g.mul_element_wise(b.mul_element_wise(f)); tmp }