Fixed tests to compile; added Point homogeneous transform & test; added Transform inversion & test; Implemented Transform for AffineMatrix3

This commit is contained in:
kvark 2013-11-01 19:42:09 -04:00
parent 04b257e217
commit 364d959784
9 changed files with 135 additions and 44 deletions

View file

@ -34,6 +34,7 @@ pub trait Rotation
: Eq
+ ApproxEq<S>
{
fn identity() -> Self;
fn rotate_point(&self, point: &P) -> P;
fn rotate_vec(&self, vec: &V) -> V;
@ -111,6 +112,9 @@ impl<S: Float> ToMat2<S> for Basis2<S> {
}
impl<S: Float> Rotation<S, [S, ..2], Vec2<S>, Point2<S>> for Basis2<S> {
#[inline]
fn identity() -> Basis2<S> { Basis2{ mat: Mat2::identity() } }
#[inline]
fn rotate_point(&self, _point: &Point2<S>) -> Point2<S> { fail!("Not yet implemented") }
@ -229,6 +233,9 @@ impl<S: Float> ToQuat<S> for Basis3<S> {
}
impl<S: Float> Rotation<S, [S, ..3], Vec3<S>, Point3<S>> for Basis3<S> {
#[inline]
fn identity() -> Basis3<S> { Basis3{ mat: Mat3::identity() } }
#[inline]
fn rotate_point(&self, _point: &Point3<S>) -> Point3<S> { fail!("Not yet implemented") }
@ -288,6 +295,9 @@ impl<S: Float> ToQuat<S> for Quat<S> {
}
impl<S: Float> Rotation<S, [S, ..3], Vec3<S>, Point3<S>> for Quat<S> {
#[inline]
fn identity() -> Quat<S> { Quat::identity() }
#[inline]
fn rotate_point(&self, _point: &Point3<S>) -> Point3<S> { fail!("Not yet implemented") }
@ -310,4 +320,4 @@ impl<S: Float> Rotation<S, [S, ..3], Vec3<S>, Point3<S>> for Quat<S> {
fn invert_self(&mut self) { *self = self.invert() }
}
impl<S: Float> Rotation3<S> for Quat<S> {}
impl<S: Float> Rotation3<S> for Quat<S> {}

View file

@ -31,13 +31,23 @@ pub trait Transform
P: Point<S,V,Slice>
>
{
fn identity() -> Self;
fn transform_vec(&self, vec: &V) -> V;
fn transform_point(&self, point: &P) -> P;
fn invert(&self) -> Option<Self>;
#[inline]
fn transform_ray(&self, ray: &Ray<P,V>) -> Ray<P,V> {
Ray::new( self.transform_point(&ray.origin), self.transform_vec(&ray.direction) )
}
#[inline]
fn invert_self(&mut self)-> bool {
match self.invert() {
Some(t) => {*self = t; true},
None => false,
}
}
}
/// A generic transformation consisting of a rotation,
@ -57,6 +67,15 @@ impl
R: Rotation<S, Slice, V, P>
>
Transform<S, Slice, V, P> for Decomposed<S,V,R> {
#[inline]
fn identity() -> Decomposed<S,V,R> {
Decomposed {
scale: num::one(),
rot: Rotation::identity(),
disp: num::zero(),
}
}
#[inline]
fn transform_vec(&self, vec: &V) -> V {
self.rot.rotate_vec( &vec.mul_s( self.scale.clone() ))
@ -66,6 +85,23 @@ Transform<S, Slice, V, P> for Decomposed<S,V,R> {
fn transform_point(&self, point: &P) -> P {
self.rot.rotate_point( &point.mul_s( self.scale.clone() )).add_v( &self.disp )
}
#[inline]
fn invert(&self) -> Option<Decomposed<S,V,R>> {
if self.scale.approx_eq( &num::zero() ) {
None
}else {
let _1 : S = num::one();
let s = _1 / self.scale;
let r = self.rot.invert();
let d = r.rotate_vec( &self.disp ).mul_s( -s );
Some( Decomposed {
scale: s,
rot: r,
disp: d,
})
}
}
}
pub trait Transform3<S>
@ -92,6 +128,11 @@ pub struct AffineMatrix3<S> {
impl<S : Clone + Float>
Transform<S, [S, ..3], Vec3<S>, Point3<S>> for AffineMatrix3<S> {
#[inline]
fn identity() -> AffineMatrix3<S> {
AffineMatrix3 { mat: Mat4::identity() }
}
#[inline]
fn transform_vec(&self, vec: &Vec3<S>) -> Vec3<S> {
self.mat.mul_v( &vec.extend(num::zero()) ).truncate()
@ -100,6 +141,11 @@ Transform<S, [S, ..3], Vec3<S>, Point3<S>> for AffineMatrix3<S> {
#[inline]
fn transform_point(&self, point: &Point3<S>) -> Point3<S> {
Point3::from_homogeneous( &self.mat.mul_v( &point.to_homogeneous() ))
}
#[inline]
fn invert(&self) -> Option<AffineMatrix3<S>> {
self.mat.invert().map(|m| AffineMatrix3{ mat: m })
}
}
@ -120,4 +166,3 @@ impl<S: Float> Transform3D<S> {
Transform3D( Decomposed { scale: scale, rot: rot, disp: disp })
}
}

View file

@ -55,7 +55,7 @@ macro_rules! vec(
#[inline]
pub fn zero() -> $Self<$S> { $Self::from_value(zero()) }
/// The additive identity of the vector.
/// The multiplicative identity of the vector.
#[inline]
pub fn ident() -> $Self<$S> { $Self::from_value(one()) }
}

View file

@ -15,10 +15,12 @@
use cgmath::matrix::*;
use cgmath::vector::*;
type float = f64;
pub mod mat2 {
use cgmath::matrix::*;
use cgmath::vector::*;
type float = f64;
pub static A: Mat2<float> = Mat2 { x: Vec2 { x: 1.0, y: 3.0 },
y: Vec2 { x: 2.0, y: 4.0 } };
@ -34,6 +36,7 @@ pub mod mat2 {
pub mod mat3 {
use cgmath::matrix::*;
use cgmath::vector::*;
type float = f64;
pub static A: Mat3<float> = Mat3 { x: Vec3 { x: 1.0, y: 4.0, z: 7.0 },
y: Vec3 { x: 2.0, y: 5.0, z: 8.0 },
@ -55,6 +58,7 @@ pub mod mat3 {
pub mod mat4 {
use cgmath::matrix::*;
use cgmath::vector::*;
type float = f64;
pub static A: Mat4<float> = Mat4 { x: Vec4 { x: 1.0, y: 5.0, z: 9.0, w: 13.0 },
y: Vec4 { x: 2.0, y: 6.0, z: 10.0, w: 14.0 },

View file

@ -18,11 +18,13 @@ use cgmath::point::*;
#[test]
fn test_from_points() {
assert_eq!(Plane::from_points(Point3::new(5f, 0f, 5f),
Point3::new(5f, 5f, 5f),
Point3::new(5f, 0f, -1f)), Some(Plane::from_abcd(-1f, 0f, 0f, 5f)));
assert_eq!(Plane::from_points(Point3::new(5.0, 0.0, 5.0),
Point3::new(5.0, 5.0, 5.0),
Point3::new(5.0, 0.0, -1.0)),
Some(Plane::from_abcd(-1.0, 0.0, 0.0, 5.0)));
assert_eq!(Plane::from_points(Point3::new(0f, 5f, -5f),
Point3::new(0f, 5f, 0f),
Point3::new(0f, 5f, 5f)), None); // The points are parallel
assert_eq!(Plane::from_points(Point3::new(0.0, 5.0, -5.0),
Point3::new(0.0, 5.0, 0.0),
Point3::new(0.0, 5.0, 5.0)),
None); // The points are parallel
}

View file

@ -18,5 +18,5 @@ use cgmath::point::*;
#[test]
fn test_homogeneous() {
let p = Point3::new(1.0, 2.0, 3.0);
assert_eq!(p, Point3::from_homogeneous( &p.to_homogeneous() ));
assert_approx_eq!(p, Point3::from_homogeneous( &p.to_homogeneous() ));
}

View file

@ -13,6 +13,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#[feature(globs)];
extern mod cgmath;
// pub mod array;
@ -25,6 +27,7 @@ pub mod plane;
pub mod point;
// pub mod ray;
// pub mod rotation;
pub mod transform;
// pub mod projection;

27
src/tests/transform.rs Normal file
View file

@ -0,0 +1,27 @@
// 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 cgmath::quaternion::*;
use cgmath::transform::*;
use cgmath::vector::*;
#[test]
fn test_invert() {
let v = Vec3::new(1.0, 2.0, 3.0);
let t = Transform3D::new( 1.5, Quat::new(0.5,0.5,0.5,0.5), Vec3::new(6.0,-7.0,8.0) );
let ti = t.invert().expect("Expected successful inversion");
let vt = t.transform_vec( &v );
assert_approx_eq!(v, ti.transform_vec( &vt ));
}

View file

@ -36,9 +36,9 @@ fn test_comp_add() {
assert_eq!(Vec3::new(1, 2, 3).comp_add(), 6);
assert_eq!(Vec4::new(1, 2, 3, 4).comp_add(), 10);
assert_eq!(Vec2::new(3f, 4f).comp_add(), 7f);
assert_eq!(Vec3::new(4f, 5f, 6f).comp_add(), 15f);
assert_eq!(Vec4::new(5f, 6f, 7f, 8f).comp_add(), 26f);
assert_eq!(Vec2::new(3.0, 4.0).comp_add(), 7.0);
assert_eq!(Vec3::new(4.0, 5.0, 6.0).comp_add(), 15.0);
assert_eq!(Vec4::new(5.0, 6.0, 7.0, 8.0).comp_add(), 26.0);
}
#[test]
@ -47,9 +47,9 @@ fn test_comp_mul() {
assert_eq!(Vec3::new(1, 2, 3).comp_mul(), 6);
assert_eq!(Vec4::new(1, 2, 3, 4).comp_mul(), 24);
assert_eq!(Vec2::new(3f, 4f).comp_mul(), 12f);
assert_eq!(Vec3::new(4f, 5f, 6f).comp_mul(), 120f);
assert_eq!(Vec4::new(5f, 6f, 7f, 8f).comp_mul(), 1680f);
assert_eq!(Vec2::new(3.0, 4.0).comp_mul(), 12.0);
assert_eq!(Vec3::new(4.0, 5.0, 6.0).comp_mul(), 120.0);
assert_eq!(Vec4::new(5.0, 6.0, 7.0, 8.0).comp_mul(), 1680.0);
}
#[test]
@ -58,9 +58,9 @@ fn test_comp_min() {
assert_eq!(Vec3::new(1, 2, 3).comp_min(), 1);
assert_eq!(Vec4::new(1, 2, 3, 4).comp_min(), 1);
assert_eq!(Vec2::new(3f, 4f).comp_min(), 3f);
assert_eq!(Vec3::new(4f, 5f, 6f).comp_min(), 4f);
assert_eq!(Vec4::new(5f, 6f, 7f, 8f).comp_min(), 5f);
assert_eq!(Vec2::new(3.0, 4.0).comp_min(), 3.0);
assert_eq!(Vec3::new(4.0, 5.0, 6.0).comp_min(), 4.0);
assert_eq!(Vec4::new(5.0, 6.0, 7.0, 8.0).comp_min(), 5.0);
}
#[test]
@ -69,9 +69,9 @@ fn test_comp_max() {
assert_eq!(Vec3::new(1, 2, 3).comp_max(), 3);
assert_eq!(Vec4::new(1, 2, 3, 4).comp_max(), 4);
assert_eq!(Vec2::new(3f, 4f).comp_max(), 4f);
assert_eq!(Vec3::new(4f, 5f, 6f).comp_max(), 6f);
assert_eq!(Vec4::new(5f, 6f, 7f, 8f).comp_max(), 8f);
assert_eq!(Vec2::new(3.0, 4.0).comp_max(), 4.0);
assert_eq!(Vec3::new(4.0, 5.0, 6.0).comp_max(), 6.0);
assert_eq!(Vec4::new(5.0, 6.0, 7.0, 8.0).comp_max(), 8.0);
}
#[test]
@ -88,9 +88,9 @@ fn test_cross() {
#[test]
fn test_is_perpendicular() {
assert!(Vec2::new(1f, 0f).is_perpendicular(&Vec2::new(0f, 1f)));
assert!(Vec3::new(0f, 1f, 0f).is_perpendicular(&Vec3::new(0f, 0f, 1f)));
assert!(Vec4::new(1f, 0f, 0f, 0f).is_perpendicular(&Vec4::new(0f, 0f, 0f, 1f)));
assert!(Vec2::new(1.0, 0.0).is_perpendicular(&Vec2::new(0.0, 1.0)));
assert!(Vec3::new(0.0, 1.0, 0.0).is_perpendicular(&Vec3::new(0.0, 0.0, 1.0)));
assert!(Vec4::new(1.0, 0.0, 0.0, 0.0).is_perpendicular(&Vec4::new(0.0, 0.0, 0.0, 1.0)));
}
#[cfg(test)]
@ -99,8 +99,8 @@ mod test_length {
#[test]
fn test_vec2(){
let (a, a_res) = (Vec2::new(3f, 4f), 5f); // (3, 4, 5) Pythagorean triple
let (b, b_res) = (Vec2::new(5f, 12f), 13f); // (5, 12, 13) Pythagorean triple
let (a, a_res) = (Vec2::new(3.0, 4.0), 5.0); // (3, 4, 5) Pythagorean triple
let (b, b_res) = (Vec2::new(5.0, 12.0), 13.0); // (5, 12, 13) Pythagorean triple
assert_eq!(a.length2(), a_res * a_res);
assert_eq!(b.length2(), b_res * b_res);
@ -111,8 +111,8 @@ mod test_length {
#[test]
fn test_vec3(){
let (a, a_res) = (Vec3::new(2f, 3f, 6f), 7f); // (2, 3, 6, 7) Pythagorean quadruple
let (b, b_res) = (Vec3::new(1f, 4f, 8f), 9f); // (1, 4, 8, 9) Pythagorean quadruple
let (a, a_res) = (Vec3::new(2.0, 3.0, 6.0), 7.0); // (2, 3, 6, 7) Pythagorean quadruple
let (b, b_res) = (Vec3::new(1.0, 4.0, 8.0), 9.0); // (1, 4, 8, 9) Pythagorean quadruple
assert_eq!(a.length2(), a_res * a_res);
assert_eq!(b.length2(), b_res * b_res);
@ -123,8 +123,8 @@ mod test_length {
#[test]
fn test_vec4(){
let (a, a_res) = (Vec4::new(1f, 2f, 4f, 10f), 11f); // (1, 2, 4, 10, 11) Pythagorean quintuple
let (b, b_res) = (Vec4::new(1f, 2f, 8f, 10f), 13f); // (1, 2, 8, 10, 13) Pythagorean quintuple
let (a, a_res) = (Vec4::new(1.0, 2.0, 4.0, 10.0), 11.0); // (1, 2, 4, 10, 11) Pythagorean quintuple
let (b, b_res) = (Vec4::new(1.0, 2.0, 8.0, 10.0), 13.0); // (1, 2, 8, 10, 13) Pythagorean quintuple
assert_eq!(a.length2(), a_res * a_res);
assert_eq!(b.length2(), b_res * b_res);
@ -136,23 +136,23 @@ mod test_length {
#[test]
fn test_angle() {
assert_approx_eq!(Vec2::new(1f, 0f).angle(&Vec2::new(0f, 1f)), rad(Real::frac_pi_2()));
assert_approx_eq!(Vec2::new(10f, 0f).angle(&Vec2::new(0f, 5f)), rad(Real::frac_pi_2()));
assert_approx_eq!(Vec2::new(-1f, 0f).angle(&Vec2::new(0f, 1f)), -rad(Real::frac_pi_2()));
assert_approx_eq!(Vec2::new(1.0, 0.0).angle(&Vec2::new(0.0, 1.0)), rad(Real::frac_pi_2()));
assert_approx_eq!(Vec2::new(10.0, 0.0).angle(&Vec2::new(0.0, 5.0)), rad(Real::frac_pi_2()));
assert_approx_eq!(Vec2::new(-1.0, 0.0).angle(&Vec2::new(0.0, 1.0)), -rad(Real::frac_pi_2()));
assert_approx_eq!(Vec3::new(1f, 0f, 1f).angle(&Vec3::new(1f, 1f, 0f)), rad(Real::frac_pi_3()));
assert_approx_eq!(Vec3::new(10f, 0f, 10f).angle(&Vec3::new(5f, 5f, 0f)), rad(Real::frac_pi_3()));
assert_approx_eq!(Vec3::new(-1f, 0f, -1f).angle(&Vec3::new(1f, -1f, 0f)), rad(2f * Real::frac_pi_3()));
assert_approx_eq!(Vec3::new(1.0, 0.0, 1.0).angle(&Vec3::new(1.0, 1.0, 0.0)), rad(Real::frac_pi_3()));
assert_approx_eq!(Vec3::new(10.0, 0.0, 10.0).angle(&Vec3::new(5.0, 5.0, 0.0)), rad(Real::frac_pi_3()));
assert_approx_eq!(Vec3::new(-1.0, 0.0, -1.0).angle(&Vec3::new(1.0, -1.0, 0.0)), rad(2.0 * Real::frac_pi_3()));
assert_approx_eq!(Vec4::new(1f, 0f, 1f, 0f).angle(&Vec4::new(0f, 1f, 0f, 1f)), rad(Real::frac_pi_2()));
assert_approx_eq!(Vec4::new(10f, 0f, 10f, 0f).angle(&Vec4::new(0f, 5f, 0f, 5f)), rad(Real::frac_pi_2()));
assert_approx_eq!(Vec4::new(-1f, 0f, -1f, 0f).angle(&Vec4::new(0f, 1f, 0f, 1f)), rad(Real::frac_pi_2()));
assert_approx_eq!(Vec4::new(1.0, 0.0, 1.0, 0.0).angle(&Vec4::new(0.0, 1.0, 0.0, 1.0)), rad(Real::frac_pi_2()));
assert_approx_eq!(Vec4::new(10.0, 0.0, 10.0, 0.0).angle(&Vec4::new(0.0, 5.0, 0.0, 5.0)), rad(Real::frac_pi_2()));
assert_approx_eq!(Vec4::new(-1.0, 0.0, -1.0, 0.0).angle(&Vec4::new(0.0, 1.0, 0.0, 1.0)), rad(Real::frac_pi_2()));
}
#[test]
fn test_normalize() {
// TODO: test normalize_to, normalize_self, and normalize_self_to
assert_approx_eq!(Vec2::new(3f, 4f).normalize(), Vec2::new(3f/5f, 4f/5f));
assert_approx_eq!(Vec3::new(2f, 3f, 6f).normalize(), Vec3::new(2f/7f, 3f/7f, 6f/7f));
assert_approx_eq!(Vec4::new(1f, 2f, 4f, 10f).normalize(), Vec4::new(1f/11f, 2f/11f, 4f/11f, 10f/11f));
// TODO: test normalize_to, normalize_sel.0, and normalize_self_to
assert_approx_eq!(Vec2::new(3.0, 4.0).normalize(), Vec2::new(3.0/5.0, 4.0/5.0));
assert_approx_eq!(Vec3::new(2.0, 3.0, 6.0).normalize(), Vec3::new(2.0/7.0, 3.0/7.0, 6.0/7.0));
assert_approx_eq!(Vec4::new(1.0, 2.0, 4.0, 10.0).normalize(), Vec4::new(1.0/11.0, 2.0/11.0, 4.0/11.0, 10.0/11.0));
}