diff --git a/src/structs/iso.rs b/src/structs/iso.rs index f3e84c2c..decbdf6d 100644 --- a/src/structs/iso.rs +++ b/src/structs/iso.rs @@ -1,3 +1,4 @@ +use std::fmt; use std::ops::{Add, Sub, Mul, Neg}; use rand::{Rand, Rng}; @@ -44,33 +45,37 @@ pub struct Iso3 { } impl Iso3 { - /// Reorient and translate this transformation such that its local `x` axis points to a given - /// direction. Note that the usually known `look_at` function does the same thing but with the - /// `z` axis. See `look_at_z` for that. + /// Creates an isometry that corresponds to the local frame of an observer standing at the + /// point `eye` and looking toward `target`. + /// + /// It maps the view direction `target - eye` to the positive `z` axis and the origin to the + /// `eye`. /// /// # Arguments - /// * eye - The new translation of the transformation. - /// * at - The point to look at. `at - eye` is the direction the matrix `x` 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. + /// * eye - The observer position. + /// * target - The target position. + /// * up - Vertical direction. The only requirement of this parameter is to not be collinear + /// to `eye - at`. Non-collinearity is not checked. #[inline] - pub fn look_at(eye: &Pnt3, at: &Pnt3, up: &Vec3) -> Iso3 { - Iso3::new_with_rotmat(eye.as_vec().clone(), Rot3::look_at(&(*at - *eye), up)) + pub fn new_observer_frame(eye: &Pnt3, target: &Pnt3, up: &Vec3) -> Iso3 { + 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 - /// direction. + /// Builds a look-at view matrix. + /// + /// 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 - /// * eye - The new translation of the transformation. - /// * at - The point to look at. `at - eye` is the direction the matrix `x` 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. + /// * eye - The eye position. + /// * target - The target position. + /// * up - The vertical view direction. It must not be to collinear to `eye - target`. #[inline] - pub fn look_at_z(eye: &Pnt3, at: &Pnt3, up: &Vec3) -> Iso3 { - Iso3::new_with_rotmat(eye.as_vec().clone(), Rot3::look_at_z(&(*at - *eye), up)) + pub fn new_look_at(eye: &Pnt3, target: &Pnt3, up: &Vec3) -> Iso3 { + 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); vec_mul_iso_impl!(Iso2, Vec2); arbitrary_iso_impl!(Iso2); +iso_display_impl!(Iso2); iso_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); vec_mul_iso_impl!(Iso3, Vec3); arbitrary_iso_impl!(Iso3); +iso_display_impl!(Iso3); diff --git a/src/structs/iso_macros.rs b/src/structs/iso_macros.rs index 79484bf7..c5f42099 100644 --- a/src/structs/iso_macros.rs +++ b/src/structs/iso_macros.rs @@ -403,3 +403,26 @@ macro_rules! arbitrary_iso_impl( } ) ); + +macro_rules! iso_display_impl( + ($t: ident) => ( + impl fmt::Display for $t { + 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, "}}") + } + } + ) +); diff --git a/src/structs/mat.rs b/src/structs/mat.rs index 6ca710fc..9b580206 100644 --- a/src/structs/mat.rs +++ b/src/structs/mat.rs @@ -2,6 +2,7 @@ #![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::mem; use std::slice::{Iter, IterMut}; @@ -82,6 +83,7 @@ eigen_qr_impl!(Mat1, Vec1); arbitrary_impl!(Mat1, m11); rand_impl!(Mat1, m11); mean_impl!(Mat1, Vec1, 1); +mat_display_impl!(Mat1, 1); /// Square matrix of dimension 2. #[repr(C)] @@ -136,6 +138,7 @@ eigen_qr_impl!(Mat2, Vec2); arbitrary_impl!(Mat2, m11, m12, m21, m22); rand_impl!(Mat2, m11, m12, m21, m22); mean_impl!(Mat2, Vec2, 2); +mat_display_impl!(Mat2, 2); /// Square matrix of dimension 3. #[repr(C)] @@ -233,6 +236,7 @@ rand_impl!(Mat3, m31, m32, m33 ); mean_impl!(Mat3, Vec3, 3); +mat_display_impl!(Mat3, 3); /// Square matrix of dimension 4. #[repr(C)] @@ -353,6 +357,7 @@ rand_impl!(Mat4, m41, m42, m43, m44 ); mean_impl!(Mat4, Vec4, 4); +mat_display_impl!(Mat4, 4); /// Square matrix of dimension 5. #[repr(C)] @@ -490,6 +495,7 @@ rand_impl!(Mat5, m51, m52, m53, m54, m55 ); mean_impl!(Mat5, Vec5, 5); +mat_display_impl!(Mat5, 5); /// Square matrix of dimension 6. #[repr(C)] @@ -632,3 +638,4 @@ rand_impl!(Mat6, m61, m62, m63, m64, m65, m66 ); mean_impl!(Mat6, Vec6, 6); +mat_display_impl!(Mat6, 6); diff --git a/src/structs/mat_macros.rs b/src/structs/mat_macros.rs index f1c54571..8f028d0d 100644 --- a/src/structs/mat_macros.rs +++ b/src/structs/mat_macros.rs @@ -749,3 +749,56 @@ macro_rules! mean_impl( } ) ); + +macro_rules! mat_display_impl( + ($t: ident, $dim: expr) => ( + impl fmt::Display for $t { + // XXX: will will not always work correctly due to rounding errors. + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn integral_length(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 = ::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) + } + } + ) +); diff --git a/src/structs/ortho.rs b/src/structs/ortho.rs index 39a15181..e5018d2d 100644 --- a/src/structs/ortho.rs +++ b/src/structs/ortho.rs @@ -41,11 +41,11 @@ impl Ortho3 { /// Builds a 4D projection matrix (using homogeneous coordinates) for this projection. pub fn to_mat(&self) -> Mat4 { - self.to_persp_mat().mat + self.to_ortho_mat().mat } /// Build a `OrthoMat3` representing this projection. - pub fn to_persp_mat(&self) -> OrthoMat3 { + pub fn to_ortho_mat(&self) -> OrthoMat3 { OrthoMat3::new(self.width, self.height, self.znear, self.zfar) } } @@ -114,14 +114,14 @@ impl Ortho3 { #[inline] pub fn project_pnt(&self, p: &Pnt3) -> Pnt3 { // FIXME: optimize that - self.to_persp_mat().project_pnt(p) + self.to_ortho_mat().project_pnt(p) } /// Projects a vector. #[inline] pub fn project_vec(&self, p: &Vec3) -> Vec3 { // FIXME: optimize that - self.to_persp_mat().project_vec(p) + self.to_ortho_mat().project_vec(p) } } @@ -251,7 +251,7 @@ impl OrthoMat3 { impl Arbitrary for OrthoMat3 { fn arbitrary(g: &mut G) -> OrthoMat3 { let x: Ortho3 = Arbitrary::arbitrary(g); - x.to_persp_mat() + x.to_ortho_mat() } } diff --git a/src/structs/pnt.rs b/src/structs/pnt.rs index db10db23..2731cf33 100644 --- a/src/structs/pnt.rs +++ b/src/structs/pnt.rs @@ -1,6 +1,7 @@ //! Points with dimension known at compile-time. use std::mem; +use std::fmt; use std::slice::{Iter, IterMut}; use std::iter::{Iterator, FromIterator, IntoIterator}; 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); arbitrary_pnt_impl!(Pnt1, x); rand_impl!(Pnt1, x); +pnt_display_impl!(Pnt1); /// Point of dimension 2. /// @@ -102,6 +104,7 @@ pnt_from_homogeneous_impl!(Pnt2, Pnt3, z, x, y); num_float_pnt_impl!(Pnt2, Vec2); arbitrary_pnt_impl!(Pnt2, x, y); rand_impl!(Pnt2, x, y); +pnt_display_impl!(Pnt2); /// Point of dimension 3. /// @@ -149,6 +152,7 @@ pnt_from_homogeneous_impl!(Pnt3, Pnt4, w, x, y, z); num_float_pnt_impl!(Pnt3, Vec3); arbitrary_pnt_impl!(Pnt3, x, y, z); rand_impl!(Pnt3, x, y, z); +pnt_display_impl!(Pnt3); /// 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); arbitrary_pnt_impl!(Pnt4, x, y, z, w); rand_impl!(Pnt4, x, y, z, w); +pnt_display_impl!(Pnt4); /// 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); arbitrary_pnt_impl!(Pnt5, x, y, z, w, a); rand_impl!(Pnt5, x, y, z, w, a); +pnt_display_impl!(Pnt5); /// Point of dimension 6. /// @@ -300,3 +306,4 @@ iterable_mut_impl!(Pnt6, 6); num_float_pnt_impl!(Pnt6, Vec6); arbitrary_pnt_impl!(Pnt6, x, y, z, w, a, b); rand_impl!(Pnt6, x, y, z, w, a, b); +pnt_display_impl!(Pnt6); diff --git a/src/structs/pnt_macros.rs b/src/structs/pnt_macros.rs index acf891f8..8b793b15 100644 --- a/src/structs/pnt_macros.rs +++ b/src/structs/pnt_macros.rs @@ -155,3 +155,24 @@ macro_rules! arbitrary_pnt_impl( } ) ); + +macro_rules! pnt_display_impl( + ($t: ident) => ( + impl fmt::Display for $t { + 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, ")") + } + } + ) +); diff --git a/src/structs/quat.rs b/src/structs/quat.rs index 6c06817a..74db865d 100644 --- a/src/structs/quat.rs +++ b/src/structs/quat.rs @@ -1,5 +1,6 @@ //! Quaternion definition. +use std::fmt; use std::mem; use std::slice::{Iter, IterMut}; use std::ops::{Add, Sub, Mul, Div, Neg, Index, IndexMut}; @@ -154,6 +155,12 @@ impl + BaseFloat> Div> for Quat { } } +impl fmt::Display for Quat { + 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); @@ -505,6 +512,12 @@ impl> Transform> for UnitQuat { } } +impl fmt::Display for UnitQuat { + 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")] impl Arbitrary for UnitQuat { fn arbitrary(g: &mut G) -> UnitQuat { diff --git a/src/structs/rot.rs b/src/structs/rot.rs index 06b013dd..cd1c77ce 100644 --- a/src/structs/rot.rs +++ b/src/structs/rot.rs @@ -1,5 +1,6 @@ //! Rotations matrices. +use std::fmt; use std::ops::{Mul, Neg, Index}; use rand::{Rand, Rng}; use num::{Zero, One}; @@ -193,47 +194,42 @@ impl Rot3 { } impl Rot3 { - /// Create a new matrix and orient it such that its local `x` axis points to a given point. - /// Note that the usually known `look_at` function does the same thing but with the `z` axis. - /// See `look_at_z` for that. + /// Creates a rotation that corresponds to the local frame of an observer standing at the + /// origin and looking toward `dir`. + /// + /// It maps the view direction `dir` to the positive `z` axis. /// /// # Arguments - /// * at - The point to look at. It is also the direction the matrix `x` 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(at: &Vec3, up: &Vec3) -> Rot3 { - let xaxis = Norm::normalize(at); - let zaxis = Norm::normalize(&Cross::cross(up, &xaxis)); - let yaxis = Cross::cross(&zaxis, &xaxis); + /// * dir - The look direction, that is, direction the matrix `z` axis will be aligned with. + /// * up - The vertical direction. The only requirement of this parameter is to not be + /// collinear + /// to `dir`. Non-collinearity is not checked. + #[inline] + pub fn new_observer_frame(dir: &Vec3, up: &Vec3) -> Rot3 { + let zaxis = Norm::normalize(dir); + let xaxis = Norm::normalize(&Cross::cross(up, &zaxis)); + let yaxis = Norm::normalize(&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) - ) + xaxis.z , yaxis.z , zaxis.z)) } } - /// Create a new matrix and orient it such that its local `z` axis points to a given point. + /// 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 - /// * 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, up: &Vec3) -> Rot3 { - let zaxis = Norm::normalize(at); - let xaxis = Norm::normalize(&Cross::cross(up, &zaxis)); - 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) - ) - } + /// * 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, up: &Vec3) -> Rot3 { + Rot3::new_observer_frame(&(-*dir), up).inv().unwrap() } } @@ -368,6 +364,7 @@ inv_impl!(Rot2); transpose_impl!(Rot2); approx_eq_impl!(Rot2); diag_impl!(Rot2, Vec2); +rot_display_impl!(Rot2); submat_impl!(Rot3, Mat3); rotate_impl!(Rot3, Vec3, Pnt3); @@ -390,3 +387,4 @@ inv_impl!(Rot3); transpose_impl!(Rot3); approx_eq_impl!(Rot3); diag_impl!(Rot3, Vec3); +rot_display_impl!(Rot3); diff --git a/src/structs/rot_macros.rs b/src/structs/rot_macros.rs index 25e9ba8d..7c6573e5 100644 --- a/src/structs/rot_macros.rs +++ b/src/structs/rot_macros.rs @@ -326,3 +326,17 @@ macro_rules! absolute_impl( } ) ); + +macro_rules! rot_display_impl( + ($t: ident) => ( + impl fmt::Display for $t { + 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, "}}") + } + } + ) +); diff --git a/src/structs/sim.rs b/src/structs/sim.rs index 87af2ed1..dd7d32f1 100644 --- a/src/structs/sim.rs +++ b/src/structs/sim.rs @@ -1,3 +1,4 @@ +use std::fmt; use std::ops::{Mul, Neg}; use rand::{Rand, Rng}; @@ -64,6 +65,7 @@ sim_to_homogeneous_impl!(Sim2, Mat3); sim_approx_eq_impl!(Sim2); sim_rand_impl!(Sim2); sim_arbitrary_impl!(Sim2); +sim_display_impl!(Sim2); sim_impl!(Sim3, Iso3, Rot3, Vec3, Vec3); dim_impl!(Sim3, 3); @@ -81,3 +83,4 @@ sim_to_homogeneous_impl!(Sim3, Mat4); sim_approx_eq_impl!(Sim3); sim_rand_impl!(Sim3); sim_arbitrary_impl!(Sim3); +sim_display_impl!(Sim3); diff --git a/src/structs/sim_macros.rs b/src/structs/sim_macros.rs index 75d6cde9..29c575ad 100644 --- a/src/structs/sim_macros.rs +++ b/src/structs/sim_macros.rs @@ -118,7 +118,7 @@ macro_rules! sim_mul_sim_impl( #[inline] fn mul(self, right: $t) -> $t { $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.scale * right.scale) } @@ -174,10 +174,11 @@ macro_rules! sim_inv_impl( fn inv_mut(&mut self) -> bool { self.scale = ::one::() / self.scale; 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; - // always succeed + // Always succeed. true } @@ -186,7 +187,7 @@ macro_rules! sim_inv_impl( let mut res = *self; res.inv_mut(); - // always succeed + // Always succeed. Some(res) } } @@ -243,7 +244,12 @@ macro_rules! sim_rand_impl( impl Rand for $t { #[inline] fn rand(rng: &mut R) -> $t { - $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 fmt::Display for $t { + 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, "}}") + } + } + ) +); diff --git a/src/structs/vec.rs b/src/structs/vec.rs index 01015cf4..37849331 100644 --- a/src/structs/vec.rs +++ b/src/structs/vec.rs @@ -4,6 +4,7 @@ use std::ops::{Add, Sub, Mul, Div, Neg, Index, IndexMut}; use std::mem; use std::slice::{Iter, IterMut}; use std::iter::{Iterator, FromIterator, IntoIterator}; +use std::fmt; use rand::{Rand, Rng}; use num::{Zero, One}; use traits::operations::{ApproxEq, POrd, POrdering, Axpy, Absolute, Mean}; @@ -71,6 +72,7 @@ absolute_vec_impl!(Vec1, x); arbitrary_impl!(Vec1, x); rand_impl!(Vec1, x); mean_impl!(Vec1); +vec_display_impl!(Vec1); /// Vector of dimension 2. /// @@ -128,6 +130,7 @@ absolute_vec_impl!(Vec2, x, y); arbitrary_impl!(Vec2, x, y); rand_impl!(Vec2, x, y); mean_impl!(Vec2); +vec_display_impl!(Vec2); /// Vector of dimension 3. /// @@ -187,6 +190,7 @@ absolute_vec_impl!(Vec3, x, y, z); arbitrary_impl!(Vec3, x, y, z); rand_impl!(Vec3, x, y, z); mean_impl!(Vec3); +vec_display_impl!(Vec3); /// Vector of dimension 4. @@ -249,6 +253,7 @@ absolute_vec_impl!(Vec4, x, y, z, w); arbitrary_impl!(Vec4, x, y, z, w); rand_impl!(Vec4, x, y, z, w); mean_impl!(Vec4); +vec_display_impl!(Vec4); /// 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); rand_impl!(Vec5, x, y, z, w, a); mean_impl!(Vec5); +vec_display_impl!(Vec5); /// 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); rand_impl!(Vec6, x, y, z, w, a, b); mean_impl!(Vec6); +vec_display_impl!(Vec6); diff --git a/src/structs/vec_macros.rs b/src/structs/vec_macros.rs index 1accc4d7..f07e27ba 100644 --- a/src/structs/vec_macros.rs +++ b/src/structs/vec_macros.rs @@ -820,3 +820,25 @@ macro_rules! mean_impl( } ) ); + +macro_rules! vec_display_impl( + ($t: ident) => ( + impl fmt::Display for $t { + 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, ")") + } + } + ) +); diff --git a/tests/mat.rs b/tests/mat.rs index d820481c..1eeefd9d 100644 --- a/tests/mat.rs +++ b/tests/mat.rs @@ -2,8 +2,8 @@ extern crate nalgebra as na; extern crate rand; use rand::random; -use na::{Vec1, Vec3, Mat1, Mat2, Mat3, Mat4, Mat5, Mat6, Rot2, Rot3, Persp3, PerspMat3, Ortho3, - OrthoMat3, DMat, DVec, Row, Col, BaseFloat, Diag, Transpose, RowSlice, ColSlice, Shape}; +use na::{Rot2, Rot3, Iso2, Iso3, Sim2, Sim3, Vec3, Mat1, Mat2, Mat3, Mat4, Mat5, Mat6, DMat, DVec, + Row, Col, Diag, Transpose, RowSlice, ColSlice, Shape}; macro_rules! test_inv_mat_impl( ($t: ty) => ( @@ -12,7 +12,9 @@ macro_rules! test_inv_mat_impl( match na::inv(&randmat) { 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] -fn test_rotation2() { - for _ in 0usize .. 10000 { - let randmat: na::Rot2 = na::one(); - let ang = Vec1::new(na::abs(&random::()) % ::pi()); +fn test_inv_rot2() { + test_inv_mat_impl!(Rot2); +} - assert!(na::approx_eq(&na::rotation(&na::append_rotation(&randmat, &ang)), &ang)); - } +#[test] +fn test_inv_rot3() { + test_inv_mat_impl!(Rot3); +} + +#[test] +fn test_inv_iso2() { + test_inv_mat_impl!(Iso2); +} + +#[test] +fn test_inv_iso3() { + test_inv_mat_impl!(Iso3); +} + +#[test] +fn test_inv_sim2() { + test_inv_mat_impl!(Sim2); +} + +#[test] +fn test_inv_sim3() { + test_inv_mat_impl!(Sim3); } #[test] @@ -199,59 +221,6 @@ fn test_index_mat2() { assert!(mat[(0, 1)] == na::transpose(&mat)[(1, 0)]); } -#[test] -fn test_inv_rotation3() { - for _ in 0usize .. 10000 { - let randmat: Rot3 = na::one(); - let dir: Vec3 = random(); - let ang = na::normalize(&dir) * (na::abs(&random::()) % ::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 = random(); - let r2: Rot3 = random(); - - let delta = na::rotation_between(&r1, &r2); - - assert!(na::approx_eq(&(delta * r1), &r2)) -} - -#[test] -fn test_rot3_angle_between() { - let r1: Rot3 = random(); - let r2: Rot3 = 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 = random(); - let r2: Rot2 = random(); - - let delta = na::rotation_between(&r1, &r2); - - assert!(na::approx_eq(&(delta * r1), &r2)) -} - -#[test] -fn test_rot2_angle_between() { - let r1: Rot2 = random(); - let r2: Rot2 = 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_mean_dmat() { @@ -755,86 +724,6 @@ fn test_row_3() { 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] fn test_cholesky_const() { diff --git a/tests/transforms.rs b/tests/transforms.rs new file mode 100644 index 00000000..1b1fa6c9 --- /dev/null +++ b/tests/transforms.rs @@ -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 = na::one(); + let ang = Vec1::new(na::abs(&random::()) % ::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 = na::one(); + let dir: Vec3 = random(); + let ang = na::normalize(&dir) * (na::abs(&random::()) % ::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 = random(); + let r2: Rot3 = random(); + + let delta = na::rotation_between(&r1, &r2); + + assert!(na::approx_eq(&(delta * r1), &r2)) +} + +#[test] +fn test_rot3_angle_between() { + let r1: Rot3 = random(); + let r2: Rot3 = 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 = random(); + let r2: Rot2 = random(); + + let delta = na::rotation_between(&r1, &r2); + + assert!(na::approx_eq(&(delta * r1), &r2)) +} + +#[test] +fn test_rot2_angle_between() { + let r1: Rot2 = random(); + let r2: Rot2 = 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::>(); + let target = random::>(); + let up = random::>(); + 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::>(); + let up = random::>(); + 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::>(); + let target = random::>(); + let up = random::>(); + let observer = Iso3::new_observer_frame(&eye, &target, &up); + + assert_eq!(&(observer * na::orig::>()), &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::>(); + let up = random::>(); + 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)); +} diff --git a/tests/vec.rs b/tests/vec.rs index bd97361f..aab6d7d1 100644 --- a/tests/vec.rs +++ b/tests/vec.rs @@ -1,10 +1,16 @@ extern crate rand; +#[cfg(feature="generic_sizes")] extern crate typenum; extern crate nalgebra as na; use rand::random; +use na::{Vec1, Vec2, Vec3, Vec4, Vec5, Vec6, Mat3, Rot2, Rot3, Iterable, IterableMut}; + +#[cfg(feature="generic_sizes")] 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( ($t: ty, $n: ty) => ( @@ -295,6 +301,7 @@ fn test_outer_vec3() { 12.0, 15.0, 18.0)); } +#[cfg(feature="generic_sizes")] #[test] fn test_vecn10_add_mul() { for _ in 0usize .. 10000 {