Skip to content

Commit

Permalink
Now there is only one one (#513)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
hayashi-stl committed Aug 12, 2020
1 parent 816c043 commit 84da664
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 58 deletions.
24 changes: 9 additions & 15 deletions src/matrix.rs
Expand Up @@ -1038,10 +1038,6 @@ impl<S: BaseFloat> approx::UlpsEq for Matrix4<S> {
}

impl<S: BaseFloat> Transform<Point2<S>> for Matrix3<S> {
fn one() -> Matrix3<S> {
One::one()
}

fn look_at(eye: Point2<S>, center: Point2<S>, up: Vector2<S>) -> Matrix3<S> {
let dir = center - eye;
Matrix3::from(Matrix2::look_at(dir, up))
Expand All @@ -1065,10 +1061,6 @@ impl<S: BaseFloat> Transform<Point2<S>> for Matrix3<S> {
}

impl<S: BaseFloat> Transform<Point3<S>> for Matrix3<S> {
fn one() -> Matrix3<S> {
One::one()
}

fn look_at(eye: Point3<S>, center: Point3<S>, up: Vector3<S>) -> Matrix3<S> {
let dir = center - eye;
Matrix3::look_at(dir, up)
Expand All @@ -1092,10 +1084,6 @@ impl<S: BaseFloat> Transform<Point3<S>> for Matrix3<S> {
}

impl<S: BaseFloat> Transform<Point3<S>> for Matrix4<S> {
fn one() -> Matrix4<S> {
One::one()
}

fn look_at(eye: Point3<S>, center: Point3<S>, up: Vector3<S>) -> Matrix4<S> {
Matrix4::look_at(eye, center, up)
}
Expand All @@ -1117,11 +1105,17 @@ impl<S: BaseFloat> Transform<Point3<S>> for Matrix4<S> {
}
}

impl<S: BaseFloat> Transform2<S> for Matrix3<S> {}
impl<S: BaseFloat> Transform2 for Matrix3<S> {
type Scalar = S;
}

impl<S: BaseFloat> Transform3<S> for Matrix3<S> {}
impl<S: BaseFloat> Transform3 for Matrix3<S> {
type Scalar = S;
}

impl<S: BaseFloat> Transform3<S> for Matrix4<S> {}
impl<S: BaseFloat> Transform3 for Matrix4<S> {
type Scalar = S;
}

macro_rules! impl_matrix {
($MatrixN:ident, $VectorN:ident { $($field:ident : $row_index:expr),+ }) => {
Expand Down
8 changes: 6 additions & 2 deletions src/quaternion.rs
Expand Up @@ -480,7 +480,9 @@ impl<S: BaseFloat> From<Quaternion<S>> for Basis3<S> {
}
}

impl<S: BaseFloat> Rotation<Point3<S>> for Quaternion<S> {
impl<S: BaseFloat> Rotation for Quaternion<S> {
type Space = Point3<S>;

#[inline]
fn look_at(dir: Vector3<S>, up: Vector3<S>) -> Quaternion<S> {
Matrix3::look_at(dir, up).into()
Expand Down Expand Up @@ -526,7 +528,9 @@ impl<S: BaseFloat> Rotation<Point3<S>> for Quaternion<S> {
}
}

impl<S: BaseFloat> Rotation3<S> for Quaternion<S> {
impl<S: BaseFloat> Rotation3 for Quaternion<S> {
type Scalar = S;

#[inline]
fn from_axis_angle<A: Into<Rad<S>>>(axis: Vector3<S>, angle: A) -> Quaternion<S> {
let (s, c) = Rad::sin_cos(angle.into() * cast(0.5f64).unwrap());
Expand Down
75 changes: 52 additions & 23 deletions src/rotation.rs
Expand Up @@ -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<P: EuclideanSpace>: Sized + Copy + One
pub trait Rotation: Sized + Copy + One
where
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
Self: approx::AbsDiffEq<Epsilon = P::Scalar>,
Self: approx::RelativeEq<Epsilon = P::Scalar>,
Self: approx::UlpsEq<Epsilon = P::Scalar>,
P::Scalar: BaseFloat,
Self: approx::AbsDiffEq<Epsilon = <<Self as Rotation>::Space as EuclideanSpace>::Scalar>,
Self: approx::RelativeEq<Epsilon = <<Self as Rotation>::Space as EuclideanSpace>::Scalar>,
Self: approx::UlpsEq<Epsilon = <<Self as Rotation>::Space as EuclideanSpace>::Scalar>,
<Self::Space as EuclideanSpace>::Scalar: BaseFloat,
Self: iter::Product<Self>,
{
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: <Self::Space as EuclideanSpace>::Diff,
up: <Self::Space as EuclideanSpace>::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: <Self::Space as EuclideanSpace>::Diff,
b: <Self::Space as EuclideanSpace>::Diff,
) -> Self;

/// Rotate a vector using this rotation.
fn rotate_vector(&self, vec: P::Diff) -> P::Diff;
fn rotate_vector(
&self,
vec: <Self::Space as EuclideanSpace>::Diff,
) -> <Self::Space as EuclideanSpace>::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,
Expand All @@ -62,38 +73,48 @@ where
}

/// A two-dimensional rotation.
pub trait Rotation2<S: BaseFloat>:
Rotation<Point2<S>> + Into<Matrix2<S>> + Into<Basis2<S>>
pub trait Rotation2:
Rotation<Space = Point2<<Self as Rotation2>::Scalar>>
+ Into<Matrix2<<Self as Rotation2>::Scalar>>
+ Into<Basis2<<Self as Rotation2>::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<A: Into<Rad<S>>>(theta: A) -> Self;
fn from_angle<A: Into<Rad<Self::Scalar>>>(theta: A) -> Self;
}

/// A three-dimensional rotation.
pub trait Rotation3<S: BaseFloat>:
Rotation<Point3<S>> + Into<Matrix3<S>> + Into<Basis3<S>> + Into<Quaternion<S>> + From<Euler<Rad<S>>>
pub trait Rotation3:
Rotation<Space = Point3<<Self as Rotation3>::Scalar>>
+ Into<Matrix3<<Self as Rotation3>::Scalar>>
+ Into<Basis3<<Self as Rotation3>::Scalar>>
+ Into<Quaternion<<Self as Rotation3>::Scalar>>
+ From<Euler<Rad<<Self as Rotation3>::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<A: Into<Rad<S>>>(axis: Vector3<S>, angle: A) -> Self;
fn from_axis_angle<A: Into<Rad<Self::Scalar>>>(axis: Vector3<Self::Scalar>, angle: A) -> Self;

/// Create a rotation from an angle around the `x` axis (pitch).
#[inline]
fn from_angle_x<A: Into<Rad<S>>>(theta: A) -> Self {
fn from_angle_x<A: Into<Rad<Self::Scalar>>>(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<A: Into<Rad<S>>>(theta: A) -> Self {
fn from_angle_y<A: Into<Rad<Self::Scalar>>>(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<A: Into<Rad<S>>>(theta: A) -> Self {
fn from_angle_z<A: Into<Rad<Self::Scalar>>>(theta: A) -> Self {
Rotation3::from_axis_angle(Vector3::unit_z(), theta)
}
}
Expand Down Expand Up @@ -183,7 +204,9 @@ impl<'a, S: 'a + BaseFloat> iter::Product<&'a Basis2<S>> for Basis2<S> {
}
}

impl<S: BaseFloat> Rotation<Point2<S>> for Basis2<S> {
impl<S: BaseFloat> Rotation for Basis2<S> {
type Space = Point2<S>;

#[inline]
fn look_at(dir: Vector2<S>, up: Vector2<S>) -> Basis2<S> {
Basis2 {
Expand Down Expand Up @@ -262,7 +285,9 @@ impl<S: BaseFloat> approx::UlpsEq for Basis2<S> {
}
}

impl<S: BaseFloat> Rotation2<S> for Basis2<S> {
impl<S: BaseFloat> Rotation2 for Basis2<S> {
type Scalar = S;

fn from_angle<A: Into<Rad<S>>>(theta: A) -> Basis2<S> {
Basis2 {
mat: Matrix2::from_angle(theta),
Expand Down Expand Up @@ -334,7 +359,9 @@ impl<'a, S: 'a + BaseFloat> iter::Product<&'a Basis3<S>> for Basis3<S> {
}
}

impl<S: BaseFloat> Rotation<Point3<S>> for Basis3<S> {
impl<S: BaseFloat> Rotation for Basis3<S> {
type Space = Point3<S>;

#[inline]
fn look_at(dir: Vector3<S>, up: Vector3<S>) -> Basis3<S> {
Basis3 {
Expand Down Expand Up @@ -414,7 +441,9 @@ impl<S: BaseFloat> approx::UlpsEq for Basis3<S> {
}
}

impl<S: BaseFloat> Rotation3<S> for Basis3<S> {
impl<S: BaseFloat> Rotation3 for Basis3<S> {
type Scalar = S;

fn from_axis_angle<A: Into<Rad<S>>>(axis: Vector3<S>, angle: A) -> Basis3<S> {
Basis3 {
mat: Matrix3::from_axis_angle(axis, angle),
Expand Down
62 changes: 46 additions & 16 deletions src/transform.rs
Expand Up @@ -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<P: EuclideanSpace>: Sized {
/// Create an identity transformation. That is, a transformation which
/// does nothing.
fn one() -> Self;

pub trait Transform<P: EuclideanSpace>: 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;
Expand Down Expand Up @@ -69,21 +67,41 @@ pub struct Decomposed<V: VectorSpace, R> {
pub disp: V,
}

impl<P: EuclideanSpace, R: Rotation<P>> Transform<P> for Decomposed<P::Diff, R>
impl<P: EuclideanSpace, R: Rotation<Space = P>> One for Decomposed<P::Diff, R>
where
P::Scalar: BaseFloat,
// FIXME: Investigate why this is needed!
P::Diff: VectorSpace,
{
#[inline]
fn one() -> Decomposed<P::Diff, R> {
fn one() -> Self {
Decomposed {
scale: P::Scalar::one(),
rot: R::one(),
disp: P::Diff::zero(),
}
}
}

impl<P: EuclideanSpace, R: Rotation<Space = P>> Mul for Decomposed<P::Diff, R>
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<P::Diff, R>) -> Self::Output {
self.concat(&rhs)
}
}

impl<P: EuclideanSpace, R: Rotation<Space = P>> Transform<P> for Decomposed<P::Diff, R>
where
P::Scalar: BaseFloat,
P::Diff: VectorSpace,
{
#[inline]
fn look_at(eye: P, center: P, up: P::Diff) -> Decomposed<P::Diff, R> {
let rot = R::look_at(center - eye, up);
Expand Down Expand Up @@ -138,10 +156,18 @@ where
}
}

pub trait Transform2<S: BaseNum>: Transform<Point2<S>> + Into<Matrix3<S>> {}
pub trait Transform3<S: BaseNum>: Transform<Point3<S>> + Into<Matrix4<S>> {}
pub trait Transform2:
Transform<Point2<<Self as Transform2>::Scalar>> + Into<Matrix3<<Self as Transform2>::Scalar>>
{
type Scalar: BaseNum;
}
pub trait Transform3:
Transform<Point3<<Self as Transform3>::Scalar>> + Into<Matrix4<<Self as Transform3>::Scalar>>
{
type Scalar: BaseNum;
}

impl<S: BaseFloat, R: Rotation2<S>> From<Decomposed<Vector2<S>, R>> for Matrix3<S> {
impl<S: BaseFloat, R: Rotation2<Scalar = S>> From<Decomposed<Vector2<S>, R>> for Matrix3<S> {
fn from(dec: Decomposed<Vector2<S>, R>) -> Matrix3<S> {
let m: Matrix2<_> = dec.rot.into();
let mut m: Matrix3<_> = (&m * dec.scale).into();
Expand All @@ -150,7 +176,7 @@ impl<S: BaseFloat, R: Rotation2<S>> From<Decomposed<Vector2<S>, R>> for Matrix3<
}
}

impl<S: BaseFloat, R: Rotation3<S>> From<Decomposed<Vector3<S>, R>> for Matrix4<S> {
impl<S: BaseFloat, R: Rotation3<Scalar = S>> From<Decomposed<Vector3<S>, R>> for Matrix4<S> {
fn from(dec: Decomposed<Vector3<S>, R>) -> Matrix4<S> {
let m: Matrix3<_> = dec.rot.into();
let mut m: Matrix4<_> = (&m * dec.scale).into();
Expand All @@ -159,9 +185,13 @@ impl<S: BaseFloat, R: Rotation3<S>> From<Decomposed<Vector3<S>, R>> for Matrix4<
}
}

impl<S: BaseFloat, R: Rotation2<S>> Transform2<S> for Decomposed<Vector2<S>, R> {}
impl<S: BaseFloat, R: Rotation2<Scalar = S>> Transform2 for Decomposed<Vector2<S>, R> {
type Scalar = S;
}

impl<S: BaseFloat, R: Rotation3<S>> Transform3<S> for Decomposed<Vector3<S>, R> {}
impl<S: BaseFloat, R: Rotation3<Scalar = S>> Transform3 for Decomposed<Vector3<S>, R> {
type Scalar = S;
}

impl<S: VectorSpace, R, E: BaseFloat> approx::AbsDiffEq for Decomposed<S, R>
where
Expand Down
4 changes: 2 additions & 2 deletions tests/rotation.rs
Expand Up @@ -20,11 +20,11 @@ use cgmath::*;
mod rotation {
use super::cgmath::*;

pub fn a2<R: Rotation2<f64>>() -> R {
pub fn a2<R: Rotation2<Scalar = f64>>() -> R {
Rotation2::from_angle(Deg(30.0))
}

pub fn a3<R: Rotation3<f64>>() -> R {
pub fn a3<R: Rotation3<Scalar = f64>>() -> R {
let axis = Vector3::new(1.0, 1.0, 0.0).normalize();
Rotation3::from_axis_angle(axis, Deg(30.0))
}
Expand Down
37 changes: 37 additions & 0 deletions tests/transform.rs
Expand Up @@ -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);
Expand Down

0 comments on commit 84da664

Please sign in to comment.