From f8f4924e47143e6e96083f177c9b9c442e5253e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Thu, 24 Mar 2016 19:03:29 +0100 Subject: [PATCH 01/20] Add vector perametrizable by their sizes. --- Cargo.toml | 6 +- benches/vec.rs | 37 ++++- src/lib.rs | 3 +- src/structs/dvec.rs | 1 + src/structs/dvec_macros.rs | 307 +----------------------------------- src/structs/mod.rs | 3 + src/structs/pnt.rs | 12 +- src/structs/quat.rs | 2 +- src/structs/vec.rs | 12 +- src/structs/vec_macros.rs | 2 +- src/structs/vecn.rs | 112 +++++++++++++ src/structs/vecn_macros.rs | 312 +++++++++++++++++++++++++++++++++++++ src/traits/structure.rs | 2 +- tests/vec.rs | 15 +- 14 files changed, 505 insertions(+), 321 deletions(-) create mode 100644 src/structs/vecn.rs create mode 100644 src/structs/vecn_macros.rs diff --git a/Cargo.toml b/Cargo.toml index 7214eb49..54905910 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nalgebra" -version = "0.5.1" +version = "0.6.0" authors = [ "Sébastien Crozet " ] # FIXME: add the contributors. description = "Linear algebra library for computer physics, computer graphics and general low-dimensional linear algebra for Rust." @@ -17,12 +17,14 @@ path = "src/lib.rs" [features] # Generate arbitrary instances of nalgebra types for testing with quickcheck -arbitrary = ["quickcheck"] +arbitrary = [ "quickcheck" ] [dependencies] rustc-serialize = "0.3.*" rand = "0.3.*" num = "0.1.*" +generic-array = "0.2.*" +typenum = "1.3.*" [dependencies.quickcheck] optional = true diff --git a/benches/vec.rs b/benches/vec.rs index 9d12e711..d5ff30c3 100644 --- a/benches/vec.rs +++ b/benches/vec.rs @@ -2,11 +2,13 @@ extern crate test; extern crate rand; +extern crate typenum; extern crate nalgebra as na; use rand::{IsaacRng, Rng}; use test::Bencher; -use na::{Vec2, Vec3, Vec4}; +use typenum::{U2, U3, U4}; +use na::{Vec2, Vec3, Vec4, VecN}; use std::ops::{Add, Sub, Mul, Div}; #[path="common/macros.rs"] @@ -15,45 +17,78 @@ mod macros; bench_binop!(_bench_vec2_add_v, Vec2, Vec2, add); bench_binop!(_bench_vec3_add_v, Vec3, Vec3, add); bench_binop!(_bench_vec4_add_v, Vec4, Vec4, add); +bench_binop!(_bench_vecn2_add_v, VecN, VecN, add); +bench_binop!(_bench_vecn3_add_v, VecN, VecN, add); +bench_binop!(_bench_vecn4_add_v, VecN, VecN, add); bench_binop!(_bench_vec2_sub_v, Vec2, Vec2, sub); bench_binop!(_bench_vec3_sub_v, Vec3, Vec3, sub); bench_binop!(_bench_vec4_sub_v, Vec4, Vec4, sub); +bench_binop!(_bench_vecn2_sub_v, VecN, VecN, sub); +bench_binop!(_bench_vecn3_sub_v, VecN, VecN, sub); +bench_binop!(_bench_vecn4_sub_v, VecN, VecN, sub); bench_binop!(_bench_vec2_mul_v, Vec2, Vec2, mul); bench_binop!(_bench_vec3_mul_v, Vec3, Vec3, mul); bench_binop!(_bench_vec4_mul_v, Vec4, Vec4, mul); +bench_binop!(_bench_vecn2_mul_v, VecN, VecN, mul); +bench_binop!(_bench_vecn3_mul_v, VecN, VecN, mul); +bench_binop!(_bench_vecn4_mul_v, VecN, VecN, mul); bench_binop!(_bench_vec2_div_v, Vec2, Vec2, div); bench_binop!(_bench_vec3_div_v, Vec3, Vec3, div); bench_binop!(_bench_vec4_div_v, Vec4, Vec4, div); +bench_binop!(_bench_vecn2_div_v, VecN, VecN, div); +bench_binop!(_bench_vecn3_div_v, VecN, VecN, div); +bench_binop!(_bench_vecn4_div_v, VecN, VecN, div); bench_binop!(_bench_vec2_add_s, Vec2, f32, add); bench_binop!(_bench_vec3_add_s, Vec3, f32, add); bench_binop!(_bench_vec4_add_s, Vec4, f32, add); +bench_binop!(_bench_vecn2_add_s, VecN, f32, add); +bench_binop!(_bench_vecn3_add_s, VecN, f32, add); +bench_binop!(_bench_vecn4_add_s, VecN, f32, add); bench_binop!(_bench_vec2_sub_s, Vec2, f32, sub); bench_binop!(_bench_vec3_sub_s, Vec3, f32, sub); bench_binop!(_bench_vec4_sub_s, Vec4, f32, sub); +bench_binop!(_bench_vecn2_sub_s, VecN, f32, sub); +bench_binop!(_bench_vecn3_sub_s, VecN, f32, sub); +bench_binop!(_bench_vecn4_sub_s, VecN, f32, sub); bench_binop!(_bench_vec2_mul_s, Vec2, f32, mul); bench_binop!(_bench_vec3_mul_s, Vec3, f32, mul); bench_binop!(_bench_vec4_mul_s, Vec4, f32, mul); +bench_binop!(_bench_vecn2_mul_s, VecN, f32, mul); +bench_binop!(_bench_vecn3_mul_s, VecN, f32, mul); +bench_binop!(_bench_vecn4_mul_s, VecN, f32, mul); bench_binop!(_bench_vec2_div_s, Vec2, f32, div); bench_binop!(_bench_vec3_div_s, Vec3, f32, div); bench_binop!(_bench_vec4_div_s, Vec4, f32, div); +bench_binop!(_bench_vecn2_div_s, VecN, f32, div); +bench_binop!(_bench_vecn3_div_s, VecN, f32, div); +bench_binop!(_bench_vecn4_div_s, VecN, f32, div); bench_binop_na!(_bench_vec2_dot, Vec2, Vec2, dot); bench_binop_na!(_bench_vec3_dot, Vec3, Vec3, dot); bench_binop_na!(_bench_vec4_dot, Vec4, Vec4, dot); +bench_binop_na!(_bench_vecn2_dot, VecN, VecN, dot); +bench_binop_na!(_bench_vecn3_dot, VecN, VecN, dot); +bench_binop_na!(_bench_vecn4_dot, VecN, VecN, dot); bench_binop_na!(_bench_vec3_cross, Vec3, Vec3, cross); bench_unop!(_bench_vec2_norm, Vec2, norm); bench_unop!(_bench_vec3_norm, Vec3, norm); bench_unop!(_bench_vec4_norm, Vec4, norm); +bench_unop!(_bench_vecn2_norm, VecN, norm); +bench_unop!(_bench_vecn3_norm, VecN, norm); +bench_unop!(_bench_vecn4_norm, VecN, norm); bench_unop!(_bench_vec2_normalize, Vec2, normalize); bench_unop!(_bench_vec3_normalize, Vec3, normalize); bench_unop!(_bench_vec4_normalize, Vec4, normalize); +bench_unop!(_bench_vecn2_normalize, VecN, normalize); +bench_unop!(_bench_vecn3_normalize, VecN, normalize); +bench_unop!(_bench_vecn4_normalize, VecN, normalize); diff --git a/src/lib.rs b/src/lib.rs index b20c3689..9f6b175e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,6 +76,7 @@ Feel free to add your project to this list if you happen to use **nalgebra**! extern crate rustc_serialize; extern crate rand; extern crate num; +extern crate generic_array; #[cfg(feature="arbitrary")] extern crate quickcheck; @@ -141,7 +142,7 @@ pub use structs::{ Mat1, Mat2, Mat3, Mat4, Mat5, Mat6, Rot2, Rot3, Rot4, - Vec0, Vec1, Vec2, Vec3, Vec4, Vec5, Vec6, + VecN, Vec0, Vec1, Vec2, Vec3, Vec4, Vec5, Vec6, Pnt0, Pnt1, Pnt2, Pnt3, Pnt4, Pnt5, Pnt6, Persp3, PerspMat3, Ortho3, OrthoMat3, diff --git a/src/structs/dvec.rs b/src/structs/dvec.rs index c9eb91ce..de774271 100644 --- a/src/structs/dvec.rs +++ b/src/structs/dvec.rs @@ -62,6 +62,7 @@ impl DVec { DVec { at: (0..dim).map(|i| f(i)).collect() } } + /// The vector length. #[inline] pub fn len(&self) -> usize { self.at.len() diff --git a/src/structs/dvec_macros.rs b/src/structs/dvec_macros.rs index 35d6c10f..76d54f2e 100644 --- a/src/structs/dvec_macros.rs +++ b/src/structs/dvec_macros.rs @@ -2,6 +2,8 @@ macro_rules! dvec_impl( ($dvec: ident) => ( + vecn_dvec_common_impl!($dvec); + impl $dvec { /// Builds a vector filled with zeros. /// @@ -11,68 +13,6 @@ macro_rules! dvec_impl( pub fn new_zeros(dim: usize) -> $dvec { $dvec::from_elem(dim, ::zero()) } - - /// Tests if all components of the vector are zeroes. - #[inline] - pub fn is_zero(&self) -> bool { - self.as_ref().iter().all(|e| e.is_zero()) - } - } - - impl $dvec { - /// Slices this vector. - #[inline] - pub fn as_ref<'a>(&'a self) -> &'a [N] { - &self.at[.. self.len()] - } - - /// Mutably slices this vector. - #[inline] - pub fn as_mut<'a>(&'a mut self) -> &'a mut [N] { - let len = self.len(); - &mut self.at[.. len] - } - } - - impl Shape for $dvec { - #[inline] - fn shape(&self) -> usize { - self.len() - } - } - - impl Indexable for $dvec { - #[inline] - fn swap(&mut self, i: usize, j: usize) { - assert!(i < self.len()); - assert!(j < self.len()); - self.as_mut().swap(i, j); - } - - #[inline] - unsafe fn unsafe_at(&self, i: usize) -> N { - *self.at[..].get_unchecked(i) - } - - #[inline] - unsafe fn unsafe_set(&mut self, i: usize, val: N) { - *self.at[..].get_unchecked_mut(i) = val - } - - } - - impl Index for $dvec where [N]: Index { - type Output = <[N] as Index>::Output; - - fn index(&self, i: T) -> &<[N] as Index>::Output { - &self.as_ref()[i] - } - } - - impl IndexMut for $dvec where [N]: IndexMut { - fn index_mut(&mut self, i: T) -> &mut <[N] as Index>::Output { - &mut self.as_mut()[i] - } } impl $dvec { @@ -94,33 +34,6 @@ macro_rules! dvec_impl( } } - impl Iterable for $dvec { - #[inline] - fn iter<'l>(&'l self) -> Iter<'l, N> { - self.as_ref().iter() - } - } - - impl IterableMut for $dvec { - #[inline] - fn iter_mut<'l>(&'l mut self) -> IterMut<'l, N> { - self.as_mut().iter_mut() - } - } - - impl + Mul> Axpy for $dvec { - fn axpy(&mut self, a: &N, x: &$dvec) { - assert!(self.len() == x.len()); - - for i in 0..x.len() { - unsafe { - let self_i = self.unsafe_at(i); - self.unsafe_set(i, self_i + *a * x.unsafe_at(i)) - } - } - } - } - impl> $dvec { /// Computes the canonical basis for the given dimension. A canonical basis is a set of /// vectors, mutually orthogonal, with all its component equal to 0.0 except one which is equal @@ -128,7 +41,7 @@ macro_rules! dvec_impl( pub fn canonical_basis_with_dim(dim: usize) -> Vec<$dvec> { let mut res : Vec<$dvec> = Vec::new(); - for i in 0..dim { + for i in 0 .. dim { let mut basis_element : $dvec = $dvec::new_zeros(dim); basis_element[i] = ::one(); @@ -147,7 +60,7 @@ macro_rules! dvec_impl( let dim = self.len(); let mut res : Vec<$dvec> = Vec::new(); - for i in 0..dim { + for i in 0 .. dim { let mut basis_element : $dvec = $dvec::new_zeros(self.len()); basis_element[i] = ::one(); @@ -175,217 +88,13 @@ macro_rules! dvec_impl( res } } - - impl + Zero> Mul<$dvec> for $dvec { - type Output = $dvec; - - #[inline] - fn mul(self, right: $dvec) -> $dvec { - assert!(self.len() == right.len()); - - let mut res = self; - - for (left, right) in res.as_mut().iter_mut().zip(right.as_ref().iter()) { - *left = *left * *right - } - - res - } - } - - impl + Zero> Div<$dvec> for $dvec { - type Output = $dvec; - - #[inline] - fn div(self, right: $dvec) -> $dvec { - assert!(self.len() == right.len()); - - let mut res = self; - - for (left, right) in res.as_mut().iter_mut().zip(right.as_ref().iter()) { - *left = *left / *right - } - - res - } - } - - impl + Zero> Add<$dvec> for $dvec { - type Output = $dvec; - - #[inline] - fn add(self, right: $dvec) -> $dvec { - assert!(self.len() == right.len()); - - let mut res = self; - - for (left, right) in res.as_mut().iter_mut().zip(right.as_ref().iter()) { - *left = *left + *right - } - - res - } - } - - impl + Zero> Sub<$dvec> for $dvec { - type Output = $dvec; - - #[inline] - fn sub(self, right: $dvec) -> $dvec { - assert!(self.len() == right.len()); - - let mut res = self; - - for (left, right) in res.as_mut().iter_mut().zip(right.as_ref().iter()) { - *left = *left - *right - } - - res - } - } - - impl + Zero + Copy> Neg for $dvec { - type Output = $dvec; - - #[inline] - fn neg(self) -> $dvec { - FromIterator::from_iter(self.as_ref().iter().map(|a| -*a)) - } - } - - impl Dot for $dvec { - #[inline] - fn dot(&self, other: &$dvec) -> N { - assert!(self.len() == other.len()); - let mut res: N = ::zero(); - for i in 0..self.len() { - res = res + unsafe { self.unsafe_at(i) * other.unsafe_at(i) }; - } - res - } - } - - impl Norm for $dvec { - #[inline] - fn sqnorm(&self) -> N { - Dot::dot(self, self) - } - - #[inline] - fn normalize(&self) -> $dvec { - let mut res : $dvec = self.clone(); - let _ = res.normalize_mut(); - res - } - - #[inline] - fn normalize_mut(&mut self) -> N { - let l = Norm::norm(self); - - for n in self.as_mut().iter_mut() { - *n = *n / l; - } - - l - } - } - - impl> Mean for $dvec { - #[inline] - fn mean(&self) -> N { - let normalizer = ::cast(1.0f64 / self.len() as f64); - self.iter().fold(::zero(), |acc, x| acc + *x * normalizer) - } - } - - impl> ApproxEq for $dvec { - #[inline] - fn approx_epsilon(_: Option<$dvec>) -> N { - ApproxEq::approx_epsilon(None::) - } - - #[inline] - fn approx_ulps(_: Option<$dvec>) -> u32 { - ApproxEq::approx_ulps(None::) - } - - #[inline] - fn approx_eq_eps(&self, other: &$dvec, epsilon: &N) -> bool { - let mut zip = self.as_ref().iter().zip(other.as_ref().iter()); - zip.all(|(a, b)| ApproxEq::approx_eq_eps(a, b, epsilon)) - } - - #[inline] - fn approx_eq_ulps(&self, other: &$dvec, ulps: u32) -> bool { - let mut zip = self.as_ref().iter().zip(other.as_ref().iter()); - zip.all(|(a, b)| ApproxEq::approx_eq_ulps(a, b, ulps)) - } - } - - impl + Zero> Mul for $dvec { - type Output = $dvec; - - #[inline] - fn mul(self, right: N) -> $dvec { - let mut res = self; - - for e in res.as_mut().iter_mut() { - *e = *e * right - } - - res - } - } - - impl + Zero> Div for $dvec { - type Output = $dvec; - - #[inline] - fn div(self, right: N) -> $dvec { - let mut res = self; - - for e in res.as_mut().iter_mut() { - *e = *e / right - } - - res - } - } - - impl + Zero> Add for $dvec { - type Output = $dvec; - - #[inline] - fn add(self, right: N) -> $dvec { - let mut res = self; - - for e in res.as_mut().iter_mut() { - *e = *e + right - } - - res - } - } - - impl + Zero> Sub for $dvec { - type Output = $dvec; - - #[inline] - fn sub(self, right: N) -> $dvec { - let mut res = self; - - for e in res.as_mut().iter_mut() { - *e = *e - right - } - - res - } - } ) ); macro_rules! small_dvec_impl ( ($dvec: ident, $dim: expr, $($idx: expr),*) => ( + dvec_impl!($dvec); + impl $dvec { /// The number of elements of this vector. #[inline] @@ -434,8 +143,6 @@ macro_rules! small_dvec_impl ( } } } - - dvec_impl!($dvec); ) ); @@ -490,7 +197,7 @@ macro_rules! small_dvec_from_impl ( let mut at: [N; $dim] = [ $( $zeros, )* ]; - for i in 0..dim { + for i in 0 .. dim { at[i] = f(i); } diff --git a/src/structs/mod.rs b/src/structs/mod.rs index a8ee812f..d5ab760e 100644 --- a/src/structs/mod.rs +++ b/src/structs/mod.rs @@ -3,6 +3,7 @@ pub use self::dmat::{DMat, DMat1, DMat2, DMat3, DMat4, DMat5, DMat6}; pub use self::dvec::{DVec, DVec1, DVec2, DVec3, DVec4, DVec5, DVec6}; pub use self::vec::{Vec0, Vec1, Vec2, Vec3, Vec4, Vec5, Vec6}; +pub use self::vecn::VecN; pub use self::pnt::{Pnt0, Pnt1, Pnt2, Pnt3, Pnt4, Pnt5, Pnt6}; pub use self::mat::{Identity, Mat1, Mat2, Mat3, Mat4, Mat5, Mat6}; pub use self::rot::{Rot2, Rot3, Rot4}; @@ -13,6 +14,8 @@ pub use self::quat::{Quat, UnitQuat}; mod dmat_macros; mod dmat; +mod vecn_macros; +mod vecn; mod dvec_macros; mod dvec; mod vec_macros; diff --git a/src/structs/pnt.rs b/src/structs/pnt.rs index 2731a413..a937c73a 100644 --- a/src/structs/pnt.rs +++ b/src/structs/pnt.rs @@ -48,7 +48,7 @@ pub struct Pnt1 { new_impl!(Pnt1, x); orig_impl!(Pnt1, x); -ord_impl!(Pnt1, x,); +pord_impl!(Pnt1, x,); scalar_mul_impl!(Pnt1, x); scalar_div_impl!(Pnt1, x); scalar_add_impl!(Pnt1, x); @@ -90,7 +90,7 @@ pub struct Pnt2 { new_impl!(Pnt2, x, y); orig_impl!(Pnt2, x, y); -ord_impl!(Pnt2, x, y); +pord_impl!(Pnt2, x, y); scalar_mul_impl!(Pnt2, x, y); scalar_div_impl!(Pnt2, x, y); scalar_add_impl!(Pnt2, x, y); @@ -134,7 +134,7 @@ pub struct Pnt3 { new_impl!(Pnt3, x, y, z); orig_impl!(Pnt3, x, y, z); -ord_impl!(Pnt3, x, y, z); +pord_impl!(Pnt3, x, y, z); scalar_mul_impl!(Pnt3, x, y, z); scalar_div_impl!(Pnt3, x, y, z); scalar_add_impl!(Pnt3, x, y, z); @@ -180,7 +180,7 @@ pub struct Pnt4 { new_impl!(Pnt4, x, y, z, w); orig_impl!(Pnt4, x, y, z, w); -ord_impl!(Pnt4, x, y, z, w); +pord_impl!(Pnt4, x, y, z, w); scalar_mul_impl!(Pnt4, x, y, z, w); scalar_div_impl!(Pnt4, x, y, z, w); scalar_add_impl!(Pnt4, x, y, z, w); @@ -228,7 +228,7 @@ pub struct Pnt5 { new_impl!(Pnt5, x, y, z, w, a); orig_impl!(Pnt5, x, y, z, w, a); -ord_impl!(Pnt5, x, y, z, w, a); +pord_impl!(Pnt5, x, y, z, w, a); scalar_mul_impl!(Pnt5, x, y, z, w, a); scalar_div_impl!(Pnt5, x, y, z, w, a); scalar_add_impl!(Pnt5, x, y, z, w, a); @@ -278,7 +278,7 @@ pub struct Pnt6 { new_impl!(Pnt6, x, y, z, w, a, b); orig_impl!(Pnt6, x, y, z, w, a, b); -ord_impl!(Pnt6, x, y, z, w, a, b); +pord_impl!(Pnt6, x, y, z, w, a, b); scalar_mul_impl!(Pnt6, x, y, z, w, a, b); scalar_div_impl!(Pnt6, x, y, z, w, a, b); scalar_add_impl!(Pnt6, x, y, z, w, a, b); diff --git a/src/structs/quat.rs b/src/structs/quat.rs index 3aa3af07..d3a3485e 100644 --- a/src/structs/quat.rs +++ b/src/structs/quat.rs @@ -515,7 +515,7 @@ impl Arbitrary for UnitQuat { } -ord_impl!(Quat, w, i, j, k); +pord_impl!(Quat, w, i, j, k); vec_axis_impl!(Quat, w, i, j, k); vec_cast_impl!(Quat, w, i, j, k); conversion_impl!(Quat, 4); diff --git a/src/structs/vec.rs b/src/structs/vec.rs index cf0f7c20..32517fff 100644 --- a/src/structs/vec.rs +++ b/src/structs/vec.rs @@ -49,7 +49,7 @@ pub struct Vec1 { } new_impl!(Vec1, x); -ord_impl!(Vec1, x,); +pord_impl!(Vec1, x,); vec_axis_impl!(Vec1, x); vec_cast_impl!(Vec1, x); conversion_impl!(Vec1, 1); @@ -103,7 +103,7 @@ pub struct Vec2 { } new_impl!(Vec2, x, y); -ord_impl!(Vec2, x, y); +pord_impl!(Vec2, x, y); vec_axis_impl!(Vec2, x, y); vec_cast_impl!(Vec2, x, y); conversion_impl!(Vec2, 2); @@ -159,7 +159,7 @@ pub struct Vec3 { } new_impl!(Vec3, x, y, z); -ord_impl!(Vec3, x, y, z); +pord_impl!(Vec3, x, y, z); vec_axis_impl!(Vec3, x, y, z); vec_cast_impl!(Vec3, x, y, z); conversion_impl!(Vec3, 3); @@ -218,7 +218,7 @@ pub struct Vec4 { } new_impl!(Vec4, x, y, z, w); -ord_impl!(Vec4, x, y, z, w); +pord_impl!(Vec4, x, y, z, w); vec_axis_impl!(Vec4, x, y, z, w); vec_cast_impl!(Vec4, x, y, z, w); conversion_impl!(Vec4, 4); @@ -278,7 +278,7 @@ pub struct Vec5 { } new_impl!(Vec5, x, y, z, w, a); -ord_impl!(Vec5, x, y, z, w, a); +pord_impl!(Vec5, x, y, z, w, a); vec_axis_impl!(Vec5, x, y, z, w, a); vec_cast_impl!(Vec5, x, y, z, w, a); conversion_impl!(Vec5, 5); @@ -340,7 +340,7 @@ pub struct Vec6 { } new_impl!(Vec6, x, y, z, w, a, b); -ord_impl!(Vec6, x, y, z, w, a, b); +pord_impl!(Vec6, x, y, z, w, a, b); vec_axis_impl!(Vec6, x, y, z, w, a, b); vec_cast_impl!(Vec6, x, y, z, w, a, b); conversion_impl!(Vec6, 6); diff --git a/src/structs/vec_macros.rs b/src/structs/vec_macros.rs index 64bae53a..e358a226 100644 --- a/src/structs/vec_macros.rs +++ b/src/structs/vec_macros.rs @@ -74,7 +74,7 @@ macro_rules! at_fast_impl( // FIXME: N should be bounded by Ord instead of BaseFloat… // However, f32/f64 does not implement Ord… -macro_rules! ord_impl( +macro_rules! pord_impl( ($t: ident, $comp0: ident, $($compN: ident),*) => ( impl POrd for $t { #[inline] diff --git a/src/structs/vecn.rs b/src/structs/vecn.rs new file mode 100644 index 00000000..ecf41461 --- /dev/null +++ b/src/structs/vecn.rs @@ -0,0 +1,112 @@ +use std::slice::{Iter, IterMut}; +use std::iter::{FromIterator, IntoIterator}; +use std::ops::{Add, Sub, Mul, Div, Neg, Index, IndexMut}; +use std::mem; +use rand::{Rand, Rng}; +use num::{Zero, One}; +use generic_array::{GenericArray, ArrayLength}; +use traits::operations::{ApproxEq, Axpy, Mean}; +use traits::geometry::{Dot, Norm}; +use traits::structure::{Iterable, IterableMut, Indexable, Shape, BaseFloat, BaseNum, Cast, Dim}; +#[cfg(feature="arbitrary")] +use quickcheck::{Arbitrary, Gen}; + +/// A static array of arbitrary dimension. +#[repr(C)] +#[derive(Eq, PartialEq, Debug)] // FIXME: Hash, RustcEncodable, RustcDecodable +pub struct VecN> { + /// The underlying data of the vector. + pub at: GenericArray +} + +impl> Clone for VecN { + fn clone(&self) -> VecN { + VecN::new(self.at.clone()) + } +} + +impl> Copy for VecN + where D::ArrayType: Copy { } + +impl> VecN { + /// Creates a new vector from a given arbirtarily-sized array. + #[inline] + pub fn new(components: GenericArray) -> VecN { + VecN { + at: components + } + } + + /// The vector length. + #[inline] + pub fn len(&self) -> usize { + self.at.len() + } +} + +impl> Dim for VecN { + fn dim(_unused: Option) -> usize { + D::to_usize() + } +} + +impl> FromIterator for VecN { + #[inline] + fn from_iter>(param: I) -> VecN { + let mut res: VecN = unsafe { mem::uninitialized() }; + + let mut it = param.into_iter(); + + for e in res.iter_mut() { + *e = it.next().expect("Not enough data into the provided iterator to initialize this `VecN`."); + } + + res + } +} + +impl> Rand for VecN { + #[inline] + fn rand(rng: &mut R) -> VecN { + let mut res: VecN = unsafe { mem::uninitialized() }; + + for e in res.iter_mut() { + *e = Rand::rand(rng) + } + + res + } +} + +impl> One for VecN { + #[inline] + fn one() -> VecN { + let mut res: VecN = unsafe { mem::uninitialized() }; + + for e in res.iter_mut() { + *e = ::one() + } + + res + } +} + +impl> Zero for VecN { + #[inline] + fn zero() -> VecN { + let mut res: VecN = unsafe { mem::uninitialized() }; + + for e in res.iter_mut() { + *e = ::zero() + } + + res + } + + #[inline] + fn is_zero(&self) -> bool { + self.iter().all(|e| e.is_zero()) + } +} + +vecn_dvec_common_impl!(VecN, D); diff --git a/src/structs/vecn_macros.rs b/src/structs/vecn_macros.rs new file mode 100644 index 00000000..b874cc37 --- /dev/null +++ b/src/structs/vecn_macros.rs @@ -0,0 +1,312 @@ +#![macro_use] + +macro_rules! vecn_dvec_common_impl( + ($vecn: ident $(, $param: ident)*) => ( + impl)*> $vecn { + /// Tests if all components of the vector are zeroes. + #[inline] + pub fn is_zero(&self) -> bool { + self.as_ref().iter().all(|e| e.is_zero()) + } + } + + impl)*> AsRef<[N]> for $vecn { + #[inline] + fn as_ref(&self) -> &[N] { + &self[.. self.len()] + } + } + + impl)*> AsMut<[N]> for $vecn { + #[inline] + fn as_mut(&mut self) -> &mut [N] { + let len = self.len(); + &mut self[.. len] + } + } + + impl)*> Shape for $vecn { + #[inline] + fn shape(&self) -> usize { + self.len() + } + } + + impl)*> Indexable for $vecn { + #[inline] + fn swap(&mut self, i: usize, j: usize) { + assert!(i < self.len()); + assert!(j < self.len()); + self.as_mut().swap(i, j); + } + + #[inline] + unsafe fn unsafe_at(&self, i: usize) -> N { + *self[..].get_unchecked(i) + } + + #[inline] + unsafe fn unsafe_set(&mut self, i: usize, val: N) { + *self[..].get_unchecked_mut(i) = val + } + + } + + impl)*> Index for $vecn where [N]: Index { + type Output = <[N] as Index>::Output; + + fn index(&self, i: T) -> &<[N] as Index>::Output { + &self.as_ref()[i] + } + } + + impl)*> IndexMut for $vecn where [N]: IndexMut { + fn index_mut(&mut self, i: T) -> &mut <[N] as Index>::Output { + &mut self.as_mut()[i] + } + } + + impl)*> Iterable for $vecn { + #[inline] + fn iter<'l>(&'l self) -> Iter<'l, N> { + self.as_ref().iter() + } + } + + impl)*> IterableMut for $vecn { + #[inline] + fn iter_mut<'l>(&'l mut self) -> IterMut<'l, N> { + self.as_mut().iter_mut() + } + } + + impl + Mul $(, $param : ArrayLength)*> + Axpy for $vecn { + fn axpy(&mut self, a: &N, x: &$vecn) { + assert!(self.len() == x.len()); + + for i in 0 .. x.len() { + unsafe { + let self_i = self.unsafe_at(i); + self.unsafe_set(i, self_i + *a * x.unsafe_at(i)) + } + } + } + } + + impl + Zero $(, $param : ArrayLength)*> + Mul<$vecn> for $vecn { + type Output = $vecn; + + #[inline] + fn mul(self, right: $vecn) -> $vecn { + assert!(self.len() == right.len()); + + let mut res = self; + + for (left, right) in res.as_mut().iter_mut().zip(right.as_ref().iter()) { + *left = *left * *right + } + + res + } + } + + impl + Zero $(, $param : ArrayLength)*> + Div<$vecn> for $vecn { + type Output = $vecn; + + #[inline] + fn div(self, right: $vecn) -> $vecn { + assert!(self.len() == right.len()); + + let mut res = self; + + for (left, right) in res.as_mut().iter_mut().zip(right.as_ref().iter()) { + *left = *left / *right + } + + res + } + } + + impl + Zero $(, $param : ArrayLength)*> + Add<$vecn> for $vecn { + type Output = $vecn; + + #[inline] + fn add(self, right: $vecn) -> $vecn { + assert!(self.len() == right.len()); + + let mut res = self; + + for (left, right) in res.as_mut().iter_mut().zip(right.as_ref().iter()) { + *left = *left + *right + } + + res + } + } + + impl + Zero $(, $param : ArrayLength)*> + Sub<$vecn> for $vecn { + type Output = $vecn; + + #[inline] + fn sub(self, right: $vecn) -> $vecn { + assert!(self.len() == right.len()); + + let mut res = self; + + for (left, right) in res.as_mut().iter_mut().zip(right.as_ref().iter()) { + *left = *left - *right + } + + res + } + } + + impl + Zero + Copy $(, $param : ArrayLength)*> Neg for $vecn { + type Output = $vecn; + + #[inline] + fn neg(mut self) -> $vecn { + for e in self.as_mut().iter_mut() { + *e = -*e; + } + + self + } + } + + impl)*> Dot for $vecn { + #[inline] + fn dot(&self, other: &$vecn) -> N { + assert!(self.len() == other.len()); + let mut res: N = ::zero(); + for i in 0 .. self.len() { + res = res + unsafe { self.unsafe_at(i) * other.unsafe_at(i) }; + } + res + } + } + + impl)*> Norm for $vecn { + #[inline] + fn sqnorm(&self) -> N { + Dot::dot(self, self) + } + + #[inline] + fn normalize(&self) -> $vecn { + let mut res : $vecn = self.clone(); + let _ = res.normalize_mut(); + res + } + + #[inline] + fn normalize_mut(&mut self) -> N { + let l = Norm::norm(self); + + for n in self.as_mut().iter_mut() { + *n = *n / l; + } + + l + } + } + + impl $(, $param : ArrayLength)*> Mean for $vecn { + #[inline] + fn mean(&self) -> N { + let normalizer = ::cast(1.0f64 / self.len() as f64); + self.iter().fold(::zero(), |acc, x| acc + *x * normalizer) + } + } + + impl $(, $param : ArrayLength)*> ApproxEq for $vecn { + #[inline] + fn approx_epsilon(_: Option<$vecn>) -> N { + ApproxEq::approx_epsilon(None::) + } + + #[inline] + fn approx_ulps(_: Option<$vecn>) -> u32 { + ApproxEq::approx_ulps(None::) + } + + #[inline] + fn approx_eq_eps(&self, other: &$vecn, epsilon: &N) -> bool { + let mut zip = self.as_ref().iter().zip(other.as_ref().iter()); + zip.all(|(a, b)| ApproxEq::approx_eq_eps(a, b, epsilon)) + } + + #[inline] + fn approx_eq_ulps(&self, other: &$vecn, ulps: u32) -> bool { + let mut zip = self.as_ref().iter().zip(other.as_ref().iter()); + zip.all(|(a, b)| ApproxEq::approx_eq_ulps(a, b, ulps)) + } + } + + impl + Zero $(, $param : ArrayLength)*> + Mul for $vecn { + type Output = $vecn; + + #[inline] + fn mul(self, right: N) -> $vecn { + let mut res = self; + + for e in res.as_mut().iter_mut() { + *e = *e * right + } + + res + } + } + + impl + Zero $(, $param : ArrayLength)*> Div for $vecn { + type Output = $vecn; + + #[inline] + fn div(self, right: N) -> $vecn { + let mut res = self; + + for e in res.as_mut().iter_mut() { + *e = *e / right + } + + res + } + } + + impl + Zero $(, $param : ArrayLength)*> Add for $vecn { + type Output = $vecn; + + #[inline] + fn add(self, right: N) -> $vecn { + let mut res = self; + + for e in res.as_mut().iter_mut() { + *e = *e + right + } + + res + } + } + + impl + Zero $(, $param : ArrayLength)*> Sub for $vecn { + type Output = $vecn; + + #[inline] + fn sub(self, right: N) -> $vecn { + let mut res = self; + + for e in res.as_mut().iter_mut() { + *e = *e - right + } + + res + } + } + ) +); diff --git a/src/traits/structure.rs b/src/traits/structure.rs index 13f7a943..28d49fc0 100644 --- a/src/traits/structure.rs +++ b/src/traits/structure.rs @@ -158,7 +158,7 @@ pub trait RowSlice { /// Trait of objects having a spacial dimension known at compile time. pub trait Dim: Sized { /// The dimension of the object. - fn dim(unused_mut: Option) -> usize; + fn dim(_unused: Option) -> usize; } /// Trait to get the diagonal of square matrices. diff --git a/tests/vec.rs b/tests/vec.rs index 40bf02c9..48d3656a 100644 --- a/tests/vec.rs +++ b/tests/vec.rs @@ -1,8 +1,10 @@ -extern crate nalgebra as na; extern crate rand; +extern crate typenum; +extern crate nalgebra as na; use rand::random; -use na::{Vec0, Vec1, Vec2, Vec3, Vec4, Vec5, Vec6, Mat3, Rot2, Rot3, Iterable, IterableMut}; +use typenum::U10; +use na::{VecN, Vec0, Vec1, Vec2, Vec3, Vec4, Vec5, Vec6, Mat3, Rot2, Rot3, Iterable, IterableMut}; macro_rules! test_iterator_impl( ($t: ty, $n: ty) => ( @@ -318,6 +320,15 @@ fn test_outer_vec3() { 12.0, 15.0, 18.0)); } +#[test] +fn test_vecn10_add_mul() { + for _ in 0usize .. 10000 { + let v1: VecN = random(); + + assert!(na::approx_eq(&(v1 + v1), &(v1 * 2.0))) + } +} + #[test] fn test_vec3_rotation_between() { From 0c8b8bfcdb4f9be383f0cc6304f697ecbad6b4d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Thu, 24 Mar 2016 19:03:40 +0100 Subject: [PATCH 02/20] Add similarity transformations Sim2 and Sim3 (uniform scale followed by a rotation followed by a translation). --- README.md | 12 +- src/lib.rs | 13 +- src/structs/iso.rs | 18 ++- src/structs/iso_macros.rs | 27 +++- src/structs/mod.rs | 3 + src/structs/pnt.rs | 23 +++- src/structs/sim.rs | 80 ++++++++++++ src/structs/sim_macros.rs | 264 ++++++++++++++++++++++++++++++++++++++ src/structs/vec.rs | 23 +++- 9 files changed, 444 insertions(+), 19 deletions(-) create mode 100644 src/structs/sim.rs create mode 100644 src/structs/sim_macros.rs diff --git a/README.md b/README.md index 6a3150d3..68dc1126 100644 --- a/README.md +++ b/README.md @@ -44,15 +44,17 @@ fn main() { **nalgebra** is meant to be a general-purpose, low-dimensional, linear algebra library, with an optimized set of tools for computer graphics and physics. Those features include: -* Vectors with static sizes: `Vec0`, `Vec1`, `Vec2`, `Vec3`, `Vec4`, `Vec5`, `Vec6`. +* Vectors with predefined static sizes: `Vec0`, `Vec1`, `Vec2`, `Vec3`, `Vec4`, `Vec5`, `Vec6`. +* Vector with a user-defined static size: `VecN`. * Points with static sizes: `Pnt0`, `Pnt1`, `Pnt2`, `Pnt3`, `Pnt4`, `Pnt5`, `Pnt6`. * Square matrices with static sizes: `Mat1`, `Mat2`, `Mat3`, `Mat4`, `Mat5`, `Mat6 `. * Rotation matrices: `Rot2`, `Rot3`, `Rot4`. * Quaternions: `Quat`, `UnitQuat`. -* Isometries: `Iso2`, `Iso3`, `Iso4`. +* Isometries (translation * rotation): `Iso2`, `Iso3`, `Iso4`. +* Similarities (translation * rotation * uniform scale): `Sim2`, `Sim3`. * 3D projections for computer graphics: `Persp3`, `PerspMat3`, `Ortho3`, `OrthoMat3`. -* Dynamically sized vector: `DVec`. -* Dynamically sized (square or rectangular) matrix: `DMat`. +* Dynamically sized heap-allocated vector: `DVec`. +* Dynamically sized stack-allocated vectors with a maximum size: `DVec1` to `DVec6`. +* Dynamically sized heap-allocated (square or rectangular) matrix: `DMat`. * A few methods for data analysis: `Cov`, `Mean`. * Almost one trait per functionality: useful for generic programming. -* Operator overloading using multidispatch. diff --git a/src/lib.rs b/src/lib.rs index 9f6b175e..f5953f7e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,18 +41,20 @@ fn main() { **nalgebra** is meant to be a general-purpose, low-dimensional, linear algebra library, with an optimized set of tools for computer graphics and physics. Those features include: -* Vectors with static sizes: `Vec0`, `Vec1`, `Vec2`, `Vec3`, `Vec4`, `Vec5`, `Vec6`. +* Vectors with predefined static sizes: `Vec0`, `Vec1`, `Vec2`, `Vec3`, `Vec4`, `Vec5`, `Vec6`. +* Vector with a user-defined static size: `VecN`. * Points with static sizes: `Pnt0`, `Pnt1`, `Pnt2`, `Pnt3`, `Pnt4`, `Pnt5`, `Pnt6`. * Square matrices with static sizes: `Mat1`, `Mat2`, `Mat3`, `Mat4`, `Mat5`, `Mat6 `. * Rotation matrices: `Rot2`, `Rot3`, `Rot4`. * Quaternions: `Quat`, `UnitQuat`. -* Isometries: `Iso2`, `Iso3`, `Iso4`. +* Isometries (translation * rotation): `Iso2`, `Iso3`, `Iso4`. +* Similarity transformations (translation * rotation * uniform scale): `Sim2`, `Sim3`. * 3D projections for computer graphics: `Persp3`, `PerspMat3`, `Ortho3`, `OrthoMat3`. -* Dynamically sized vector: `DVec`. -* Dynamically sized (square or rectangular) matrix: `DMat`. +* Dynamically sized heap-allocated vector: `DVec`. +* Dynamically sized stack-allocated vectors with a maximum size: `DVec1` to `DVec6`. +* Dynamically sized heap-allocated (square or rectangular) matrix: `DMat`. * A few methods for data analysis: `Cov`, `Mean`. * Almost one trait per functionality: useful for generic programming. -* Operator overloading using multidispatch. ## **nalgebra** in use @@ -139,6 +141,7 @@ pub use structs::{ DMat, DMat1, DMat2, DMat3, DMat4, DMat5, DMat6, DVec, DVec1, DVec2, DVec3, DVec4, DVec5, DVec6, Iso2, Iso3, Iso4, + Sim2, Sim3, Mat1, Mat2, Mat3, Mat4, Mat5, Mat6, Rot2, Rot3, Rot4, diff --git a/src/structs/iso.rs b/src/structs/iso.rs index f42d48d5..7c022ed5 100644 --- a/src/structs/iso.rs +++ b/src/structs/iso.rs @@ -1,7 +1,3 @@ -//! Isometric transformations. - -#![allow(missing_docs)] - use std::ops::{Add, Sub, Mul, Neg}; use rand::{Rand, Rng}; @@ -22,7 +18,8 @@ use quickcheck::{Arbitrary, Gen}; /// Two dimensional isometry. /// -/// This is the composition of a rotation followed by a translation. +/// This is the composition of a rotation followed by a translation. Vectors `Vec2` are not +/// affected by the translational component of this transformation while points `Pnt2` are. /// Isometries conserve angles and distances, hence do not allow shearing nor scaling. #[repr(C)] #[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Debug, Copy)] @@ -35,7 +32,8 @@ pub struct Iso2 { /// Three dimensional isometry. /// -/// This is the composition of a rotation followed by a translation. +/// This is the composition of a rotation followed by a translation. Vectors `Vec3` are not +/// affected by the translational component of this transformation while points `Pnt3` are. /// Isometries conserve angles and distances, hence do not allow shearing nor scaling. #[repr(C)] #[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Debug, Copy)] @@ -48,6 +46,8 @@ pub struct Iso3 { /// Four dimensional isometry. /// +/// This is the composition of a rotation followed by a translation. Vectors `Vec4` are not +/// affected by the translational component of this transformation while points `Pnt4` are. /// Isometries conserve angles and distances, hence do not allow shearing nor scaling. #[repr(C)] #[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Debug, Copy)] @@ -117,6 +117,8 @@ translate_impl!(Iso2, Pnt2); iso_mul_iso_impl!(Iso2); iso_mul_pnt_impl!(Iso2, Pnt2); pnt_mul_iso_impl!(Iso2, Pnt2); +iso_mul_vec_impl!(Iso2, Vec2); +vec_mul_iso_impl!(Iso2, Vec2); arbitrary_iso_impl!(Iso2); iso_impl!(Iso3, Rot3, Vec3, Vec3); @@ -137,6 +139,8 @@ translate_impl!(Iso3, Pnt3); iso_mul_iso_impl!(Iso3); iso_mul_pnt_impl!(Iso3, Pnt3); pnt_mul_iso_impl!(Iso3, Pnt3); +iso_mul_vec_impl!(Iso3, Vec3); +vec_mul_iso_impl!(Iso3, Vec3); arbitrary_iso_impl!(Iso3); // iso_impl!(Iso4, Rot4, Vec4, Vec4); @@ -157,5 +161,7 @@ translate_impl!(Iso4, Pnt4); iso_mul_iso_impl!(Iso4); iso_mul_pnt_impl!(Iso4, Pnt4); pnt_mul_iso_impl!(Iso4, Pnt4); +iso_mul_vec_impl!(Iso4, Vec4); +vec_mul_iso_impl!(Iso4, Vec4); // FIXME: as soon as Rot4: Arbitrary // arbitrary_iso_impl!(Iso4); diff --git a/src/structs/iso_macros.rs b/src/structs/iso_macros.rs index 7cdd3136..79484bf7 100644 --- a/src/structs/iso_macros.rs +++ b/src/structs/iso_macros.rs @@ -3,7 +3,7 @@ macro_rules! iso_impl( ($t: ident, $submat: ident, $subvec: ident, $subrotvec: ident) => ( impl $t { - /// Creates a new isometry from a rotation matrix and a vector. + /// Creates a new isometry from an axis-angle rotation, and a vector. #[inline] pub fn new(translation: $subvec, rotation: $subrotvec) -> $t { $t { @@ -89,6 +89,19 @@ macro_rules! iso_mul_pnt_impl( ) ); +macro_rules! iso_mul_vec_impl( + ($t: ident, $tv: ident) => ( + impl Mul<$tv> for $t { + type Output = $tv; + + #[inline] + fn mul(self, right: $tv) -> $tv { + self.rotation * right + } + } + ) +); + macro_rules! pnt_mul_iso_impl( ($t: ident, $tv: ident) => ( impl Mul<$t> for $tv { @@ -101,6 +114,18 @@ macro_rules! pnt_mul_iso_impl( ) ); +macro_rules! vec_mul_iso_impl( + ($t: ident, $tv: ident) => ( + impl Mul<$t> for $tv { + type Output = $tv; + #[inline] + fn mul(self, right: $t) -> $tv { + self * right.rotation + } + } + ) +); + macro_rules! translation_impl( ($t: ident, $tv: ident) => ( impl Translation<$tv> for $t { diff --git a/src/structs/mod.rs b/src/structs/mod.rs index d5ab760e..c2888f56 100644 --- a/src/structs/mod.rs +++ b/src/structs/mod.rs @@ -8,6 +8,7 @@ pub use self::pnt::{Pnt0, Pnt1, Pnt2, Pnt3, Pnt4, Pnt5, Pnt6}; pub use self::mat::{Identity, Mat1, Mat2, Mat3, Mat4, Mat5, Mat6}; pub use self::rot::{Rot2, Rot3, Rot4}; pub use self::iso::{Iso2, Iso3, Iso4}; +pub use self::sim::{Sim2, Sim3}; pub use self::persp::{Persp3, PerspMat3}; pub use self::ortho::{Ortho3, OrthoMat3}; pub use self::quat::{Quat, UnitQuat}; @@ -29,6 +30,8 @@ mod rot_macros; mod rot; mod iso_macros; mod iso; +mod sim_macros; +mod sim; mod persp; mod ortho; diff --git a/src/structs/pnt.rs b/src/structs/pnt.rs index a937c73a..f2d6fca0 100644 --- a/src/structs/pnt.rs +++ b/src/structs/pnt.rs @@ -1,4 +1,4 @@ -//! Points with dimensions known at compile-time. +//! Points with dimension known at compile-time. #![allow(missing_docs)] // we allow missing to avoid having to document the point components. @@ -19,6 +19,9 @@ use quickcheck::{Arbitrary, Gen}; /// Point of dimension 0. +/// +/// The main differance between a point and a vector is that a vector is not affected by +/// translations. #[repr(C)] #[derive(Eq, PartialEq, Clone, Debug, Copy)] pub struct Pnt0(pub PhantomData); @@ -39,6 +42,9 @@ impl Repeat for Pnt0 { } /// Point of dimension 1. +/// +/// The main differance between a point and a vector is that a vector is not affected by +/// translations. #[repr(C)] #[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Hash, Debug, Copy)] pub struct Pnt1 { @@ -79,6 +85,9 @@ arbitrary_pnt_impl!(Pnt1, x); rand_impl!(Pnt1, x); /// Point of dimension 2. +/// +/// The main differance between a point and a vector is that a vector is not affected by +/// translations. #[repr(C)] #[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Hash, Debug, Copy)] pub struct Pnt2 { @@ -121,6 +130,9 @@ arbitrary_pnt_impl!(Pnt2, x, y); rand_impl!(Pnt2, x, y); /// Point of dimension 3. +/// +/// The main differance between a point and a vector is that a vector is not affected by +/// translations. #[repr(C)] #[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Hash, Debug, Copy)] pub struct Pnt3 { @@ -165,6 +177,9 @@ arbitrary_pnt_impl!(Pnt3, x, y, z); rand_impl!(Pnt3, x, y, z); /// Point of dimension 4. +/// +/// The main differance between a point and a vector is that a vector is not affected by +/// translations. #[repr(C)] #[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Hash, Debug, Copy)] pub struct Pnt4 { @@ -211,6 +226,9 @@ arbitrary_pnt_impl!(Pnt4, x, y, z, w); rand_impl!(Pnt4, x, y, z, w); /// Point of dimension 5. +/// +/// The main differance between a point and a vector is that a vector is not affected by +/// translations. #[repr(C)] #[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Hash, Debug, Copy)] pub struct Pnt5 { @@ -259,6 +277,9 @@ arbitrary_pnt_impl!(Pnt5, x, y, z, w, a); rand_impl!(Pnt5, x, y, z, w, a); /// Point of dimension 6. +/// +/// The main differance between a point and a vector is that a vector is not affected by +/// translations. #[repr(C)] #[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Hash, Debug, Copy)] pub struct Pnt6 { diff --git a/src/structs/sim.rs b/src/structs/sim.rs new file mode 100644 index 00000000..299c385e --- /dev/null +++ b/src/structs/sim.rs @@ -0,0 +1,80 @@ +use std::ops::{Mul, Neg}; + +use rand::{Rand, Rng}; +use num::One; +use structs::mat::{Mat3, Mat4}; +use traits::structure::{Dim, Col, BaseFloat, BaseNum}; +use traits::operations::{Inv, ApproxEq}; +use traits::geometry::{Rotation, Transform, Transformation, Translation, ToHomogeneous}; + +use structs::vec::{Vec1, Vec2, Vec3}; +use structs::pnt::{Pnt2, Pnt3}; +use structs::rot::{Rot2, Rot3}; +use structs::iso::{Iso2, Iso3}; + +// FIXME: the name is not explicit at all but coherent with the other tree-letters names… +/// A two-dimensional similarity transformation. +/// +/// This is a composition of a uniform scale, followed by a rotation, followed by a translation. +/// Vectors `Vec2` are not affected by the translational component of this transformation while +/// points `Pnt2` are. +/// Similarity transformations conserve angles. Distances are multiplied by some constant (the +/// scale factor). The scale factor cannot be zero. +#[repr(C)] +#[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Debug, Copy)] +pub struct Sim2 { + /// The uniform scale applicable by this similarity transformation. + scale: N, + /// The isometry applicable by this similarity transformation. + pub isometry: Iso2 +} + +/// A three-dimensional similarity transformation. +/// +/// This is a composition of a scale, followed by a rotation, followed by a translation. +/// Vectors `Vec3` are not affected by the translational component of this transformation while +/// points `Pnt3` are. +/// Similarity transformations conserve angles. Distances are multiplied by some constant (the +/// scale factor). The scale factor cannot be zero. +#[repr(C)] +#[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Debug, Copy)] +pub struct Sim3 { + /// The uniform scale applicable by this similarity transformation. + scale: N, + /// The isometry applicable by this similarity transformation. + pub isometry: Iso3 +} + +sim_impl!(Sim2, Iso2, Rot2, Vec2, Vec1); +dim_impl!(Sim2, 2); +sim_scale_impl!(Sim2); +sim_one_impl!(Sim2); +sim_mul_sim_impl!(Sim2); +sim_mul_pnt_vec_impl!(Sim2, Pnt2); +pnt_vec_mul_sim_impl!(Sim2, Pnt2); +sim_mul_pnt_vec_impl!(Sim2, Vec2); +pnt_vec_mul_sim_impl!(Sim2, Vec2); +transformation_impl!(Sim2); +sim_transform_impl!(Sim2, Pnt2); +sim_inv_impl!(Sim2); +sim_to_homogeneous_impl!(Sim2, Mat3); +sim_approx_eq_impl!(Sim2); +sim_rand_impl!(Sim2); +sim_arbitrary_impl!(Sim2); + +sim_impl!(Sim3, Iso3, Rot3, Vec3, Vec3); +dim_impl!(Sim3, 3); +sim_scale_impl!(Sim3); +sim_one_impl!(Sim3); +sim_mul_sim_impl!(Sim3); +sim_mul_pnt_vec_impl!(Sim3, Pnt3); +pnt_vec_mul_sim_impl!(Sim3, Pnt3); +sim_mul_pnt_vec_impl!(Sim3, Vec3); +pnt_vec_mul_sim_impl!(Sim3, Vec3); +transformation_impl!(Sim3); +sim_transform_impl!(Sim3, Pnt3); +sim_inv_impl!(Sim3); +sim_to_homogeneous_impl!(Sim3, Mat4); +sim_approx_eq_impl!(Sim3); +sim_rand_impl!(Sim3); +sim_arbitrary_impl!(Sim3); diff --git a/src/structs/sim_macros.rs b/src/structs/sim_macros.rs new file mode 100644 index 00000000..75d6cde9 --- /dev/null +++ b/src/structs/sim_macros.rs @@ -0,0 +1,264 @@ +#![macro_use] + +macro_rules! sim_impl( + ($t: ident, $iso: ident, $rotmat: ident, $subvec: ident, $subrotvec: ident) => ( + impl $t { + /// Creates a new similarity transformation from a vector, an axis-angle rotation, and a scale factor. + /// + /// The scale factor may be negative but not zero. + #[inline] + pub fn new(translation: $subvec, rotation: $subrotvec, scale: N) -> $t { + assert!(!scale.is_zero(), "A similarity transformation scale factor cannot be zero."); + + $t { + scale: scale, + isometry: $iso::new(translation, rotation) + } + } + + /// Creates a new similarity transformation from a rotation matrix, a vector, and a scale factor. + /// + /// The scale factor may be negative but not zero. + #[inline] + pub fn new_with_rotmat(translation: $subvec, rotation: $rotmat, scale: N) -> $t { + assert!(!scale.is_zero(), "A similarity transformation scale factor cannot be zero."); + + $t { + scale: scale, + isometry: $iso::new_with_rotmat(translation, rotation) + } + } + + /// Creates a new similarity transformation from an isometry and a scale factor. + /// + /// The scale factor may be negative but not zero. + #[inline] + pub fn new_with_iso(isometry: $iso, scale: N) -> $t { + assert!(!scale.is_zero(), "A similarity transformation scale factor cannot be zero."); + + $t { + scale: scale, + isometry: isometry + } + } + } + ) +); + +macro_rules! sim_scale_impl( + ($t: ident) => ( + impl $t { + /// The scale factor of this similarity transformation. + #[inline] + pub fn scale(&self) -> N { + self.scale + } + + /// The inverse scale factor of this similarity transformation. + #[inline] + pub fn inv_scale(&self) -> N { + ::one::() / self.scale + } + + /// Appends in-place a scale to this similarity transformation. + #[inline] + pub fn append_scale_mut(&mut self, s: &N) { + assert!(!s.is_zero(), "Cannot append a zero scale to a similarity transformation."); + self.scale = *s * self.scale; + self.isometry.translation = self.isometry.translation * *s; + } + + /// Appends a scale to this similarity transformation. + #[inline] + pub fn append_scale(&self, s: &N) -> $t { + assert!(!s.is_zero(), "Cannot append a zero scale to a similarity transformation."); + $t::new_with_rotmat(self.isometry.translation * *s, self.isometry.rotation, self.scale * *s) + } + + /// Prepends in-place a scale to this similarity transformation. + #[inline] + pub fn prepend_scale_mut(&mut self, s: &N) { + assert!(!s.is_zero(), "Cannot prepend a zero scale to a similarity transformation."); + self.scale = self.scale * *s; + } + + /// Prepends a scale to this similarity transformation. + #[inline] + pub fn prepend_scale(&self, s: &N) -> $t { + assert!(!s.is_zero(), "A similarity transformation scale must not be zero."); + $t::new_with_iso(self.isometry, self.scale * *s) + } + + /// Sets the scale of this similarity transformation. + #[inline] + pub fn set_scale(&mut self, s: N) { + assert!(!s.is_zero(), "A similarity transformation scale must not be zero."); + self.scale = s + } + } + ) +); + +macro_rules! sim_one_impl( + ($t: ident) => ( + impl One for $t { + #[inline] + fn one() -> $t { + $t::new_with_iso(::one(), ::one()) + } + } + ) +); + +macro_rules! sim_mul_sim_impl( + ($t: ident) => ( + impl Mul<$t> for $t { + type Output = $t; + + #[inline] + fn mul(self, right: $t) -> $t { + $t::new_with_rotmat( + self.isometry.translation + self.isometry.rotation * (right.isometry.translation * right.scale), + self.isometry.rotation * right.isometry.rotation, + self.scale * right.scale) + } + } + ) +); + +macro_rules! sim_mul_pnt_vec_impl( + ($t: ident, $tv: ident) => ( + impl Mul<$tv> for $t { + type Output = $tv; + + #[inline] + fn mul(self, right: $tv) -> $tv { + self.isometry * (right * self.scale) + } + } + ) +); + +macro_rules! pnt_vec_mul_sim_impl( + ($t: ident, $tv: ident) => ( + impl Mul<$t> for $tv { + type Output = $tv; + #[inline] + fn mul(self, right: $t) -> $tv { + self * right.isometry * right.scale + } + } + ) +); + +macro_rules! sim_transform_impl( + ($t: ident, $tp: ident) => ( + impl Transform<$tp> for $t { + #[inline] + fn transform(&self, p: &$tp) -> $tp { + self.isometry.transform(p) * self.scale + } + + #[inline] + fn inv_transform(&self, p: &$tp) -> $tp { + self.isometry.inv_transform(p) / self.scale + } + } + ) +); + +macro_rules! sim_inv_impl( + ($t: ident) => ( + impl> Inv for $t { + #[inline] + 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. + self.isometry.translation = self.isometry.translation * self.scale; + + // always succeed + true + } + + #[inline] + fn inv(&self) -> Option<$t> { + let mut res = *self; + res.inv_mut(); + + // always succeed + Some(res) + } + } + ) +); + +macro_rules! sim_to_homogeneous_impl( + ($t: ident, $th: ident) => ( + impl ToHomogeneous<$th> for $t { + fn to_homogeneous(&self) -> $th { + let mut res = (*self.isometry.rotation.submat() * self.scale).to_homogeneous(); + + // copy the translation + let dim = Dim::dim(None::<$th>); + + res.set_col(dim - 1, self.isometry.translation.as_pnt().to_homogeneous().to_vec()); + + res + } + } + ) +); + +macro_rules! sim_approx_eq_impl( + ($t: ident) => ( + impl> ApproxEq for $t { + #[inline] + fn approx_epsilon(_: Option<$t>) -> N { + ApproxEq::approx_epsilon(None::) + } + + #[inline] + fn approx_ulps(_: Option<$t>) -> u32 { + ApproxEq::approx_ulps(None::) + } + + #[inline] + fn approx_eq_eps(&self, other: &$t, epsilon: &N) -> bool { + ApproxEq::approx_eq_eps(&self.scale, &other.scale, epsilon) && + ApproxEq::approx_eq_eps(&self.isometry, &other.isometry, epsilon) + } + + #[inline] + fn approx_eq_ulps(&self, other: &$t, ulps: u32) -> bool { + ApproxEq::approx_eq_ulps(&self.scale, &other.scale, ulps) && + ApproxEq::approx_eq_ulps(&self.isometry, &other.isometry, ulps) + } + } + ) +); + +macro_rules! sim_rand_impl( + ($t: ident) => ( + impl Rand for $t { + #[inline] + fn rand(rng: &mut R) -> $t { + $t::new_with_iso(rng.gen(), rng.gen()) + } + } + ) +); + +macro_rules! sim_arbitrary_impl( + ($t: ident) => ( + #[cfg(feature="arbitrary")] + impl Arbitrary for $t { + fn arbitrary(g: &mut G) -> $t { + $t::new_with_iso( + Arbitrary::arbitrary(g), + Arbitrary::arbitrary(g) + ) + } + } + ) +); diff --git a/src/structs/vec.rs b/src/structs/vec.rs index 32517fff..3b2339f4 100644 --- a/src/structs/vec.rs +++ b/src/structs/vec.rs @@ -1,4 +1,4 @@ -//! Vectors with dimensions known at compile-time. +//! Vectors with dimension known at compile-time. #![allow(missing_docs)] // we allow missing to avoid having to document the dispatch traits. @@ -21,6 +21,9 @@ use quickcheck::{Arbitrary, Gen}; /// Vector of dimension 0. +/// +/// The main differance between a point and a vector is that a vector is not affected by +/// translations. #[repr(C)] #[derive(Eq, PartialEq, Clone, Debug, Copy)] pub struct Vec0(pub PhantomData); @@ -41,6 +44,9 @@ impl Repeat for Vec0 { } /// Vector of dimension 1. +/// +/// The main differance between a point and a vector is that a vector is not affected by +/// translations. #[repr(C)] #[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Hash, Debug, Copy)] pub struct Vec1 { @@ -93,6 +99,9 @@ rand_impl!(Vec1, x); mean_impl!(Vec1); /// Vector of dimension 2. +/// +/// The main differance between a point and a vector is that a vector is not affected by +/// translations. #[repr(C)] #[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Hash, Debug, Copy)] pub struct Vec2 { @@ -147,6 +156,9 @@ rand_impl!(Vec2, x, y); mean_impl!(Vec2); /// Vector of dimension 3. +/// +/// The main differance between a point and a vector is that a vector is not affected by +/// translations. #[repr(C)] #[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Hash, Debug, Copy)] pub struct Vec3 { @@ -204,6 +216,9 @@ mean_impl!(Vec3); /// Vector of dimension 4. +/// +/// The main differance between a point and a vector is that a vector is not affected by +/// translations. #[repr(C)] #[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Hash, Debug, Copy)] pub struct Vec4 { @@ -262,6 +277,9 @@ rand_impl!(Vec4, x, y, z, w); mean_impl!(Vec4); /// Vector of dimension 5. +/// +/// The main differance between a point and a vector is that a vector is not affected by +/// translations. #[repr(C)] #[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Hash, Debug, Copy)] pub struct Vec5 { @@ -322,6 +340,9 @@ rand_impl!(Vec5, x, y, z, w, a); mean_impl!(Vec5); /// Vector of dimension 6. +/// +/// The main differance between a point and a vector is that a vector is not affected by +/// translations. #[repr(C)] #[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Hash, Debug, Copy)] pub struct Vec6 { From 88fb33cf4431b105d8da9df3bac2547afdfa6ed0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Thu, 24 Mar 2016 19:03:44 +0100 Subject: [PATCH 03/20] Remove useless or incomplete structs: `Vec0`, `Pnt0`, `Iso4`, `Rot4`. --- README.md | 12 +- src/lib.rs | 18 +-- src/structs/iso.rs | 60 +-------- src/structs/mod.rs | 9 +- src/structs/pnt.rs | 24 ---- src/structs/rot.rs | 114 +---------------- src/structs/sim.rs | 3 + src/structs/spec/vec0.rs | 257 --------------------------------------- src/structs/vec.rs | 24 ---- 9 files changed, 31 insertions(+), 490 deletions(-) delete mode 100644 src/structs/spec/vec0.rs diff --git a/README.md b/README.md index 68dc1126..718350af 100644 --- a/README.md +++ b/README.md @@ -44,17 +44,17 @@ fn main() { **nalgebra** is meant to be a general-purpose, low-dimensional, linear algebra library, with an optimized set of tools for computer graphics and physics. Those features include: -* Vectors with predefined static sizes: `Vec0`, `Vec1`, `Vec2`, `Vec3`, `Vec4`, `Vec5`, `Vec6`. +* Vectors with predefined static sizes: `Vec1`, `Vec2`, `Vec3`, `Vec4`, `Vec5`, `Vec6`. * Vector with a user-defined static size: `VecN`. -* Points with static sizes: `Pnt0`, `Pnt1`, `Pnt2`, `Pnt3`, `Pnt4`, `Pnt5`, `Pnt6`. +* Points with static sizes: ``Pnt1`, `Pnt2`, `Pnt3`, `Pnt4`, `Pnt5`, `Pnt6`. * Square matrices with static sizes: `Mat1`, `Mat2`, `Mat3`, `Mat4`, `Mat5`, `Mat6 `. -* Rotation matrices: `Rot2`, `Rot3`, `Rot4`. +* Rotation matrices: `Rot2`, `Rot3` * Quaternions: `Quat`, `UnitQuat`. -* Isometries (translation * rotation): `Iso2`, `Iso3`, `Iso4`. -* Similarities (translation * rotation * uniform scale): `Sim2`, `Sim3`. +* Isometries (translation * rotation): `Iso2`, `Iso3` +* Similarity transformations (translation * rotation * uniform scale): `Sim2`, `Sim3`. * 3D projections for computer graphics: `Persp3`, `PerspMat3`, `Ortho3`, `OrthoMat3`. * Dynamically sized heap-allocated vector: `DVec`. * Dynamically sized stack-allocated vectors with a maximum size: `DVec1` to `DVec6`. * Dynamically sized heap-allocated (square or rectangular) matrix: `DMat`. -* A few methods for data analysis: `Cov`, `Mean`. +* Linear algebra and data analysis operators: `Cov`, `Mean`, `qr`, `cholesky`. * Almost one trait per functionality: useful for generic programming. diff --git a/src/lib.rs b/src/lib.rs index f5953f7e..706d8b40 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,19 +41,19 @@ fn main() { **nalgebra** is meant to be a general-purpose, low-dimensional, linear algebra library, with an optimized set of tools for computer graphics and physics. Those features include: -* Vectors with predefined static sizes: `Vec0`, `Vec1`, `Vec2`, `Vec3`, `Vec4`, `Vec5`, `Vec6`. +* Vectors with predefined static sizes: `Vec1`, `Vec2`, `Vec3`, `Vec4`, `Vec5`, `Vec6`. * Vector with a user-defined static size: `VecN`. -* Points with static sizes: `Pnt0`, `Pnt1`, `Pnt2`, `Pnt3`, `Pnt4`, `Pnt5`, `Pnt6`. +* Points with static sizes: `Pnt1`, `Pnt2`, `Pnt3`, `Pnt4`, `Pnt5`, `Pnt6`. * Square matrices with static sizes: `Mat1`, `Mat2`, `Mat3`, `Mat4`, `Mat5`, `Mat6 `. -* Rotation matrices: `Rot2`, `Rot3`, `Rot4`. +* Rotation matrices: `Rot2`, `Rot3` * Quaternions: `Quat`, `UnitQuat`. -* Isometries (translation * rotation): `Iso2`, `Iso3`, `Iso4`. +* Isometries (translation * rotation): `Iso2`, `Iso3` * Similarity transformations (translation * rotation * uniform scale): `Sim2`, `Sim3`. * 3D projections for computer graphics: `Persp3`, `PerspMat3`, `Ortho3`, `OrthoMat3`. * Dynamically sized heap-allocated vector: `DVec`. * Dynamically sized stack-allocated vectors with a maximum size: `DVec1` to `DVec6`. * Dynamically sized heap-allocated (square or rectangular) matrix: `DMat`. -* A few methods for data analysis: `Cov`, `Mean`. +* Linear algebra and data analysis operators: `Cov`, `Mean`, `qr`, `cholesky`. * Almost one trait per functionality: useful for generic programming. @@ -140,13 +140,13 @@ pub use structs::{ Identity, DMat, DMat1, DMat2, DMat3, DMat4, DMat5, DMat6, DVec, DVec1, DVec2, DVec3, DVec4, DVec5, DVec6, - Iso2, Iso3, Iso4, + Iso2, Iso3, Sim2, Sim3, Mat1, Mat2, Mat3, Mat4, Mat5, Mat6, - Rot2, Rot3, Rot4, - VecN, Vec0, Vec1, Vec2, Vec3, Vec4, Vec5, Vec6, - Pnt0, Pnt1, Pnt2, Pnt3, Pnt4, Pnt5, Pnt6, + Rot2, Rot3, + VecN, Vec1, Vec2, Vec3, Vec4, Vec5, Vec6, + Pnt1, Pnt2, Pnt3, Pnt4, Pnt5, Pnt6, Persp3, PerspMat3, Ortho3, OrthoMat3, Quat, UnitQuat diff --git a/src/structs/iso.rs b/src/structs/iso.rs index 7c022ed5..f3e84c2c 100644 --- a/src/structs/iso.rs +++ b/src/structs/iso.rs @@ -2,15 +2,14 @@ use std::ops::{Add, Sub, Mul, Neg}; use rand::{Rand, Rng}; use num::One; -use structs::mat::{Mat3, Mat4, Mat5}; +use structs::mat::{Mat3, Mat4}; use traits::structure::{Cast, Dim, Col, BaseFloat, BaseNum}; use traits::operations::{Inv, ApproxEq}; use traits::geometry::{RotationMatrix, Rotation, Rotate, AbsoluteRotate, Transform, Transformation, Translate, Translation, ToHomogeneous}; - -use structs::vec::{Vec1, Vec2, Vec3, Vec4}; -use structs::pnt::{Pnt2, Pnt3, Pnt4}; -use structs::rot::{Rot2, Rot3, Rot4}; +use structs::vec::{Vec1, Vec2, Vec3}; +use structs::pnt::{Pnt2, Pnt3}; +use structs::rot::{Rot2, Rot3}; #[cfg(feature="arbitrary")] use quickcheck::{Arbitrary, Gen}; @@ -44,20 +43,6 @@ pub struct Iso3 { pub translation: Vec3 } -/// Four dimensional isometry. -/// -/// This is the composition of a rotation followed by a translation. Vectors `Vec4` are not -/// affected by the translational component of this transformation while points `Pnt4` are. -/// Isometries conserve angles and distances, hence do not allow shearing nor scaling. -#[repr(C)] -#[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Debug, Copy)] -pub struct Iso4 { - /// The rotation applicable by this isometry. - pub rotation: Rot4, - /// The translation applicable by this isometry. - pub translation: Vec4 -} - 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 @@ -69,6 +54,7 @@ impl Iso3 { /// aligned with. /// * up - Vector pointing up. The only requirement of this parameter is to not be colinear /// with `at`. Non-colinearity 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)) } @@ -82,23 +68,12 @@ impl Iso3 { /// aligned with /// * up - Vector pointing `up`. The only requirement of this parameter is to not be colinear /// with `at`. Non-colinearity is not checked. + #[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)) } } -impl Iso4 { - // XXX remove that when iso_impl works for Iso4 - /// Creates a new isometry from a rotation matrix and a vector. - #[inline] - pub fn new_with_rotmat(translation: Vec4, rotation: Rot4) -> Iso4 { - Iso4 { - rotation: rotation, - translation: translation - } - } -} - iso_impl!(Iso2, Rot2, Vec2, Vec1); rotation_matrix_impl!(Iso2, Rot2, Vec2, Vec1); rotation_impl!(Iso2, Rot2, Vec1); @@ -142,26 +117,3 @@ pnt_mul_iso_impl!(Iso3, Pnt3); iso_mul_vec_impl!(Iso3, Vec3); vec_mul_iso_impl!(Iso3, Vec3); arbitrary_iso_impl!(Iso3); - -// iso_impl!(Iso4, Rot4, Vec4, Vec4); -// rotation_matrix_impl!(Iso4, Rot4, Vec4, Vec4); -// rotation_impl!(Iso4, Rot4, Vec4); -dim_impl!(Iso4, 4); -one_impl!(Iso4); -absolute_rotate_impl!(Iso4, Vec4); -// rand_impl!(Iso4); -approx_eq_impl!(Iso4); -to_homogeneous_impl!(Iso4, Mat5); -inv_impl!(Iso4); -transform_impl!(Iso4, Pnt4); -transformation_impl!(Iso4); -rotate_impl!(Iso4, Vec4); -translation_impl!(Iso4, Vec4); -translate_impl!(Iso4, Pnt4); -iso_mul_iso_impl!(Iso4); -iso_mul_pnt_impl!(Iso4, Pnt4); -pnt_mul_iso_impl!(Iso4, Pnt4); -iso_mul_vec_impl!(Iso4, Vec4); -vec_mul_iso_impl!(Iso4, Vec4); -// FIXME: as soon as Rot4: Arbitrary -// arbitrary_iso_impl!(Iso4); diff --git a/src/structs/mod.rs b/src/structs/mod.rs index c2888f56..c5354463 100644 --- a/src/structs/mod.rs +++ b/src/structs/mod.rs @@ -2,12 +2,12 @@ pub use self::dmat::{DMat, DMat1, DMat2, DMat3, DMat4, DMat5, DMat6}; pub use self::dvec::{DVec, DVec1, DVec2, DVec3, DVec4, DVec5, DVec6}; -pub use self::vec::{Vec0, Vec1, Vec2, Vec3, Vec4, Vec5, Vec6}; +pub use self::vec::{Vec1, Vec2, Vec3, Vec4, Vec5, Vec6}; pub use self::vecn::VecN; -pub use self::pnt::{Pnt0, Pnt1, Pnt2, Pnt3, Pnt4, Pnt5, Pnt6}; +pub use self::pnt::{Pnt1, Pnt2, Pnt3, Pnt4, Pnt5, Pnt6}; pub use self::mat::{Identity, Mat1, Mat2, Mat3, Mat4, Mat5, Mat6}; -pub use self::rot::{Rot2, Rot3, Rot4}; -pub use self::iso::{Iso2, Iso3, Iso4}; +pub use self::rot::{Rot2, Rot3}; +pub use self::iso::{Iso2, Iso3}; pub use self::sim::{Sim2, Sim3}; pub use self::persp::{Persp3, PerspMat3}; pub use self::ortho::{Ortho3, OrthoMat3}; @@ -40,7 +40,6 @@ mod ortho; mod spec { mod identity; mod mat; - mod vec0; mod vec; mod primitives; // mod complex; diff --git a/src/structs/pnt.rs b/src/structs/pnt.rs index f2d6fca0..b7524246 100644 --- a/src/structs/pnt.rs +++ b/src/structs/pnt.rs @@ -2,7 +2,6 @@ #![allow(missing_docs)] // we allow missing to avoid having to document the point components. -use std::marker::PhantomData; use std::mem; use std::slice::{Iter, IterMut}; use std::iter::{Iterator, FromIterator, IntoIterator}; @@ -18,29 +17,6 @@ use structs::vec::{Vec1, Vec2, Vec3, Vec4, Vec5, Vec6}; use quickcheck::{Arbitrary, Gen}; -/// Point of dimension 0. -/// -/// The main differance between a point and a vector is that a vector is not affected by -/// translations. -#[repr(C)] -#[derive(Eq, PartialEq, Clone, Debug, Copy)] -pub struct Pnt0(pub PhantomData); - -impl Pnt0 { - /// Creates a new point. - #[inline] - pub fn new() -> Pnt0 { - Pnt0(PhantomData) - } -} - -impl Repeat for Pnt0 { - #[inline] - fn repeat(_: N) -> Pnt0 { - Pnt0(PhantomData) - } -} - /// Point of dimension 1. /// /// The main differance between a point and a vector is that a vector is not affected by diff --git a/src/structs/rot.rs b/src/structs/rot.rs index aa60f33d..b3a2791e 100644 --- a/src/structs/rot.rs +++ b/src/structs/rot.rs @@ -9,9 +9,9 @@ use traits::geometry::{Rotate, Rotation, AbsoluteRotate, RotationMatrix, Rotatio ToHomogeneous, Norm, Cross}; use traits::structure::{Cast, Dim, Row, Col, BaseFloat, BaseNum, Eye, Diag}; use traits::operations::{Absolute, Inv, Transpose, ApproxEq}; -use structs::vec::{Vec1, Vec2, Vec3, Vec4}; -use structs::pnt::{Pnt2, Pnt3, Pnt4}; -use structs::mat::{Mat2, Mat3, Mat4, Mat5}; +use structs::vec::{Vec1, Vec2, Vec3}; +use structs::pnt::{Pnt2, Pnt3}; +use structs::mat::{Mat2, Mat3, Mat4}; #[cfg(feature="arbitrary")] use quickcheck::{Arbitrary, Gen}; @@ -345,92 +345,6 @@ impl Arbitrary for Rot3 { } -/// Four dimensional rotation matrix. -#[repr(C)] -#[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Debug, Hash, Copy)] -pub struct Rot4 { - submat: Mat4 -} - -// impl Rot4 { -// pub fn new(left_iso: Quat, right_iso: Quat) -> Rot4 { -// assert!(left_iso.is_unit()); -// assert!(right_iso.is_unright); -// -// let mat_left_iso = Mat4::new( -// left_iso.x, -left_iso.y, -left_iso.z, -left_iso.w, -// left_iso.y, left_iso.x, -left_iso.w, left_iso.z, -// left_iso.z, left_iso.w, left_iso.x, -left_iso.y, -// left_iso.w, -left_iso.z, left_iso.y, left_iso.x); -// let mat_right_iso = Mat4::new( -// right_iso.x, -right_iso.y, -right_iso.z, -right_iso.w, -// right_iso.y, right_iso.x, right_iso.w, -right_iso.z, -// right_iso.z, -right_iso.w, right_iso.x, right_iso.y, -// right_iso.w, right_iso.z, -right_iso.y, right_iso.x); -// -// Rot4 { -// submat: mat_left_iso * mat_right_iso -// } -// } -// } - -impl AbsoluteRotate> for Rot4 { - #[inline] - fn absolute_rotate(&self, v: &Vec4) -> Vec4 { - Vec4::new( - ::abs(&self.submat.m11) * v.x + ::abs(&self.submat.m12) * v.y + - ::abs(&self.submat.m13) * v.z + ::abs(&self.submat.m14) * v.w, - - ::abs(&self.submat.m21) * v.x + ::abs(&self.submat.m22) * v.y + - ::abs(&self.submat.m23) * v.z + ::abs(&self.submat.m24) * v.w, - - ::abs(&self.submat.m31) * v.x + ::abs(&self.submat.m32) * v.y + - ::abs(&self.submat.m33) * v.z + ::abs(&self.submat.m34) * v.w, - - ::abs(&self.submat.m41) * v.x + ::abs(&self.submat.m42) * v.y + - ::abs(&self.submat.m43) * v.z + ::abs(&self.submat.m44) * v.w) - } -} - -impl -Rotation> for Rot4 { - #[inline] - fn rotation(&self) -> Vec4 { - panic!("Not yet implemented") - } - - #[inline] - fn inv_rotation(&self) -> Vec4 { - panic!("Not yet implemented") - } - - #[inline] - fn append_rotation_mut(&mut self, _: &Vec4) { - panic!("Not yet implemented") - } - - #[inline] - fn append_rotation(&self, _: &Vec4) -> Rot4 { - panic!("Not yet implemented") - } - - #[inline] - fn prepend_rotation_mut(&mut self, _: &Vec4) { - panic!("Not yet implemented") - } - - #[inline] - fn prepend_rotation(&self, _: &Vec4) -> Rot4 { - panic!("Not yet implemented") - } - - #[inline] - fn set_rotation(&mut self, _: Vec4) { - panic!("Not yet implemented") - } -} - - /* * Common implementations. */ @@ -478,25 +392,3 @@ inv_impl!(Rot3); transpose_impl!(Rot3); approx_eq_impl!(Rot3); diag_impl!(Rot3, Vec3); - -submat_impl!(Rot4, Mat4); -rotate_impl!(Rot4, Vec4, Pnt4); -transform_impl!(Rot4, Vec4, Pnt4); -dim_impl!(Rot4, 4); -rot_mul_rot_impl!(Rot4); -rot_mul_vec_impl!(Rot4, Vec4); -vec_mul_rot_impl!(Rot4, Vec4); -rot_mul_pnt_impl!(Rot4, Pnt4); -pnt_mul_rot_impl!(Rot4, Pnt4); -one_impl!(Rot4); -eye_impl!(Rot4); -rotation_matrix_impl!(Rot4, Vec4, Vec4); -col_impl!(Rot4, Vec4); -row_impl!(Rot4, Vec4); -index_impl!(Rot4); -absolute_impl!(Rot4, Mat4); -to_homogeneous_impl!(Rot4, Mat5); -inv_impl!(Rot4); -transpose_impl!(Rot4); -approx_eq_impl!(Rot4); -diag_impl!(Rot4, Vec4); diff --git a/src/structs/sim.rs b/src/structs/sim.rs index 299c385e..87af2ed1 100644 --- a/src/structs/sim.rs +++ b/src/structs/sim.rs @@ -12,6 +12,9 @@ use structs::pnt::{Pnt2, Pnt3}; use structs::rot::{Rot2, Rot3}; use structs::iso::{Iso2, Iso3}; +#[cfg(feature="arbitrary")] +use quickcheck::{Arbitrary, Gen}; + // FIXME: the name is not explicit at all but coherent with the other tree-letters names… /// A two-dimensional similarity transformation. /// diff --git a/src/structs/spec/vec0.rs b/src/structs/spec/vec0.rs deleted file mode 100644 index 84cf6b8b..00000000 --- a/src/structs/spec/vec0.rs +++ /dev/null @@ -1,257 +0,0 @@ -use std::ops::{Add, Sub, Mul, Div, Neg, Index, IndexMut}; -use std::marker::PhantomData; -use std::mem; -use std::slice::{Iter, IterMut}; -use std::iter::{FromIterator, IntoIterator}; -use rand::{Rand, Rng}; -use num::{Zero, One}; -use traits::operations::ApproxEq; -use traits::structure::{Iterable, IterableMut, Indexable, Basis, Dim, Shape, BaseFloat, BaseNum, Bounded}; -use traits::geometry::{Translation, Dot, Norm}; -use structs::vec; - -impl Zero for vec::Vec0 { - #[inline] - fn zero() -> vec::Vec0 { - vec::Vec0(PhantomData) - } - - #[inline] - fn is_zero(&self) -> bool { - true - } -} - -impl Index for vec::Vec0 { - type Output = N; - - #[inline] - fn index(&self, _: usize) -> &N { - panic!("Canot index a Vec0.") - } -} - -impl IndexMut for vec::Vec0 { - #[inline] - fn index_mut(&mut self, _: usize) -> &mut N { - panic!("Canot index a Vec0.") - } -} - -impl Shape for vec::Vec0 { - #[inline] - fn shape(&self) -> usize { - 0 - } -} - -impl Indexable for vec::Vec0 { - #[inline] - fn swap(&mut self, _: usize, _: usize) { - } - - #[inline] - unsafe fn unsafe_at(&self, _: usize) -> N { - panic!("Cannot index a Vec0.") - } - - #[inline] - unsafe fn unsafe_set(&mut self, _: usize, _: N) { - } -} - -impl Iterable for vec::Vec0 { - #[inline] - fn iter<'l>(&'l self) -> Iter<'l, N> { - unsafe { mem::transmute::<&'l vec::Vec0, &'l [N; 0]>(self).iter() } - } -} - -impl IterableMut for vec::Vec0 { - #[inline] - fn iter_mut<'l>(&'l mut self) -> IterMut<'l, N> { - unsafe { mem::transmute::<&'l mut vec::Vec0, &'l mut [N; 0]>(self).iter_mut() } - } -} - -impl Dim for vec::Vec0 { - #[inline] - fn dim(_: Option>) -> usize { - 0 - } -} - -impl Basis for vec::Vec0 { - #[inline(always)] - fn canonical_basis) -> bool>(_: F) { } - - #[inline(always)] - fn orthonormal_subspace_basis) -> bool>(_: &vec::Vec0, _: F) { } - - #[inline(always)] - fn canonical_basis_element(_: usize) -> Option> { - None - } -} - -impl Add for vec::Vec0 { - type Output = vec::Vec0; - - #[inline] - fn add(self, _: T) -> vec::Vec0 { - vec::Vec0(PhantomData) - } -} - -impl Sub for vec::Vec0 { - type Output = vec::Vec0; - - #[inline] - fn sub(self, _: T) -> vec::Vec0 { - vec::Vec0(PhantomData) - } -} - -impl + Copy> Neg for vec::Vec0 { - type Output = vec::Vec0; - - #[inline] - fn neg(self) -> vec::Vec0 { - vec::Vec0(PhantomData) - } -} - -impl Dot for vec::Vec0 { - #[inline] - fn dot(&self, _: &vec::Vec0) -> N { - ::zero() - } -} - -impl Mul for vec::Vec0 { - type Output = vec::Vec0; - - #[inline] - fn mul(self, _: T) -> vec::Vec0 { - vec::Vec0(PhantomData) - } -} - -impl Div for vec::Vec0 { - type Output = vec::Vec0; - - #[inline] - fn div(self, _: T) -> vec::Vec0 { - vec::Vec0(PhantomData) - } -} - -impl + Neg> Translation> for vec::Vec0 { - #[inline] - fn translation(&self) -> vec::Vec0 { - *self - } - - #[inline] - fn inv_translation(&self) -> vec::Vec0 { - -*self - } - - #[inline] - fn append_translation_mut(&mut self, t: &vec::Vec0) { - *self = *t + *self; - } - - #[inline] - fn append_translation(&self, t: &vec::Vec0) -> vec::Vec0 { - *t + self - } - - #[inline] - fn prepend_translation_mut(&mut self, t: &vec::Vec0) { - *self = *self + *t; - } - - #[inline] - fn prepend_translation(&self, t: &vec::Vec0) -> vec::Vec0 { - *self + *t - } - - #[inline] - fn set_translation(&mut self, _: vec::Vec0) { - } -} - -impl Norm for vec::Vec0 { - #[inline] - fn sqnorm(&self) -> N { - ::zero() - } - - #[inline] - fn norm(&self) -> N { - ::zero() - } - - #[inline] - fn normalize(&self) -> vec::Vec0 { - ::zero() - } - - #[inline] - fn normalize_mut(&mut self) -> N { - ::zero() - } -} - -impl> ApproxEq for vec::Vec0 { - #[inline] - fn approx_epsilon(_: Option>) -> N { - ApproxEq::approx_epsilon(None::) - } - - fn approx_ulps(_: Option>) -> u32 { - ApproxEq::approx_ulps(None::) - } - - #[inline] - fn approx_eq_eps(&self, _: &vec::Vec0, _: &N) -> bool { - true - } - - #[inline] - fn approx_eq_ulps(&self, _: &vec::Vec0, _: u32) -> bool { - true - } -} - -impl One for vec::Vec0 { - #[inline] - fn one() -> vec::Vec0 { - vec::Vec0(PhantomData) - } -} - -impl FromIterator for vec::Vec0 { - #[inline] - fn from_iter>(_: I) -> vec::Vec0 { - vec::Vec0(PhantomData) - } -} - -impl Bounded for vec::Vec0 { - #[inline] - fn max_value() -> vec::Vec0 { - vec::Vec0(PhantomData) - } - - #[inline] - fn min_value() -> vec::Vec0 { - vec::Vec0(PhantomData) - } -} - -impl Rand for vec::Vec0 { - #[inline] - fn rand(_: &mut R) -> vec::Vec0 { vec::Vec0(PhantomData) } -} diff --git a/src/structs/vec.rs b/src/structs/vec.rs index 3b2339f4..6ac503f1 100644 --- a/src/structs/vec.rs +++ b/src/structs/vec.rs @@ -3,7 +3,6 @@ #![allow(missing_docs)] // we allow missing to avoid having to document the dispatch traits. use std::ops::{Add, Sub, Mul, Div, Neg, Index, IndexMut}; -use std::marker::PhantomData; use std::mem; use std::slice::{Iter, IterMut}; use std::iter::{Iterator, FromIterator, IntoIterator}; @@ -20,29 +19,6 @@ use structs::pnt::{Pnt1, Pnt2, Pnt3, Pnt4, Pnt5, Pnt6}; use quickcheck::{Arbitrary, Gen}; -/// Vector of dimension 0. -/// -/// The main differance between a point and a vector is that a vector is not affected by -/// translations. -#[repr(C)] -#[derive(Eq, PartialEq, Clone, Debug, Copy)] -pub struct Vec0(pub PhantomData); - -impl Vec0 { - /// Creates a new vector. - #[inline] - pub fn new() -> Vec0 { - Vec0(PhantomData) - } -} - -impl Repeat for Vec0 { - #[inline] - fn repeat(_: N) -> Vec0 { - Vec0(PhantomData) - } -} - /// Vector of dimension 1. /// /// The main differance between a point and a vector is that a vector is not affected by From ceed9e52bde204c4666cb4c3f429c49861d9b7a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Thu, 24 Mar 2016 19:03:48 +0100 Subject: [PATCH 04/20] Implement `Arbitrary` for `VecN`. --- src/structs/vecn.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/structs/vecn.rs b/src/structs/vecn.rs index ecf41461..05689a79 100644 --- a/src/structs/vecn.rs +++ b/src/structs/vecn.rs @@ -19,6 +19,9 @@ pub struct VecN> { pub at: GenericArray } +unsafe impl> Send for VecN { +} + impl> Clone for VecN { fn clone(&self) -> VecN { VecN::new(self.at.clone()) @@ -109,4 +112,12 @@ impl> Zero for VecN { } } +#[cfg(feature="arbitrary")] +impl> Arbitrary for VecN { + #[inline] + fn arbitrary(g: &mut G) -> VecN { + (0 .. D::to_usize()).map(|_| Arbitrary::arbitrary(g)).collect() + } +} + vecn_dvec_common_impl!(VecN, D); From 8dbfbe10a5e5f74a77c27118292a108125051fc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Thu, 24 Mar 2016 19:03:52 +0100 Subject: [PATCH 05/20] Add a changelog (at last!) --- CHANGELOG | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 CHANGELOG diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 00000000..ec5c8596 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,16 @@ +# Change Log +All notable changes to `nalgebra`, starting with the version 0.6.0 will be +documented in this file (sorry I didn't maintain one before). + +This project adheres to [Semantic Versioning](http://semver.org/). + +## [0.6.0] - 2016-03-24 +### Added + * Dependency to [generic-array](https://crates.io/crates/generic-array) + * Staticly sized vectors with user-defined sizes: `VecN`. + * Similarity transformations (an uniform scale followed by a rotation followed + by a translation): `Sim2`, `Sim3`. + +### Removed + * Zero-sized elements `Vec0`, `Pnt0`. + * Incomplete 4-dimensional transformations `Rot4` and `Iso4`. From 87a80c2de60ef490c743d201d82e5bd1a7122101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Thu, 24 Mar 2016 19:03:57 +0100 Subject: [PATCH 06/20] Fix tests. --- src/structs/vecn_macros.rs | 4 ++-- tests/vec.rs | 27 +-------------------------- 2 files changed, 3 insertions(+), 28 deletions(-) diff --git a/src/structs/vecn_macros.rs b/src/structs/vecn_macros.rs index b874cc37..c4bd8945 100644 --- a/src/structs/vecn_macros.rs +++ b/src/structs/vecn_macros.rs @@ -13,7 +13,7 @@ macro_rules! vecn_dvec_common_impl( impl)*> AsRef<[N]> for $vecn { #[inline] fn as_ref(&self) -> &[N] { - &self[.. self.len()] + &self.at[.. self.len()] } } @@ -21,7 +21,7 @@ macro_rules! vecn_dvec_common_impl( #[inline] fn as_mut(&mut self) -> &mut [N] { let len = self.len(); - &mut self[.. len] + &mut self.at[.. len] } } diff --git a/tests/vec.rs b/tests/vec.rs index 48d3656a..bd97361f 100644 --- a/tests/vec.rs +++ b/tests/vec.rs @@ -4,7 +4,7 @@ extern crate nalgebra as na; use rand::random; use typenum::U10; -use na::{VecN, Vec0, Vec1, Vec2, Vec3, Vec4, Vec5, Vec6, Mat3, Rot2, Rot3, Iterable, IterableMut}; +use na::{VecN, Vec1, Vec2, Vec3, Vec4, Vec5, Vec6, Mat3, Rot2, Rot3, Iterable, IterableMut}; macro_rules! test_iterator_impl( ($t: ty, $n: ty) => ( @@ -112,11 +112,6 @@ fn test_cross_vec3() { } } -#[test] -fn test_commut_dot_vec0() { - test_commut_dot_impl!(Vec0); -} - #[test] fn test_commut_dot_vec1() { test_commut_dot_impl!(Vec1); @@ -147,11 +142,6 @@ fn test_commut_dot_vec6() { test_commut_dot_impl!(Vec6); } -#[test] -fn test_basis_vec0() { - test_basis_impl!(Vec0); -} - #[test] fn test_basis_vec1() { test_basis_impl!(Vec1); @@ -182,11 +172,6 @@ fn test_basis_vec6() { test_basis_impl!(Vec6); } -#[test] -fn test_subspace_basis_vec0() { - test_subspace_basis_impl!(Vec0); -} - #[test] fn test_subspace_basis_vec1() { test_subspace_basis_impl!(Vec1); @@ -217,11 +202,6 @@ fn test_subspace_basis_vec6() { test_subspace_basis_impl!(Vec6); } -#[test] -fn test_scalar_op_vec0() { - test_scalar_op_impl!(Vec0, f64); -} - #[test] fn test_scalar_op_vec1() { test_scalar_op_impl!(Vec1, f64); @@ -252,11 +232,6 @@ fn test_scalar_op_vec6() { test_scalar_op_impl!(Vec6, f64); } -#[test] -fn test_iterator_vec0() { - test_iterator_impl!(Vec0, f64); -} - #[test] fn test_iterator_vec1() { test_iterator_impl!(Vec1, f64); From c1ec00cfe74073605f16c29f188120d81011994b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Thu, 24 Mar 2016 19:04:01 +0100 Subject: [PATCH 07/20] Complete the documentation. --- README.md | 14 +++++++------- src/lib.rs | 14 +++++++------- src/structs/dmat.rs | 8 ++++++-- src/structs/dmat_macros.rs | 4 +++- src/structs/dvec.rs | 2 -- src/structs/pnt.rs | 2 -- src/structs/quat.rs | 2 -- src/structs/rot.rs | 2 -- src/structs/rot_macros.rs | 1 + src/structs/vec.rs | 2 -- src/structs/vec_macros.rs | 11 +++++++---- 11 files changed, 31 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 718350af..aa735021 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,12 @@ nalgebra * real time computer graphics. * real time computer physics. -An on-line version of this documentation is available [here](http://nalgebra.org). +An on-line version of this documentation is available [here](http://nalgebra.org/doc/nalgebra). ## Using **nalgebra** -All the functionality of **nalgebra** is grouped in one place: the root module `nalgebra::`. -This module re-exports everything and includes free functions for all traits methods doing -out-of-place modifications. +All the functionality of **nalgebra** is grouped in one place: the root module `nalgebra::`. This +module re-exports everything and includes free functions for all traits methods performing +out-of-place operations. * You can import the whole prelude using: @@ -46,12 +46,12 @@ an optimized set of tools for computer graphics and physics. Those features incl * Vectors with predefined static sizes: `Vec1`, `Vec2`, `Vec3`, `Vec4`, `Vec5`, `Vec6`. * Vector with a user-defined static size: `VecN`. -* Points with static sizes: ``Pnt1`, `Pnt2`, `Pnt3`, `Pnt4`, `Pnt5`, `Pnt6`. +* Points with static sizes: `Pnt1`, `Pnt2`, `Pnt3`, `Pnt4`, `Pnt5`, `Pnt6`. * Square matrices with static sizes: `Mat1`, `Mat2`, `Mat3`, `Mat4`, `Mat5`, `Mat6 `. * Rotation matrices: `Rot2`, `Rot3` * Quaternions: `Quat`, `UnitQuat`. -* Isometries (translation * rotation): `Iso2`, `Iso3` -* Similarity transformations (translation * rotation * uniform scale): `Sim2`, `Sim3`. +* Isometries (translation ⨯ rotation): `Iso2`, `Iso3` +* Similarity transformations (translation ⨯ rotation ⨯ uniform scale): `Sim2`, `Sim3`. * 3D projections for computer graphics: `Persp3`, `PerspMat3`, `Ortho3`, `OrthoMat3`. * Dynamically sized heap-allocated vector: `DVec`. * Dynamically sized stack-allocated vectors with a maximum size: `DVec1` to `DVec6`. diff --git a/src/lib.rs b/src/lib.rs index 706d8b40..d5af670a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,16 +3,16 @@ **nalgebra** is a low-dimensional linear algebra library written for Rust targeting: -* general-purpose linear algebra (still lacks a lot of features…). +* low-dimensional general-purpose linear algebra (still lacks a lot of features…). * real time computer graphics. * real time computer physics. -An on-line version of this documentation is available [here](http://nalgebra.org). +An on-line version of this documentation is available [here](http://nalgebra.org/doc/nalgebra). ## Using **nalgebra** -All the functionality of **nalgebra** is grouped in one place: the root module `nalgebra::`. -This module re-exports everything and includes free functions for all traits methods doing -out-of-place modifications. +All the functionality of **nalgebra** is grouped in one place: the root module `nalgebra::`. This +module re-exports everything and includes free functions for all traits methods performing +out-of-place operations. * You can import the whole prelude using: @@ -47,8 +47,8 @@ an optimized set of tools for computer graphics and physics. Those features incl * Square matrices with static sizes: `Mat1`, `Mat2`, `Mat3`, `Mat4`, `Mat5`, `Mat6 `. * Rotation matrices: `Rot2`, `Rot3` * Quaternions: `Quat`, `UnitQuat`. -* Isometries (translation * rotation): `Iso2`, `Iso3` -* Similarity transformations (translation * rotation * uniform scale): `Sim2`, `Sim3`. +* Isometries (translation ⨯ rotation): `Iso2`, `Iso3` +* Similarity transformations (translation ⨯ rotation ⨯ uniform scale): `Sim2`, `Sim3`. * 3D projections for computer graphics: `Persp3`, `PerspMat3`, `Ortho3`, `OrthoMat3`. * Dynamically sized heap-allocated vector: `DVec`. * Dynamically sized stack-allocated vectors with a maximum size: `DVec1` to `DVec6`. diff --git a/src/structs/dmat.rs b/src/structs/dmat.rs index 345c0742..36b3df4b 100644 --- a/src/structs/dmat.rs +++ b/src/structs/dmat.rs @@ -1,7 +1,5 @@ //! Matrix with dimensions unknown at compile-time. -#![allow(missing_docs)] // we hide doc to not have to document the $trhs double dispatch trait. - use std::cmp; use std::mem; use std::iter::repeat; @@ -131,6 +129,7 @@ impl DMat { dmat_impl!(DMat, DVec); +/// A stack-allocated dynamically sized matrix with at most one row and column. pub struct DMat1 { nrows: usize, ncols: usize, @@ -141,6 +140,7 @@ small_dmat_impl!(DMat1, DVec1, 1, 0); small_dmat_from_impl!(DMat1, 1, ::zero()); +/// A stack-allocated dynamically sized square or rectangular matrix with at most 2 rows and columns. pub struct DMat2 { nrows: usize, ncols: usize, @@ -153,6 +153,7 @@ small_dmat_from_impl!(DMat2, 2, ::zero(), ::zero(), ::zero(), ::zero()); +/// A stack-allocated dynamically sized square or rectangular matrix with at most 3 rows and columns. pub struct DMat3 { nrows: usize, ncols: usize, @@ -167,6 +168,7 @@ small_dmat_from_impl!(DMat3, 3, ::zero(), ::zero(), ::zero(), ::zero(), ::zero(), ::zero()); +/// A stack-allocated dynamically sized square or rectangular matrix with at most 4 rows and columns. pub struct DMat4 { nrows: usize, ncols: usize, @@ -183,6 +185,7 @@ small_dmat_from_impl!(DMat4, 4, ::zero(), ::zero(), ::zero(), ::zero(), ::zero(), ::zero(), ::zero(), ::zero()); +/// A stack-allocated dynamically sized square or rectangular matrix with at most 5 rows and columns. pub struct DMat5 { nrows: usize, ncols: usize, @@ -201,6 +204,7 @@ small_dmat_from_impl!(DMat5, 5, ::zero(), ::zero(), ::zero(), ::zero(), ::zero() ::zero(), ::zero(), ::zero(), ::zero(), ::zero()); +/// A stack-allocated dynamically sized square or rectangular matrix with at most 6 rows and columns. pub struct DMat6 { nrows: usize, ncols: usize, diff --git a/src/structs/dmat_macros.rs b/src/structs/dmat_macros.rs index 4ec9c5ef..4891fb3a 100644 --- a/src/structs/dmat_macros.rs +++ b/src/structs/dmat_macros.rs @@ -19,6 +19,7 @@ macro_rules! dmat_impl( self.mij.iter().all(|e| e.is_zero()) } + /// Set this matrix components to zero. #[inline] pub fn reset(&mut self) { for mij in self.mij.iter_mut() { @@ -881,7 +882,8 @@ macro_rules! small_dmat_from_impl( } } - impl $dmat { + impl $dmat { + /// Creates a new matrix with uninitialized components (with `mem::uninitialized()`). #[inline] pub unsafe fn new_uninitialized(nrows: usize, ncols: usize) -> $dmat { assert!(nrows <= $dim); diff --git a/src/structs/dvec.rs b/src/structs/dvec.rs index de774271..e40e54aa 100644 --- a/src/structs/dvec.rs +++ b/src/structs/dvec.rs @@ -1,7 +1,5 @@ //! Vector with dimensions unknown at compile-time. -#![allow(missing_docs)] // we hide doc to not have to document the $trhs double dispatch trait. - use std::slice::{Iter, IterMut}; use std::iter::{FromIterator, IntoIterator}; use std::iter::repeat; diff --git a/src/structs/pnt.rs b/src/structs/pnt.rs index b7524246..db10db23 100644 --- a/src/structs/pnt.rs +++ b/src/structs/pnt.rs @@ -1,7 +1,5 @@ //! Points with dimension known at compile-time. -#![allow(missing_docs)] // we allow missing to avoid having to document the point components. - use std::mem; use std::slice::{Iter, IterMut}; use std::iter::{Iterator, FromIterator, IntoIterator}; diff --git a/src/structs/quat.rs b/src/structs/quat.rs index d3a3485e..6c06817a 100644 --- a/src/structs/quat.rs +++ b/src/structs/quat.rs @@ -1,7 +1,5 @@ //! Quaternion definition. -#![allow(missing_docs)] // we allow missing to avoid having to document the dispatch trait. - use std::mem; use std::slice::{Iter, IterMut}; use std::ops::{Add, Sub, Mul, Div, Neg, Index, IndexMut}; diff --git a/src/structs/rot.rs b/src/structs/rot.rs index b3a2791e..06b013dd 100644 --- a/src/structs/rot.rs +++ b/src/structs/rot.rs @@ -1,7 +1,5 @@ //! Rotations matrices. -#![allow(missing_docs)] - use std::ops::{Mul, Neg, Index}; use rand::{Rand, Rng}; use num::{Zero, One}; diff --git a/src/structs/rot_macros.rs b/src/structs/rot_macros.rs index a9c38326..25e9ba8d 100644 --- a/src/structs/rot_macros.rs +++ b/src/structs/rot_macros.rs @@ -3,6 +3,7 @@ macro_rules! submat_impl( ($t: ident, $submat: ident) => ( impl $t { + /// This rotation's underlying matrix. #[inline] pub fn submat<'r>(&'r self) -> &'r $submat { &self.submat diff --git a/src/structs/vec.rs b/src/structs/vec.rs index 6ac503f1..01015cf4 100644 --- a/src/structs/vec.rs +++ b/src/structs/vec.rs @@ -1,7 +1,5 @@ //! Vectors with dimension known at compile-time. -#![allow(missing_docs)] // we allow missing to avoid having to document the dispatch traits. - use std::ops::{Add, Sub, Mul, Div, Neg, Index, IndexMut}; use std::mem; use std::slice::{Iter, IterMut}; diff --git a/src/structs/vec_macros.rs b/src/structs/vec_macros.rs index e358a226..1accc4d7 100644 --- a/src/structs/vec_macros.rs +++ b/src/structs/vec_macros.rs @@ -278,6 +278,7 @@ macro_rules! dim_impl( macro_rules! container_impl( ($t: ident) => ( impl $t { + /// The dimension of this entity. #[inline] pub fn len(&self) -> usize { Dim::dim(None::<$t>) @@ -291,18 +292,18 @@ macro_rules! basis_impl( impl> Basis for $t { #[inline] fn canonical_basis) -> bool>(mut f: F) { - for i in 0..$dim { + for i in 0 .. $dim { if !f(Basis::canonical_basis_element(i).unwrap()) { return } } } #[inline] fn orthonormal_subspace_basis) -> bool>(n: &$t, mut f: F) { - // compute the basis of the orthogonal subspace using Gram-Schmidt - // orthogonalization algorithm + // Compute the basis of the orthogonal subspace using Gram-Schmidt + // orthogonalization algorithm. let mut basis: Vec<$t> = Vec::new(); - for i in 0..$dim { + for i in 0 .. $dim { let mut basis_element : $t = ::zero(); unsafe { @@ -743,6 +744,7 @@ macro_rules! transform_impl( macro_rules! vec_as_pnt_impl( ($tv: ident, $t: ident, $($compN: ident),+) => ( impl $tv { + /// Converts this vector to a point. #[inline] pub fn to_pnt(self) -> $t { $t::new( @@ -750,6 +752,7 @@ macro_rules! vec_as_pnt_impl( ) } + /// Reinterprets this vector as a point. #[inline] pub fn as_pnt(&self) -> &$t { unsafe { From cbbf5f138da890c71b297d443a2faadf2e19ee05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Thu, 24 Mar 2016 19:04:06 +0100 Subject: [PATCH 08/20] Remove the double-dispatch trick for Cast implementations. It is obsolete now that rustc supports multiple trait implementations. --- src/structs/spec/primitives.rs | 347 ++++++++++++++------------------- 1 file changed, 149 insertions(+), 198 deletions(-) diff --git a/src/structs/spec/primitives.rs b/src/structs/spec/primitives.rs index de9e6195..636bdf6b 100644 --- a/src/structs/spec/primitives.rs +++ b/src/structs/spec/primitives.rs @@ -1,219 +1,170 @@ //! nalgebra trait implementation for primitive types. -#![allow(missing_docs)] -#![allow(non_camel_case_types)] - use traits::structure::Cast; -// Double dispatch traits to drive the Cast method for primitive types. -macro_rules! primitive_double_dispatch_cast_decl_trait( - ($t: ident, $tcast: ident) => ( - pub trait $tcast { - fn to(Self) -> $t; - } - ) -); - -macro_rules! primitive_double_dispatch_cast_impl( - ($ttarget: ident, $tself: ident, $tcast: ident) => ( - impl $tcast for $tself{ +macro_rules! primitive_cast_impl( + ($from: ty, $to: ty) => ( + impl Cast<$from> for $to { #[inline(always)] - fn to(v: $tself) -> $ttarget { - v as $ttarget + fn from(t: $from) -> $to { + t as $to } } ) ); -macro_rules! primitive_cast_redispatch_impl( - ($t:ident, $tcast: ident) => ( - impl Cast for $t { - #[inline(always)] - fn from(t: T) -> $t { - $tcast::to(t) - } - } - ) -); +primitive_cast_impl!(f64, f64); +primitive_cast_impl!(f64, f32); +primitive_cast_impl!(f64, i64); +primitive_cast_impl!(f64, i32); +primitive_cast_impl!(f64, i16); +primitive_cast_impl!(f64, i8); +primitive_cast_impl!(f64, u64); +primitive_cast_impl!(f64, u32); +primitive_cast_impl!(f64, u16); +primitive_cast_impl!(f64, u8); +primitive_cast_impl!(f64, isize); +primitive_cast_impl!(f64, usize); -primitive_double_dispatch_cast_decl_trait!(f64, f64Cast); -primitive_double_dispatch_cast_decl_trait!(f32, f32Cast); -primitive_double_dispatch_cast_decl_trait!(i64, i64Cast); -primitive_double_dispatch_cast_decl_trait!(i32, i32Cast); -primitive_double_dispatch_cast_decl_trait!(i16, i16Cast); -primitive_double_dispatch_cast_decl_trait!(i8, i8Cast); -primitive_double_dispatch_cast_decl_trait!(u64, u64Cast); -primitive_double_dispatch_cast_decl_trait!(u32, u32Cast); -primitive_double_dispatch_cast_decl_trait!(u16, u16Cast); -primitive_double_dispatch_cast_decl_trait!(u8, u8Cast); -primitive_double_dispatch_cast_decl_trait!(isize, isizeCast); -primitive_double_dispatch_cast_decl_trait!(usize, usizeCast); +primitive_cast_impl!(f32, f64); +primitive_cast_impl!(f32, f32); +primitive_cast_impl!(f32, i64); +primitive_cast_impl!(f32, i32); +primitive_cast_impl!(f32, i16); +primitive_cast_impl!(f32, i8); +primitive_cast_impl!(f32, u64); +primitive_cast_impl!(f32, u32); +primitive_cast_impl!(f32, u16); +primitive_cast_impl!(f32, u8); +primitive_cast_impl!(f32, isize); +primitive_cast_impl!(f32, usize); -primitive_cast_redispatch_impl!(f64, f64Cast); -primitive_cast_redispatch_impl!(f32, f32Cast); -primitive_cast_redispatch_impl!(i64, i64Cast); -primitive_cast_redispatch_impl!(i32, i32Cast); -primitive_cast_redispatch_impl!(i16, i16Cast); -primitive_cast_redispatch_impl!(i8, i8Cast); -primitive_cast_redispatch_impl!(u64, u64Cast); -primitive_cast_redispatch_impl!(u32, u32Cast); -primitive_cast_redispatch_impl!(u16, u16Cast); -primitive_cast_redispatch_impl!(u8, u8Cast); -primitive_cast_redispatch_impl!(isize, isizeCast); -primitive_cast_redispatch_impl!(usize, usizeCast); +primitive_cast_impl!(i64, f64); +primitive_cast_impl!(i64, f32); +primitive_cast_impl!(i64, i64); +primitive_cast_impl!(i64, i32); +primitive_cast_impl!(i64, i16); +primitive_cast_impl!(i64, i8); +primitive_cast_impl!(i64, u64); +primitive_cast_impl!(i64, u32); +primitive_cast_impl!(i64, u16); +primitive_cast_impl!(i64, u8); +primitive_cast_impl!(i64, isize); +primitive_cast_impl!(i64, usize); -primitive_double_dispatch_cast_impl!(f64, f64, f64Cast); -primitive_double_dispatch_cast_impl!(f64, f32, f64Cast); -primitive_double_dispatch_cast_impl!(f64, i64, f64Cast); -primitive_double_dispatch_cast_impl!(f64, i32, f64Cast); -primitive_double_dispatch_cast_impl!(f64, i16, f64Cast); -primitive_double_dispatch_cast_impl!(f64, i8, f64Cast); -primitive_double_dispatch_cast_impl!(f64, u64, f64Cast); -primitive_double_dispatch_cast_impl!(f64, u32, f64Cast); -primitive_double_dispatch_cast_impl!(f64, u16, f64Cast); -primitive_double_dispatch_cast_impl!(f64, u8, f64Cast); -primitive_double_dispatch_cast_impl!(f64, isize, f64Cast); -primitive_double_dispatch_cast_impl!(f64, usize, f64Cast); +primitive_cast_impl!(i32, f64); +primitive_cast_impl!(i32, f32); +primitive_cast_impl!(i32, i64); +primitive_cast_impl!(i32, i32); +primitive_cast_impl!(i32, i16); +primitive_cast_impl!(i32, i8); +primitive_cast_impl!(i32, u64); +primitive_cast_impl!(i32, u32); +primitive_cast_impl!(i32, u16); +primitive_cast_impl!(i32, u8); +primitive_cast_impl!(i32, isize); +primitive_cast_impl!(i32, usize); -primitive_double_dispatch_cast_impl!(f32, f64, f32Cast); -primitive_double_dispatch_cast_impl!(f32, f32, f32Cast); -primitive_double_dispatch_cast_impl!(f32, i64, f32Cast); -primitive_double_dispatch_cast_impl!(f32, i32, f32Cast); -primitive_double_dispatch_cast_impl!(f32, i16, f32Cast); -primitive_double_dispatch_cast_impl!(f32, i8, f32Cast); -primitive_double_dispatch_cast_impl!(f32, u64, f32Cast); -primitive_double_dispatch_cast_impl!(f32, u32, f32Cast); -primitive_double_dispatch_cast_impl!(f32, u16, f32Cast); -primitive_double_dispatch_cast_impl!(f32, u8, f32Cast); -primitive_double_dispatch_cast_impl!(f32, isize, f32Cast); -primitive_double_dispatch_cast_impl!(f32, usize, f32Cast); +primitive_cast_impl!(i16, f64); +primitive_cast_impl!(i16, f32); +primitive_cast_impl!(i16, i64); +primitive_cast_impl!(i16, i32); +primitive_cast_impl!(i16, i16); +primitive_cast_impl!(i16, i8); +primitive_cast_impl!(i16, u64); +primitive_cast_impl!(i16, u32); +primitive_cast_impl!(i16, u16); +primitive_cast_impl!(i16, u8); +primitive_cast_impl!(i16, isize); +primitive_cast_impl!(i16, usize); -primitive_double_dispatch_cast_impl!(i64, f64, i64Cast); -primitive_double_dispatch_cast_impl!(i64, f32, i64Cast); -primitive_double_dispatch_cast_impl!(i64, i64, i64Cast); -primitive_double_dispatch_cast_impl!(i64, i32, i64Cast); -primitive_double_dispatch_cast_impl!(i64, i16, i64Cast); -primitive_double_dispatch_cast_impl!(i64, i8, i64Cast); -primitive_double_dispatch_cast_impl!(i64, u64, i64Cast); -primitive_double_dispatch_cast_impl!(i64, u32, i64Cast); -primitive_double_dispatch_cast_impl!(i64, u16, i64Cast); -primitive_double_dispatch_cast_impl!(i64, u8, i64Cast); -primitive_double_dispatch_cast_impl!(i64, isize, i64Cast); -primitive_double_dispatch_cast_impl!(i64, usize, i64Cast); +primitive_cast_impl!(i8, f64); +primitive_cast_impl!(i8, f32); +primitive_cast_impl!(i8, i64); +primitive_cast_impl!(i8, i32); +primitive_cast_impl!(i8, i16); +primitive_cast_impl!(i8, i8); +primitive_cast_impl!(i8, u64); +primitive_cast_impl!(i8, u32); +primitive_cast_impl!(i8, u16); +primitive_cast_impl!(i8, u8); +primitive_cast_impl!(i8, isize); +primitive_cast_impl!(i8, usize); -primitive_double_dispatch_cast_impl!(i32, f64, i32Cast); -primitive_double_dispatch_cast_impl!(i32, f32, i32Cast); -primitive_double_dispatch_cast_impl!(i32, i64, i32Cast); -primitive_double_dispatch_cast_impl!(i32, i32, i32Cast); -primitive_double_dispatch_cast_impl!(i32, i16, i32Cast); -primitive_double_dispatch_cast_impl!(i32, i8, i32Cast); -primitive_double_dispatch_cast_impl!(i32, u64, i32Cast); -primitive_double_dispatch_cast_impl!(i32, u32, i32Cast); -primitive_double_dispatch_cast_impl!(i32, u16, i32Cast); -primitive_double_dispatch_cast_impl!(i32, u8, i32Cast); -primitive_double_dispatch_cast_impl!(i32, isize, i32Cast); -primitive_double_dispatch_cast_impl!(i32, usize, i32Cast); +primitive_cast_impl!(u64, f64); +primitive_cast_impl!(u64, f32); +primitive_cast_impl!(u64, i64); +primitive_cast_impl!(u64, i32); +primitive_cast_impl!(u64, i16); +primitive_cast_impl!(u64, i8); +primitive_cast_impl!(u64, u64); +primitive_cast_impl!(u64, u32); +primitive_cast_impl!(u64, u16); +primitive_cast_impl!(u64, u8); +primitive_cast_impl!(u64, isize); +primitive_cast_impl!(u64, usize); -primitive_double_dispatch_cast_impl!(i16, f64, i16Cast); -primitive_double_dispatch_cast_impl!(i16, f32, i16Cast); -primitive_double_dispatch_cast_impl!(i16, i64, i16Cast); -primitive_double_dispatch_cast_impl!(i16, i32, i16Cast); -primitive_double_dispatch_cast_impl!(i16, i16, i16Cast); -primitive_double_dispatch_cast_impl!(i16, i8, i16Cast); -primitive_double_dispatch_cast_impl!(i16, u64, i16Cast); -primitive_double_dispatch_cast_impl!(i16, u32, i16Cast); -primitive_double_dispatch_cast_impl!(i16, u16, i16Cast); -primitive_double_dispatch_cast_impl!(i16, u8, i16Cast); -primitive_double_dispatch_cast_impl!(i16, isize, i16Cast); -primitive_double_dispatch_cast_impl!(i16, usize, i16Cast); +primitive_cast_impl!(u32, f64); +primitive_cast_impl!(u32, f32); +primitive_cast_impl!(u32, i64); +primitive_cast_impl!(u32, i32); +primitive_cast_impl!(u32, i16); +primitive_cast_impl!(u32, i8); +primitive_cast_impl!(u32, u64); +primitive_cast_impl!(u32, u32); +primitive_cast_impl!(u32, u16); +primitive_cast_impl!(u32, u8); +primitive_cast_impl!(u32, isize); +primitive_cast_impl!(u32, usize); -primitive_double_dispatch_cast_impl!(i8, f64, i8Cast); -primitive_double_dispatch_cast_impl!(i8, f32, i8Cast); -primitive_double_dispatch_cast_impl!(i8, i64, i8Cast); -primitive_double_dispatch_cast_impl!(i8, i32, i8Cast); -primitive_double_dispatch_cast_impl!(i8, i16, i8Cast); -primitive_double_dispatch_cast_impl!(i8, i8, i8Cast); -primitive_double_dispatch_cast_impl!(i8, u64, i8Cast); -primitive_double_dispatch_cast_impl!(i8, u32, i8Cast); -primitive_double_dispatch_cast_impl!(i8, u16, i8Cast); -primitive_double_dispatch_cast_impl!(i8, u8, i8Cast); -primitive_double_dispatch_cast_impl!(i8, isize, i8Cast); -primitive_double_dispatch_cast_impl!(i8, usize, i8Cast); +primitive_cast_impl!(u16, f64); +primitive_cast_impl!(u16, f32); +primitive_cast_impl!(u16, i64); +primitive_cast_impl!(u16, i32); +primitive_cast_impl!(u16, i16); +primitive_cast_impl!(u16, i8); +primitive_cast_impl!(u16, u64); +primitive_cast_impl!(u16, u32); +primitive_cast_impl!(u16, u16); +primitive_cast_impl!(u16, u8); +primitive_cast_impl!(u16, isize); +primitive_cast_impl!(u16, usize); -primitive_double_dispatch_cast_impl!(u64, f64, u64Cast); -primitive_double_dispatch_cast_impl!(u64, f32, u64Cast); -primitive_double_dispatch_cast_impl!(u64, i64, u64Cast); -primitive_double_dispatch_cast_impl!(u64, i32, u64Cast); -primitive_double_dispatch_cast_impl!(u64, i16, u64Cast); -primitive_double_dispatch_cast_impl!(u64, i8, u64Cast); -primitive_double_dispatch_cast_impl!(u64, u64, u64Cast); -primitive_double_dispatch_cast_impl!(u64, u32, u64Cast); -primitive_double_dispatch_cast_impl!(u64, u16, u64Cast); -primitive_double_dispatch_cast_impl!(u64, u8, u64Cast); -primitive_double_dispatch_cast_impl!(u64, isize, u64Cast); -primitive_double_dispatch_cast_impl!(u64, usize, u64Cast); +primitive_cast_impl!(u8, f64); +primitive_cast_impl!(u8, f32); +primitive_cast_impl!(u8, i64); +primitive_cast_impl!(u8, i32); +primitive_cast_impl!(u8, i16); +primitive_cast_impl!(u8, i8); +primitive_cast_impl!(u8, u64); +primitive_cast_impl!(u8, u32); +primitive_cast_impl!(u8, u16); +primitive_cast_impl!(u8, u8); +primitive_cast_impl!(u8, isize); +primitive_cast_impl!(u8, usize); -primitive_double_dispatch_cast_impl!(u32, f64, u32Cast); -primitive_double_dispatch_cast_impl!(u32, f32, u32Cast); -primitive_double_dispatch_cast_impl!(u32, i64, u32Cast); -primitive_double_dispatch_cast_impl!(u32, i32, u32Cast); -primitive_double_dispatch_cast_impl!(u32, i16, u32Cast); -primitive_double_dispatch_cast_impl!(u32, i8, u32Cast); -primitive_double_dispatch_cast_impl!(u32, u64, u32Cast); -primitive_double_dispatch_cast_impl!(u32, u32, u32Cast); -primitive_double_dispatch_cast_impl!(u32, u16, u32Cast); -primitive_double_dispatch_cast_impl!(u32, u8, u32Cast); -primitive_double_dispatch_cast_impl!(u32, isize, u32Cast); -primitive_double_dispatch_cast_impl!(u32, usize, u32Cast); +primitive_cast_impl!(usize, f64); +primitive_cast_impl!(usize, f32); +primitive_cast_impl!(usize, i64); +primitive_cast_impl!(usize, i32); +primitive_cast_impl!(usize, i16); +primitive_cast_impl!(usize, i8); +primitive_cast_impl!(usize, u64); +primitive_cast_impl!(usize, u32); +primitive_cast_impl!(usize, u16); +primitive_cast_impl!(usize, u8); +primitive_cast_impl!(usize, isize); +primitive_cast_impl!(usize, usize); -primitive_double_dispatch_cast_impl!(u16, f64, u16Cast); -primitive_double_dispatch_cast_impl!(u16, f32, u16Cast); -primitive_double_dispatch_cast_impl!(u16, i64, u16Cast); -primitive_double_dispatch_cast_impl!(u16, i32, u16Cast); -primitive_double_dispatch_cast_impl!(u16, i16, u16Cast); -primitive_double_dispatch_cast_impl!(u16, i8, u16Cast); -primitive_double_dispatch_cast_impl!(u16, u64, u16Cast); -primitive_double_dispatch_cast_impl!(u16, u32, u16Cast); -primitive_double_dispatch_cast_impl!(u16, u16, u16Cast); -primitive_double_dispatch_cast_impl!(u16, u8, u16Cast); -primitive_double_dispatch_cast_impl!(u16, isize, u16Cast); -primitive_double_dispatch_cast_impl!(u16, usize, u16Cast); - -primitive_double_dispatch_cast_impl!(u8, f64, u8Cast); -primitive_double_dispatch_cast_impl!(u8, f32, u8Cast); -primitive_double_dispatch_cast_impl!(u8, i64, u8Cast); -primitive_double_dispatch_cast_impl!(u8, i32, u8Cast); -primitive_double_dispatch_cast_impl!(u8, i16, u8Cast); -primitive_double_dispatch_cast_impl!(u8, i8, u8Cast); -primitive_double_dispatch_cast_impl!(u8, u64, u8Cast); -primitive_double_dispatch_cast_impl!(u8, u32, u8Cast); -primitive_double_dispatch_cast_impl!(u8, u16, u8Cast); -primitive_double_dispatch_cast_impl!(u8, u8, u8Cast); -primitive_double_dispatch_cast_impl!(u8, isize, u8Cast); -primitive_double_dispatch_cast_impl!(u8, usize, u8Cast); - -primitive_double_dispatch_cast_impl!(usize, f64, usizeCast); -primitive_double_dispatch_cast_impl!(usize, f32, usizeCast); -primitive_double_dispatch_cast_impl!(usize, i64, usizeCast); -primitive_double_dispatch_cast_impl!(usize, i32, usizeCast); -primitive_double_dispatch_cast_impl!(usize, i16, usizeCast); -primitive_double_dispatch_cast_impl!(usize, i8, usizeCast); -primitive_double_dispatch_cast_impl!(usize, u64, usizeCast); -primitive_double_dispatch_cast_impl!(usize, u32, usizeCast); -primitive_double_dispatch_cast_impl!(usize, u16, usizeCast); -primitive_double_dispatch_cast_impl!(usize, u8, usizeCast); -primitive_double_dispatch_cast_impl!(usize, isize, usizeCast); -primitive_double_dispatch_cast_impl!(usize, usize, usizeCast); - -primitive_double_dispatch_cast_impl!(isize, f64, isizeCast); -primitive_double_dispatch_cast_impl!(isize, f32, isizeCast); -primitive_double_dispatch_cast_impl!(isize, i64, isizeCast); -primitive_double_dispatch_cast_impl!(isize, i32, isizeCast); -primitive_double_dispatch_cast_impl!(isize, i16, isizeCast); -primitive_double_dispatch_cast_impl!(isize, i8, isizeCast); -primitive_double_dispatch_cast_impl!(isize, u64, isizeCast); -primitive_double_dispatch_cast_impl!(isize, u32, isizeCast); -primitive_double_dispatch_cast_impl!(isize, u16, isizeCast); -primitive_double_dispatch_cast_impl!(isize, u8, isizeCast); -primitive_double_dispatch_cast_impl!(isize, isize, isizeCast); -primitive_double_dispatch_cast_impl!(isize, usize, isizeCast); +primitive_cast_impl!(isize, f64); +primitive_cast_impl!(isize, f32); +primitive_cast_impl!(isize, i64); +primitive_cast_impl!(isize, i32); +primitive_cast_impl!(isize, i16); +primitive_cast_impl!(isize, i8); +primitive_cast_impl!(isize, u64); +primitive_cast_impl!(isize, u32); +primitive_cast_impl!(isize, u16); +primitive_cast_impl!(isize, u8); +primitive_cast_impl!(isize, isize); +primitive_cast_impl!(isize, usize); From fe73e8743fc80206fc79f42310ccc8590c72f8db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Thu, 24 Mar 2016 19:04:09 +0100 Subject: [PATCH 09/20] Minor comment fix. --- src/structs/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/structs/mod.rs b/src/structs/mod.rs index c5354463..7af9a619 100644 --- a/src/structs/mod.rs +++ b/src/structs/mod.rs @@ -35,7 +35,7 @@ mod sim; mod persp; mod ortho; -// specialization for some 1d, 2d and 3d operations +// Specialization for some 1d, 2d and 3d operations. #[doc(hidden)] mod spec { mod identity; From b4c6c99dd7b769fdcdd3f6de1846528f625a7dbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Thu, 24 Mar 2016 19:09:32 +0100 Subject: [PATCH 10/20] Update the changelog. --- CHANGELOG | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index ec5c8596..40f402e8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,7 +4,7 @@ documented in this file (sorry I didn't maintain one before). This project adheres to [Semantic Versioning](http://semver.org/). -## [0.6.0] - 2016-03-24 +## [unreleased] [0.6.0] ### Added * Dependency to [generic-array](https://crates.io/crates/generic-array) * Staticly sized vectors with user-defined sizes: `VecN`. @@ -14,3 +14,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Removed * Zero-sized elements `Vec0`, `Pnt0`. * Incomplete 4-dimensional transformations `Rot4` and `Iso4`. + +### Modified + * Vectors are now multipliable à isometries. This will result into a pure rotation (this is how + vectors differ from point semantically: they design directions so they are not translatable). From 60c0f32e1c59fad0e5ec36a9e5d2a41b2eab4055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Thu, 24 Mar 2016 19:37:56 +0100 Subject: [PATCH 11/20] Feature-gate the `VecN` structure. `rustc` is has a hard time compiling it from time to time. --- CHANGELOG | 4 ++-- Cargo.toml | 15 +++++++++++---- README.md | 2 +- src/lib.rs | 9 +++++++-- src/structs/mod.rs | 5 ++++- 5 files changed, 25 insertions(+), 10 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 40f402e8..a1487f88 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,8 +6,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [unreleased] [0.6.0] ### Added - * Dependency to [generic-array](https://crates.io/crates/generic-array) - * Staticly sized vectors with user-defined sizes: `VecN`. + * (feature=generic_sizes): Dependency to [generic-array](https://crates.io/crates/generic-array) + * (feature=ganedic_sizes): Staticly sized vectors with user-defined sizes: `VecN`. * Similarity transformations (an uniform scale followed by a rotation followed by a translation): `Sim2`, `Sim3`. diff --git a/Cargo.toml b/Cargo.toml index 54905910..513404a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,14 +18,21 @@ path = "src/lib.rs" [features] # Generate arbitrary instances of nalgebra types for testing with quickcheck arbitrary = [ "quickcheck" ] +generic_sizes = [ "generic-array", "typenum" ] [dependencies] rustc-serialize = "0.3.*" rand = "0.3.*" -num = "0.1.*" -generic-array = "0.2.*" -typenum = "1.3.*" +num = "0.1.*" + +[dependencies.generic-array] +optional = true +version = "0.2.*" + +[dependencies.typenum] +optional = true +version = "1.3.*" [dependencies.quickcheck] optional = true -version = "0.2.*" +version = "0.2.*" diff --git a/README.md b/README.md index aa735021..a986f1e4 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ fn main() { an optimized set of tools for computer graphics and physics. Those features include: * Vectors with predefined static sizes: `Vec1`, `Vec2`, `Vec3`, `Vec4`, `Vec5`, `Vec6`. -* Vector with a user-defined static size: `VecN`. +* Vector with a user-defined static size: `VecN` (available only with the `generic_sizes` feature). * Points with static sizes: `Pnt1`, `Pnt2`, `Pnt3`, `Pnt4`, `Pnt5`, `Pnt6`. * Square matrices with static sizes: `Mat1`, `Mat2`, `Mat3`, `Mat4`, `Mat5`, `Mat6 `. * Rotation matrices: `Rot2`, `Rot3` diff --git a/src/lib.rs b/src/lib.rs index d5af670a..f6531611 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,7 +42,7 @@ fn main() { an optimized set of tools for computer graphics and physics. Those features include: * Vectors with predefined static sizes: `Vec1`, `Vec2`, `Vec3`, `Vec4`, `Vec5`, `Vec6`. -* Vector with a user-defined static size: `VecN`. +* Vector with a user-defined static size: `VecN` (available only with the `generic_sizes` feature). * Points with static sizes: `Pnt1`, `Pnt2`, `Pnt3`, `Pnt4`, `Pnt5`, `Pnt6`. * Square matrices with static sizes: `Mat1`, `Mat2`, `Mat3`, `Mat4`, `Mat5`, `Mat6 `. * Rotation matrices: `Rot2`, `Rot3` @@ -78,6 +78,8 @@ Feel free to add your project to this list if you happen to use **nalgebra**! extern crate rustc_serialize; extern crate rand; extern crate num; + +#[cfg(feature="generic_sizes")] extern crate generic_array; #[cfg(feature="arbitrary")] @@ -136,6 +138,9 @@ pub use traits::{ UniformSphereSample }; +#[cfg(feature="generic_sizes")] +pub use structs::VecN; + pub use structs::{ Identity, DMat, DMat1, DMat2, DMat3, DMat4, DMat5, DMat6, @@ -145,7 +150,7 @@ pub use structs::{ Mat1, Mat2, Mat3, Mat4, Mat5, Mat6, Rot2, Rot3, - VecN, Vec1, Vec2, Vec3, Vec4, Vec5, Vec6, + Vec1, Vec2, Vec3, Vec4, Vec5, Vec6, Pnt1, Pnt2, Pnt3, Pnt4, Pnt5, Pnt6, Persp3, PerspMat3, Ortho3, OrthoMat3, diff --git a/src/structs/mod.rs b/src/structs/mod.rs index 7af9a619..00d818bd 100644 --- a/src/structs/mod.rs +++ b/src/structs/mod.rs @@ -3,7 +3,6 @@ pub use self::dmat::{DMat, DMat1, DMat2, DMat3, DMat4, DMat5, DMat6}; pub use self::dvec::{DVec, DVec1, DVec2, DVec3, DVec4, DVec5, DVec6}; pub use self::vec::{Vec1, Vec2, Vec3, Vec4, Vec5, Vec6}; -pub use self::vecn::VecN; pub use self::pnt::{Pnt1, Pnt2, Pnt3, Pnt4, Pnt5, Pnt6}; pub use self::mat::{Identity, Mat1, Mat2, Mat3, Mat4, Mat5, Mat6}; pub use self::rot::{Rot2, Rot3}; @@ -13,9 +12,13 @@ pub use self::persp::{Persp3, PerspMat3}; pub use self::ortho::{Ortho3, OrthoMat3}; pub use self::quat::{Quat, UnitQuat}; +#[cfg(feature="generic_sizes")] +pub use self::vecn::VecN; + mod dmat_macros; mod dmat; mod vecn_macros; +#[cfg(feature="generic_sizes")] mod vecn; mod dvec_macros; mod dvec; From 02001667f73fe70273ae1f4151a440fe5e500c3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Mon, 28 Mar 2016 14:56:25 +0200 Subject: [PATCH 12/20] Fix look_at matrices + implement Display for statically sized structures. --- src/structs/iso.rs | 45 ++++---- src/structs/iso_macros.rs | 23 ++++ src/structs/mat.rs | 7 ++ src/structs/mat_macros.rs | 53 +++++++++ src/structs/ortho.rs | 10 +- src/structs/pnt.rs | 7 ++ src/structs/pnt_macros.rs | 21 ++++ src/structs/quat.rs | 13 +++ src/structs/rot.rs | 56 +++++----- src/structs/rot_macros.rs | 14 +++ src/structs/sim.rs | 3 + src/structs/sim_macros.rs | 41 ++++++- src/structs/vec.rs | 7 ++ src/structs/vec_macros.rs | 22 ++++ tests/mat.rs | 173 ++++++------------------------ tests/transforms.rs | 220 ++++++++++++++++++++++++++++++++++++++ tests/vec.rs | 9 +- 17 files changed, 523 insertions(+), 201 deletions(-) create mode 100644 tests/transforms.rs 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 { From 5b7af11555615c28f6e3bf855fc5aa5a44a853b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Mon, 28 Mar 2016 15:18:28 +0200 Subject: [PATCH 13/20] Add Sim/Rot, Sim/Iso, Iso/Rot multiplications. --- src/structs/iso.rs | 6 ++-- src/structs/iso_macros.rs | 48 ++++++++++++++++++---------- src/structs/sim.rs | 8 ++--- src/structs/sim_macros.rs | 66 ++++++++++++++++++++++++++++++++++----- tests/transforms.rs | 48 ++++++++++++++++++++-------- 5 files changed, 132 insertions(+), 44 deletions(-) diff --git a/src/structs/iso.rs b/src/structs/iso.rs index decbdf6d..e401fa7f 100644 --- a/src/structs/iso.rs +++ b/src/structs/iso.rs @@ -95,10 +95,9 @@ rotate_impl!(Iso2, Vec2); translation_impl!(Iso2, Vec2); translate_impl!(Iso2, Pnt2); iso_mul_iso_impl!(Iso2); +iso_mul_rot_impl!(Iso2, Rot2); iso_mul_pnt_impl!(Iso2, Pnt2); -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); @@ -118,9 +117,8 @@ rotate_impl!(Iso3, Vec3); translation_impl!(Iso3, Vec3); translate_impl!(Iso3, Pnt3); iso_mul_iso_impl!(Iso3); +iso_mul_rot_impl!(Iso3, Rot3); iso_mul_pnt_impl!(Iso3, Pnt3); -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 c5f42099..ccb778ee 100644 --- a/src/structs/iso_macros.rs +++ b/src/structs/iso_macros.rs @@ -76,6 +76,30 @@ macro_rules! iso_mul_iso_impl( ) ); +macro_rules! iso_mul_rot_impl( + ($t: ident, $tr: ident) => ( + impl Mul<$tr> for $t { + type Output = $t; + + #[inline] + fn mul(self, right: $tr) -> $t { + $t::new_with_rotmat(self.translation, self.rotation * right) + } + } + + impl Mul<$t> for $tr { + type Output = $t; + + #[inline] + fn mul(self, right: $t) -> $t { + $t::new_with_rotmat( + self * right.translation, + self * right.rotation) + } + } + ) +); + macro_rules! iso_mul_pnt_impl( ($t: ident, $tv: ident) => ( impl Mul<$tv> for $t { @@ -86,6 +110,14 @@ macro_rules! iso_mul_pnt_impl( self.rotation * right + self.translation } } + + impl Mul<$t> for $tv { + type Output = $tv; + #[inline] + fn mul(self, right: $t) -> $tv { + (self + right.translation) * right.rotation + } + } ) ); @@ -99,23 +131,7 @@ macro_rules! iso_mul_vec_impl( self.rotation * right } } - ) -); -macro_rules! pnt_mul_iso_impl( - ($t: ident, $tv: ident) => ( - impl Mul<$t> for $tv { - type Output = $tv; - #[inline] - fn mul(self, right: $t) -> $tv { - (self + right.translation) * right.rotation - } - } - ) -); - -macro_rules! vec_mul_iso_impl( - ($t: ident, $tv: ident) => ( impl Mul<$t> for $tv { type Output = $tv; #[inline] diff --git a/src/structs/sim.rs b/src/structs/sim.rs index dd7d32f1..e2f0a183 100644 --- a/src/structs/sim.rs +++ b/src/structs/sim.rs @@ -54,10 +54,10 @@ dim_impl!(Sim2, 2); sim_scale_impl!(Sim2); sim_one_impl!(Sim2); sim_mul_sim_impl!(Sim2); +sim_mul_iso_impl!(Sim2, Iso2); +sim_mul_rot_impl!(Sim2, Rot2); sim_mul_pnt_vec_impl!(Sim2, Pnt2); -pnt_vec_mul_sim_impl!(Sim2, Pnt2); sim_mul_pnt_vec_impl!(Sim2, Vec2); -pnt_vec_mul_sim_impl!(Sim2, Vec2); transformation_impl!(Sim2); sim_transform_impl!(Sim2, Pnt2); sim_inv_impl!(Sim2); @@ -72,10 +72,10 @@ dim_impl!(Sim3, 3); sim_scale_impl!(Sim3); sim_one_impl!(Sim3); sim_mul_sim_impl!(Sim3); +sim_mul_iso_impl!(Sim3, Iso3); +sim_mul_rot_impl!(Sim3, Rot3); sim_mul_pnt_vec_impl!(Sim3, Pnt3); -pnt_vec_mul_sim_impl!(Sim3, Pnt3); sim_mul_pnt_vec_impl!(Sim3, Vec3); -pnt_vec_mul_sim_impl!(Sim3, Vec3); transformation_impl!(Sim3); sim_transform_impl!(Sim3, Pnt3); sim_inv_impl!(Sim3); diff --git a/src/structs/sim_macros.rs b/src/structs/sim_macros.rs index 29c575ad..97ad7bfa 100644 --- a/src/structs/sim_macros.rs +++ b/src/structs/sim_macros.rs @@ -126,6 +126,62 @@ macro_rules! sim_mul_sim_impl( ) ); +macro_rules! sim_mul_iso_impl( + ($t: ident, $ti: ident) => ( + impl Mul<$ti> for $t { + type Output = $t; + + #[inline] + fn mul(self, right: $ti) -> $t { + $t::new_with_rotmat( + self.isometry.translation + self.isometry.rotation * (right.translation * self.scale), + self.isometry.rotation * right.rotation, + self.scale) + } + } + + impl Mul<$t> for $ti { + type Output = $t; + + #[inline] + fn mul(self, right: $t) -> $t { + $t::new_with_rotmat( + self.translation + self.rotation * right.isometry.translation, + self.rotation * right.isometry.rotation, + right.scale) + } + } + ) +); + +macro_rules! sim_mul_rot_impl( + ($t: ident, $tr: ident) => ( + impl Mul<$tr> for $t { + type Output = $t; + + #[inline] + fn mul(self, right: $tr) -> $t { + $t::new_with_rotmat( + self.isometry.translation, + self.isometry.rotation * right, + self.scale) + } + } + + impl Mul<$t> for $tr { + type Output = $t; + + #[inline] + fn mul(self, right: $t) -> $t { + $t::new_with_rotmat( + self * right.isometry.translation, + self * right.isometry.rotation, + right.scale) + } + } + ) +); + macro_rules! sim_mul_pnt_vec_impl( ($t: ident, $tv: ident) => ( impl Mul<$tv> for $t { @@ -133,19 +189,15 @@ macro_rules! sim_mul_pnt_vec_impl( #[inline] fn mul(self, right: $tv) -> $tv { - self.isometry * (right * self.scale) + (self.isometry * right) * self.scale } } - ) -); -macro_rules! pnt_vec_mul_sim_impl( - ($t: ident, $tv: ident) => ( impl Mul<$t> for $tv { type Output = $tv; #[inline] fn mul(self, right: $t) -> $tv { - self * right.isometry * right.scale + (self * right.scale) * right.isometry } } ) @@ -161,7 +213,7 @@ macro_rules! sim_transform_impl( #[inline] fn inv_transform(&self, p: &$tp) -> $tp { - self.isometry.inv_transform(p) / self.scale + self.isometry.inv_transform(&(*p / self.scale)) } } ) diff --git a/tests/transforms.rs b/tests/transforms.rs index 1b1fa6c9..7a109d64 100644 --- a/tests/transforms.rs +++ b/tests/transforms.rs @@ -2,30 +2,22 @@ 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}; +use na::{Pnt2, Pnt3, Vec3, Vec1, Rot2, Rot3, Persp3, PerspMat3, Ortho3, OrthoMat3, Iso2, Iso3, + Sim2, Sim3, BaseFloat, Transform}; macro_rules! test_transform_inv_transform_impl( ($t: ty, $p: ty) => ( for _ in 0usize .. 10000 { let randmat: $t = random(); - let randvec: $p = random(); + let expected: $p = random(); - let tvec = randmat.transform(&randvec); - let original = randmat.inv_transform(&randvec); + let computed = randmat.inv_transform(&randmat.transform(&expected)); - assert!(na::approx_eq(&randvec, &original)); + assert!(na::approx_eq(&computed, &expected)); } ); ); -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 { @@ -218,3 +210,33 @@ fn test_ortho() { assert!(na::approx_eq(&pm.znear(), &24.0)); assert!(na::approx_eq(&pm.zfar(), &61.0)); } + +#[test] +fn test_transform_inv_transform_rot2() { + test_transform_inv_transform_impl!(Rot2, Pnt2); +} + +#[test] +fn test_transform_inv_transform_rot3() { + test_transform_inv_transform_impl!(Rot3, Pnt3); +} + +#[test] +fn test_transform_inv_transform_iso2() { + test_transform_inv_transform_impl!(Iso2, Pnt2); +} + +#[test] +fn test_transform_inv_transform_iso3() { + test_transform_inv_transform_impl!(Iso3, Pnt3); +} + +#[test] +fn test_transform_inv_transform_sim2() { + test_transform_inv_transform_impl!(Sim2, Pnt2); +} + +#[test] +fn test_transform_inv_transform_sim3() { + test_transform_inv_transform_impl!(Sim3, Pnt3); +} From 91e14670eddaa9459ffa6779e2298096dd09501c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Mon, 28 Mar 2016 17:05:44 +0200 Subject: [PATCH 14/20] Add multiplications between Iso, Rot, and Sim. --- src/structs/iso_macros.rs | 17 +----- src/structs/sim_macros.rs | 16 +++--- tests/transforms.rs | 106 ++++++++++++++++++++++++-------------- 3 files changed, 75 insertions(+), 64 deletions(-) diff --git a/src/structs/iso_macros.rs b/src/structs/iso_macros.rs index ccb778ee..6c3fcc37 100644 --- a/src/structs/iso_macros.rs +++ b/src/structs/iso_macros.rs @@ -111,13 +111,8 @@ macro_rules! iso_mul_pnt_impl( } } - impl Mul<$t> for $tv { - type Output = $tv; - #[inline] - fn mul(self, right: $t) -> $tv { - (self + right.translation) * right.rotation - } - } + // NOTE: there is no viable pre-multiplication definition because of the translation + // component. ) ); @@ -131,14 +126,6 @@ macro_rules! iso_mul_vec_impl( self.rotation * right } } - - impl Mul<$t> for $tv { - type Output = $tv; - #[inline] - fn mul(self, right: $t) -> $tv { - self * right.rotation - } - } ) ); diff --git a/src/structs/sim_macros.rs b/src/structs/sim_macros.rs index 97ad7bfa..65f3e371 100644 --- a/src/structs/sim_macros.rs +++ b/src/structs/sim_macros.rs @@ -189,17 +189,13 @@ macro_rules! sim_mul_pnt_vec_impl( #[inline] fn mul(self, right: $tv) -> $tv { - (self.isometry * right) * self.scale + self.isometry * (right * self.scale) } } - impl Mul<$t> for $tv { - type Output = $tv; - #[inline] - fn mul(self, right: $t) -> $tv { - (self * right.scale) * right.isometry - } - } + + // NOTE: there is no viable pre-multiplication definition because of the translation + // component. ) ); @@ -208,12 +204,12 @@ macro_rules! sim_transform_impl( impl Transform<$tp> for $t { #[inline] fn transform(&self, p: &$tp) -> $tp { - self.isometry.transform(p) * self.scale + self.isometry.transform(&(*p * self.scale)) } #[inline] fn inv_transform(&self, p: &$tp) -> $tp { - self.isometry.inv_transform(&(*p / self.scale)) + self.isometry.inv_transform(p) / self.scale } } ) diff --git a/tests/transforms.rs b/tests/transforms.rs index 7a109d64..3b7ec6cb 100644 --- a/tests/transforms.rs +++ b/tests/transforms.rs @@ -2,21 +2,8 @@ extern crate nalgebra as na; extern crate rand; use rand::random; -use na::{Pnt2, Pnt3, Vec3, Vec1, Rot2, Rot3, Persp3, PerspMat3, Ortho3, OrthoMat3, Iso2, Iso3, - Sim2, Sim3, BaseFloat, Transform}; - -macro_rules! test_transform_inv_transform_impl( - ($t: ty, $p: ty) => ( - for _ in 0usize .. 10000 { - let randmat: $t = random(); - let expected: $p = random(); - - let computed = randmat.inv_transform(&randmat.transform(&expected)); - - assert!(na::approx_eq(&computed, &expected)); - } - ); -); +use na::{Pnt2, Pnt3, Vec2, Vec3, Vec1, Rot2, Rot3, Persp3, PerspMat3, Ortho3, OrthoMat3, Iso2, + Iso3, Sim2, Sim3, BaseFloat, Transform}; #[test] fn test_rotation2() { @@ -211,32 +198,73 @@ fn test_ortho() { assert!(na::approx_eq(&pm.zfar(), &61.0)); } -#[test] -fn test_transform_inv_transform_rot2() { - test_transform_inv_transform_impl!(Rot2, Pnt2); -} +macro_rules! test_transform_inv_transform_impl( + ($fnname: ident, $t: ty, $p: ty) => ( + #[test] + fn $fnname() { + for _ in 0usize .. 10000 { + let randmat: $t = random(); + let expected: $p = random(); + + let computed = randmat.inv_transform(&randmat.transform(&expected)); + println!("computed: {}, expected: {}", computed, expected); + + assert!(na::approx_eq(&computed, &expected)); + } + } + ); +); -#[test] -fn test_transform_inv_transform_rot3() { - test_transform_inv_transform_impl!(Rot3, Pnt3); -} +test_transform_inv_transform_impl!(test_transform_inv_transform_rot2, Rot2, Pnt2); +test_transform_inv_transform_impl!(test_transform_inv_transform_rot3, Rot3, Pnt3); +test_transform_inv_transform_impl!(test_transform_inv_transform_iso2, Iso2, Pnt2); +test_transform_inv_transform_impl!(test_transform_inv_transform_iso3, Iso3, Pnt3); +test_transform_inv_transform_impl!(test_transform_inv_transform_sim2, Sim2, Pnt2); +test_transform_inv_transform_impl!(test_transform_inv_transform_sim3, Sim3, Pnt3); -#[test] -fn test_transform_inv_transform_iso2() { - test_transform_inv_transform_impl!(Iso2, Pnt2); -} +macro_rules! test_transform_mul_assoc( + ($fnname: ident, $t1: ty, $t2: ty, $p: ty) => ( + #[test] + fn $fnname() { + for _ in 0usize .. 10000 { + let t1: $t1 = random(); + let t2: $t2 = random(); + let p: $p = random(); -#[test] -fn test_transform_inv_transform_iso3() { - test_transform_inv_transform_impl!(Iso3, Pnt3); -} + let t1p = t1 * p; + let t2p = t2 * p; + let t1t2 = t1 * t2; + let t2t1 = t2 * t1; + + assert!(na::approx_eq(&(t1t2 * p), &(t1 * t2p))); + assert!(na::approx_eq(&(t2t1 * p), &(t2 * t1p))); + } + } + ); +); -#[test] -fn test_transform_inv_transform_sim2() { - test_transform_inv_transform_impl!(Sim2, Pnt2); -} +test_transform_mul_assoc!(test_transform_inv_transform_sim3_sim3_pnt3, Sim3, Sim3, Pnt3); +test_transform_mul_assoc!(test_transform_inv_transform_sim3_iso3_pnt3, Sim3, Iso3, Pnt3); +test_transform_mul_assoc!(test_transform_inv_transform_sim3_rot3_pnt3, Sim3, Rot3, Pnt3); +test_transform_mul_assoc!(test_transform_inv_transform_iso3_iso3_pnt3, Iso3, Iso3, Pnt3); +test_transform_mul_assoc!(test_transform_inv_transform_iso3_rot3_pnt3, Iso3, Rot3, Pnt3); +test_transform_mul_assoc!(test_transform_inv_transform_rot3_rot3_pnt3, Rot3, Rot3, Pnt3); +test_transform_mul_assoc!(test_transform_inv_transform_sim3_sim3_vec3, Sim3, Sim3, Vec3); +test_transform_mul_assoc!(test_transform_inv_transform_sim3_iso3_vec3, Sim3, Iso3, Vec3); +test_transform_mul_assoc!(test_transform_inv_transform_sim3_rot3_vec3, Sim3, Rot3, Vec3); +test_transform_mul_assoc!(test_transform_inv_transform_iso3_iso3_vec3, Iso3, Iso3, Vec3); +test_transform_mul_assoc!(test_transform_inv_transform_iso3_rot3_vec3, Iso3, Rot3, Vec3); +test_transform_mul_assoc!(test_transform_inv_transform_rot3_rot3_vec3, Rot3, Rot3, Vec3); -#[test] -fn test_transform_inv_transform_sim3() { - test_transform_inv_transform_impl!(Sim3, Pnt3); -} +test_transform_mul_assoc!(test_transform_inv_transform_sim2_sim2_pnt2, Sim2, Sim2, Pnt2); +test_transform_mul_assoc!(test_transform_inv_transform_sim2_iso2_pnt2, Sim2, Iso2, Pnt2); +test_transform_mul_assoc!(test_transform_inv_transform_sim2_rot2_pnt2, Sim2, Rot2, Pnt2); +test_transform_mul_assoc!(test_transform_inv_transform_iso2_iso2_pnt2, Iso2, Iso2, Pnt2); +test_transform_mul_assoc!(test_transform_inv_transform_iso2_rot2_pnt2, Iso2, Rot2, Pnt2); +test_transform_mul_assoc!(test_transform_inv_transform_rot2_rot2_pnt2, Rot2, Rot2, Pnt2); +test_transform_mul_assoc!(test_transform_inv_transform_sim2_sim2_vec2, Sim2, Sim2, Vec2); +test_transform_mul_assoc!(test_transform_inv_transform_sim2_iso2_vec2, Sim2, Iso2, Vec2); +test_transform_mul_assoc!(test_transform_inv_transform_sim2_rot2_vec2, Sim2, Rot2, Vec2); +test_transform_mul_assoc!(test_transform_inv_transform_iso2_iso2_vec2, Iso2, Iso2, Vec2); +test_transform_mul_assoc!(test_transform_inv_transform_iso2_rot2_vec2, Iso2, Rot2, Vec2); +test_transform_mul_assoc!(test_transform_inv_transform_rot2_rot2_vec2, Rot2, Rot2, Vec2); From 4c58e3791045200143e9f3ff98e96ad65f73892e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Thu, 31 Mar 2016 21:22:02 +0200 Subject: [PATCH 15/20] Make look_at, perspective, and orthographic projection matrices conform to computer-graphics convensions. The `look_at` method itself has been split into a right-handed and a left-handed variant: `look_at_rh` and `look_at_lh`. Fix #171, #158, #88, #79. --- src/structs/iso.rs | 40 ++++++--- src/structs/ortho.rs | 207 +++++++++++++++++++++++++++++++------------ src/structs/persp.rs | 62 +++++++------ src/structs/rot.rs | 31 +++++-- tests/transforms.rs | 72 ++++++++------- 5 files changed, 279 insertions(+), 133 deletions(-) diff --git a/src/structs/iso.rs b/src/structs/iso.rs index e401fa7f..a96584ef 100644 --- a/src/structs/iso.rs +++ b/src/structs/iso.rs @@ -16,7 +16,7 @@ use structs::rot::{Rot2, Rot3}; use quickcheck::{Arbitrary, Gen}; -/// Two dimensional isometry. +/// Two dimensional **direct** isometry. /// /// This is the composition of a rotation followed by a translation. Vectors `Vec2` are not /// affected by the translational component of this transformation while points `Pnt2` are. @@ -30,7 +30,7 @@ pub struct Iso2 { pub translation: Vec2 } -/// Three dimensional isometry. +/// Three dimensional **direct** isometry. /// /// This is the composition of a rotation followed by a translation. Vectors `Vec3` are not /// affected by the translational component of this transformation while points `Pnt3` are. @@ -62,20 +62,40 @@ impl Iso3 { Iso3::new_with_rotmat(eye.as_vec().clone(), new_rotmat) } - /// Builds a look-at view matrix. + /// Builds a right-handed 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. + /// This conforms to the common notion of right handed look-at matrix from the computer + /// graphics community. /// /// # Arguments /// * eye - The eye position. /// * target - The target position. - /// * up - The vertical view direction. It must not be to collinear to `eye - target`. + /// * up - A vector approximately aligned with required the vertical axis. The only + /// requirement of this parameter is to not be collinear to `target - eye`. #[inline] - 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) + pub fn look_at_rh(eye: &Pnt3, target: &Pnt3, up: &Vec3) -> Iso3 { + let rot = Rot3::look_at_rh(&(*target - *eye), up); + let trans = rot * (-*eye); + + Iso3::new_with_rotmat(trans.to_vec(), rot) + } + + /// Builds a left-handed look-at view matrix. + /// + /// This conforms to the common notion of left handed look-at matrix from the computer + /// graphics community. + /// + /// # Arguments + /// * eye - The eye position. + /// * target - The target position. + /// * up - A vector approximately aligned with required the vertical axis. The only + /// requirement of this parameter is to not be collinear to `target - eye`. + #[inline] + pub fn look_at_lh(eye: &Pnt3, target: &Pnt3, up: &Vec3) -> Iso3 { + let rot = Rot3::look_at_lh(&(*target - *eye), up); + let trans = rot * (-*eye); + + Iso3::new_with_rotmat(trans.to_vec(), rot) } } diff --git a/src/structs/ortho.rs b/src/structs/ortho.rs index e5018d2d..1bff6367 100644 --- a/src/structs/ortho.rs +++ b/src/structs/ortho.rs @@ -7,18 +7,24 @@ use quickcheck::{Arbitrary, Gen}; /// A 3D orthographic projection stored without any matrix. /// -/// Reading or modifying its individual properties is cheap but applying the transformation is costly. +/// This flips the `z` axis and maps a axis-aligned cube to the unit cube with corners varying from +/// `(-1, -1, -1)` to `(1, 1, 1)`. Reading or modifying its individual properties is cheap but +/// applying the transformation is costly. #[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Debug, Copy)] pub struct Ortho3 { - width: N, - height: N, + left: N, + right: N, + bottom: N, + top: N, znear: N, zfar: N } /// A 3D orthographic projection stored as a 4D matrix. /// -/// Reading or modifying its individual properties is costly but applying the transformation is cheap. +/// This flips the `z` axis and maps a axis-aligned cube to the unit cube with corners varying from +/// `(-1, -1, -1)` to `(1, 1, 1)`. Reading or modifying its individual properties is costly but +/// applying the transformation is cheap. #[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Debug, Copy)] pub struct OrthoMat3 { mat: Mat4 @@ -26,14 +32,16 @@ pub struct OrthoMat3 { impl Ortho3 { /// Creates a new 3D orthographic projection. - pub fn new(width: N, height: N, znear: N, zfar: N) -> Ortho3 { + pub fn new(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> Ortho3 { assert!(!::is_zero(&(zfar - znear))); - assert!(!::is_zero(&width)); - assert!(!::is_zero(&height)); + assert!(!::is_zero(&(left - right))); + assert!(!::is_zero(&(top - bottom))); Ortho3 { - width: width, - height: height, + left: left, + right: right, + bottom: bottom, + top: top, znear: znear, zfar: zfar } @@ -41,37 +49,51 @@ impl Ortho3 { /// Builds a 4D projection matrix (using homogeneous coordinates) for this projection. pub fn to_mat(&self) -> Mat4 { - self.to_ortho_mat().mat + self.to_persp_mat().mat } /// Build a `OrthoMat3` representing this projection. - pub fn to_ortho_mat(&self) -> OrthoMat3 { - OrthoMat3::new(self.width, self.height, self.znear, self.zfar) + pub fn to_persp_mat(&self) -> OrthoMat3 { + OrthoMat3::new(self.left, self.right, self.bottom, self.top, self.znear, self.zfar) } } #[cfg(feature="arbitrary")] impl Arbitrary for Ortho3 { fn arbitrary(g: &mut G) -> Ortho3 { - let width = reject(g, |x| !::is_zero(x)); - let height = reject(g, |x| !::is_zero(x)); - let znear = Arbitrary::arbitrary(g); - let zfar = reject(g, |&x: &N| !::is_zero(&(x - znear))); + let left = Arbitrary::arbitrary(g); + let right = reject(g, |x: &N| *x > left); + let bottom = Arbitrary::arbitrary(g); + let top = reject(g, |x: &N| *x > bottom); + let znear = Arbitrary::arbitrary(g); + let zfar = reject(g, |x: &N| *x > znear); Ortho3::new(width, height, znear, zfar) } } impl Ortho3 { - /// The width of the view cuboid. + /// The smallest x-coordinate of the view cuboid. #[inline] - pub fn width(&self) -> N { - self.width.clone() + pub fn left(&self) -> N { + self.left.clone() } - /// The height of the view cuboid. + /// The largest x-coordinate of the view cuboid. #[inline] - pub fn height(&self) -> N { - self.height.clone() + pub fn right(&self) -> N { + self.right.clone() + } + + /// The smallest y-coordinate of the view cuboid. + #[inline] + pub fn bottom(&self) -> N { + self.bottom.clone() + } + + /// The largest y-coordinate of the view cuboid. + #[inline] + pub fn top(&self) -> N { + self.top.clone() } /// The near plane offset of the view cuboid. @@ -86,27 +108,45 @@ impl Ortho3 { self.zfar.clone() } - /// Sets the width of the view cuboid. + /// Sets the smallest x-coordinate of the view cuboid. #[inline] - pub fn set_width(&mut self, width: N) { - self.width = width + pub fn set_left(&mut self, left: N) { + assert!(left < self.right, "The left corner must be farther than the right corner."); + self.left = left } - /// Sets the height of the view cuboid. + /// Sets the largest x-coordinate of the view cuboid. #[inline] - pub fn set_height(&mut self, height: N) { - self.height = height + pub fn set_right(&mut self, right: N) { + assert!(right > self.left, "The left corner must be farther than the right corner."); + self.right = right + } + + /// Sets the smallest y-coordinate of the view cuboid. + #[inline] + pub fn set_bottom(&mut self, bottom: N) { + assert!(bottom < self.top, "The top corner must be higher than the bottom corner."); + self.bottom = bottom + } + + /// Sets the largest y-coordinate of the view cuboid. + #[inline] + pub fn set_top(&mut self, top: N) { + assert!(top > self.bottom, "The top corner must be higher than the left corner."); + self.top = top } /// Sets the near plane offset of the view cuboid. #[inline] pub fn set_znear(&mut self, znear: N) { + assert!(znear < self.zfar, "The far plane must be farther than the near plane."); self.znear = znear } /// Sets the far plane offset of the view cuboid. #[inline] pub fn set_zfar(&mut self, zfar: N) { + assert!(zfar > self.znear, "The far plane must be farther than the near plane."); self.zfar = zfar } @@ -114,34 +154,47 @@ impl Ortho3 { #[inline] pub fn project_pnt(&self, p: &Pnt3) -> Pnt3 { // FIXME: optimize that - self.to_ortho_mat().project_pnt(p) + self.to_persp_mat().project_pnt(p) } /// Projects a vector. #[inline] pub fn project_vec(&self, p: &Vec3) -> Vec3 { // FIXME: optimize that - self.to_ortho_mat().project_vec(p) + self.to_persp_mat().project_vec(p) } } impl OrthoMat3 { - /// Creates a new orthographic projection matrix from the width, heihgt, znear and zfar planes of the view cuboid. - pub fn new(width: N, height: N, znear: N, zfar: N) -> OrthoMat3 { - assert!(!::is_zero(&(zfar - znear))); - assert!(!::is_zero(&width)); - assert!(!::is_zero(&height)); + /// Creates a new orthographic projection matrix. + pub fn new(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> OrthoMat3 { + assert!(left < right, "The left corner must be farther than the right corner."); + assert!(bottom < top, "The top corner must be higher than the bottom corner."); + assert!(znear < zfar, "The far plane must be farther than the near plane."); let mat: Mat4 = ::one(); let mut res = OrthoMat3 { mat: mat }; - res.set_width(width); - res.set_height(height); + res.set_left_and_right(left, right); + res.set_bottom_and_top(bottom, top); res.set_znear_and_zfar(znear, zfar); res } + /// Creates a new orthographic projection matrix from an aspect ratio and the vertical field of view. + pub fn new_with_fov(aspect: N, vfov: N, znear: N, zfar: N) -> OrthoMat3 { + assert!(znear < zfar, "The far plane must be farther than the near plane."); + assert!(!::is_zero(&aspect)); + + let _1: N = ::one(); + let _2 = _1 + _1; + let width = zfar * (vfov / _2).tan(); + let height = width / aspect; + + OrthoMat3::new(-width / _2, width / _2, -height / _2, height / _2, znear, zfar) + } + /// Creates a new orthographic matrix from a 4D matrix. /// /// This is unsafe because the input matrix is not checked to be a orthographic projection. @@ -158,42 +211,68 @@ impl OrthoMat3 { &self.mat } - /// The width of the view cuboid. + /// The smallest x-coordinate of the view cuboid. #[inline] - pub fn width(&self) -> N { - >::from(2.0) / self.mat.m11 + pub fn left(&self) -> N { + (-::one::() - self.mat.m14) / self.mat.m11 } - /// The height of the view cuboid. + /// The largest x-coordinate of the view cuboid. #[inline] - pub fn height(&self) -> N { - >::from(2.0) / self.mat.m22 + pub fn right(&self) -> N { + (::one::() - self.mat.m14) / self.mat.m11 + } + + /// The smallest y-coordinate of the view cuboid. + #[inline] + pub fn bottom(&self) -> N { + (-::one::() - self.mat.m24) / self.mat.m22 + } + + /// The largest y-coordinate of the view cuboid. + #[inline] + pub fn top(&self) -> N { + (::one::() - self.mat.m24) / self.mat.m22 } /// The near plane offset of the view cuboid. #[inline] pub fn znear(&self) -> N { - (self.mat.m34 + ::one()) / self.mat.m33 + (::one::() + self.mat.m34) / self.mat.m33 } /// The far plane offset of the view cuboid. #[inline] pub fn zfar(&self) -> N { - (self.mat.m34 - ::one()) / self.mat.m33 + (-::one::() + self.mat.m34) / self.mat.m33 } - /// Sets the width of the view cuboid. + /// Sets the smallest x-coordinate of the view cuboid. #[inline] - pub fn set_width(&mut self, width: N) { - assert!(!::is_zero(&width)); - self.mat.m11 = >::from(2.0) / width; + pub fn set_left(&mut self, left: N) { + let right = self.right(); + self.set_left_and_right(left, right); } - /// Sets the height of the view cuboid. + /// Sets the largest x-coordinate of the view cuboid. #[inline] - pub fn set_height(&mut self, height: N) { - assert!(!::is_zero(&height)); - self.mat.m22 = >::from(2.0) / height; + pub fn set_right(&mut self, right: N) { + let left = self.left(); + self.set_left_and_right(left, right); + } + + /// Sets the smallest y-coordinate of the view cuboid. + #[inline] + pub fn set_bottom(&mut self, bottom: N) { + let top = self.top(); + self.set_bottom_and_top(bottom, top); + } + + /// Sets the largest y-coordinate of the view cuboid. + #[inline] + pub fn set_top(&mut self, top: N) { + let bottom = self.bottom(); + self.set_bottom_and_top(bottom, top); } /// Sets the near plane offset of the view cuboid. @@ -210,6 +289,22 @@ impl OrthoMat3 { self.set_znear_and_zfar(znear, zfar); } + /// Sets the view cuboid coordinates along the `x` axis. + #[inline] + pub fn set_left_and_right(&mut self, left: N, right: N) { + assert!(left < right, "The left corner must be farther than the right corner."); + self.mat.m11 = >::from(2.0) / (right - left); + self.mat.m14 = -(right + left) / (right - left); + } + + /// Sets the view cuboid coordinates along the `y` axis. + #[inline] + pub fn set_bottom_and_top(&mut self, bottom: N, top: N) { + assert!(bottom < top, "The top corner must be higher than the bottom corner."); + self.mat.m22 = >::from(2.0) / (top - bottom); + self.mat.m24 = -(top + bottom) / (top - bottom); + } + /// Sets the near and far plane offsets of the view cuboid. #[inline] pub fn set_znear_and_zfar(&mut self, znear: N, zfar: N) { @@ -222,8 +317,8 @@ impl OrthoMat3 { #[inline] pub fn project_pnt(&self, p: &Pnt3) -> Pnt3 { Pnt3::new( - self.mat.m11 * p.x, - self.mat.m22 * p.y, + self.mat.m11 * p.x + self.mat.m14, + self.mat.m22 * p.y + self.mat.m24, self.mat.m33 * p.z + self.mat.m34 ) } @@ -251,7 +346,7 @@ impl OrthoMat3 { impl Arbitrary for OrthoMat3 { fn arbitrary(g: &mut G) -> OrthoMat3 { let x: Ortho3 = Arbitrary::arbitrary(g); - x.to_ortho_mat() + x.to_persp_mat() } } diff --git a/src/structs/persp.rs b/src/structs/persp.rs index af9289cd..b9d70dda 100644 --- a/src/structs/persp.rs +++ b/src/structs/persp.rs @@ -7,18 +7,22 @@ use quickcheck::{Arbitrary, Gen}; /// A 3D perspective projection stored without any matrix. /// -/// Reading or modifying its individual properties is cheap but applying the transformation is costly. +/// This maps a frustrum cube to the unit cube with corners varying from `(-1, -1, -1)` to +/// `(1, 1, 1)`. Reading or modifying its individual properties is cheap but applying the +/// transformation is costly. #[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Debug, Copy)] pub struct Persp3 { aspect: N, - fov: N, + fovy: N, znear: N, zfar: N } /// A 3D perspective projection stored as a 4D matrix. /// -/// Reading or modifying its individual properties is costly but applying the transformation is cheap. +/// This maps a frustrum to the unit cube with corners varying from `(-1, -1, -1)` to +/// `(1, 1, 1)`. Reading or modifying its individual properties is costly but applying the +/// transformation is cheap. #[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Debug, Copy)] pub struct PerspMat3 { mat: Mat4 @@ -26,13 +30,13 @@ pub struct PerspMat3 { impl Persp3 { /// Creates a new 3D perspective projection. - pub fn new(aspect: N, fov: N, znear: N, zfar: N) -> Persp3 { + pub fn new(aspect: N, fovy: N, znear: N, zfar: N) -> Persp3 { assert!(!::is_zero(&(zfar - znear))); assert!(!::is_zero(&aspect)); Persp3 { aspect: aspect, - fov: fov, + fovy: fovy, znear: znear, zfar: zfar } @@ -45,7 +49,7 @@ impl Persp3 { /// Build a `PerspMat3` representing this projection. pub fn to_persp_mat(&self) -> PerspMat3 { - PerspMat3::new(self.aspect, self.fov, self.znear, self.zfar) + PerspMat3::new(self.aspect, self.fovy, self.znear, self.zfar) } } @@ -66,10 +70,10 @@ impl Persp3 { self.aspect.clone() } - /// Gets the field of view of the view frustrum. + /// Gets the y field of view of the view frustrum. #[inline] - pub fn fov(&self) -> N { - self.fov.clone() + pub fn fovy(&self) -> N { + self.fovy.clone() } /// Gets the near plane offset of the view frustrum. @@ -92,12 +96,12 @@ impl Persp3 { self.aspect = aspect; } - /// Sets the field of view of the view frustrum. + /// Sets the y field of view of the view frustrum. /// /// This method does not build any matrix. #[inline] - pub fn set_fov(&mut self, fov: N) { - self.fov = fov; + pub fn set_fovy(&mut self, fovy: N) { + self.fovy = fovy; } /// Sets the near plane offset of the view frustrum. @@ -132,19 +136,19 @@ impl Persp3 { } impl PerspMat3 { - /// Creates a new persepctive matrix from the aspect ratio, field of view, and near/far planes. - pub fn new(aspect: N, fov: N, znear: N, zfar: N) -> PerspMat3 { + /// Creates a new perspective matrix from the aspect ratio, y field of view, and near/far planes. + pub fn new(aspect: N, fovy: N, znear: N, zfar: N) -> PerspMat3 { assert!(!::is_zero(&(znear - zfar))); assert!(!::is_zero(&aspect)); let mat: Mat4 = ::one(); let mut res = PerspMat3 { mat: mat }; - res.set_fov(fov); + res.set_fovy(fovy); res.set_aspect(aspect); res.set_znear_and_zfar(znear, zfar); res.mat.m44 = ::zero(); - res.mat.m43 = ::one(); + res.mat.m43 = -::one::(); res } @@ -168,12 +172,12 @@ impl PerspMat3 { /// Gets the `width / height` aspect ratio of the view frustrum. #[inline] pub fn aspect(&self) -> N { - -self.mat.m22 / self.mat.m11 + self.mat.m22 / self.mat.m11 } - /// Gets the field of view of the view frustrum. + /// Gets the y field of view of the view frustrum. #[inline] - pub fn fov(&self) -> N { + pub fn fovy(&self) -> N { let _1: N = ::one(); let _2 = _1 + _1; @@ -185,7 +189,7 @@ impl PerspMat3 { pub fn znear(&self) -> N { let _1: N = ::one(); let _2 = _1 + _1; - let ratio = (self.mat.m33 + _1) / (self.mat.m33 - _1); + let ratio = (-self.mat.m33 + _1) / (-self.mat.m33 - _1); self.mat.m34 / (_2 * ratio) - self.mat.m34 / _2 } @@ -195,29 +199,29 @@ impl PerspMat3 { pub fn zfar(&self) -> N { let _1: N = ::one(); let _2 = _1 + _1; - let ratio = (self.mat.m33 + _1) / (self.mat.m33 - _1); + let ratio = (-self.mat.m33 + _1) / (-self.mat.m33 - _1); (self.mat.m34 - ratio * self.mat.m34) / _2 } - // FIXME: add a method to retriev znear and zfar at once ? + // FIXME: add a method to retrieve znear and zfar simultaneously? /// Updates this projection matrix with a new `width / height` aspect ratio of the view /// frustrum. #[inline] pub fn set_aspect(&mut self, aspect: N) { assert!(!::is_zero(&aspect)); - self.mat.m11 = -self.mat.m22 / aspect; + self.mat.m11 = self.mat.m22 / aspect; } - /// Updates this projection with a new field of view of the view frustrum. + /// Updates this projection with a new y field of view of the view frustrum. #[inline] - pub fn set_fov(&mut self, fov: N) { + pub fn set_fovy(&mut self, fovy: N) { let _1: N = ::one(); let _2 = _1 + _1; let old_m22 = self.mat.m22.clone(); - self.mat.m22 = _1 / (fov / _2).tan(); + self.mat.m22 = _1 / (fovy / _2).tan(); self.mat.m11 = self.mat.m11 * (self.mat.m22 / old_m22); } @@ -241,7 +245,7 @@ impl PerspMat3 { let _1: N = ::one(); let _2 = _1 + _1; - self.mat.m33 = -(zfar + znear) / (znear - zfar); + self.mat.m33 = (zfar + znear) / (znear - zfar); self.mat.m34 = zfar * znear * _2 / (znear - zfar); } @@ -249,7 +253,7 @@ impl PerspMat3 { #[inline] pub fn project_pnt(&self, p: &Pnt3) -> Pnt3 { let _1: N = ::one(); - let inv_denom = _1 / p.z; + let inv_denom = -_1 / p.z; Pnt3::new( self.mat.m11 * p.x * inv_denom, self.mat.m22 * p.y * inv_denom, @@ -261,7 +265,7 @@ impl PerspMat3 { #[inline] pub fn project_vec(&self, p: &Vec3) -> Vec3 { let _1: N = ::one(); - let inv_denom = _1 / p.z; + let inv_denom = -_1 / p.z; Vec3::new( self.mat.m11 * p.x * inv_denom, self.mat.m22 * p.y * inv_denom, diff --git a/src/structs/rot.rs b/src/structs/rot.rs index cd1c77ce..40ef54de 100644 --- a/src/structs/rot.rs +++ b/src/structs/rot.rs @@ -218,19 +218,36 @@ impl Rot3 { } } - /// Builds a look-at view matrix with no translational component. + + /// Builds a right-handed look-at view matrix without translation. /// - /// 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. + /// This conforms to the common notion of right handed look-at matrix from the computer + /// graphics community. /// /// # Arguments - /// * dir - The view direction. - /// * up - The vertical direction. The only requirement of this parameter is to not be - /// collinear to `dir`. + /// * eye - The eye position. + /// * target - The target position. + /// * up - A vector approximately aligned with required the vertical axis. The only + /// requirement of this parameter is to not be collinear to `target - eye`. #[inline] - pub fn new_look_at(dir: &Vec3, up: &Vec3) -> Rot3 { + pub fn look_at_rh(dir: &Vec3, up: &Vec3) -> Rot3 { Rot3::new_observer_frame(&(-*dir), up).inv().unwrap() } + + /// Builds a left-handed look-at view matrix without translation. + /// + /// This conforms to the common notion of left handed look-at matrix from the computer + /// graphics community. + /// + /// # Arguments + /// * eye - The eye position. + /// * target - The target position. + /// * up - A vector approximately aligned with required the vertical axis. The only + /// requirement of this parameter is to not be collinear to `target - eye`. + #[inline] + pub fn look_at_lh(dir: &Vec3, up: &Vec3) -> Rot3 { + Rot3::new_observer_frame(&(*dir), up).inv().unwrap() + } } impl> diff --git a/tests/transforms.rs b/tests/transforms.rs index 3b7ec6cb..8db23131 100644 --- a/tests/transforms.rs +++ b/tests/transforms.rs @@ -2,8 +2,8 @@ extern crate nalgebra as na; extern crate rand; use rand::random; -use na::{Pnt2, Pnt3, Vec2, Vec3, Vec1, Rot2, Rot3, Persp3, PerspMat3, Ortho3, OrthoMat3, Iso2, - Iso3, Sim2, Sim3, BaseFloat, Transform}; +use na::{Pnt2, Pnt3, Vec2, Vec3, Vec1, Rot2, Rot3, Persp3, PerspMat3, Ortho3, OrthoMat3, + Iso2, Iso3, Sim2, Sim3, BaseFloat, Transform}; #[test] fn test_rotation2() { @@ -71,25 +71,27 @@ fn test_rot2_angle_between() { #[test] -fn test_look_at_iso3() { +fn test_look_at_rh_iso3() { for _ in 0usize .. 10000 { let eye = random::>(); let target = random::>(); let up = random::>(); - let viewmat = Iso3::new_look_at(&eye, &target, &up); + let viewmat = Iso3::look_at_rh(&eye, &target, &up); - assert_eq!(&(viewmat * eye), &na::orig()); + let origin: Pnt3 = na::orig(); + assert_eq!(&(viewmat * eye), &origin); assert!(na::approx_eq(&na::normalize(&(viewmat * (target - eye))), &-Vec3::z())); } } #[test] -fn test_look_at_rot3() { +fn test_look_at_rh_rot3() { for _ in 0usize .. 10000 { let dir = random::>(); let up = random::>(); - let viewmat = Rot3::new_look_at(&dir, &up); + let viewmat = Rot3::look_at_rh(&dir, &up); + println!("found: {}", viewmat * dir); assert!(na::approx_eq(&na::normalize(&(viewmat * dir)), &-Vec3::z())); } } @@ -124,16 +126,16 @@ fn test_persp() { 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.fovy() == 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.fovy(), &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); + p.set_fovy(0.1); + pm.set_fovy(0.1); assert!(na::approx_eq(&p.to_mat(), pm.as_mat())); p.set_znear(24.0); @@ -149,52 +151,60 @@ fn test_persp() { assert!(na::approx_eq(&p.to_mat(), pm.as_mat())); assert!(p.aspect() == 23.0); - assert!(p.fov() == 0.1); + assert!(p.fovy() == 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.fovy(), &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); + let mut p = Ortho3::new(-0.3, 5.2, -3.9, -1.0, 1.5, 10.0); + let mut pm = OrthoMat3::new(-0.3, 5.2, -3.9, -1.0, 1.5, 10.0); assert!(p.to_mat() == pm.to_mat()); - assert!(p.width() == 42.0); - assert!(p.height() == 0.5); + assert!(p.left() == -0.3); + assert!(p.right() == 5.2); + assert!(p.bottom() == -3.9); + assert!(p.top() == -1.0); 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.left(), &-0.3)); + assert!(na::approx_eq(&pm.right(), &5.2)); + assert!(na::approx_eq(&pm.bottom(), &-3.9)); + assert!(na::approx_eq(&pm.top(), &-1.0)); 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); + p.set_left(0.1); + pm.set_left(0.1); assert!(na::approx_eq(&p.to_mat(), pm.as_mat())); - p.set_znear(24.0); - pm.set_znear(24.0); + p.set_right(10.1); + pm.set_right(10.1); + assert!(na::approx_eq(&p.to_mat(), pm.as_mat())); + + p.set_top(24.0); + pm.set_top(24.0); + assert!(na::approx_eq(&p.to_mat(), pm.as_mat())); + + p.set_bottom(-23.0); + pm.set_bottom(-23.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); + p.set_znear(21.0); + pm.set_znear(21.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.znear() == 21.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.znear(), &21.0)); assert!(na::approx_eq(&pm.zfar(), &61.0)); } From 35ae97dc290e989bc29a32fd09f4436e7845674e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Thu, 31 Mar 2016 21:24:37 +0200 Subject: [PATCH 16/20] Fix benchmarks. --- benches/vec.rs | 90 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 36 deletions(-) diff --git a/benches/vec.rs b/benches/vec.rs index d5ff30c3..2e33fa62 100644 --- a/benches/vec.rs +++ b/benches/vec.rs @@ -1,14 +1,15 @@ #![feature(test)] +#[cfg(feature = "generic_sizes")] +extern crate typenum; + extern crate test; extern crate rand; -extern crate typenum; extern crate nalgebra as na; use rand::{IsaacRng, Rng}; use test::Bencher; -use typenum::{U2, U3, U4}; -use na::{Vec2, Vec3, Vec4, VecN}; +use na::{Vec2, Vec3, Vec4}; use std::ops::{Add, Sub, Mul, Div}; #[path="common/macros.rs"] @@ -17,78 +18,95 @@ mod macros; bench_binop!(_bench_vec2_add_v, Vec2, Vec2, add); bench_binop!(_bench_vec3_add_v, Vec3, Vec3, add); bench_binop!(_bench_vec4_add_v, Vec4, Vec4, add); -bench_binop!(_bench_vecn2_add_v, VecN, VecN, add); -bench_binop!(_bench_vecn3_add_v, VecN, VecN, add); -bench_binop!(_bench_vecn4_add_v, VecN, VecN, add); bench_binop!(_bench_vec2_sub_v, Vec2, Vec2, sub); bench_binop!(_bench_vec3_sub_v, Vec3, Vec3, sub); bench_binop!(_bench_vec4_sub_v, Vec4, Vec4, sub); -bench_binop!(_bench_vecn2_sub_v, VecN, VecN, sub); -bench_binop!(_bench_vecn3_sub_v, VecN, VecN, sub); -bench_binop!(_bench_vecn4_sub_v, VecN, VecN, sub); bench_binop!(_bench_vec2_mul_v, Vec2, Vec2, mul); bench_binop!(_bench_vec3_mul_v, Vec3, Vec3, mul); bench_binop!(_bench_vec4_mul_v, Vec4, Vec4, mul); -bench_binop!(_bench_vecn2_mul_v, VecN, VecN, mul); -bench_binop!(_bench_vecn3_mul_v, VecN, VecN, mul); -bench_binop!(_bench_vecn4_mul_v, VecN, VecN, mul); bench_binop!(_bench_vec2_div_v, Vec2, Vec2, div); bench_binop!(_bench_vec3_div_v, Vec3, Vec3, div); bench_binop!(_bench_vec4_div_v, Vec4, Vec4, div); -bench_binop!(_bench_vecn2_div_v, VecN, VecN, div); -bench_binop!(_bench_vecn3_div_v, VecN, VecN, div); -bench_binop!(_bench_vecn4_div_v, VecN, VecN, div); bench_binop!(_bench_vec2_add_s, Vec2, f32, add); bench_binop!(_bench_vec3_add_s, Vec3, f32, add); bench_binop!(_bench_vec4_add_s, Vec4, f32, add); -bench_binop!(_bench_vecn2_add_s, VecN, f32, add); -bench_binop!(_bench_vecn3_add_s, VecN, f32, add); -bench_binop!(_bench_vecn4_add_s, VecN, f32, add); bench_binop!(_bench_vec2_sub_s, Vec2, f32, sub); bench_binop!(_bench_vec3_sub_s, Vec3, f32, sub); bench_binop!(_bench_vec4_sub_s, Vec4, f32, sub); -bench_binop!(_bench_vecn2_sub_s, VecN, f32, sub); -bench_binop!(_bench_vecn3_sub_s, VecN, f32, sub); -bench_binop!(_bench_vecn4_sub_s, VecN, f32, sub); bench_binop!(_bench_vec2_mul_s, Vec2, f32, mul); bench_binop!(_bench_vec3_mul_s, Vec3, f32, mul); bench_binop!(_bench_vec4_mul_s, Vec4, f32, mul); -bench_binop!(_bench_vecn2_mul_s, VecN, f32, mul); -bench_binop!(_bench_vecn3_mul_s, VecN, f32, mul); -bench_binop!(_bench_vecn4_mul_s, VecN, f32, mul); bench_binop!(_bench_vec2_div_s, Vec2, f32, div); bench_binop!(_bench_vec3_div_s, Vec3, f32, div); bench_binop!(_bench_vec4_div_s, Vec4, f32, div); -bench_binop!(_bench_vecn2_div_s, VecN, f32, div); -bench_binop!(_bench_vecn3_div_s, VecN, f32, div); -bench_binop!(_bench_vecn4_div_s, VecN, f32, div); bench_binop_na!(_bench_vec2_dot, Vec2, Vec2, dot); bench_binop_na!(_bench_vec3_dot, Vec3, Vec3, dot); bench_binop_na!(_bench_vec4_dot, Vec4, Vec4, dot); -bench_binop_na!(_bench_vecn2_dot, VecN, VecN, dot); -bench_binop_na!(_bench_vecn3_dot, VecN, VecN, dot); -bench_binop_na!(_bench_vecn4_dot, VecN, VecN, dot); bench_binop_na!(_bench_vec3_cross, Vec3, Vec3, cross); bench_unop!(_bench_vec2_norm, Vec2, norm); bench_unop!(_bench_vec3_norm, Vec3, norm); bench_unop!(_bench_vec4_norm, Vec4, norm); -bench_unop!(_bench_vecn2_norm, VecN, norm); -bench_unop!(_bench_vecn3_norm, VecN, norm); -bench_unop!(_bench_vecn4_norm, VecN, norm); bench_unop!(_bench_vec2_normalize, Vec2, normalize); bench_unop!(_bench_vec3_normalize, Vec3, normalize); bench_unop!(_bench_vec4_normalize, Vec4, normalize); -bench_unop!(_bench_vecn2_normalize, VecN, normalize); -bench_unop!(_bench_vecn3_normalize, VecN, normalize); -bench_unop!(_bench_vecn4_normalize, VecN, normalize); + +#[cfg(feature = "generic_sizes")] +mod bench_vecn { + use typenum::{U2, U3, U4}; + use na::VecN; + + bench_binop!(_bench_vecn2_add_v, VecN, VecN, add); + bench_binop!(_bench_vecn3_add_v, VecN, VecN, add); + bench_binop!(_bench_vecn4_add_v, VecN, VecN, add); + + bench_binop!(_bench_vecn2_sub_v, VecN, VecN, sub); + bench_binop!(_bench_vecn3_sub_v, VecN, VecN, sub); + bench_binop!(_bench_vecn4_sub_v, VecN, VecN, sub); + + bench_binop!(_bench_vecn2_mul_v, VecN, VecN, mul); + bench_binop!(_bench_vecn3_mul_v, VecN, VecN, mul); + bench_binop!(_bench_vecn4_mul_v, VecN, VecN, mul); + + bench_binop!(_bench_vecn2_div_v, VecN, VecN, div); + bench_binop!(_bench_vecn3_div_v, VecN, VecN, div); + bench_binop!(_bench_vecn4_div_v, VecN, VecN, div); + + bench_binop!(_bench_vecn2_add_s, VecN, f32, add); + bench_binop!(_bench_vecn3_add_s, VecN, f32, add); + bench_binop!(_bench_vecn4_add_s, VecN, f32, add); + + bench_binop!(_bench_vecn2_sub_s, VecN, f32, sub); + bench_binop!(_bench_vecn3_sub_s, VecN, f32, sub); + bench_binop!(_bench_vecn4_sub_s, VecN, f32, sub); + + bench_binop!(_bench_vecn2_mul_s, VecN, f32, mul); + bench_binop!(_bench_vecn3_mul_s, VecN, f32, mul); + bench_binop!(_bench_vecn4_mul_s, VecN, f32, mul); + + bench_binop!(_bench_vecn2_div_s, VecN, f32, div); + bench_binop!(_bench_vecn3_div_s, VecN, f32, div); + bench_binop!(_bench_vecn4_div_s, VecN, f32, div); + + bench_binop_na!(_bench_vecn2_dot, VecN, VecN, dot); + bench_binop_na!(_bench_vecn3_dot, VecN, VecN, dot); + bench_binop_na!(_bench_vecn4_dot, VecN, VecN, dot); + + bench_unop!(_bench_vecn2_norm, VecN, norm); + bench_unop!(_bench_vecn3_norm, VecN, norm); + bench_unop!(_bench_vecn4_norm, VecN, norm); + + bench_unop!(_bench_vecn2_normalize, VecN, normalize); + bench_unop!(_bench_vecn3_normalize, VecN, normalize); + bench_unop!(_bench_vecn4_normalize, VecN, normalize); +} From 20bb99c3127cc49cdad7677dd68ee143e72190a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Thu, 31 Mar 2016 21:30:23 +0200 Subject: [PATCH 17/20] Minor documentation fixes. Fix #174. --- src/structs/dmat.rs | 2 +- src/structs/dvec.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/structs/dmat.rs b/src/structs/dmat.rs index 36b3df4b..b99b25f1 100644 --- a/src/structs/dmat.rs +++ b/src/structs/dmat.rs @@ -108,7 +108,7 @@ impl DMat { } impl DMat { - /// Builds a matrix using an initialization function. + /// Builds a matrix filled with the results of a function applied to each of its component coordinates. #[inline(always)] pub fn from_fn N>(nrows: usize, ncols: usize, mut f: F) -> DMat { DMat { diff --git a/src/structs/dvec.rs b/src/structs/dvec.rs index e40e54aa..bc067e47 100644 --- a/src/structs/dvec.rs +++ b/src/structs/dvec.rs @@ -54,7 +54,7 @@ impl DVec { } impl DVec { - /// Builds a vector filled with the result of a function. + /// Builds a vector filled with the results of a function applied to each of its component coordinates. #[inline(always)] pub fn from_fn N>(dim: usize, mut f: F) -> DVec { DVec { at: (0..dim).map(|i| f(i)).collect() } From 2fa7ef0e00dd63cbde4fb958172b6eb8d6fa526a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Thu, 31 Mar 2016 21:31:01 +0200 Subject: [PATCH 18/20] Update the changelog. --- CHANGELOG | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index a1487f88..f7ce5156 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,20 +1,32 @@ # Change Log All notable changes to `nalgebra`, starting with the version 0.6.0 will be -documented in this file (sorry I didn't maintain one before). +documented here. This project adheres to [Semantic Versioning](http://semver.org/). -## [unreleased] [0.6.0] +## [0.6.0] +**Announcement:** a users forum has been created for `nalgebra`, `ncollide`, and `nphysics`. See +you [there](http://users.nphysics.org)! + ### Added - * (feature=generic_sizes): Dependency to [generic-array](https://crates.io/crates/generic-array) - * (feature=ganedic_sizes): Staticly sized vectors with user-defined sizes: `VecN`. + * Dependency to [generic-array](https://crates.io/crates/generic-array). Feature-gated: requires + `features="generic_sizes"`. + * Staticly sized vectors with user-defined sizes: `VecN`. Feature-gated: requires + `features="generic_sizes"`. * Similarity transformations (an uniform scale followed by a rotation followed by a translation): `Sim2`, `Sim3`. ### Removed - * Zero-sized elements `Vec0`, `Pnt0`. - * Incomplete 4-dimensional transformations `Rot4` and `Iso4`. + * Removed zero-sized elements `Vec0`, `Pnt0`. + * Removed 4-dimensional transformations `Rot4` and `Iso4` (which had an implementation to incomplete to be useful). ### Modified * Vectors are now multipliable à isometries. This will result into a pure rotation (this is how vectors differ from point semantically: they design directions so they are not translatable). + * `{Iso3, Rot3}::look_at` reimplemented and renamed to `::look_at_rh` and `::look_at_lh` to agree + with the computer graphics community (in particuliar, the GLM library). Use the `::look_at_rh` + variant to build a view matrix that + may be successfully used with `Persp` and `Ortho`. + * The old `{Iso3, Rot3}::look_at` implementations are now called `::new_observer_frame`. + * Rename every `fov` on `Persp` to `fovy. + * Fixed the perspective and orthographic projection matrices. From 0dc3b91abd3ca8fb692d44247f374aa5e7407a34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Thu, 31 Mar 2016 21:44:42 +0200 Subject: [PATCH 19/20] Fix Ortho when the feature `arbitrary` is enabled. --- src/structs/ortho.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/structs/ortho.rs b/src/structs/ortho.rs index 1bff6367..f5ac882f 100644 --- a/src/structs/ortho.rs +++ b/src/structs/ortho.rs @@ -67,7 +67,7 @@ impl Arbitrary for Ortho3 { let top = reject(g, |x: &N| *x > bottom); let znear = Arbitrary::arbitrary(g); let zfar = reject(g, |x: &N| *x > znear); - Ortho3::new(width, height, znear, zfar) + Ortho3::new(left, right, bottom, top, znear, zfar) } } From a52ecc73b136b2463dce57c82f0197dda45bdd3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crozet?= Date: Thu, 31 Mar 2016 21:46:01 +0200 Subject: [PATCH 20/20] Minor CHANGELOG fix. --- CHANGELOG | 18 +++++++++--------- Makefile | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index f7ce5156..c3a202fc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -9,24 +9,24 @@ This project adheres to [Semantic Versioning](http://semver.org/). you [there](http://users.nphysics.org)! ### Added - * Dependency to [generic-array](https://crates.io/crates/generic-array). Feature-gated: requires - `features="generic_sizes"`. - * Staticly sized vectors with user-defined sizes: `VecN`. Feature-gated: requires - `features="generic_sizes"`. - * Similarity transformations (an uniform scale followed by a rotation followed - by a translation): `Sim2`, `Sim3`. + * Added a dependency to [generic-array](https://crates.io/crates/generic-array). Feature-gated: + requires `features="generic_sizes"`. + * Added statically sized vectors with user-defined sizes: `VecN`. Feature-gated: requires + `features="generic_sizes"`. + * Added similarity transformations (an uniform scale followed by a rotation followed by a + translation): `Sim2`, `Sim3`. ### Removed * Removed zero-sized elements `Vec0`, `Pnt0`. * Removed 4-dimensional transformations `Rot4` and `Iso4` (which had an implementation to incomplete to be useful). ### Modified - * Vectors are now multipliable à isometries. This will result into a pure rotation (this is how + * Vectors are now multipliable with isometries. This will result into a pure rotation (this is how vectors differ from point semantically: they design directions so they are not translatable). * `{Iso3, Rot3}::look_at` reimplemented and renamed to `::look_at_rh` and `::look_at_lh` to agree - with the computer graphics community (in particuliar, the GLM library). Use the `::look_at_rh` + with the computer graphics community (in particular, the GLM library). Use the `::look_at_rh` variant to build a view matrix that may be successfully used with `Persp` and `Ortho`. * The old `{Iso3, Rot3}::look_at` implementations are now called `::new_observer_frame`. - * Rename every `fov` on `Persp` to `fovy. + * Rename every `fov` on `Persp` to `fovy`. * Fixed the perspective and orthographic projection matrices. diff --git a/Makefile b/Makefile index fd047981..31318000 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ tmp=_git_distcheck all: - cargo build --release + cargo build --release --features arbitrary test: cargo test