Fix look_at matrices + implement Display for statically sized structures.

This commit is contained in:
Sébastien Crozet 2016-03-28 14:56:25 +02:00
parent 60c0f32e1c
commit 02001667f7
17 changed files with 523 additions and 201 deletions

View File

@ -1,3 +1,4 @@
use std::fmt;
use std::ops::{Add, Sub, Mul, Neg}; use std::ops::{Add, Sub, Mul, Neg};
use rand::{Rand, Rng}; use rand::{Rand, Rng};
@ -44,33 +45,37 @@ pub struct Iso3<N> {
} }
impl<N: Clone + BaseFloat> Iso3<N> { impl<N: Clone + BaseFloat> Iso3<N> {
/// Reorient and translate this transformation such that its local `x` axis points to a given /// Creates an isometry that corresponds to the local frame of an observer standing at the
/// direction. Note that the usually known `look_at` function does the same thing but with the /// point `eye` and looking toward `target`.
/// `z` axis. See `look_at_z` for that. ///
/// It maps the view direction `target - eye` to the positive `z` axis and the origin to the
/// `eye`.
/// ///
/// # Arguments /// # Arguments
/// * eye - The new translation of the transformation. /// * eye - The observer position.
/// * at - The point to look at. `at - eye` is the direction the matrix `x` axis will be /// * target - The target position.
/// aligned with. /// * up - Vertical direction. The only requirement of this parameter is to not be collinear
/// * up - Vector pointing up. The only requirement of this parameter is to not be colinear /// to `eye - at`. Non-collinearity is not checked.
/// with `at`. Non-colinearity is not checked.
#[inline] #[inline]
pub fn look_at(eye: &Pnt3<N>, at: &Pnt3<N>, up: &Vec3<N>) -> Iso3<N> { pub fn new_observer_frame(eye: &Pnt3<N>, target: &Pnt3<N>, up: &Vec3<N>) -> Iso3<N> {
Iso3::new_with_rotmat(eye.as_vec().clone(), Rot3::look_at(&(*at - *eye), up)) let new_rotmat = Rot3::new_observer_frame(&(*target - *eye), up);
Iso3::new_with_rotmat(eye.as_vec().clone(), new_rotmat)
} }
/// Reorient and translate this transformation such that its local `z` axis points to a given /// Builds a look-at view matrix.
/// direction. ///
/// This conforms to the common notion of "look-at" matrix from the computer graphics
/// community. Its maps the view direction `target - eye` to the **negative** `z` axis and the
/// `eye` to the origin.
/// ///
/// # Arguments /// # Arguments
/// * eye - The new translation of the transformation. /// * eye - The eye position.
/// * at - The point to look at. `at - eye` is the direction the matrix `x` axis will be /// * target - The target position.
/// aligned with /// * up - The vertical view direction. It must not be to collinear to `eye - target`.
/// * up - Vector pointing `up`. The only requirement of this parameter is to not be colinear
/// with `at`. Non-colinearity is not checked.
#[inline] #[inline]
pub fn look_at_z(eye: &Pnt3<N>, at: &Pnt3<N>, up: &Vec3<N>) -> Iso3<N> { pub fn new_look_at(eye: &Pnt3<N>, target: &Pnt3<N>, up: &Vec3<N>) -> Iso3<N> {
Iso3::new_with_rotmat(eye.as_vec().clone(), Rot3::look_at_z(&(*at - *eye), up)) let new_rotmat = Rot3::new_look_at(&(*target - *eye), up);
Iso3::new_with_rotmat(new_rotmat * (-*eye.as_vec()), new_rotmat)
} }
} }
@ -95,6 +100,7 @@ pnt_mul_iso_impl!(Iso2, Pnt2);
iso_mul_vec_impl!(Iso2, Vec2); iso_mul_vec_impl!(Iso2, Vec2);
vec_mul_iso_impl!(Iso2, Vec2); vec_mul_iso_impl!(Iso2, Vec2);
arbitrary_iso_impl!(Iso2); arbitrary_iso_impl!(Iso2);
iso_display_impl!(Iso2);
iso_impl!(Iso3, Rot3, Vec3, Vec3); iso_impl!(Iso3, Rot3, Vec3, Vec3);
rotation_matrix_impl!(Iso3, Rot3, Vec3, Vec3); rotation_matrix_impl!(Iso3, Rot3, Vec3, Vec3);
@ -117,3 +123,4 @@ pnt_mul_iso_impl!(Iso3, Pnt3);
iso_mul_vec_impl!(Iso3, Vec3); iso_mul_vec_impl!(Iso3, Vec3);
vec_mul_iso_impl!(Iso3, Vec3); vec_mul_iso_impl!(Iso3, Vec3);
arbitrary_iso_impl!(Iso3); arbitrary_iso_impl!(Iso3);
iso_display_impl!(Iso3);

View File

@ -403,3 +403,26 @@ macro_rules! arbitrary_iso_impl(
} }
) )
); );
macro_rules! iso_display_impl(
($t: ident) => (
impl<N: fmt::Display + BaseFloat> fmt::Display for $t<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(writeln!(f, "Isometry {{"));
if let Some(precision) = f.precision() {
try!(writeln!(f, "... translation: {:.*}", precision, self.translation));
try!(writeln!(f, "... rotation matrix:"));
try!(write!(f, "{:.*}", precision, *self.rotation.submat()));
}
else {
try!(writeln!(f, "... translation: {}", self.translation));
try!(writeln!(f, "... rotation matrix:"));
try!(write!(f, "{}", *self.rotation.submat()));
}
writeln!(f, "}}")
}
}
)
);

View File

