From 84da664455df0ec6d7583131e985ffd3a184a2f4 Mon Sep 17 00:00:00 2001 From: josh65536 Date: Wed, 12 Aug 2020 15:05:38 -0400 Subject: [PATCH] Now there is only one one (#513) * Now there is only one one * Rotation no longer has a parameter * Moved some type parameters to associated types * Relaxed some bounds and simplified a bound * Removed unnecessary bound in * Deduplicated multiplication code --- src/matrix.rs | 24 ++++++--------- src/quaternion.rs | 8 +++-- src/rotation.rs | 75 ++++++++++++++++++++++++++++++++-------------- src/transform.rs | 62 ++++++++++++++++++++++++++++---------- tests/rotation.rs | 4 +-- tests/transform.rs | 37 +++++++++++++++++++++++ 6 files changed, 152 insertions(+), 58 deletions(-) diff --git a/src/matrix.rs b/src/matrix.rs index 3b64f91..ea0e9de 100644 --- a/src/matrix.rs +++ b/src/matrix.rs @@ -1038,10 +1038,6 @@ impl approx::UlpsEq for Matrix4 { } impl Transform> for Matrix3 { - fn one() -> Matrix3 { - One::one() - } - fn look_at(eye: Point2, center: Point2, up: Vector2) -> Matrix3 { let dir = center - eye; Matrix3::from(Matrix2::look_at(dir, up)) @@ -1065,10 +1061,6 @@ impl Transform> for Matrix3 { } impl Transform> for Matrix3 { - fn one() -> Matrix3 { - One::one() - } - fn look_at(eye: Point3, center: Point3, up: Vector3) -> Matrix3 { let dir = center - eye; Matrix3::look_at(dir, up) @@ -1092,10 +1084,6 @@ impl Transform> for Matrix3 { } impl Transform> for Matrix4 { - fn one() -> Matrix4 { - One::one() - } - fn look_at(eye: Point3, center: Point3, up: Vector3) -> Matrix4 { Matrix4::look_at(eye, center, up) } @@ -1117,11 +1105,17 @@ impl Transform> for Matrix4 { } } -impl Transform2 for Matrix3 {} +impl Transform2 for Matrix3 { + type Scalar = S; +} -impl Transform3 for Matrix3 {} +impl Transform3 for Matrix3 { + type Scalar = S; +} -impl Transform3 for Matrix4 {} +impl Transform3 for Matrix4 { + type Scalar = S; +} macro_rules! impl_matrix { ($MatrixN:ident, $VectorN:ident { $($field:ident : $row_index:expr),+ }) => { diff --git a/src/quaternion.rs b/src/quaternion.rs index 3f704a1..6a48b52 100644 --- a/src/quaternion.rs +++ b/src/quaternion.rs @@ -480,7 +480,9 @@ impl From> for Basis3 { } } -impl Rotation> for Quaternion { +impl Rotation for Quaternion { + type Space = Point3; + #[inline] fn look_at(dir: Vector3, up: Vector3) -> Quaternion { Matrix3::look_at(dir, up).into() @@ -526,7 +528,9 @@ impl Rotation> for Quaternion { } } -impl Rotation3 for Quaternion { +impl Rotation3 for Quaternion { + type Scalar = S; + #[inline] fn from_axis_angle>>(axis: Vector3, angle: A) -> Quaternion { let (s, c) = Rad::sin_cos(angle.into() * cast(0.5f64).unwrap()); diff --git a/src/rotation.rs b/src/rotation.rs index a1de85b..e7fa1f6 100644 --- a/src/rotation.rs +++ b/src/rotation.rs @@ -30,30 +30,41 @@ use vector::{Vector2, Vector3}; /// A trait for a generic rotation. A rotation is a transformation that /// creates a circular motion, and preserves at least one point in the space. -pub trait Rotation: Sized + Copy + One +pub trait Rotation: Sized + Copy + One where // FIXME: Ugly type signatures - blocked by rust-lang/rust#24092 - Self: approx::AbsDiffEq, - Self: approx::RelativeEq, - Self: approx::UlpsEq, - P::Scalar: BaseFloat, + Self: approx::AbsDiffEq::Space as EuclideanSpace>::Scalar>, + Self: approx::RelativeEq::Space as EuclideanSpace>::Scalar>, + Self: approx::UlpsEq::Space as EuclideanSpace>::Scalar>, + ::Scalar: BaseFloat, Self: iter::Product, { + type Space: EuclideanSpace; + /// Create a rotation to a given direction with an 'up' vector. - fn look_at(dir: P::Diff, up: P::Diff) -> Self; + fn look_at( + dir: ::Diff, + up: ::Diff, + ) -> Self; /// Create a shortest rotation to transform vector 'a' into 'b'. /// Both given vectors are assumed to have unit length. - fn between_vectors(a: P::Diff, b: P::Diff) -> Self; + fn between_vectors( + a: ::Diff, + b: ::Diff, + ) -> Self; /// Rotate a vector using this rotation. - fn rotate_vector(&self, vec: P::Diff) -> P::Diff; + fn rotate_vector( + &self, + vec: ::Diff, + ) -> ::Diff; /// Rotate a point using this rotation, by converting it to its /// representation as a vector. #[inline] - fn rotate_point(&self, point: P) -> P { - P::from_vec(self.rotate_vector(point.to_vec())) + fn rotate_point(&self, point: Self::Space) -> Self::Space { + Self::Space::from_vec(self.rotate_vector(point.to_vec())) } /// Create a new rotation which "un-does" this rotation. That is, @@ -62,38 +73,48 @@ where } /// A two-dimensional rotation. -pub trait Rotation2: - Rotation> + Into> + Into> +pub trait Rotation2: + Rotation::Scalar>> + + Into::Scalar>> + + Into::Scalar>> { + type Scalar: BaseFloat; + /// Create a rotation by a given angle. Thus is a redundant case of both /// from_axis_angle() and from_euler() for 2D space. - fn from_angle>>(theta: A) -> Self; + fn from_angle>>(theta: A) -> Self; } /// A three-dimensional rotation. -pub trait Rotation3: - Rotation> + Into> + Into> + Into> + From>> +pub trait Rotation3: + Rotation::Scalar>> + + Into::Scalar>> + + Into::Scalar>> + + Into::Scalar>> + + From::Scalar>>> { + type Scalar: BaseFloat; + /// Create a rotation using an angle around a given axis. /// /// The specified axis **must be normalized**, or it represents an invalid rotation. - fn from_axis_angle>>(axis: Vector3, angle: A) -> Self; + fn from_axis_angle>>(axis: Vector3, angle: A) -> Self; /// Create a rotation from an angle around the `x` axis (pitch). #[inline] - fn from_angle_x>>(theta: A) -> Self { + fn from_angle_x>>(theta: A) -> Self { Rotation3::from_axis_angle(Vector3::unit_x(), theta) } /// Create a rotation from an angle around the `y` axis (yaw). #[inline] - fn from_angle_y>>(theta: A) -> Self { + fn from_angle_y>>(theta: A) -> Self { Rotation3::from_axis_angle(Vector3::unit_y(), theta) } /// Create a rotation from an angle around the `z` axis (roll). #[inline] - fn from_angle_z>>(theta: A) -> Self { + fn from_angle_z>>(theta: A) -> Self { Rotation3::from_axis_angle(Vector3::unit_z(), theta) } } @@ -183,7 +204,9 @@ impl<'a, S: 'a + BaseFloat> iter::Product<&'a Basis2> for Basis2 { } } -impl Rotation> for Basis2 { +impl Rotation for Basis2 { + type Space = Point2; + #[inline] fn look_at(dir: Vector2, up: Vector2) -> Basis2 { Basis2 { @@ -262,7 +285,9 @@ impl approx::UlpsEq for Basis2 { } } -impl Rotation2 for Basis2 { +impl Rotation2 for Basis2 { + type Scalar = S; + fn from_angle>>(theta: A) -> Basis2 { Basis2 { mat: Matrix2::from_angle(theta), @@ -334,7 +359,9 @@ impl<'a, S: 'a + BaseFloat> iter::Product<&'a Basis3> for Basis3 { } } -impl Rotation> for Basis3 { +impl Rotation for Basis3 { + type Space = Point3; + #[inline] fn look_at(dir: Vector3, up: Vector3) -> Basis3 { Basis3 { @@ -414,7 +441,9 @@ impl approx::UlpsEq for Basis3 { } } -impl Rotation3 for Basis3 { +impl Rotation3 for Basis3 { + type Scalar = S; + fn from_axis_angle>>(axis: Vector3, angle: A) -> Basis3 { Basis3 { mat: Matrix3::from_axis_angle(axis, angle), diff --git a/src/transform.rs b/src/transform.rs index fbe4c37..0b3453c 100644 --- a/src/transform.rs +++ b/src/transform.rs @@ -22,14 +22,12 @@ use point::{Point2, Point3}; use rotation::*; use vector::{Vector2, Vector3}; +use std::ops::Mul; + /// A trait representing an [affine /// transformation](https://en.wikipedia.org/wiki/Affine_transformation) that /// can be applied to points or vectors. An affine transformation is one which -pub trait Transform: Sized { - /// Create an identity transformation. That is, a transformation which - /// does nothing. - fn one() -> Self; - +pub trait Transform: Sized + One { /// Create a transformation that rotates a vector to look at `center` from /// `eye`, using `up` for orientation. fn look_at(eye: P, center: P, up: P::Diff) -> Self; @@ -69,21 +67,41 @@ pub struct Decomposed { pub disp: V, } -impl> Transform

for Decomposed +impl> One for Decomposed where P::Scalar: BaseFloat, - // FIXME: Investigate why this is needed! - P::Diff: VectorSpace, { - #[inline] - fn one() -> Decomposed { + fn one() -> Self { Decomposed { scale: P::Scalar::one(), rot: R::one(), disp: P::Diff::zero(), } } +} +impl> Mul for Decomposed +where + P::Scalar: BaseFloat, + P::Diff: VectorSpace, +{ + type Output = Self; + + /// Multiplies the two transforms together. + /// The result should be as if the two transforms were converted + /// to matrices, then multiplied, then converted back with + /// a (currently nonexistent) function that tries to convert + /// a matrix into a `Decomposed`. + fn mul(self, rhs: Decomposed) -> Self::Output { + self.concat(&rhs) + } +} + +impl> Transform

for Decomposed +where + P::Scalar: BaseFloat, + P::Diff: VectorSpace, +{ #[inline] fn look_at(eye: P, center: P, up: P::Diff) -> Decomposed { let rot = R::look_at(center - eye, up); @@ -138,10 +156,18 @@ where } } -pub trait Transform2: Transform> + Into> {} -pub trait Transform3: Transform> + Into> {} +pub trait Transform2: + Transform::Scalar>> + Into::Scalar>> +{ + type Scalar: BaseNum; +} +pub trait Transform3: + Transform::Scalar>> + Into::Scalar>> +{ + type Scalar: BaseNum; +} -impl> From, R>> for Matrix3 { +impl> From, R>> for Matrix3 { fn from(dec: Decomposed, R>) -> Matrix3 { let m: Matrix2<_> = dec.rot.into(); let mut m: Matrix3<_> = (&m * dec.scale).into(); @@ -150,7 +176,7 @@ impl> From, R>> for Matrix3< } } -impl> From, R>> for Matrix4 { +impl> From, R>> for Matrix4 { fn from(dec: Decomposed, R>) -> Matrix4 { let m: Matrix3<_> = dec.rot.into(); let mut m: Matrix4<_> = (&m * dec.scale).into(); @@ -159,9 +185,13 @@ impl> From, R>> for Matrix4< } } -impl> Transform2 for Decomposed, R> {} +impl> Transform2 for Decomposed, R> { + type Scalar = S; +} -impl> Transform3 for Decomposed, R> {} +impl> Transform3 for Decomposed, R> { + type Scalar = S; +} impl approx::AbsDiffEq for Decomposed where diff --git a/tests/rotation.rs b/tests/rotation.rs index b661d31..65915c6 100644 --- a/tests/rotation.rs +++ b/tests/rotation.rs @@ -20,11 +20,11 @@ use cgmath::*; mod rotation { use super::cgmath::*; - pub fn a2>() -> R { + pub fn a2>() -> R { Rotation2::from_angle(Deg(30.0)) } - pub fn a3>() -> R { + pub fn a3>() -> R { let axis = Vector3::new(1.0, 1.0, 0.0).normalize(); Rotation3::from_axis_angle(axis, Deg(30.0)) } diff --git a/tests/transform.rs b/tests/transform.rs index 7670bc1..1dcc357 100644 --- a/tests/transform.rs +++ b/tests/transform.rs @@ -21,6 +21,43 @@ extern crate serde_json; use cgmath::*; +#[test] +fn test_mul() { + let t1 = Decomposed { + scale: 2.0f64, + rot: Quaternion::new(0.5f64.sqrt(), 0.5f64.sqrt(), 0.0, 0.0), + disp: Vector3::new(1.0f64, 2.0, 3.0), + }; + let t2 = Decomposed { + scale: 3.0f64, + rot: Quaternion::new(0.5f64.sqrt(), 0.0, 0.5f64.sqrt(), 0.0), + disp: Vector3::new(-2.0, 1.0, 0.0), + }; + + let actual = t1 * t2; + + let expected = Decomposed { + scale: 6.0f64, + rot: Quaternion::new(0.5, 0.5, 0.5, 0.5), + disp: Vector3::new(-3.0, 2.0, 5.0), + }; + + assert_ulps_eq!(actual, expected); +} + +#[test] +fn test_mul_one() { + let t = Decomposed { + scale: 2.0f64, + rot: Quaternion::new(0.5f64.sqrt(), 0.5f64.sqrt(), 0.0, 0.0), + disp: Vector3::new(1.0f64, 2.0, 3.0), + }; + let one = Decomposed::one(); + + assert_ulps_eq!(t * one, t); + assert_ulps_eq!(one * t, t); +} + #[test] fn test_invert() { let v = Vector3::new(1.0f64, 2.0, 3.0);