// Copyright 2013 The Lmath Developers. For a full listing of the authors, // refer to the AUTHORS 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. //! Various three-dimensional rotation types and impls. //! //! Some of these are more useful for constructing matricies and quaternions. //! than for general use. For example due to issues with gimble lock, it is //! not reccomended that Euler rotations be used for translations, but //! they _are_ useful for intuitively specifying rotations. //! //! # Examples //! //! ~~~rust //! Euler::new::(1.0, 2.0, 0.0).to_mat3() //! ~~~ //! //! ~~~rust //! AxisY::(0.3).to_quat() //! ~~~ use std::cast; use math::*; use math::{Point3, Ray3}; /// A two-dimensional rotation pub trait Rotation2: Eq + ApproxEq + ToMat2 { pub fn rotate_point2(&self, point: Point2) -> Point2; pub fn rotate_vec2(&self, vec: &Vec2) -> Vec2; pub fn rotate_ray2(&self, ray: &Ray2) -> Ray2; pub fn to_rotation_mat2(&self) -> RotationMat2; } /// A three-dimensional rotation pub trait Rotation3: Eq + ApproxEq + ToMat3 + ToMat4 + ToQuat { pub fn rotate_point3(&self, point: &Point3) -> Point3; pub fn rotate_vec3(&self, vec: &Vec3) -> Vec3; pub fn rotate_ray3(&self, ray: &Ray3) -> Ray3; pub fn to_rotation_mat3(&self) -> RotationMat3; } /// A two-dimensional rotation matrix. /// /// The matrix is guaranteed to be orthogonal, so some operations can be /// implemented more efficiently than the implementations for `math::Mat2`. To /// enforce orthogonality at the type level the operations have been restricted /// to a subeset of those implemented on `Mat3`. #[deriving(Eq, Clone)] pub struct RotationMat2 { priv mat: Mat2 } impl_approx!(RotationMat2 { mat }) impl RotationMat2 { #[inline] pub fn as_mat2<'a>(&'a self) -> & 'a Mat2 { unsafe { cast::transmute(self) } } } impl Rotation2 for RotationMat2 { pub fn rotate_point2(&self, point: Point2) -> Point2 { point.with_vec2(|vec| self.rotate_vec2(vec)) } pub fn rotate_vec2(&self, vec: &Vec2) -> Vec2 { self.mat.mul_v(vec) } pub fn rotate_ray2(&self, _ray: &Ray2) -> Ray2 { fail!("Not yet implemented.") } #[inline] pub fn to_rotation_mat2(&self) -> RotationMat2 { RotationMat2 { mat: self.to_mat2() } } } impl ToMat2 for RotationMat2 { #[inline] pub fn to_mat2(&self) -> Mat2 { self.mat.clone() } } impl RotationMat2 { #[inline] pub fn identity() -> RotationMat2 { RotationMat2 { mat: Mat2::identity() } } #[inline] pub fn zero() -> RotationMat2 { RotationMat2 { mat: Mat2::zero() } } } impl Neg> for RotationMat2 { #[inline] pub fn neg(&self) -> RotationMat2 { RotationMat2 { mat: -self.mat } } } impl Rotation3 for Quat { pub fn rotate_point3(&self, point: &Point3) -> Point3 { point.with_vec3(|vec| self.rotate_vec3(vec)) } pub fn rotate_vec3(&self, vec: &Vec3) -> Vec3 { self.mul_v(vec) } pub fn rotate_ray3(&self, _ray: &Ray3) -> Ray3 { fail!("Not yet implemented.") } #[inline] pub fn to_rotation_mat3(&self) -> RotationMat3 { RotationMat3 { mat: self.to_mat3() } } } /// A three-dimensional rotation matrix. /// /// The matrix is guaranteed to be orthogonal, so some operations, specifically /// inversion, can be implemented more efficiently than the implementations for /// `math::Mat3`. To enforce orthogonality at the type level the operations have /// been restricted to a subeset of those implemented on `Mat3`. #[deriving(Eq, Clone)] pub struct RotationMat3 { priv mat: Mat3 } impl_approx!(RotationMat3 { mat }) impl RotationMat3 { #[inline] pub fn as_mat3<'a>(&'a self) -> & 'a Mat3 { unsafe { cast::transmute(self) } } } impl Rotation3 for RotationMat3 { pub fn rotate_point3(&self, point: &Point3) -> Point3 { point.with_vec3(|vec| self.rotate_vec3(vec)) } pub fn rotate_vec3(&self, vec: &Vec3) -> Vec3 { self.mat.mul_v(vec) } pub fn rotate_ray3(&self, _ray: &Ray3) -> Ray3 { fail!("Not yet implemented.") } #[inline] pub fn to_rotation_mat3(&self) -> RotationMat3 { RotationMat3 { mat: self.to_mat3() } } } impl ToQuat for RotationMat3 { #[inline] pub fn to_quat(&self) -> Quat { self.mat.to_quat() } } impl ToMat3 for RotationMat3 { #[inline] pub fn to_mat3(&self) -> Mat3 { self.mat.clone() } } impl ToMat4 for RotationMat3 { #[inline] pub fn to_mat4(&self) -> Mat4 { self.mat.to_mat4() } } impl RotationMat3 { #[inline] pub fn identity() -> RotationMat3 { RotationMat3 { mat: Mat3::identity() } } #[inline] pub fn zero() -> RotationMat3 { RotationMat3 { mat: Mat3::zero() } } } impl Neg> for RotationMat3 { #[inline] pub fn neg(&self) -> RotationMat3 { RotationMat3 { mat: -self.mat } } } impl RotationMat3 { pub fn look_at(dir: &Vec3, up: &Vec3) -> RotationMat3 { RotationMat3 { mat: Mat3::look_at(dir, up) } } } /// Euler angles /// /// # Fields /// /// - `pitch`: the angular rotation around the `x` axis in radians /// - `yaw`: the angular rotation around the `y` axis in radians /// - `roll`: the angular rotation around the `z` axis in radians #[deriving(Eq, Clone)] pub struct Euler { pitch: T, yaw: T, roll: T } impl_dimensioned!(Euler, T, 3) impl_to_vec!(Euler, 3) impl_as_vec!(Euler, 3) impl_swap_components!(Euler) impl_approx!(Euler { pitch, yaw, roll }) pub trait ToEuler { pub fn to_euler(&self) -> Euler; } impl Euler { #[inline] pub fn new(pitch: T, yaw: T, roll: T) -> Euler { Euler { pitch: pitch, yaw: yaw, roll: roll } } } impl Rotation3 for Euler { pub fn rotate_point3(&self, _point: &Point3) -> Point3 { fail!("Not yet implemented.") } pub fn rotate_vec3(&self, _vec: &Vec3) -> Vec3 { fail!("Not yet implemented.") } pub fn rotate_ray3(&self, _ray: &Ray3) -> Ray3 { fail!("Not yet implemented.") } #[inline] pub fn to_rotation_mat3(&self) -> RotationMat3 { RotationMat3 { mat: self.to_mat3() } } } impl ToQuat for Euler { pub fn to_quat(&self) -> Quat { // http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles#Conversion let xdiv2 = self.pitch / two!(T); let ydiv2 = self.yaw / two!(T); let zdiv2 = self.roll / two!(T); Quat::new(zdiv2.cos() * xdiv2.cos() * ydiv2.cos() + zdiv2.sin() * xdiv2.sin() * ydiv2.sin(), zdiv2.sin() * xdiv2.cos() * ydiv2.cos() - zdiv2.cos() * xdiv2.sin() * ydiv2.sin(), zdiv2.cos() * xdiv2.sin() * ydiv2.cos() + zdiv2.sin() * xdiv2.cos() * ydiv2.sin(), zdiv2.cos() * xdiv2.cos() * ydiv2.sin() - zdiv2.sin() * xdiv2.sin() * ydiv2.cos()) } } impl ToMat3 for Euler { pub fn to_mat3(&self) -> Mat3 { // http://en.wikipedia.org/wiki/Rotation_matrix#General_rotations let cx = self.pitch.cos(); let sx = self.pitch.sin(); let cy = self.yaw.cos(); let sy = self.yaw.sin(); let cz = self.roll.cos(); let sz = self.roll.sin(); Mat3::new(cy * cz, cy * sz, -sy, -cx * sz + sx * sy * cz, cx * cz + sx * sy * sz, sx * cy, sx * sz + cx * sy * cz, -sx * cz + cx * sy * sz, cx * cy) } } impl ToMat4 for Euler { pub fn to_mat4(&self) -> Mat4 { // http://en.wikipedia.org/wiki/Rotation_matrix#General_rotations let cx = self.pitch.cos(); let sx = self.pitch.sin(); let cy = self.yaw.cos(); let sy = self.yaw.sin(); let cz = self.roll.cos(); let sz = self.roll.sin(); Mat4::new(cy * cz, cy * sz, -sy, zero!(T), -cx * sz + sx * sy * cz, cx * cz + sx * sy * sz, sx * cy, zero!(T), sx * sz + cx * sy * cz, -sx * cz + cx * sy * sz, cx * cy, zero!(T), zero!(T), zero!(T), zero!(T), one!(T)) } } #[cfg(test)] mod euler_tests { // TODO } /// A rotation about an arbitrary axis /// /// # Fields /// /// - `axis`: The axis vector about which to rotate. /// - `angle`: The angle of rotation in radians. #[deriving(Eq, Clone)] pub struct AxisAngle { axis: Vec3, angle: T, } impl_approx!(AxisAngle { axis, angle }) pub trait ToAxisAngle { pub fn to_axis_angle(&self) -> AxisAngle; } impl AxisAngle { pub fn new(axis: Vec3, angle: T) -> AxisAngle { AxisAngle { axis: axis, angle: angle } } } impl Rotation3 for AxisAngle { pub fn rotate_point3(&self, point: &Point3) -> Point3 { point.with_vec3(|vec| self.rotate_vec3(vec)) } pub fn rotate_vec3(&self, vec: &Vec3) -> Vec3 { // Rodrigues' rotation formula // http://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula // // ~~~ // v cos θ + (k × v) sin θ + k(k ⋅ v)(1 - cos θ) // ~~~ // // Where: // - `v` = vec // - `k` = self.axis // - `θ` = self.angle vec.mul_s(self.angle.cos()) .add_v(&self.axis.cross(vec)).mul_s(self.angle.sin()) .add_v(&self.axis.mul_s(self.axis.dot(vec)) .mul_s(one!(T) - self.angle.cos())) } pub fn rotate_ray3(&self, _ray: &Ray3) -> Ray3 { fail!("Not yet implemented.") } #[inline] pub fn to_rotation_mat3(&self) -> RotationMat3 { RotationMat3 { mat: self.to_mat3() } } } impl ToQuat for AxisAngle { pub fn to_quat(&self) -> Quat { let half = self.angle / two!(T); Quat::from_sv(half.cos(), self.axis.mul_s(half.sin())) } } impl ToMat3 for AxisAngle { pub fn to_mat3(&self) -> Mat3 { let c = self.angle.cos(); let s = self.angle.sin(); let _1_c = one!(T) - c; Mat3::new(_1_c * self.axis.x * self.axis.x + c, _1_c * self.axis.x * self.axis.y + s * self.axis.z, _1_c * self.axis.x * self.axis.z - s * self.axis.y, _1_c * self.axis.x * self.axis.y - s * self.axis.z, _1_c * self.axis.y * self.axis.y + c, _1_c * self.axis.y * self.axis.z + s * self.axis.x, _1_c * self.axis.x * self.axis.z + s * self.axis.y, _1_c * self.axis.y * self.axis.z - s * self.axis.x, _1_c * self.axis.z * self.axis.z + c) } } impl ToMat4 for AxisAngle { pub fn to_mat4(&self) -> Mat4 { let c = self.angle.cos(); let s = self.angle.sin(); let _1_c = one!(T) - c; Mat4::new(_1_c * self.axis.x * self.axis.x + c, _1_c * self.axis.x * self.axis.y + s * self.axis.z, _1_c * self.axis.x * self.axis.z - s * self.axis.y, zero!(T), _1_c * self.axis.x * self.axis.y - s * self.axis.z, _1_c * self.axis.y * self.axis.y + c, _1_c * self.axis.y * self.axis.z + s * self.axis.x, zero!(T), _1_c * self.axis.x * self.axis.z + s * self.axis.y, _1_c * self.axis.y * self.axis.z - s * self.axis.x, _1_c * self.axis.z * self.axis.z + c, zero!(T), zero!(T), zero!(T), zero!(T), one!(T)) } } #[cfg(test)] mod axis_angle_tests { use math::*; use transform::*; #[test] fn test_to_quat() { let v = Vec3::new(1f, 0f, 0f); let q = AxisAngle::new(Vec3::new(0f, 0f, -1f), (-45f).to_radians()).to_quat(); // http://www.wolframalpha.com/input/?i={1,0}+rotate+-45+degrees assert_approx_eq!(q.mul_v(&v), Vec3::new(1f/2f.sqrt(), 1f/2f.sqrt(), 0f)); assert_eq!(q.mul_v(&v).magnitude(), v.magnitude()); assert_approx_eq!(q.to_mat3(), Mat3::new( 1f/2f.sqrt(), 1f/2f.sqrt(), 0f, -1f/2f.sqrt(), 1f/2f.sqrt(), 0f, 0f, 0f, 1f)); } } /// An angle around the X axis (pitch), in radians. #[deriving(Eq, Clone)] pub struct AngleX(T); impl_approx!(AngleX) impl Rotation3 for AngleX { pub fn rotate_point3(&self, _point: &Point3) -> Point3 { fail!("Not yet implemented.") } pub fn rotate_vec3(&self, _vec: &Vec3) -> Vec3 { fail!("Not yet implemented.") } pub fn rotate_ray3(&self, _ray: &Ray3) -> Ray3 { fail!("Not yet implemented.") } #[inline] pub fn to_rotation_mat3(&self) -> RotationMat3 { RotationMat3 { mat: self.to_mat3() } } } impl ToQuat for AngleX { pub fn to_quat(&self) -> Quat { Quat::new(((**self) / two!(T)).cos(), (**self).sin(), zero!(T), zero!(T)) } } impl ToMat3 for AngleX { pub fn to_mat3(&self) -> Mat3 { // http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations let cos_theta = (**self).cos(); let sin_theta = (**self).sin(); Mat3::new(one!(T), zero!(T), zero!(T), zero!(T), cos_theta.clone(), sin_theta.clone(), zero!(T), -sin_theta.clone(), cos_theta.clone()) } } impl ToMat4 for AngleX { pub fn to_mat4(&self) -> Mat4 { // http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations let cos_theta = (**self).cos(); let sin_theta = (**self).sin(); Mat4::new(one!(T), zero!(T), zero!(T), zero!(T), zero!(T), cos_theta.clone(), sin_theta.clone(), zero!(T), zero!(T), -sin_theta.clone(), cos_theta.clone(), zero!(T), zero!(T), zero!(T), zero!(T), one!(T)) } } #[cfg(test)] mod angle_x_tests { // TODO } /// An angle around the X axis (yaw), in radians. #[deriving(Eq, Clone)] pub struct AngleY(T); impl_approx!(AngleY) impl Rotation3 for AngleY { pub fn rotate_point3(&self, _point: &Point3) -> Point3 { fail!("Not yet implemented.") } pub fn rotate_vec3(&self, _vec: &Vec3) -> Vec3 { fail!("Not yet implemented.") } pub fn rotate_ray3(&self, _ray: &Ray3) -> Ray3 { fail!("Not yet implemented.") } #[inline] pub fn to_rotation_mat3(&self) -> RotationMat3 { RotationMat3 { mat: self.to_mat3() } } } impl ToQuat for AngleY { pub fn to_quat(&self) -> Quat { Quat::new(((**self) / two!(T)).cos(), zero!(T), (**self).sin(), zero!(T)) } } impl ToMat3 for AngleY { pub fn to_mat3(&self) -> Mat3 { // http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations let cos_theta = (**self).cos(); let sin_theta = (**self).sin(); Mat3::new(cos_theta.clone(), zero!(T), -sin_theta.clone(), zero!(T), one!(T), zero!(T), sin_theta.clone(), zero!(T), cos_theta.clone()) } } impl ToMat4 for AngleY { pub fn to_mat4(&self) -> Mat4 { // http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations let cos_theta = (**self).cos(); let sin_theta = (**self).sin(); Mat4::new(cos_theta.clone(), zero!(T), -sin_theta.clone(), zero!(T), zero!(T), one!(T), zero!(T), zero!(T), sin_theta.clone(), zero!(T), cos_theta.clone(), zero!(T), zero!(T), zero!(T), zero!(T), one!(T)) } } #[cfg(test)] mod angle_y_tests { // TODO } /// An angle around the Z axis (roll), in radians. #[deriving(Eq, Clone)] pub struct AngleZ(T); impl_approx!(AngleZ) impl Rotation2 for AngleZ { pub fn rotate_point2(&self, _point: Point2) -> Point2 { fail!("Not yet implemented.") } pub fn rotate_vec2(&self, _vec: &Vec2) -> Vec2 { fail!("Not yet implemented.") } pub fn rotate_ray2(&self, _ray: &Ray2) -> Ray2 { fail!("Not yet implemented.") } #[inline] pub fn to_rotation_mat2(&self) -> RotationMat2 { fail!("Not yet implemented.") } } impl Rotation3 for AngleZ { pub fn rotate_point3(&self, _point: &Point3) -> Point3 { fail!("Not yet implemented.") } pub fn rotate_vec3(&self, _vec: &Vec3) -> Vec3 { fail!("Not yet implemented.") } pub fn rotate_ray3(&self, _ray: &Ray3) -> Ray3 { fail!("Not yet implemented.") } #[inline] pub fn to_rotation_mat3(&self) -> RotationMat3 { RotationMat3 { mat: self.to_mat3() } } } impl ToQuat for AngleZ { pub fn to_quat(&self) -> Quat { Quat::new(((**self) / two!(T)).cos(), zero!(T), zero!(T), (**self).sin()) } } impl ToMat3 for AngleZ { pub fn to_mat3(&self) -> Mat3 { // http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations let cos_theta = (**self).cos(); let sin_theta = (**self).sin(); Mat3::new(cos_theta.clone(), sin_theta.clone(), zero!(T), -sin_theta.clone(), cos_theta.clone(), zero!(T), zero!(T), zero!(T), one!(T)) } } impl ToMat4 for AngleZ { pub fn to_mat4(&self) -> Mat4 { // http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations let cos_theta = (**self).cos(); let sin_theta = (**self).sin(); Mat4::new(cos_theta.clone(), sin_theta.clone(), zero!(T), zero!(T), -sin_theta.clone(), cos_theta.clone(), zero!(T), zero!(T), zero!(T), zero!(T), one!(T), zero!(T), zero!(T), zero!(T), zero!(T), one!(T)) } } #[cfg(test)] mod angle_z_tests { // TODO }