@ -2,6 +2,7 @@
#![allow(missing_docs)] // we allow missing to avoid having to document the mij components. #![allow(missing_docs)] // we allow missing to avoid having to document the mij components.
use std::fmt;
use std::ops::{Add, Sub, Mul, Div, Index, IndexMut}; use std::ops::{Add, Sub, Mul, Div, Index, IndexMut};
use std::mem; use std::mem;
use std::slice::{Iter, IterMut}; use std::slice::{Iter, IterMut};
@ -82,6 +83,7 @@ eigen_qr_impl!(Mat1, Vec1);
arbitrary_impl!(Mat1, m11); arbitrary_impl!(Mat1, m11);
rand_impl!(Mat1, m11); rand_impl!(Mat1, m11);
mean_impl!(Mat1, Vec1, 1); mean_impl!(Mat1, Vec1, 1);
mat_display_impl!(Mat1, 1);
/// Square matrix of dimension 2. /// Square matrix of dimension 2.
#[repr(C)] #[repr(C)]
@ -136,6 +138,7 @@ eigen_qr_impl!(Mat2, Vec2);
arbitrary_impl!(Mat2, m11, m12, m21, m22); arbitrary_impl!(Mat2, m11, m12, m21, m22);
rand_impl!(Mat2, m11, m12, m21, m22); rand_impl!(Mat2, m11, m12, m21, m22);
mean_impl!(Mat2, Vec2, 2); mean_impl!(Mat2, Vec2, 2);
mat_display_impl!(Mat2, 2);
/// Square matrix of dimension 3. /// Square matrix of dimension 3.
#[repr(C)] #[repr(C)]
@ -233,6 +236,7 @@ rand_impl!(Mat3,
m31, m32, m33 m31, m32, m33
); );
mean_impl!(Mat3, Vec3, 3); mean_impl!(Mat3, Vec3, 3);
mat_display_impl!(Mat3, 3);
/// Square matrix of dimension 4. /// Square matrix of dimension 4.
#[repr(C)] #[repr(C)]
@ -353,6 +357,7 @@ rand_impl!(Mat4,
m41, m42, m43, m44 m41, m42, m43, m44
); );
mean_impl!(Mat4, Vec4, 4); mean_impl!(Mat4, Vec4, 4);
mat_display_impl!(Mat4, 4);
/// Square matrix of dimension 5. /// Square matrix of dimension 5.
#[repr(C)] #[repr(C)]
@ -490,6 +495,7 @@ rand_impl!(Mat5,
m51, m52, m53, m54, m55 m51, m52, m53, m54, m55
); );
mean_impl!(Mat5, Vec5, 5); mean_impl!(Mat5, Vec5, 5);
mat_display_impl!(Mat5, 5);
/// Square matrix of dimension 6. /// Square matrix of dimension 6.
#[repr(C)] #[repr(C)]
@ -632,3 +638,4 @@ rand_impl!(Mat6,
m61, m62, m63, m64, m65, m66 m61, m62, m63, m64, m65, m66
); );
mean_impl!(Mat6, Vec6, 6); mean_impl!(Mat6, Vec6, 6);
mat_display_impl!(Mat6, 6);

View File

@ -749,3 +749,56 @@ macro_rules! mean_impl(
} }
) )
); );
macro_rules! mat_display_impl(
($t: ident, $dim: expr) => (
impl<N: fmt::Display + BaseFloat> fmt::Display for $t<N> {
// XXX: will will not always work correctly due to rounding errors.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn integral_length<N: BaseFloat>(val: &N) -> usize {
let mut res = 1;
let mut curr: N = ::cast(10.0f64);
while curr <= *val {
curr = curr * ::cast(10.0f64);
res = res + 1;
}
if val.is_sign_negative() {
res + 1
}
else {
res
}
}
let mut max_decimal_length = 0;
let mut decimal_lengths: $t<usize> = ::zero();
for i in 0 .. $dim {
for j in 0 .. $dim {
decimal_lengths[(i, j)] = integral_length(&self[(i, j)].clone());
max_decimal_length = ::max(max_decimal_length, decimal_lengths[(i, j)]);
}
}
let precision = f.precision().unwrap_or(3);
let max_number_length = max_decimal_length + precision + 1;
try!(writeln!(f, " ┌ {:>width$} ┐", "", width = max_number_length * $dim + $dim - 1));
for i in 0 .. $dim {
try!(write!(f, ""));
for j in 0 .. $dim {
let number_length = decimal_lengths[(i, j)] + precision + 1;
let pad = max_number_length - number_length;
try!(write!(f, " {:>thepad$}", "", thepad = pad));
try!(write!(f, "{:.*}", precision, (*self)[(i, j)]));
}
try!(writeln!(f, ""));
}
writeln!(f, " └ {:>width$} ┘", "", width = max_number_length * $dim + $dim - 1)
}
}
)
);

View File

