From e4e9b6909ec5f7a37d740e1a5a244d7468da5965 Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Mon, 30 Sep 2013 14:30:40 +1000 Subject: [PATCH] Reduce the number of rotation types, shifting some of the functionality to the quaternion and matrix constructors. --- src/cgmath/matrix.rs | 65 +++++++++- src/cgmath/quaternion.rs | 47 ++++++- src/cgmath/rotation.rs | 259 +++++---------------------------------- 3 files changed, 142 insertions(+), 229 deletions(-) diff --git a/src/cgmath/matrix.rs b/src/cgmath/matrix.rs index 117ec26..e00662d 100644 --- a/src/cgmath/matrix.rs +++ b/src/cgmath/matrix.rs @@ -17,7 +17,7 @@ use std::num::{Zero, zero, One, one, cast, sqrt}; -use angle::{Rad, sin, cos}; +use angle::{Angle, Rad, sin, cos, sin_cos}; use array::{Array, build}; use quaternion::{Quat, ToQuat}; use vector::{Vector, EuclideanVector}; @@ -121,6 +121,69 @@ impl Mat3 { Mat3::from_cols(up, side, dir) } + + /// Create a matrix from a rotation around the `x` axis (pitch). + pub fn from_angle_x>(theta: A) -> Mat3 { + // http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations + let (s, c) = sin_cos(theta); + Mat3::new(one(), zero(), zero(), + zero(), c.clone(), s.clone(), + zero(), -s.clone(), c.clone()) + } + + /// Create a matrix from a rotation around the `y` axis (yaw). + pub fn from_angle_y>(theta: A) -> Mat3 { + // http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations + let (s, c) = sin_cos(theta); + Mat3::new(c.clone(), zero(), -s.clone(), + zero(), one(), zero(), + s.clone(), zero(), c.clone()) + } + + /// Create a matrix from a rotation around the `z` axis (roll). + pub fn from_angle_z>(theta: A) -> Mat3 { + // http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations + let (s, c) = sin_cos(theta); + Mat3::new(c.clone(), s.clone(), zero(), + -s.clone(), c.clone(), zero(), + zero(), zero(), one()) + } + + /// Create a matrix from a set of euler angles. + /// + /// # Parameters + /// + /// - `x`: the angular rotation around the `x` axis (pitch). + /// - `y`: the angular rotation around the `y` axis (yaw). + /// - `z`: the angular rotation around the `z` axis (roll). + pub fn from_euler>(x: A, y: A, z: A) -> Mat3 { + // http://en.wikipedia.org/wiki/Rotation_matrix#General_rotations + let (sx, cx) = sin_cos(x); + let (sy, cy) = sin_cos(y); + let (sz, cz) = sin_cos(z); + + 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) + } + + /// Create a matrix from a rotation around an arbitrary axis + pub fn from_axis_angle>(axis: &Vec3, angle: A) -> Mat3 { + let (s, c) = sin_cos(angle); + let _1subc = one::() - c; + + Mat3::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) + } } impl Mat4 { diff --git a/src/cgmath/quaternion.rs b/src/cgmath/quaternion.rs index 846f38f..8de82ff 100644 --- a/src/cgmath/quaternion.rs +++ b/src/cgmath/quaternion.rs @@ -15,7 +15,7 @@ use std::num::{zero, one, cast, sqrt}; -use angle::{Angle, Rad, acos, cos, sin}; +use angle::{Angle, Rad, acos, cos, sin, sin_cos}; use array::{Array, build}; use matrix::{Mat3, ToMat3}; use vector::{Vec3, Vector, EuclideanVector}; @@ -50,6 +50,51 @@ impl Quat { Mat3::look_at(dir, up).to_quat() } + /// Create a matrix from a rotation around the `x` axis (pitch). + #[inline] + pub fn from_angle_x>(theta: A) -> Quat { + Quat::new(cos(theta.mul_s(cast(0.5))), sin(theta), zero(), zero()) + } + + /// Create a matrix from a rotation around the `y` axis (yaw). + #[inline] + pub fn from_angle_y>(theta: A) -> Quat { + Quat::new(cos(theta.mul_s(cast(0.5))), zero(), sin(theta), zero()) + } + + /// Create a matrix from a rotation around the `z` axis (roll). + #[inline] + pub fn from_angle_z>(theta: A) -> Quat { + Quat::new(cos(theta.mul_s(cast(0.5))), zero(), zero(), sin(theta)) + } + + /// Create a quaternion from a set of euler angles. + /// + /// # Parameters + /// + /// - `x`: the angular rotation around the `x` axis (pitch). + /// - `y`: the angular rotation around the `y` axis (yaw). + /// - `z`: the angular rotation around the `z` axis (roll). + pub fn from_euler>(x: A, y: A, z: A) -> Quat { + // http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles#Conversion + let (sx2, cx2) = sin_cos(x.mul_s(cast(0.5))); + let (sy2, cy2) = sin_cos(y.mul_s(cast(0.5))); + let (sz2, cz2) = sin_cos(z.mul_s(cast(0.5))); + + Quat::new(cz2 * cx2 * cy2 + sz2 * sx2 * sy2, + sz2 * cx2 * cy2 - cz2 * sx2 * sy2, + cz2 * sx2 * cy2 + sz2 * cx2 * sy2, + cz2 * cx2 * sy2 - sz2 * sx2 * cy2) + } + + /// Create a quaternion from a rotation around an arbitrary axis + #[inline] + pub fn from_axis_angle>(axis: &Vec3, angle: A) -> Quat { + let half = angle.mul_s(cast(0.5)); + Quat::from_sv(cos(half.clone()), + axis.mul_s(sin(half))) + } + /// The additive identity, ie: `q = 0 + 0i + 0j + 0i` #[inline] pub fn zero() -> Quat { diff --git a/src/cgmath/rotation.rs b/src/cgmath/rotation.rs index eae3973..64193db 100644 --- a/src/cgmath/rotation.rs +++ b/src/cgmath/rotation.rs @@ -13,10 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::num::{cast, one}; - -use angle::{Angle, sin, cos, sin_cos}; -use array::Array; +use angle::Angle; use matrix::Matrix; use matrix::{Mat2, ToMat2}; use matrix::{Mat3, ToMat3}; @@ -156,6 +153,37 @@ impl Rot3 { Rot3 { mat: Mat3::look_at(dir, up) } } + /// Create a rotation matrix from a rotation around the `x` axis (pitch). + pub fn from_angle_x>(theta: A) -> Rot3 { + Rot3 { mat: Mat3::from_angle_x(theta) } + } + + /// Create a rotation matrix from a rotation around the `y` axis (yaw). + pub fn from_angle_y>(theta: A) -> Rot3 { + Rot3 { mat: Mat3::from_angle_y(theta) } + } + + /// Create a rotation matrix from a rotation around the `z` axis (roll). + pub fn from_angle_z>(theta: A) -> Rot3 { + Rot3 { mat: Mat3::from_angle_z(theta) } + } + + /// Create a rotation matrix from a set of euler angles. + /// + /// # Parameters + /// + /// - `x`: the angular rotation around the `x` axis (pitch). + /// - `y`: the angular rotation around the `y` axis (yaw). + /// - `z`: the angular rotation around the `z` axis (roll). + pub fn from_euler>(x: A, y: A, z: A) -> Rot3 { + Rot3 { mat: Mat3::from_euler(x, y ,z) } + } + + /// Create a rotation matrix from a rotation around an arbitrary axis. + pub fn from_axis_angle>(axis: &Vec3, angle: A) -> Rot3 { + Rot3 { mat: Mat3::from_axis_angle(axis, angle) } + } + #[inline] pub fn as_mat3<'a>(&'a self) -> &'a Mat3 { &'a self.mat } } @@ -258,226 +286,3 @@ impl Rotation3 for Quat { #[inline] fn invert_self(&mut self) { *self = self.invert() } } - -/// Euler angles -/// -/// # Fields -/// -/// - `x`: the angular rotation around the `x` axis (pitch) -/// - `y`: the angular rotation around the `y` axis (yaw) -/// - `z`: the angular rotation around the `z` axis (roll) -/// -/// # Notes -/// -/// Whilst Euler angles are more intuitive to specify than quaternions, -/// they are not recommended for general use because they are prone to gimble -/// lock, and are hard to interpolate between. -#[deriving(Eq, Clone)] -pub struct Euler { x: A, y: A, z: A } - -array!(impl Euler -> [A, ..3] _3) - -impl> Euler { - #[inline] - pub fn new(x: A, y: A, z: A) -> Euler { - Euler { x: x, y: y, z: z } - } -} - -pub trait ToEuler { - fn to_euler(&self) -> Euler; -} - -impl> ToMat3 for Euler { - fn to_mat3(&self) -> Mat3 { - // http://en.wikipedia.org/wiki/Rotation_matrix#General_rotations - let (sx, cx) = sin_cos(self.x.clone()); - let (sy, cy) = sin_cos(self.y.clone()); - let (sz, cz) = sin_cos(self.z.clone()); - - 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> ToRot3 for Euler { - #[inline] - fn to_rot3(&self) -> Rot3 { - Rot3 { mat: self.to_mat3() } - } -} - -impl> ToQuat for Euler { - fn to_quat(&self) -> Quat { - // http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles#Conversion - let (sx2, cx2) = sin_cos(self.x.mul_s(cast(0.5))); - let (sy2, cy2) = sin_cos(self.y.mul_s(cast(0.5))); - let (sz2, cz2) = sin_cos(self.z.mul_s(cast(0.5))); - - Quat::new(cz2 * cx2 * cy2 + sz2 * sx2 * sy2, - sz2 * cx2 * cy2 - cz2 * sx2 * sy2, - cz2 * sx2 * cy2 + sz2 * cx2 * sy2, - cz2 * cx2 * sy2 - sz2 * sx2 * cy2) - } -} - -impl> Rotation3 for Euler { - #[inline] - fn rotate_point3(&self, _point: &Point3) -> Point3 { fail!("Not yet implemented") } - - #[inline] - fn rotate_vec3(&self, _vec: &Vec3) -> Vec3 { fail!("Not yet implemented"); } - - #[inline] - fn rotate_ray3(&self, _ray: &Ray3) -> Ray3 { fail!("Not yet implemented") } - - #[inline] - fn concat(&self, _other: &Euler) -> Euler { fail!("Not yet implemented") } - - #[inline] - fn concat_self(&mut self, _other: &Euler) { fail!("Not yet implemented"); } - - #[inline] - fn invert(&self) -> Euler { fail!("Not yet implemented") } - - #[inline] - fn invert_self(&mut self) { fail!("Not yet implemented"); } -} - -impl> ApproxEq for Euler { - #[inline] - fn approx_epsilon() -> S { - // TODO: fix this after static methods are fixed in rustc - fail!(~"Doesn't work!"); - } - - #[inline] - fn approx_eq(&self, other: &Euler) -> bool { - self.x.approx_eq(&other.x) && - self.y.approx_eq(&other.y) && - self.z.approx_eq(&other.z) - } - - #[inline] - fn approx_eq_eps(&self, other: &Euler, approx_epsilon: &S) -> bool { - self.x.approx_eq_eps(&other.x, approx_epsilon) && - self.y.approx_eq_eps(&other.y, approx_epsilon) && - self.z.approx_eq_eps(&other.z, approx_epsilon) - } -} - -/// A rotation about an arbitrary axis -/// -/// # Fields -/// -/// - `v`: the axis of rotation -/// - `a`: the angular rotation -#[deriving(Eq, Clone)] -pub struct AxisAngle { v: Vec3, a: A } - -pub trait ToAxisAngle { - fn to_axis_angle(&self) -> AxisAngle; -} - -impl> AxisAngle { - #[inline] - pub fn new(v: Vec3, a: A) -> AxisAngle { - AxisAngle { v: v, a: a } - } -} - -impl> ToMat3 for AxisAngle { - fn to_mat3(&self) -> Mat3 { - let (s, c) = sin_cos(self.a.clone()); - let _1subc = one::() - c; - - Mat3::new(_1subc * self.v.x * self.v.x + c, - _1subc * self.v.x * self.v.y + s * self.v.z, - _1subc * self.v.x * self.v.z - s * self.v.y, - - _1subc * self.v.x * self.v.y - s * self.v.z, - _1subc * self.v.y * self.v.y + c, - _1subc * self.v.y * self.v.z + s * self.v.x, - - _1subc * self.v.x * self.v.z + s * self.v.y, - _1subc * self.v.y * self.v.z - s * self.v.x, - _1subc * self.v.z * self.v.z + c) - } -} - -impl> ToRot3 for AxisAngle { - #[inline] - fn to_rot3(&self) -> Rot3 { - Rot3 { mat: self.to_mat3() } - } -} - -impl> ToQuat for AxisAngle { - fn to_quat(&self) -> Quat { - let half = self.a.mul_s(cast(0.5)); - Quat::from_sv( - cos(half.clone()), - self.v.mul_s(sin(half.clone())) - ) - } -} - -impl> Rotation3 for AxisAngle { - #[inline] - fn rotate_point3(&self, _point: &Point3) -> Point3 { fail!("Not yet implemented") } - - #[inline] - fn rotate_vec3(&self, _vec: &Vec3) -> Vec3 { fail!("Not yet implemented"); } - - #[inline] - fn rotate_ray3(&self, _ray: &Ray3) -> Ray3 { fail!("Not yet implemented") } - - #[inline] - fn concat(&self, _other: &AxisAngle) -> AxisAngle { fail!("Not yet implemented") } - - #[inline] - fn concat_self(&mut self, _other: &AxisAngle) { fail!("Not yet implemented"); } - - #[inline] - fn invert(&self) -> AxisAngle { - AxisAngle::new(self.v.clone(), (-self.a).normalize()) - } - - #[inline] - fn invert_self(&mut self) { - self.a = (-self.a).normalize() - } -} - -impl> ApproxEq for AxisAngle { - #[inline] - fn approx_epsilon() -> S { - // TODO: fix this after static methods are fixed in rustc - fail!(~"Doesn't work!"); - } - - #[inline] - fn approx_eq(&self, other: &AxisAngle) -> bool { - self.v.approx_eq(&other.v) && - self.a.approx_eq(&other.a) - } - - #[inline] - fn approx_eq_eps(&self, other: &AxisAngle, approx_epsilon: &S) -> bool { - self.v.approx_eq_eps(&other.v, approx_epsilon) && - self.a.approx_eq_eps(&other.a, approx_epsilon) - } -} - -/// An angle around the X axis (pitch). -#[deriving(Eq, Ord, Clone)] -pub struct AngleX(A); - -/// An angle around the X axis (yaw). -#[deriving(Eq, Ord, Clone)] -pub struct AngleY(A); - -/// An angle around the Z axis (roll). -#[deriving(Eq, Ord, Clone)] -pub struct AngleZ(A);