diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..fc4711d --- /dev/null +++ b/.travis.yml @@ -0,0 +1,7 @@ +before_install: + - yes | sudo add-apt-repository ppa:hansjorg/rust + - sudo apt-get update +install: + - sudo apt-get install rust-nightly +script: + - rustpkg build cgmath \ No newline at end of file diff --git a/AUTHORS b/AUTHORS index e834dec..3fa559d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -11,6 +11,7 @@ Erick Tryzelaar Luqman Aden Maik Klein Mikko Perttunen +Tomasz Stachowiak With thanks to: diff --git a/src/cgmath/angle.rs b/src/cgmath/angle.rs index ad71f15..9e23bfe 100644 --- a/src/cgmath/angle.rs +++ b/src/cgmath/angle.rs @@ -15,11 +15,11 @@ //! Angle units for type-safe, self-documenting code. -pub use std::num::{cast, zero}; pub use std::num::{sinh, cosh, tanh}; pub use std::num::{asinh, acosh, atanh}; -use std::num::Zero; +use std::fmt; +use std::num::{Zero, zero, cast}; #[deriving(Clone, Eq, Ord, Zero)] pub struct Rad { s: S } #[deriving(Clone, Eq, Ord, Zero)] pub struct Deg { s: S } @@ -113,15 +113,15 @@ pub trait Angle /// Returns the interior bisector of the two angles #[inline] fn bisect(&self, other: Self) -> Self { - self.add_a(self.sub_a(other).mul_s(cast(0.5))).normalize() + self.add_a(self.sub_a(other).mul_s(cast(0.5).unwrap())).normalize() } fn full_turn() -> Self; - #[inline] fn turn_div_2() -> Self { let full_turn: Self = Angle::full_turn(); full_turn.div_s(cast(2)) } - #[inline] fn turn_div_3() -> Self { let full_turn: Self = Angle::full_turn(); full_turn.div_s(cast(3)) } - #[inline] fn turn_div_4() -> Self { let full_turn: Self = Angle::full_turn(); full_turn.div_s(cast(4)) } - #[inline] fn turn_div_6() -> Self { let full_turn: Self = Angle::full_turn(); full_turn.div_s(cast(6)) } + #[inline] fn turn_div_2() -> Self { let full_turn: Self = Angle::full_turn(); full_turn.div_s(cast(2).unwrap()) } + #[inline] fn turn_div_3() -> Self { let full_turn: Self = Angle::full_turn(); full_turn.div_s(cast(3).unwrap()) } + #[inline] fn turn_div_4() -> Self { let full_turn: Self = Angle::full_turn(); full_turn.div_s(cast(4).unwrap()) } + #[inline] fn turn_div_6() -> Self { let full_turn: Self = Angle::full_turn(); full_turn.div_s(cast(6).unwrap()) } } #[inline] pub fn bisect>(a: A, b: A) -> A { a.bisect(b) } @@ -163,25 +163,25 @@ impl Angle for Rad { impl Angle for Deg { #[inline] fn from>(theta: A) -> Deg { theta.to_deg() } - #[inline] fn full_turn() -> Deg { deg(cast(360)) } + #[inline] fn full_turn() -> Deg { deg(cast(360).unwrap()) } } -#[inline] pub fn sin>(theta: A) -> S { theta.to_rad().s.sin() } -#[inline] pub fn cos>(theta: A) -> S { theta.to_rad().s.cos() } -#[inline] pub fn tan>(theta: A) -> S { theta.to_rad().s.tan() } -#[inline] pub fn sin_cos>(theta: A) -> (S, S) { theta.to_rad().s.sin_cos() } +#[inline] pub fn sin(theta: Rad) -> S { theta.s.sin() } +#[inline] pub fn cos(theta: Rad) -> S { theta.s.cos() } +#[inline] pub fn tan(theta: Rad) -> S { theta.s.tan() } +#[inline] pub fn sin_cos(theta: Rad) -> (S, S) { theta.s.sin_cos() } -#[inline] pub fn cot>(theta: A) -> S { tan(theta).recip() } -#[inline] pub fn sec>(theta: A) -> S { cos(theta).recip() } -#[inline] pub fn csc>(theta: A) -> S { sin(theta).recip() } +#[inline] pub fn cot(theta: Rad) -> S { tan(theta).recip() } +#[inline] pub fn sec(theta: Rad) -> S { cos(theta).recip() } +#[inline] pub fn csc(theta: Rad) -> S { sin(theta).recip() } -#[inline] pub fn asin>(s: S) -> A { Angle::from(rad(s.asin())) } -#[inline] pub fn acos>(s: S) -> A { Angle::from(rad(s.acos())) } -#[inline] pub fn atan>(s: S) -> A { Angle::from(rad(s.atan())) } -#[inline] pub fn atan2>(a: S, b: S) -> A { Angle::from(rad(a.atan2(&b))) } +#[inline] pub fn asin(s: S) -> Rad { rad(s.asin()) } +#[inline] pub fn acos(s: S) -> Rad { rad(s.acos()) } +#[inline] pub fn atan(s: S) -> Rad { rad(s.atan()) } +#[inline] pub fn atan2(a: S, b: S) -> Rad { rad(a.atan2(&b)) } -impl ToStr for Rad { fn to_str(&self) -> ~str { fmt!("%? rad", self.s) } } -impl ToStr for Deg { fn to_str(&self) -> ~str { fmt!("%?°", self.s) } } +impl ToStr for Rad { fn to_str(&self) -> ~str { format!("{} rad", self.s) } } +impl ToStr for Deg { fn to_str(&self) -> ~str { format!("{}°", self.s) } } impl ApproxEq for Rad { #[inline] diff --git a/src/cgmath/lib.rs b/src/cgmath/lib.rs index ea340b4..29f05db 100644 --- a/src/cgmath/lib.rs +++ b/src/cgmath/lib.rs @@ -22,6 +22,9 @@ #[license = "ASL2"]; #[crate_type = "lib"]; +#[feature(globs)]; +#[feature(macro_rules)]; + pub mod array; pub mod matrix; pub mod quaternion; @@ -41,3 +44,5 @@ pub mod frustum; pub mod intersect; pub mod obb; pub mod sphere; + +pub mod ptr; diff --git a/src/cgmath/matrix.rs b/src/cgmath/matrix.rs index 117ec26..7d3642a 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::{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: Rad) -> 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: Rad) -> 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: Rad) -> 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: Rad, y: Rad, z: Rad) -> 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: Rad) -> 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 { @@ -565,7 +628,7 @@ impl ToQuat for Mat3 { fn to_quat(&self) -> Quat { // http://www.cs.ucr.edu/~vbz/resources/Quatut.pdf let trace = self.trace(); - let half: S = cast(0.5); + let half: S = cast(0.5).unwrap(); match () { () if trace >= zero::() => { let s = sqrt(one::() + trace); diff --git a/src/cgmath/point.rs b/src/cgmath/point.rs index e611e00..bc2cfb6 100644 --- a/src/cgmath/point.rs +++ b/src/cgmath/point.rs @@ -17,6 +17,7 @@ //! disinguishes them from vectors, which have a length and direction, but do //! not have a fixed position. +use std::fmt; use std::num::zero; use array::*; @@ -88,14 +89,14 @@ array!(impl Point3 -> [S, ..3] _3) impl Point, [S, ..2]> for Point2 {} impl Point, [S, ..3]> for Point3 {} -impl ToStr for Point2 { +impl ToStr for Point2 { fn to_str(&self) -> ~str { - fmt!("[%?, %?]", self.x, self.y) + format!("[{}, {}]", self.x, self.y) } } -impl ToStr for Point3 { +impl ToStr for Point3 { fn to_str(&self) -> ~str { - fmt!("[%?, %?, %?]", self.x, self.y, self.z) + format!("[{}, {}, {}]", self.x, self.y, self.z) } } diff --git a/src/cgmath/projection.rs b/src/cgmath/projection.rs index 0ede2a1..c308e26 100644 --- a/src/cgmath/projection.rs +++ b/src/cgmath/projection.rs @@ -78,8 +78,8 @@ pub struct PerspectiveFov { impl> PerspectiveFov { pub fn to_perspective(&self) -> Perspective { - let angle = self.fovy.div_s(cast(2)); - let ymax = self.near * tan(angle); + let angle = self.fovy.div_s(cast(2).unwrap()); + let ymax = self.near * tan(angle.to_rad()); let xmax = ymax * self.aspect; Perspective { @@ -104,15 +104,15 @@ impl> ToMat4 for PerspectiveFov { fn to_mat4(&self) -> Mat4 { let half_turn: A = Angle::turn_div_2(); - assert!(self.fovy > zero(), "The vertical field of view cannot be below zero, found: %?", self.fovy); - assert!(self.fovy < half_turn, "The vertical field of view cannot be greater than a half turn, found: %?", self.fovy); - assert!(self.aspect > zero(), "The aspect ratio cannot be below zero, found: %?", self.aspect); - assert!(self.near > zero(), "The near plane distance cannot be below zero, found: %?", self.near); - assert!(self.far > zero(), "The far plane distance cannot be below zero, found: %?", self.far); - assert!(self.far > self.near, "The far plane cannot be closer than the near plane, found: far: %?, near: %?", self.far, self.near); + assert!(self.fovy > zero(), "The vertical field of view cannot be below zero, found: {:?}", self.fovy); + assert!(self.fovy < half_turn, "The vertical field of view cannot be greater than a half turn, found: {:?}", self.fovy); + assert!(self.aspect > zero(), "The aspect ratio cannot be below zero, found: {:?}", self.aspect); + assert!(self.near > zero(), "The near plane distance cannot be below zero, found: {:?}", self.near); + assert!(self.far > zero(), "The far plane distance cannot be below zero, found: {:?}", self.far); + assert!(self.far > self.near, "The far plane cannot be closer than the near plane, found: far: {:?}, near: {:?}", self.far, self.near); - let f = cot(self.fovy.div_s(cast(2))); - let two: S = cast(2); + let f = cot(self.fovy.div_s(cast(2).unwrap()).to_rad()); + let two: S = cast(2).unwrap(); let c0r0 = f / self.aspect; let c0r1 = zero(); @@ -158,11 +158,11 @@ impl Projection for Perspective { impl ToMat4 for Perspective { fn to_mat4(&self) -> Mat4 { - assert!(self.left > self.right, "`left` cannot be greater than `right`, found: left: %? right: %?", self.left, self.right); - assert!(self.bottom > self.top, "`bottom` cannot be greater than `top`, found: bottom: %? top: %?", self.bottom, self.top); - assert!(self.near > self.far, "`near` cannot be greater than `far`, found: near: %? far: %?", self.near, self.far); + assert!(self.left > self.right, "`left` cannot be greater than `right`, found: left: {:?} right: {:?}", self.left, self.right); + assert!(self.bottom > self.top, "`bottom` cannot be greater than `top`, found: bottom: {:?} top: {:?}", self.bottom, self.top); + assert!(self.near > self.far, "`near` cannot be greater than `far`, found: near: {:?} far: {:?}", self.near, self.far); - let two: S = cast(2); + let two: S = cast(2).unwrap(); let c0r0 = (two * self.near) / (self.right - self.left); let c0r1 = zero(); @@ -214,11 +214,11 @@ impl Projection for Ortho { impl ToMat4 for Ortho { fn to_mat4(&self) -> Mat4 { - assert!(self.left > self.right, "`left` cannot be greater than `right`, found: left: %? right: %?", self.left, self.right); - assert!(self.bottom > self.top, "`bottom` cannot be greater than `top`, found: bottom: %? top: %?", self.bottom, self.top); - assert!(self.near > self.far, "`near` cannot be greater than `far`, found: near: %? far: %?", self.near, self.far); + assert!(self.left > self.right, "`left` cannot be greater than `right`, found: left: {:?} right: {:?}", self.left, self.right); + assert!(self.bottom > self.top, "`bottom` cannot be greater than `top`, found: bottom: {:?} top: {:?}", self.bottom, self.top); + assert!(self.near > self.far, "`near` cannot be greater than `far`, found: near: {:?} far: {:?}", self.near, self.far); - let two: S = cast(2); + let two: S = cast(2).unwrap(); let c0r0 = two / (self.right - self.left); let c0r1 = zero(); diff --git a/src/cgmath/ptr.rs b/src/cgmath/ptr.rs new file mode 100644 index 0000000..2810103 --- /dev/null +++ b/src/cgmath/ptr.rs @@ -0,0 +1,38 @@ +// Copyright 2013 The CGMath 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. + +use array::Array; +use matrix::{Mat2, Mat3, Mat4}; +use point::{Point2, Point3}; +use vector::{Vec2, Vec3, Vec4}; + +pub trait Ptr { + fn ptr<'a>(&'a self) -> &'a T; +} + +impl Ptr for Vec2 { #[inline] fn ptr<'a>(&'a self) -> &'a S { self.i(0) } } +impl Ptr for Vec3 { #[inline] fn ptr<'a>(&'a self) -> &'a S { self.i(0) } } +impl Ptr for Vec4 { #[inline] fn ptr<'a>(&'a self) -> &'a S { self.i(0) } } + +impl Ptr for Point2 { #[inline] fn ptr<'a>(&'a self) -> &'a S { self.i(0) } } +impl Ptr for Point3 { #[inline] fn ptr<'a>(&'a self) -> &'a S { self.i(0) } } + +impl Ptr for Mat2 { #[inline] fn ptr<'a>(&'a self) -> &'a S { self.i(0).i(0) } } +impl Ptr for Mat3 { #[inline] fn ptr<'a>(&'a self) -> &'a S { self.i(0).i(0) } } +impl Ptr for Mat4 { #[inline] fn ptr<'a>(&'a self) -> &'a S { self.i(0).i(0) } } + +impl<'self, T, P: Ptr> Ptr for &'self [P] { + #[inline] fn ptr<'a>(&'a self) -> &'a T { self[0].ptr() } +} diff --git a/src/cgmath/quaternion.rs b/src/cgmath/quaternion.rs index 846f38f..0e94329 100644 --- a/src/cgmath/quaternion.rs +++ b/src/cgmath/quaternion.rs @@ -13,9 +13,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::fmt; 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 +51,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: Rad) -> Quat { + Quat::new(cos(theta.mul_s(cast(0.5).unwrap())), sin(theta), zero(), zero()) + } + + /// Create a matrix from a rotation around the `y` axis (yaw). + #[inline] + pub fn from_angle_y(theta: Rad) -> Quat { + Quat::new(cos(theta.mul_s(cast(0.5).unwrap())), zero(), sin(theta), zero()) + } + + /// Create a matrix from a rotation around the `z` axis (roll). + #[inline] + pub fn from_angle_z(theta: Rad) -> Quat { + Quat::new(cos(theta.mul_s(cast(0.5).unwrap())), 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: Rad, y: Rad, z: Rad) -> Quat { + // http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles#Conversion + let (sx2, cx2) = sin_cos(x.mul_s(cast(0.5).unwrap())); + let (sy2, cy2) = sin_cos(y.mul_s(cast(0.5).unwrap())); + let (sz2, cz2) = sin_cos(z.mul_s(cast(0.5).unwrap())); + + 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: Rad) -> Quat { + let half = angle.mul_s(cast(0.5).unwrap()); + 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 { @@ -78,7 +124,7 @@ impl Quat { #[inline] pub fn mul_v(&self, vec: &Vec3) -> Vec3 { let tmp = self.v.cross(vec).add_v(&vec.mul_s(self.s.clone())); - self.v.cross(&tmp).mul_s(cast(2)).add_v(vec) + self.v.cross(&tmp).mul_s(cast(2).unwrap()).add_v(vec) } /// The sum of this quaternion and `other` @@ -198,7 +244,7 @@ impl Quat { use std::num::cast; let dot = self.dot(other); - let dot_threshold = cast(0.9995); + let dot_threshold = cast(0.9995).unwrap(); // if quaternions are close together use `nlerp` if dot > dot_threshold { @@ -207,14 +253,14 @@ impl Quat { // stay within the domain of acos() let robust_dot = dot.clamp(&-one::(), &one::()); - let theta: Rad = acos(robust_dot.clone()); // the angle between the quaternions - let theta: Rad = theta.mul_s(amount); // the fraction of theta specified by `amount` + let theta: Rad = acos(robust_dot.clone()); - let q = other.sub_q(&self.mul_s(robust_dot)) - .normalize(); + let scale1 = sin(theta.mul_s(one::() - amount)); + let scale2 = sin(theta.mul_s(amount)); - self.mul_s(cos(theta.clone())) - .add_q(&q.mul_s(sin(theta))) + self.mul_s(scale1) + .add_q(&other.mul_s(scale2)) + .mul_s(sin(theta).recip()) } } } @@ -251,8 +297,8 @@ impl Neg> for Quat { } } -impl ToStr for Quat { +impl ToStr for Quat { fn to_str(&self) -> ~str { - fmt!("%? + %?i + %?j + %?k", self.s, self.v.x, self.v.y, self.v.z) + format!("{} + {}i + {}j + {}k", self.s, self.v.x, self.v.y, self.v.z) } } diff --git a/src/cgmath/rotation.rs b/src/cgmath/rotation.rs index eae3973..28dc015 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::Rad; use matrix::Matrix; use matrix::{Mat2, ToMat2}; use matrix::{Mat3, ToMat3}; @@ -33,7 +30,7 @@ pub trait Rotation2 : Eq + ApproxEq + ToMat2 -+ ToRot2 ++ ToBasis2 { fn rotate_point2(&self, point: &Point2) -> Point2; fn rotate_vec2(&self, vec: &Vec2) -> Vec2; @@ -52,7 +49,7 @@ pub trait Rotation3 : Eq + ApproxEq + ToMat3 -+ ToRot3 ++ ToBasis3 + ToQuat { fn rotate_point3(&self, point: &Point3) -> Point3; @@ -71,30 +68,30 @@ pub trait Rotation3 /// enforce orthogonality at the type level the operations have been restricted /// to a subeset of those implemented on `Mat2`. #[deriving(Eq, Clone)] -pub struct Rot2 { +pub struct Basis2 { priv mat: Mat2 } -impl Rot2 { +impl Basis2 { #[inline] pub fn as_mat2<'a>(&'a self) -> &'a Mat2 { &'a self.mat } } -pub trait ToRot2 { - fn to_rot2(&self) -> Rot2; +pub trait ToBasis2 { + fn to_rot2(&self) -> Basis2; } -impl ToRot2 for Rot2 { +impl ToBasis2 for Basis2 { #[inline] - fn to_rot2(&self) -> Rot2 { self.clone() } + fn to_rot2(&self) -> Basis2 { self.clone() } } -impl ToMat2 for Rot2 { +impl ToMat2 for Basis2 { #[inline] fn to_mat2(&self) -> Mat2 { self.mat.clone() } } -impl Rotation2 for Rot2 { +impl Rotation2 for Basis2 { #[inline] fn rotate_point2(&self, _point: &Point2) -> Point2 { fail!("Not yet implemented") } @@ -105,15 +102,15 @@ impl Rotation2 for Rot2 { fn rotate_ray2(&self, _ray: &Ray2) -> Ray2 { fail!("Not yet implemented") } #[inline] - fn concat(&self, other: &Rot2) -> Rot2 { Rot2 { mat: self.mat.mul_m(&other.mat) } } + fn concat(&self, other: &Basis2) -> Basis2 { Basis2 { mat: self.mat.mul_m(&other.mat) } } #[inline] - fn concat_self(&mut self, other: &Rot2) { self.mat.mul_self_m(&other.mat); } + fn concat_self(&mut self, other: &Basis2) { self.mat.mul_self_m(&other.mat); } // TODO: we know the matrix is orthogonal, so this could be re-written // to be faster #[inline] - fn invert(&self) -> Rot2 { Rot2 { mat: self.mat.invert().unwrap() } } + fn invert(&self) -> Basis2 { Basis2 { mat: self.mat.invert().unwrap() } } // TODO: we know the matrix is orthogonal, so this could be re-written // to be faster @@ -121,7 +118,7 @@ impl Rotation2 for Rot2 { fn invert_self(&mut self) { self.mat.invert_self(); } } -impl ApproxEq for Rot2 { +impl ApproxEq for Basis2 { #[inline] fn approx_epsilon() -> S { // TODO: fix this after static methods are fixed in rustc @@ -129,12 +126,12 @@ impl ApproxEq for Rot2 { } #[inline] - fn approx_eq(&self, other: &Rot2) -> bool { + fn approx_eq(&self, other: &Basis2) -> bool { self.mat.approx_eq(&other.mat) } #[inline] - fn approx_eq_eps(&self, other: &Rot2, approx_epsilon: &S) -> bool { + fn approx_eq_eps(&self, other: &Basis2, approx_epsilon: &S) -> bool { self.mat.approx_eq_eps(&other.mat, approx_epsilon) } } @@ -146,40 +143,71 @@ impl ApproxEq for Rot2 { /// `math::Mat3`. To ensure orthogonality is maintained, the operations have /// been restricted to a subeset of those implemented on `Mat3`. #[deriving(Eq, Clone)] -pub struct Rot3 { +pub struct Basis3 { priv mat: Mat3 } -impl Rot3 { +impl Basis3 { #[inline] - pub fn look_at(dir: &Vec3, up: &Vec3) -> Rot3 { - Rot3 { mat: Mat3::look_at(dir, up) } + pub fn look_at(dir: &Vec3, up: &Vec3) -> Basis3 { + Basis3 { mat: Mat3::look_at(dir, up) } + } + + /// Create a rotation matrix from a rotation around the `x` axis (pitch). + pub fn from_angle_x(theta: Rad) -> Basis3 { + Basis3 { mat: Mat3::from_angle_x(theta) } + } + + /// Create a rotation matrix from a rotation around the `y` axis (yaw). + pub fn from_angle_y(theta: Rad) -> Basis3 { + Basis3 { mat: Mat3::from_angle_y(theta) } + } + + /// Create a rotation matrix from a rotation around the `z` axis (roll). + pub fn from_angle_z(theta: Rad) -> Basis3 { + Basis3 { 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: Rad, y: Rad, z: Rad) -> Basis3 { + Basis3 { 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: Rad) -> Basis3 { + Basis3 { mat: Mat3::from_axis_angle(axis, angle) } } #[inline] pub fn as_mat3<'a>(&'a self) -> &'a Mat3 { &'a self.mat } } -pub trait ToRot3 { - fn to_rot3(&self) -> Rot3; +pub trait ToBasis3 { + fn to_rot3(&self) -> Basis3; } -impl ToRot3 for Rot3 { +impl ToBasis3 for Basis3 { #[inline] - fn to_rot3(&self) -> Rot3 { self.clone() } + fn to_rot3(&self) -> Basis3 { self.clone() } } -impl ToMat3 for Rot3 { +impl ToMat3 for Basis3 { #[inline] fn to_mat3(&self) -> Mat3 { self.mat.clone() } } -impl ToQuat for Rot3 { +impl ToQuat for Basis3 { #[inline] fn to_quat(&self) -> Quat { self.mat.to_quat() } } -impl Rotation3 for Rot3 { +impl Rotation3 for Basis3 { #[inline] fn rotate_point3(&self, _point: &Point3) -> Point3 { fail!("Not yet implemented") } @@ -190,15 +218,15 @@ impl Rotation3 for Rot3 { fn rotate_ray3(&self, _ray: &Ray3) -> Ray3 { fail!("Not yet implemented") } #[inline] - fn concat(&self, other: &Rot3) -> Rot3 { Rot3 { mat: self.mat.mul_m(&other.mat) } } + fn concat(&self, other: &Basis3) -> Basis3 { Basis3 { mat: self.mat.mul_m(&other.mat) } } #[inline] - fn concat_self(&mut self, other: &Rot3) { self.mat.mul_self_m(&other.mat); } + fn concat_self(&mut self, other: &Basis3) { self.mat.mul_self_m(&other.mat); } // TODO: we know the matrix is orthogonal, so this could be re-written // to be faster #[inline] - fn invert(&self) -> Rot3 { Rot3 { mat: self.mat.invert().unwrap() } } + fn invert(&self) -> Basis3 { Basis3 { mat: self.mat.invert().unwrap() } } // TODO: we know the matrix is orthogonal, so this could be re-written // to be faster @@ -206,7 +234,7 @@ impl Rotation3 for Rot3 { fn invert_self(&mut self) { self.mat.invert_self(); } } -impl ApproxEq for Rot3 { +impl ApproxEq for Basis3 { #[inline] fn approx_epsilon() -> S { // TODO: fix this after static methods are fixed in rustc @@ -214,21 +242,21 @@ impl ApproxEq for Rot3 { } #[inline] - fn approx_eq(&self, other: &Rot3) -> bool { + fn approx_eq(&self, other: &Basis3) -> bool { self.mat.approx_eq(&other.mat) } #[inline] - fn approx_eq_eps(&self, other: &Rot3, approx_epsilon: &S) -> bool { + fn approx_eq_eps(&self, other: &Basis3, approx_epsilon: &S) -> bool { self.mat.approx_eq_eps(&other.mat, approx_epsilon) } } // Quaternion Rotation impls -impl ToRot3 for Quat { +impl ToBasis3 for Quat { #[inline] - fn to_rot3(&self) -> Rot3 { Rot3 { mat: self.to_mat3() } } + fn to_rot3(&self) -> Basis3 { Basis3 { mat: self.to_mat3() } } } impl ToQuat for Quat { @@ -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); diff --git a/src/cgmath/transform.rs b/src/cgmath/transform.rs new file mode 100644 index 0000000..5e30dac --- /dev/null +++ b/src/cgmath/transform.rs @@ -0,0 +1,40 @@ +// Copyright 2013 The CGMath 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. + +pub trait Transform { + fn transform_vec(&self, point: Point3) -> Point3; + fn transform_point(&self, point: Point3) -> Point3; + fn transform_ray(&self, ray: Ray3) -> Ray3; +} + +/// A homogeneous transformation matrix. +pub struct AffineMatrix3 { + mat: Mat4, +} + +/// A transformation in three dimensions consisting of a rotation, +/// displacement vector and scale amount. +pub struct Transform3 { + rot: R, + disp: Vec3, + scale: S, +} + +impl> Transform3 { + #[inline] + pub fn new(rot: R, disp: Vec3, scale: S) -> Transform3 { + Transform3 { rot: rot, disp: disp, scale: S } + } +} diff --git a/src/cgmath/vector.rs b/src/cgmath/vector.rs index 713fb84..bc8f8b5 100644 --- a/src/cgmath/vector.rs +++ b/src/cgmath/vector.rs @@ -13,6 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::fmt; use std::num::{Zero, zero, One, one, sqrt}; use angle::{Rad, atan2, acos}; @@ -282,20 +283,20 @@ impl EuclideanVector for Vec4 { } } -impl ToStr for Vec2 { +impl ToStr for Vec2 { fn to_str(&self) -> ~str { - fmt!("[%?, %?]", self.x, self.y) + format!("[{}, {}]", self.x, self.y) } } -impl ToStr for Vec3 { +impl ToStr for Vec3 { fn to_str(&self) -> ~str { - fmt!("[%?, %?, %?]", self.x, self.y, self.z) + format!("[{}, {}, {}]", self.x, self.y, self.z) } } -impl ToStr for Vec4 { +impl ToStr for Vec4 { fn to_str(&self) -> ~str { - fmt!("[%?, %?, %?, %?]", self.x, self.y, self.z, self.w) + format!("[{}, {}, {}, {}]", self.x, self.y, self.z, self.w) } }