@ -41,11 +41,11 @@ impl<N: BaseFloat> Ortho3<N> {
/// Builds a 4D projection matrix (using homogeneous coordinates) for this projection. /// Builds a 4D projection matrix (using homogeneous coordinates) for this projection.
pub fn to_mat(&self) -> Mat4<N> { pub fn to_mat(&self) -> Mat4<N> {
self.to_persp_mat().mat self.to_ortho_mat().mat
} }
/// Build a `OrthoMat3` representing this projection. /// Build a `OrthoMat3` representing this projection.
pub fn to_persp_mat(&self) -> OrthoMat3<N> { pub fn to_ortho_mat(&self) -> OrthoMat3<N> {
OrthoMat3::new(self.width, self.height, self.znear, self.zfar) OrthoMat3::new(self.width, self.height, self.znear, self.zfar)
} }
} }
@ -114,14 +114,14 @@ impl<N: BaseFloat + Clone> Ortho3<N> {
#[inline] #[inline]
pub fn project_pnt(&self, p: &Pnt3<N>) -> Pnt3<N> { pub fn project_pnt(&self, p: &Pnt3<N>) -> Pnt3<N> {
// FIXME: optimize that // FIXME: optimize that
self.to_persp_mat().project_pnt(p) self.to_ortho_mat().project_pnt(p)
} }
/// Projects a vector. /// Projects a vector.
#[inline] #[inline]
pub fn project_vec(&self, p: &Vec3<N>) -> Vec3<N> { pub fn project_vec(&self, p: &Vec3<N>) -> Vec3<N> {
// FIXME: optimize that // FIXME: optimize that
self.to_persp_mat().project_vec(p) self.to_ortho_mat().project_vec(p)
} }
} }
@ -251,7 +251,7 @@ impl<N: BaseFloat + Clone> OrthoMat3<N> {
impl<N: Arbitrary + BaseFloat> Arbitrary for OrthoMat3<N> { impl<N: Arbitrary + BaseFloat> Arbitrary for OrthoMat3<N> {
fn arbitrary<G: Gen>(g: &mut G) -> OrthoMat3<N> { fn arbitrary<G: Gen>(g: &mut G) -> OrthoMat3<N> {
let x: Ortho3<N> = Arbitrary::arbitrary(g); let x: Ortho3<N> = Arbitrary::arbitrary(g);
x.to_persp_mat() x.to_ortho_mat()
} }
} }

View File

@ -1,6 +1,7 @@
//! Points with dimension known at compile-time. //! Points with dimension known at compile-time.
use std::mem; use std::mem;
use std::fmt;
use std::slice::{Iter, IterMut}; use std::slice::{Iter, IterMut};
use std::iter::{Iterator, FromIterator, IntoIterator}; use std::iter::{Iterator, FromIterator, IntoIterator};
use std::ops::{Add, Sub, Mul, Div, Neg, Index, IndexMut}; use std::ops::{Add, Sub, Mul, Div, Neg, Index, IndexMut};
@ -57,6 +58,7 @@ pnt_from_homogeneous_impl!(Pnt1, Pnt2, y, x);
num_float_pnt_impl!(Pnt1, Vec1); num_float_pnt_impl!(Pnt1, Vec1);
arbitrary_pnt_impl!(Pnt1, x); arbitrary_pnt_impl!(Pnt1, x);
rand_impl!(Pnt1, x); rand_impl!(Pnt1, x);
pnt_display_impl!(Pnt1);
/// Point of dimension 2. /// Point of dimension 2.
/// ///
@ -102,6 +104,7 @@ pnt_from_homogeneous_impl!(Pnt2, Pnt3, z, x, y);
num_float_pnt_impl!(Pnt2, Vec2); num_float_pnt_impl!(Pnt2, Vec2);
arbitrary_pnt_impl!(Pnt2, x, y); arbitrary_pnt_impl!(Pnt2, x, y);
rand_impl!(Pnt2, x, y); rand_impl!(Pnt2, x, y);
pnt_display_impl!(Pnt2);
/// Point of dimension 3. /// Point of dimension 3.
/// ///
@ -149,6 +152,7 @@ pnt_from_homogeneous_impl!(Pnt3, Pnt4, w, x, y, z);
num_float_pnt_impl!(Pnt3, Vec3); num_float_pnt_impl!(Pnt3, Vec3);
arbitrary_pnt_impl!(Pnt3, x, y, z); arbitrary_pnt_impl!(Pnt3, x, y, z);
rand_impl!(Pnt3, x, y, z); rand_impl!(Pnt3, x, y, z);
pnt_display_impl!(Pnt3);
/// Point of dimension 4. /// Point of dimension 4.
/// ///
@ -198,6 +202,7 @@ pnt_from_homogeneous_impl!(Pnt4, Pnt5, a, x, y, z, w);
num_float_pnt_impl!(Pnt4, Vec4); num_float_pnt_impl!(Pnt4, Vec4);
arbitrary_pnt_impl!(Pnt4, x, y, z, w); arbitrary_pnt_impl!(Pnt4, x, y, z, w);
rand_impl!(Pnt4, x, y, z, w); rand_impl!(Pnt4, x, y, z, w);
pnt_display_impl!(Pnt4);
/// Point of dimension 5. /// Point of dimension 5.
/// ///
@ -249,6 +254,7 @@ pnt_from_homogeneous_impl!(Pnt5, Pnt6, b, x, y, z, w, a);
num_float_pnt_impl!(Pnt5, Vec5); num_float_pnt_impl!(Pnt5, Vec5);
arbitrary_pnt_impl!(Pnt5, x, y, z, w, a); arbitrary_pnt_impl!(Pnt5, x, y, z, w, a);
rand_impl!(Pnt5, x, y, z, w, a); rand_impl!(Pnt5, x, y, z, w, a);
pnt_display_impl!(Pnt5);
/// Point of dimension 6. /// Point of dimension 6.
/// ///
@ -300,3 +306,4 @@ iterable_mut_impl!(Pnt6, 6);
num_float_pnt_impl!(Pnt6, Vec6); num_float_pnt_impl!(Pnt6, Vec6);
arbitrary_pnt_impl!(Pnt6, x, y, z, w, a, b); arbitrary_pnt_impl!(Pnt6, x, y, z, w, a, b);
rand_impl!(Pnt6, x, y, z, w, a, b); rand_impl!(Pnt6, x, y, z, w, a, b);
pnt_display_impl!(Pnt6);

View File

@ -155,3 +155,24 @@ macro_rules! arbitrary_pnt_impl(
} }
) )
); );
macro_rules! pnt_display_impl(
($t: ident) => (
impl<N: fmt::Display> fmt::Display for $t<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// FIXME: differenciate them from vectors ?
try!(write!(f, "("));
let mut it = self.iter();
try!(write!(f, "{}", *it.next().unwrap()));
for comp in it {
try!(write!(f, ", {}", *comp));
}
write!(f, ")")
}
}
)
);

View File

