/* * * Computer-graphics specific implementations. * Currently, it is mostly implemented for homogeneous matrices in 2- and 3-space. * */ use num::{One, Zero}; use crate::base::allocator::Allocator; use crate::base::dimension::{DimName, DimNameDiff, DimNameSub, U1}; use crate::base::storage::{Storage, StorageMut}; use crate::base::{ DefaultAllocator, Matrix3, Matrix4, MatrixN, Scalar, SquareMatrix, Unit, Vector, Vector2, Vector3, VectorN, }; use crate::geometry::{ Isometry, IsometryMatrix3, Orthographic3, Perspective3, Point, Point2, Point3, Rotation2, Rotation3, }; use simba::scalar::{ClosedAdd, ClosedMul, RealField}; /// # Translation and scaling in any dimension impl MatrixN where N: Scalar + Zero + One, DefaultAllocator: Allocator, { /// Creates a new homogeneous matrix that applies the same scaling factor on each dimension. #[inline] pub fn new_scaling(scaling: N) -> Self { let mut res = Self::from_diagonal_element(scaling); res[(D::dim() - 1, D::dim() - 1)] = N::one(); res } /// Creates a new homogeneous matrix that applies a distinct scaling factor for each dimension. #[inline] pub fn new_nonuniform_scaling(scaling: &Vector, SB>) -> Self where D: DimNameSub, SB: Storage>, { let mut res = Self::identity(); for i in 0..scaling.len() { res[(i, i)] = scaling[i].inlined_clone(); } res } /// Creates a new homogeneous matrix that applies a pure translation. #[inline] pub fn new_translation(translation: &Vector, SB>) -> Self where D: DimNameSub, SB: Storage>, { let mut res = Self::identity(); res.fixed_slice_mut::, U1>(0, D::dim() - 1) .copy_from(translation); res } } /// # 2D transformations as a Matrix3 impl Matrix3 { /// Builds a 2 dimensional homogeneous rotation matrix from an angle in radian. #[inline] pub fn new_rotation(angle: N) -> Self { Rotation2::new(angle).to_homogeneous() } /// Creates a new homogeneous matrix that applies a scaling factor for each dimension with respect to point. /// /// Can be used to implement "zoom_to" functionality. #[inline] pub fn new_nonuniform_scaling_wrt_point(scaling: &Vector2, pt: &Point2) -> Self { let zero = N::zero(); let one = N::one(); Matrix3::new( scaling.x, zero, pt.x - pt.x * scaling.x, zero, scaling.y, pt.y - pt.y * scaling.y, zero, zero, one, ) } } /// # 3D transformations as a Matrix4 impl Matrix4 { /// Builds a 3D homogeneous rotation matrix from an axis and an angle (multiplied together). /// /// Returns the identity matrix if the given argument is zero. #[inline] pub fn new_rotation(axisangle: Vector3) -> Self { Rotation3::new(axisangle).to_homogeneous() } /// Builds a 3D homogeneous rotation matrix from an axis and an angle (multiplied together). /// /// Returns the identity matrix if the given argument is zero. #[inline] pub fn new_rotation_wrt_point(axisangle: Vector3, pt: Point3) -> Self { let rot = Rotation3::from_scaled_axis(axisangle); Isometry::rotation_wrt_point(rot, pt).to_homogeneous() } /// Creates a new homogeneous matrix that applies a scaling factor for each dimension with respect to point. /// /// Can be used to implement "zoom_to" functionality. #[inline] pub fn new_nonuniform_scaling_wrt_point(scaling: &Vector3, pt: &Point3) -> Self { let zero = N::zero(); let one = N::one(); Matrix4::new( scaling.x, zero, zero, pt.x - pt.x * scaling.x, zero, scaling.y, zero, pt.y - pt.y * scaling.y, zero, zero, scaling.z, pt.z - pt.z * scaling.z, zero, zero, zero, one, ) } /// Builds a 3D homogeneous rotation matrix from an axis and an angle (multiplied together). /// /// Returns the identity matrix if the given argument is zero. /// This is identical to `Self::new_rotation`. #[inline] pub fn from_scaled_axis(axisangle: Vector3) -> Self { Rotation3::from_scaled_axis(axisangle).to_homogeneous() } /// Creates a new rotation from Euler angles. /// /// The primitive rotations are applied in order: 1 roll − 2 pitch − 3 yaw. pub fn from_euler_angles(roll: N, pitch: N, yaw: N) -> Self { Rotation3::from_euler_angles(roll, pitch, yaw).to_homogeneous() } /// Builds a 3D homogeneous rotation matrix from an axis and a rotation angle. pub fn from_axis_angle(axis: &Unit>, angle: N) -> Self { Rotation3::from_axis_angle(axis, angle).to_homogeneous() } /// Creates a new homogeneous matrix for an orthographic projection. #[inline] pub fn new_orthographic(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> Self { Orthographic3::new(left, right, bottom, top, znear, zfar).into_inner() } /// Creates a new homogeneous matrix for a perspective projection. #[inline] pub fn new_perspective(aspect: N, fovy: N, znear: N, zfar: N) -> Self { Perspective3::new(aspect, fovy, znear, zfar).into_inner() } /// Creates an isometry that corresponds to the local frame of an observer standing at the /// point `eye` and looking toward `target`. /// /// It maps the view direction `target - eye` to the positive `z` axis and the origin to the /// `eye`. #[inline] pub fn face_towards(eye: &Point3, target: &Point3, up: &Vector3) -> Self { IsometryMatrix3::face_towards(eye, target, up).to_homogeneous() } /// Deprecated: Use [Matrix4::face_towards] instead. #[deprecated(note = "renamed to `face_towards`")] pub fn new_observer_frame(eye: &Point3, target: &Point3, up: &Vector3) -> Self { Matrix4::face_towards(eye, target, up) } /// Builds a right-handed look-at view matrix. #[inline] pub fn look_at_rh(eye: &Point3, target: &Point3, up: &Vector3) -> Self { IsometryMatrix3::look_at_rh(eye, target, up).to_homogeneous() } /// Builds a left-handed look-at view matrix. #[inline] pub fn look_at_lh(eye: &Point3, target: &Point3, up: &Vector3) -> Self { IsometryMatrix3::look_at_lh(eye, target, up).to_homogeneous() } } /// # Append/prepend translation and scaling impl> SquareMatrix { /// Computes the transformation equal to `self` followed by an uniform scaling factor. #[inline] #[must_use = "Did you mean to use append_scaling_mut()?"] pub fn append_scaling(&self, scaling: N) -> MatrixN where D: DimNameSub, DefaultAllocator: Allocator, { let mut res = self.clone_owned(); res.append_scaling_mut(scaling); res } /// Computes the transformation equal to an uniform scaling factor followed by `self`. #[inline] #[must_use = "Did you mean to use prepend_scaling_mut()?"] pub fn prepend_scaling(&self, scaling: N) -> MatrixN where D: DimNameSub, DefaultAllocator: Allocator, { let mut res = self.clone_owned(); res.prepend_scaling_mut(scaling); res } /// Computes the transformation equal to `self` followed by a non-uniform scaling factor. #[inline] #[must_use = "Did you mean to use append_nonuniform_scaling_mut()?"] pub fn append_nonuniform_scaling( &self, scaling: &Vector, SB>, ) -> MatrixN where D: DimNameSub, SB: Storage>, DefaultAllocator: Allocator, { let mut res = self.clone_owned(); res.append_nonuniform_scaling_mut(scaling); res } /// Computes the transformation equal to a non-uniform scaling factor followed by `self`. #[inline] #[must_use = "Did you mean to use prepend_nonuniform_scaling_mut()?"] pub fn prepend_nonuniform_scaling( &self, scaling: &Vector, SB>, ) -> MatrixN where D: DimNameSub, SB: Storage>, DefaultAllocator: Allocator, { let mut res = self.clone_owned(); res.prepend_nonuniform_scaling_mut(scaling); res } /// Computes the transformation equal to `self` followed by a translation. #[inline] #[must_use = "Did you mean to use append_translation_mut()?"] pub fn append_translation(&self, shift: &Vector, SB>) -> MatrixN where D: DimNameSub, SB: Storage>, DefaultAllocator: Allocator, { let mut res = self.clone_owned(); res.append_translation_mut(shift); res } /// Computes the transformation equal to a translation followed by `self`. #[inline] #[must_use = "Did you mean to use prepend_translation_mut()?"] pub fn prepend_translation( &self, shift: &Vector, SB>, ) -> MatrixN where D: DimNameSub, SB: Storage>, DefaultAllocator: Allocator + Allocator>, { let mut res = self.clone_owned(); res.prepend_translation_mut(shift); res } /// Computes in-place the transformation equal to `self` followed by an uniform scaling factor. #[inline] pub fn append_scaling_mut(&mut self, scaling: N) where S: StorageMut, D: DimNameSub, { let mut to_scale = self.fixed_rows_mut::>(0); to_scale *= scaling; } /// Computes in-place the transformation equal to an uniform scaling factor followed by `self`. #[inline] pub fn prepend_scaling_mut(&mut self, scaling: N) where S: StorageMut, D: DimNameSub, { let mut to_scale = self.fixed_columns_mut::>(0); to_scale *= scaling; } /// Computes in-place the transformation equal to `self` followed by a non-uniform scaling factor. #[inline] pub fn append_nonuniform_scaling_mut(&mut self, scaling: &Vector, SB>) where S: StorageMut, D: DimNameSub, SB: Storage>, { for i in 0..scaling.len() { let mut to_scale = self.fixed_rows_mut::(i); to_scale *= scaling[i].inlined_clone(); } } /// Computes in-place the transformation equal to a non-uniform scaling factor followed by `self`. #[inline] pub fn prepend_nonuniform_scaling_mut( &mut self, scaling: &Vector, SB>, ) where S: StorageMut, D: DimNameSub, SB: Storage>, { for i in 0..scaling.len() { let mut to_scale = self.fixed_columns_mut::(i); to_scale *= scaling[i].inlined_clone(); } } /// Computes the transformation equal to `self` followed by a translation. #[inline] pub fn append_translation_mut(&mut self, shift: &Vector, SB>) where S: StorageMut, D: DimNameSub, SB: Storage>, { for i in 0..D::dim() { for j in 0..D::dim() - 1 { let add = shift[j].inlined_clone() * self[(D::dim() - 1, i)].inlined_clone(); self[(j, i)] += add; } } } /// Computes the transformation equal to a translation followed by `self`. #[inline] pub fn prepend_translation_mut(&mut self, shift: &Vector, SB>) where D: DimNameSub, S: StorageMut, SB: Storage>, DefaultAllocator: Allocator>, { let scale = self .fixed_slice::>(D::dim() - 1, 0) .tr_dot(&shift); let post_translation = self.fixed_slice::, DimNameDiff>(0, 0) * shift; self[(D::dim() - 1, D::dim() - 1)] += scale; let mut translation = self.fixed_slice_mut::, U1>(0, D::dim() - 1); translation += post_translation; } } /// # Transformation of vectors and points impl, S: Storage> SquareMatrix where DefaultAllocator: Allocator + Allocator> + Allocator, DimNameDiff>, { /// Transforms the given vector, assuming the matrix `self` uses homogeneous coordinates. #[inline] pub fn transform_vector( &self, v: &VectorN>, ) -> VectorN> { let transform = self.fixed_slice::, DimNameDiff>(0, 0); let normalizer = self.fixed_slice::>(D::dim() - 1, 0); let n = normalizer.tr_dot(&v); if !n.is_zero() { return transform * (v / n); } transform * v } /// Transforms the given point, assuming the matrix `self` uses homogeneous coordinates. #[inline] pub fn transform_point( &self, pt: &Point::USIZE }>, ) -> Point::USIZE }> { let transform = self.fixed_slice::, DimNameDiff>(0, 0); let translation = self.fixed_slice::, U1>(0, D::dim() - 1); let normalizer = self.fixed_slice::>(D::dim() - 1, 0); let n = normalizer.tr_dot(&pt.coords) + unsafe { *self.get_unchecked((D::dim() - 1, D::dim() - 1)) }; if !n.is_zero() { (transform * pt + translation) / n } else { transform * pt + translation } } }