@ -1,5 +1,6 @@
//! Quaternion definition. //! Quaternion definition.
use std::fmt;
use std::mem; use std::mem;
use std::slice::{Iter, IterMut}; use std::slice::{Iter, IterMut};
use std::ops::{Add, Sub, Mul, Div, Neg, Index, IndexMut}; use std::ops::{Add, Sub, Mul, Div, Neg, Index, IndexMut};
@ -154,6 +155,12 @@ impl<N: ApproxEq<N> + BaseFloat> Div<Quat<N>> for Quat<N> {
} }
} }
impl<N: fmt::Display> fmt::Display for Quat<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Quaternion {} ({}, {}, {})", self.w, self.i, self.j, self.k)
}
}
rand_impl!(Quat, w, i, j, k); rand_impl!(Quat, w, i, j, k);
@ -505,6 +512,12 @@ impl<N: BaseNum + Neg<Output = N>> Transform<Pnt3<N>> for UnitQuat<N> {
} }
} }
impl<N: fmt::Display> fmt::Display for UnitQuat<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Unit quaternion {} ({}, {}, {})", self.q.w, self.q.i, self.q.j, self.q.k)
}
}
#[cfg(feature="arbitrary")] #[cfg(feature="arbitrary")]
impl<N: Arbitrary + BaseFloat> Arbitrary for UnitQuat<N> { impl<N: Arbitrary + BaseFloat> Arbitrary for UnitQuat<N> {
fn arbitrary<G: Gen>(g: &mut G) -> UnitQuat<N> { fn arbitrary<G: Gen>(g: &mut G) -> UnitQuat<N> {

View File

@ -1,5 +1,6 @@
//! Rotations matrices. //! Rotations matrices.
use std::fmt;
use std::ops::{Mul, Neg, Index}; use std::ops::{Mul, Neg, Index};
use rand::{Rand, Rng}; use rand::{Rand, Rng};
use num::{Zero, One}; use num::{Zero, One};
@ -193,48 +194,43 @@ impl<N: Clone + BaseFloat> Rot3<N> {
} }
impl<N: Clone + BaseFloat> Rot3<N> { impl<N: Clone + BaseFloat> Rot3<N> {
/// Create a new matrix and orient it such that its local `x` axis points to a given point. /// Creates a rotation that corresponds to the local frame of an observer standing at the
/// Note that the usually known `look_at` function does the same thing but with the `z` axis. /// origin and looking toward `dir`.
/// See `look_at_z` for that. ///
/// It maps the view direction `dir` to the positive `z` axis.
/// ///
/// # Arguments /// # Arguments
/// * at - The point to look at. It is also the direction the matrix `x` axis will be aligned /// * dir - The look direction, that is, direction the matrix `z` axis will be aligned with.
/// with /// * up - The vertical direction. The only requirement of this parameter is to not be
/// * up - Vector pointing `up`. The only requirement of this parameter is to not be colinear /// collinear
/// with `at`. Non-colinearity is not checked. /// to `dir`. Non-collinearity is not checked.
pub fn look_at(at: &Vec3<N>, up: &Vec3<N>) -> Rot3<N> { #[inline]
let xaxis = Norm::normalize(at); pub fn new_observer_frame(dir: &Vec3<N>, up: &Vec3<N>) -> Rot3<N> {
let zaxis = Norm::normalize(&Cross::cross(up, &xaxis)); let zaxis = Norm::normalize(dir);
let yaxis = Cross::cross(&zaxis, &xaxis);
unsafe {
Rot3::new_with_mat(Mat3::new(
xaxis.x.clone(), yaxis.x.clone(), zaxis.x.clone(),
xaxis.y.clone(), yaxis.y.clone(), zaxis.y.clone(),
xaxis.z , yaxis.z , zaxis.z)
)
}
}
/// Create a new matrix and orient it such that its local `z` axis points to a given point.
///
/// # Arguments
/// * at - The look direction, that is, direction the matrix `y` axis will be aligned with
/// * up - Vector pointing `up`. The only requirement of this parameter is to not be colinear
/// with `at`. Non-colinearity is not checked.
pub fn look_at_z(at: &Vec3<N>, up: &Vec3<N>) -> Rot3<N> {
let zaxis = Norm::normalize(at);
let xaxis = Norm::normalize(&Cross::cross(up, &zaxis)); let xaxis = Norm::normalize(&Cross::cross(up, &zaxis));
let yaxis = Cross::cross(&zaxis, &xaxis); let yaxis = Norm::normalize(&Cross::cross(&zaxis, &xaxis));
unsafe { unsafe {
Rot3::new_with_mat(Mat3::new( Rot3::new_with_mat(Mat3::new(
xaxis.x.clone(), yaxis.x.clone(), zaxis.x.clone(), xaxis.x.clone(), yaxis.x.clone(), zaxis.x.clone(),
xaxis.y.clone(), yaxis.y.clone(), zaxis.y.clone(), xaxis.y.clone(), yaxis.y.clone(), zaxis.y.clone(),
xaxis.z , yaxis.z , zaxis.z) xaxis.z , yaxis.z , zaxis.z))
)
} }
} }
/// Builds a look-at view matrix with no translational component.
///
/// This conforms to the common notion of "look-at" matrix from the computer graphics community.
/// Its maps the view direction `dir` to the **negative** `z` axis.
///
/// # Arguments
/// * dir - The view direction.
/// * up - The vertical direction. The only requirement of this parameter is to not be
/// collinear to `dir`.
#[inline]
pub fn new_look_at(dir: &Vec3<N>, up: &Vec3<N>) -> Rot3<N> {
Rot3::new_observer_frame(&(-*dir), up).inv().unwrap()
}
} }
impl<N: Clone + BaseFloat + Cast<f64>> impl<N: Clone + BaseFloat + Cast<f64>>
@ -368,6 +364,7 @@ inv_impl!(Rot2);
transpose_impl!(Rot2); transpose_impl!(Rot2);
approx_eq_impl!(Rot2); approx_eq_impl!(Rot2);
diag_impl!(Rot2, Vec2); diag_impl!(Rot2, Vec2);
rot_display_impl!(Rot2);
submat_impl!(Rot3, Mat3); submat_impl!(Rot3, Mat3);
rotate_impl!(Rot3, Vec3, Pnt3); rotate_impl!(Rot3, Vec3, Pnt3);
@ -390,3 +387,4 @@ inv_impl!(Rot3);
transpose_impl!(Rot3); transpose_impl!(Rot3);
approx_eq_impl!(Rot3); approx_eq_impl!(Rot3);
diag_impl!(Rot3, Vec3); diag_impl!(Rot3, Vec3);
rot_display_impl!(Rot3);

View File

@ -326,3 +326,17 @@ macro_rules! absolute_impl(
} }
) )
); );
macro_rules! rot_display_impl(
($t: ident) => (
impl<N: fmt::Display + BaseFloat> fmt::Display for $t<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let precision = f.precision().unwrap_or(3);
try!(writeln!(f, "Rotation matrix {{"));
try!(write!(f, "{:.*}", precision, self.submat));
writeln!(f, "}}")
}
}
)
);

View File

@ -1,3 +1,4 @@
use std::fmt;
use std::ops::{Mul, Neg}; use std::ops::{Mul, Neg};
use rand::{Rand, Rng}; use rand::{Rand, Rng};
@ -64,6 +65,7 @@ sim_to_homogeneous_impl!(Sim2, Mat3);
sim_approx_eq_impl!(Sim2); sim_approx_eq_impl!(Sim2);
sim_rand_impl!(Sim2); sim_rand_impl!(Sim2);
sim_arbitrary_impl!(Sim2); sim_arbitrary_impl!(Sim2);
sim_display_impl!(Sim2);
sim_impl!(Sim3, Iso3, Rot3, Vec3, Vec3); sim_impl!(Sim3, Iso3, Rot3, Vec3, Vec3);
dim_impl!(Sim3, 3); dim_impl!(Sim3, 3);
@ -81,3 +83,4 @@ sim_to_homogeneous_impl!(Sim3, Mat4);
sim_approx_eq_impl!(Sim3); sim_approx_eq_impl!(Sim3);
sim_rand_impl!(Sim3); sim_rand_impl!(Sim3);
sim_arbitrary_impl!(Sim3); sim_arbitrary_impl!(Sim3);
sim_display_impl!(Sim3);

View File

@ -118,7 +118,7 @@ macro_rules! sim_mul_sim_impl(
#[inline] #[inline]
fn mul(self, right: $t<N>) -> $t<N> { fn mul(self, right: $t<N>) -> $t<N> {
$t::new_with_rotmat( $t::new_with_rotmat(
self.isometry.translation + self.isometry.rotation * (right.isometry.translation * right.scale), self.isometry.translation + self.isometry.rotation * (right.isometry.translation * self.scale),
self.isometry.rotation * right.isometry.rotation, self.isometry.rotation * right.isometry.rotation,
self.scale * right.scale) self.scale * right.scale)
} }
@ -174,10 +174,11 @@ macro_rules! sim_inv_impl(
fn inv_mut(&mut self) -> bool { fn inv_mut(&mut self) -> bool {
self.scale = ::one::<N>() / self.scale; self.scale = ::one::<N>() / self.scale;
self.isometry.inv_mut(); self.isometry.inv_mut();
// We multiply by self.scale because the scale has been inverted on the previous line. // We multiply (instead of dividing) by self.scale because it has already been
// inverted.
self.isometry.translation = self.isometry.translation * self.scale; self.isometry.translation = self.isometry.translation * self.scale;
// always succeed // Always succeed.
true true
} }
@ -186,7 +187,7 @@ macro_rules! sim_inv_impl(
let mut res = *self; let mut res = *self;
res.inv_mut(); res.inv_mut();
// always succeed // Always succeed.
Some(res) Some(res)
} }
} }
@ -243,7 +244,12 @@ macro_rules! sim_rand_impl(
impl<N: Rand + BaseFloat> Rand for $t<N> { impl<N: Rand + BaseFloat> Rand for $t<N> {
#[inline] #[inline]
fn rand<R: Rng>(rng: &mut R) -> $t<N> { fn rand<R: Rng>(rng: &mut R) -> $t<N> {
$t::new_with_iso(rng.gen(), rng.gen()) let mut scale: N = rng.gen();
while scale.is_zero() {
scale = rng.gen();
}
$t::new_with_iso(rng.gen(), scale)
} }
} }
) )
@ -262,3 +268,28 @@ macro_rules! sim_arbitrary_impl(
} }
) )
); );
macro_rules! sim_display_impl(
($t: ident) => (
impl<N: fmt::Display + BaseFloat> fmt::Display for $t<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(writeln!(f, "Similarity transformation {{"));
if let Some(precision) = f.precision() {
try!(writeln!(f, "... scale factor: {:.*}", precision, self.scale));
try!(writeln!(f, "... translation: {:.*}", precision, self.isometry.translation));
try!(writeln!(f, "... rotation matrix:"));
try!(write!(f, "{:.*}", precision, *self.isometry.rotation.submat()));
}
else {
try!(writeln!(f, "... scale factor: {}", self.scale));
try!(writeln!(f, "... translation: {}", self.isometry.translation));
try!(writeln!(f, "... rotation matrix:"));
try!(write!(f, "{}", *self.isometry.rotation.submat()));
}
writeln!(f, "}}")
}
}
)
);

View File

@ -4,6 +4,7 @@ use std::ops::{Add, Sub, Mul, Div, Neg, Index, IndexMut};
use std::mem; use std::mem;
use std::slice::{Iter, IterMut}; use std::slice::{Iter, IterMut};
use std::iter::{Iterator, FromIterator, IntoIterator}; use std::iter::{Iterator, FromIterator, IntoIterator};
use std::fmt;
use rand::{Rand, Rng}; use rand::{Rand, Rng};
use num::{Zero, One}; use num::{Zero, One};
use traits::operations::{ApproxEq, POrd, POrdering, Axpy, Absolute, Mean}; use traits::operations::{ApproxEq, POrd, POrdering, Axpy, Absolute, Mean};
@ -71,6 +72,7 @@ absolute_vec_impl!(Vec1, x);
arbitrary_impl!(Vec1, x); arbitrary_impl!(Vec1, x);
rand_impl!(Vec1, x); rand_impl!(Vec1, x);
mean_impl!(Vec1); mean_impl!(Vec1);
vec_display_impl!(Vec1);
/// Vector of dimension 2. /// Vector of dimension 2.
/// ///
@ -128,6 +130,7 @@ absolute_vec_impl!(Vec2, x, y);
arbitrary_impl!(Vec2, x, y); arbitrary_impl!(Vec2, x, y);
rand_impl!(Vec2, x, y); rand_impl!(Vec2, x, y);
mean_impl!(Vec2); mean_impl!(Vec2);
vec_display_impl!(Vec2);
/// Vector of dimension 3. /// Vector of dimension 3.
/// ///
@ -187,6 +190,7 @@ absolute_vec_impl!(Vec3, x, y, z);
arbitrary_impl!(Vec3, x, y, z); arbitrary_impl!(Vec3, x, y, z);
rand_impl!(Vec3, x, y, z); rand_impl!(Vec3, x, y, z);
mean_impl!(Vec3); mean_impl!(Vec3);
vec_display_impl!(Vec3);
/// Vector of dimension 4. /// Vector of dimension 4.
@ -249,6 +253,7 @@ absolute_vec_impl!(Vec4, x, y, z, w);
arbitrary_impl!(Vec4, x, y, z, w); arbitrary_impl!(Vec4, x, y, z, w);
rand_impl!(Vec4, x, y, z, w); rand_impl!(Vec4, x, y, z, w);
mean_impl!(Vec4); mean_impl!(Vec4);
vec_display_impl!(Vec4);
/// Vector of dimension 5. /// Vector of dimension 5.
/// ///
@ -312,6 +317,7 @@ absolute_vec_impl!(Vec5, x, y, z, w, a);
arbitrary_impl!(Vec5, x, y, z, w, a); arbitrary_impl!(Vec5, x, y, z, w, a);
rand_impl!(Vec5, x, y, z, w, a); rand_impl!(Vec5, x, y, z, w, a);
mean_impl!(Vec5); mean_impl!(Vec5);
vec_display_impl!(Vec5);
/// Vector of dimension 6. /// Vector of dimension 6.
/// ///
@ -375,3 +381,4 @@ absolute_vec_impl!(Vec6, x, y, z, w, a, b);
arbitrary_impl!(Vec6, x, y, z, w, a, b); arbitrary_impl!(Vec6, x, y, z, w, a, b);
rand_impl!(Vec6, x, y, z, w, a, b); rand_impl!(Vec6, x, y, z, w, a, b);
mean_impl!(Vec6); mean_impl!(Vec6);
vec_display_impl!(Vec6);

View File

@ -820,3 +820,25 @@ macro_rules! mean_impl(
} }
) )
); );
macro_rules! vec_display_impl(
($t: ident) => (
impl<N: fmt::Display> fmt::Display for $t<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "("));
let mut it = self.iter();
let precision = f.precision().unwrap_or(8);
try!(write!(f, "{:.*}", precision, *it.next().unwrap()));
for comp in it {
try!(write!(f, ", {:.*}", precision, *comp));
}
write!(f, ")")
}
}
)
);

View File

@ -2,8 +2,8 @@ extern crate nalgebra as na;
extern crate rand; extern crate rand;
use rand::random; use rand::random;
use na::{Vec1, Vec3, Mat1, Mat2, Mat3, Mat4, Mat5, Mat6, Rot2, Rot3, Persp3, PerspMat3, Ortho3, use na::{Rot2, Rot3, Iso2, Iso3, Sim2, Sim3, Vec3, Mat1, Mat2, Mat3, Mat4, Mat5, Mat6, DMat, DVec,
OrthoMat3, DMat, DVec, Row, Col, BaseFloat, Diag, Transpose, RowSlice, ColSlice, Shape}; Row, Col, Diag, Transpose, RowSlice, ColSlice, Shape};
macro_rules! test_inv_mat_impl( macro_rules! test_inv_mat_impl(
($t: ty) => ( ($t: ty) => (
@ -12,7 +12,9 @@ macro_rules! test_inv_mat_impl(
match na::inv(&randmat) { match na::inv(&randmat) {
None => { }, None => { },
Some(i) => assert!(na::approx_eq(&(i * randmat), &na::one())) Some(i) => {
assert!(na::approx_eq(&(i * randmat), &na::one()))
}
} }
} }
); );
@ -183,13 +185,33 @@ fn test_inv_mat6() {
} }
#[test] #[test]
fn test_rotation2() { fn test_inv_rot2() {
for _ in 0usize .. 10000 { test_inv_mat_impl!(Rot2<f64>);
let randmat: na::Rot2<f64> = na::one(); }
let ang = Vec1::new(na::abs(&random::<f64>()) % <f64 as BaseFloat>::pi());
assert!(na::approx_eq(&na::rotation(&na::append_rotation(&randmat, &ang)), &ang)); #[test]
} fn test_inv_rot3() {
test_inv_mat_impl!(Rot3<f64>);
}
#[test]
fn test_inv_iso2() {
test_inv_mat_impl!(Iso2<f64>);
}
#[test]
fn test_inv_iso3() {
test_inv_mat_impl!(Iso3<f64>);
}
#[test]
fn test_inv_sim2() {
test_inv_mat_impl!(Sim2<f64>);
}
#[test]
fn test_inv_sim3() {
test_inv_mat_impl!(Sim3<f64>);
} }
#[test] #[test]
@ -199,59 +221,6 @@ fn test_index_mat2() {
assert!(mat[(0, 1)] == na::transpose(&mat)[(1, 0)]); assert!(mat[(0, 1)] == na::transpose(&mat)[(1, 0)]);
} }
#[test]
fn test_inv_rotation3() {
for _ in 0usize .. 10000 {
let randmat: Rot3<f64> = na::one();
let dir: Vec3<f64> = random();
let ang = na::normalize(&dir) * (na::abs(&random::<f64>()) % <f64 as BaseFloat>::pi());
let rot = na::append_rotation(&randmat, &ang);
assert!(na::approx_eq(&(na::transpose(&rot) * rot), &na::one()));
}
}
#[test]
fn test_rot3_rotation_between() {
let r1: Rot3<f64> = random();
let r2: Rot3<f64> = random();
let delta = na::rotation_between(&r1, &r2);
assert!(na::approx_eq(&(delta * r1), &r2))
}
#[test]
fn test_rot3_angle_between() {
let r1: Rot3<f64> = random();
let r2: Rot3<f64> = random();
let delta = na::rotation_between(&r1, &r2);
let delta_angle = na::angle_between(&r1, &r2);
assert!(na::approx_eq(&na::norm(&na::rotation(&delta)), &delta_angle))
}
#[test]
fn test_rot2_rotation_between() {
let r1: Rot2<f64> = random();
let r2: Rot2<f64> = random();
let delta = na::rotation_between(&r1, &r2);
assert!(na::approx_eq(&(delta * r1), &r2))
}
#[test]
fn test_rot2_angle_between() {
let r1: Rot2<f64> = random();
let r2: Rot2<f64> = random();
let delta = na::rotation_between(&r1, &r2);
let delta_angle = na::angle_between(&r1, &r2);
assert!(na::approx_eq(&na::norm(&na::rotation(&delta)), &delta_angle))
}
#[test] #[test]
fn test_mean_dmat() { fn test_mean_dmat() {
@ -755,86 +724,6 @@ fn test_row_3() {
assert!(second_col == Vec3::new(1.0, 4.0, 7.0)); assert!(second_col == Vec3::new(1.0, 4.0, 7.0));
} }
#[test]
fn test_persp() {
let mut p = Persp3::new(42.0f64, 0.5, 1.5, 10.0);
let mut pm = PerspMat3::new(42.0f64, 0.5, 1.5, 10.0);
assert!(p.to_mat() == pm.to_mat());
assert!(p.aspect() == 42.0);
assert!(p.fov() == 0.5);
assert!(p.znear() == 1.5);
assert!(p.zfar() == 10.0);
assert!(na::approx_eq(&pm.aspect(), &42.0));
assert!(na::approx_eq(&pm.fov(), &0.5));
assert!(na::approx_eq(&pm.znear(), &1.5));
assert!(na::approx_eq(&pm.zfar(), &10.0));
p.set_fov(0.1);
pm.set_fov(0.1);
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
p.set_znear(24.0);
pm.set_znear(24.0);
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
p.set_zfar(61.0);
pm.set_zfar(61.0);
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
p.set_aspect(23.0);
pm.set_aspect(23.0);
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
assert!(p.aspect() == 23.0);
assert!(p.fov() == 0.1);
assert!(p.znear() == 24.0);
assert!(p.zfar() == 61.0);
assert!(na::approx_eq(&pm.aspect(), &23.0));
assert!(na::approx_eq(&pm.fov(), &0.1));
assert!(na::approx_eq(&pm.znear(), &24.0));
assert!(na::approx_eq(&pm.zfar(), &61.0));
}
#[test]
fn test_ortho() {
let mut p = Ortho3::new(42.0f64, 0.5, 1.5, 10.0);
let mut pm = OrthoMat3::new(42.0f64, 0.5, 1.5, 10.0);
assert!(p.to_mat() == pm.to_mat());
assert!(p.width() == 42.0);
assert!(p.height() == 0.5);
assert!(p.znear() == 1.5);
assert!(p.zfar() == 10.0);
assert!(na::approx_eq(&pm.width(), &42.0));
assert!(na::approx_eq(&pm.height(), &0.5));
assert!(na::approx_eq(&pm.znear(), &1.5));
assert!(na::approx_eq(&pm.zfar(), &10.0));
p.set_width(0.1);
pm.set_width(0.1);
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
p.set_znear(24.0);
pm.set_znear(24.0);
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
p.set_zfar(61.0);
pm.set_zfar(61.0);
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
p.set_height(23.0);
pm.set_height(23.0);
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
assert!(p.height() == 23.0);
assert!(p.width() == 0.1);
assert!(p.znear() == 24.0);
assert!(p.zfar() == 61.0);
assert!(na::approx_eq(&pm.height(), &23.0));
assert!(na::approx_eq(&pm.width(), &0.1));
assert!(na::approx_eq(&pm.znear(), &24.0));
assert!(na::approx_eq(&pm.zfar(), &61.0));
}
#[test] #[test]
fn test_cholesky_const() { fn test_cholesky_const() {

220
tests/transforms.rs Normal file
View File

@ -0,0 +1,220 @@
extern crate nalgebra as na;
extern crate rand;
use rand::random;
use na::{Pnt3, Vec3, Vec1, Rot2, Rot3, Persp3, PerspMat3, Ortho3, OrthoMat3, Iso2, Iso3, BaseFloat,
Sim2, Sim3};
macro_rules! test_transform_inv_transform_impl(
($t: ty, $p: ty) => (
for _ in 0usize .. 10000 {
let randmat: $t = random();
let randvec: $p = random();
let tvec = randmat.transform(&randvec);
let original = randmat.inv_transform(&randvec);
assert!(na::approx_eq(&randvec, &original));
}
);
);
test_transform_inv_transform_impl!(Rot2, Pnt2);
test_transform_inv_transform_impl!(Rot3, Pnt3);
test_transform_inv_transform_impl!(Iso2, Pnt2);
test_transform_inv_transform_impl!(Iso3, Pnt3);
test_transform_inv_transform_impl!(Sim2, Pnt2);
test_transform_inv_transform_impl!(Sim3, Pnt3);
#[test]
fn test_rotation2() {
for _ in 0usize .. 10000 {
let randmat: na::Rot2<f64> = na::one();
let ang = Vec1::new(na::abs(&random::<f64>()) % <f64 as BaseFloat>::pi());
assert!(na::approx_eq(&na::rotation(&na::append_rotation(&randmat, &ang)), &ang));
}
}
#[test]
fn test_inv_rotation3() {
for _ in 0usize .. 10000 {
let randmat: Rot3<f64> = na::one();
let dir: Vec3<f64> = random();
let ang = na::normalize(&dir) * (na::abs(&random::<f64>()) % <f64 as BaseFloat>::pi());
let rot = na::append_rotation(&randmat, &ang);
assert!(na::approx_eq(&(na::transpose(&rot) * rot), &na::one()));
}
}
#[test]
fn test_rot3_rotation_between() {
let r1: Rot3<f64> = random();
let r2: Rot3<f64> = random();
let delta = na::rotation_between(&r1, &r2);
assert!(na::approx_eq(&(delta * r1), &r2))
}
#[test]
fn test_rot3_angle_between() {
let r1: Rot3<f64> = random();
let r2: Rot3<f64> = random();
let delta = na::rotation_between(&r1, &r2);
let delta_angle = na::angle_between(&r1, &r2);
assert!(na::approx_eq(&na::norm(&na::rotation(&delta)), &delta_angle))
}
#[test]
fn test_rot2_rotation_between() {
let r1: Rot2<f64> = random();
let r2: Rot2<f64> = random();
let delta = na::rotation_between(&r1, &r2);
assert!(na::approx_eq(&(delta * r1), &r2))
}
#[test]
fn test_rot2_angle_between() {
let r1: Rot2<f64> = random();
let r2: Rot2<f64> = random();
let delta = na::rotation_between(&r1, &r2);
let delta_angle = na::angle_between(&r1, &r2);
assert!(na::approx_eq(&na::norm(&na::rotation(&delta)), &delta_angle))
}
#[test]
fn test_look_at_iso3() {
for _ in 0usize .. 10000 {
let eye = random::<Pnt3<f64>>();
let target = random::<Pnt3<f64>>();
let up = random::<Vec3<f64>>();
let viewmat = Iso3::new_look_at(&eye, &target, &up);
assert_eq!(&(viewmat * eye), &na::orig());
assert!(na::approx_eq(&na::normalize(&(viewmat * (target - eye))), &-Vec3::z()));
}
}
#[test]
fn test_look_at_rot3() {
for _ in 0usize .. 10000 {
let dir = random::<Vec3<f64>>();
let up = random::<Vec3<f64>>();
let viewmat = Rot3::new_look_at(&dir, &up);
assert!(na::approx_eq(&na::normalize(&(viewmat * dir)), &-Vec3::z()));
}
}
#[test]
fn test_observer_frame_iso3() {
for _ in 0usize .. 10000 {
let eye = random::<Pnt3<f64>>();
let target = random::<Pnt3<f64>>();
let up = random::<Vec3<f64>>();
let observer = Iso3::new_observer_frame(&eye, &target, &up);
assert_eq!(&(observer * na::orig::<Pnt3<f64>>()), &eye);
assert!(na::approx_eq(&(observer * Vec3::z()), &na::normalize(&(target - eye))));
}
}
#[test]
fn test_observer_frame_rot3() {
for _ in 0usize .. 10000 {
let dir = random::<Vec3<f64>>();
let up = random::<Vec3<f64>>();
let observer = Rot3::new_observer_frame(&dir, &up);
assert!(na::approx_eq(&(observer * Vec3::z()), &na::normalize(&dir)));
}
}
#[test]
fn test_persp() {
let mut p = Persp3::new(42.0f64, 0.5, 1.5, 10.0);
let mut pm = PerspMat3::new(42.0f64, 0.5, 1.5, 10.0);
assert!(p.to_mat() == pm.to_mat());
assert!(p.aspect() == 42.0);
assert!(p.fov() == 0.5);
assert!(p.znear() == 1.5);
assert!(p.zfar() == 10.0);
assert!(na::approx_eq(&pm.aspect(), &42.0));
assert!(na::approx_eq(&pm.fov(), &0.5));
assert!(na::approx_eq(&pm.znear(), &1.5));
assert!(na::approx_eq(&pm.zfar(), &10.0));
p.set_fov(0.1);
pm.set_fov(0.1);
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
p.set_znear(24.0);
pm.set_znear(24.0);
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
p.set_zfar(61.0);
pm.set_zfar(61.0);
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
p.set_aspect(23.0);
pm.set_aspect(23.0);
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
assert!(p.aspect() == 23.0);
assert!(p.fov() == 0.1);
assert!(p.znear() == 24.0);
assert!(p.zfar() == 61.0);
assert!(na::approx_eq(&pm.aspect(), &23.0));
assert!(na::approx_eq(&pm.fov(), &0.1));
assert!(na::approx_eq(&pm.znear(), &24.0));
assert!(na::approx_eq(&pm.zfar(), &61.0));
}
#[test]
fn test_ortho() {
let mut p = Ortho3::new(42.0f64, 0.5, 1.5, 10.0);
let mut pm = OrthoMat3::new(42.0f64, 0.5, 1.5, 10.0);
assert!(p.to_mat() == pm.to_mat());
assert!(p.width() == 42.0);
assert!(p.height() == 0.5);
assert!(p.znear() == 1.5);
assert!(p.zfar() == 10.0);
assert!(na::approx_eq(&pm.width(), &42.0));
assert!(na::approx_eq(&pm.height(), &0.5));
assert!(na::approx_eq(&pm.znear(), &1.5));
assert!(na::approx_eq(&pm.zfar(), &10.0));
p.set_width(0.1);
pm.set_width(0.1);
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
p.set_znear(24.0);
pm.set_znear(24.0);
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
p.set_zfar(61.0);
pm.set_zfar(61.0);
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
p.set_height(23.0);
pm.set_height(23.0);
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
assert!(p.height() == 23.0);
assert!(p.width() == 0.1);
assert!(p.znear() == 24.0);
assert!(p.zfar() == 61.0);
assert!(na::approx_eq(&pm.height(), &23.0));
assert!(na::approx_eq(&pm.width(), &0.1));
assert!(na::approx_eq(&pm.znear(), &24.0));
assert!(na::approx_eq(&pm.zfar(), &61.0));
}

View File

@ -1,10 +1,16 @@
extern crate rand; extern crate rand;
#[cfg(feature="generic_sizes")]
extern crate typenum; extern crate typenum;
extern crate nalgebra as na; extern crate nalgebra as na;
use rand::random; use rand::random;
use na::{Vec1, Vec2, Vec3, Vec4, Vec5, Vec6, Mat3, Rot2, Rot3, Iterable, IterableMut};
#[cfg(feature="generic_sizes")]
use typenum::U10; use typenum::U10;
use na::{VecN, Vec1, Vec2, Vec3, Vec4, Vec5, Vec6, Mat3, Rot2, Rot3, Iterable, IterableMut}; #[cfg(feature="generic_sizes")]
use na::VecN;
macro_rules! test_iterator_impl( macro_rules! test_iterator_impl(
($t: ty, $n: ty) => ( ($t: ty, $n: ty) => (
@ -295,6 +301,7 @@ fn test_outer_vec3() {
12.0, 15.0, 18.0)); 12.0, 15.0, 18.0));
} }
#[cfg(feature="generic_sizes")]
#[test] #[test]
fn test_vecn10_add_mul() { fn test_vecn10_add_mul() {
for _ in 0usize .. 10000 { for _ in 0usize .. 10000 {