diff --git a/README.md b/README.md index a0f072b7..ba40c707 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,30 @@ # nalgebra -**nalgebra** is a linear algebra library written for Rust targeting: +**nalgebra** is a low-dimensional linear algebra library written for Rust targeting: -* general-purpose linear algebra (still misses a lot of features…). +* 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). ## Using **nalgebra** -All the functionalities of **nalgebra** are grouped in one place: the `na` module. +All the functionalities of **nalgebra** are grouped in one place: the root `nalgebra::` module. This module re-exports everything and includes free functions for all traits methods doing out-of-place modifications. * You can import the whole prelude using: -``` -use nalgebra::na::*; +```.ignore +use nalgebra::*; ``` The preferred way to use **nalgebra** is to import types and traits explicitly, and call free-functions using the `na::` prefix: ```.rust -extern crate nalgebra; -use nalgebra::na::{Vec3, Rot3, Rotation}; -use nalgebra::na; +extern crate "nalgebra" as na; +use na::{Vec3, Rot3, Rotation}; fn main() { let a = Vec3::new(1.0f64, 1.0, 1.0); @@ -38,35 +37,36 @@ fn main() { ``` ## Features -**nalgebra** is meant to be a general-purpose linear algebra library (but is very far from that…), -and keeps an optimized set of tools for computational graphics and physics. Those features include: +**nalgebra** is meant to be a general-purpose, low-dimensional, linear algebra +library, and keeps an optimized set of tools for computational graphics and +physics. Those features include: * Vectors with static sizes: `Vec0`, `Vec1`, `Vec2`, `Vec3`, `Vec4`, `Vec5`, `Vec6`. +* 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`. * Isometries: `Iso2`, `Iso3`, `Iso4`. +* 3D projections for computer graphics: `Persp3`, `PerspMat3`, `Ortho3`, `OrthoMat3`. * Dynamically sized vector: `DVec`. * Dynamically sized (square or rectangular) matrix: `DMat`. * A few methods for data analysis: `Cov`, `Mean`. -* Some matrix factorization algorithms: QR decomposition, ... * Almost one trait per functionality: useful for generic programming. * Operator overloading using the double trait dispatch [trick](http://smallcultfollowing.com/babysteps/blog/2012/10/04/refining-traits-slash-impls/). For example, the following works: ```rust -extern crate nalgebra; -use nalgebra::na::{Vec3, Mat3}; -use nalgebra::na; +extern crate "nalgebra" as na; +use na::{Vec3, Mat3}; fn main() { let v: Vec3 = na::zero(); let m: Mat3 = na::one(); - let _ = m * v; // matrix-vector multiplication. - let _ = v * m; // vector-matrix multiplication. - let _ = m * m; // matrix-matrix multiplication. - let _ = v * 2.0; // vector-scalar multiplication. + let _ = m * v; // matrix-vector multiplication. + let _ = v * m; // vector-matrix multiplication. + let _ = m * m; // matrix-matrix multiplication. + let _ = v * 2.0f64; // vector-scalar multiplication. } ``` @@ -76,7 +76,7 @@ and the official package manager: [cargo](https://github.com/rust-lang/cargo). Simply add the following to your `Cargo.toml` file: -``` +```.ignore [dependencies.nalgebra] git = "https://github.com/sebcrozet/nalgebra" ``` @@ -86,6 +86,7 @@ git = "https://github.com/sebcrozet/nalgebra" Here are some projects using **nalgebra**. Feel free to add your project to this list if you happen to use **nalgebra**! -* [nphysics](http://nphysics-dev.org): a real-time physics engine. -* [ncollide](http://ncollide.org): a collision detection library. -* [kiss3d](http://kiss3d.org): a minimalistic graphics engine. +* [nphysics](https://github.com/sebcrozet/nphysics): a real-time physics engine. +* [ncollide](https://github.com/sebcrozet/ncollide): a collision detection library. +* [kiss3d](https://github.com/sebcrozet/kiss3d): a minimalistic graphics engine. +* [nrays](https://github.com/sebcrozet/nrays): a ray tracer. diff --git a/src/lib.rs b/src/lib.rs index 7bfc1e53..d0903415 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ /*! # nalgebra -**nalgebra** is a linear algebra library written for Rust targeting: +**nalgebra** is a low-dimensional linear algebra library written for Rust targeting: * general-purpose linear algebra (still lacks a lot of features…). * real time computer graphics. @@ -38,14 +38,15 @@ fn main() { ``` ## Features -**nalgebra** is meant to be a general-purpose linear algebra library (but is very far from that…), -and keeps an optimized set of tools for computational graphics and physics. Those features include: +**nalgebra** is meant to be a general-purpose, low-dimensional, linear algebra library, and keeps +an optimized set of tools for computational graphics and physics. Those features include: * Vectors with static sizes: `Vec0`, `Vec1`, `Vec2`, `Vec3`, `Vec4`, `Vec5`, `Vec6`. * 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`. * Isometries: `Iso2`, `Iso3`, `Iso4`. +* 3D projections for computer graphics: `Persp3`, `PerspMat3`, `Ortho3`, `OrthoMat3`. * Dynamically sized vector: `DVec`. * Dynamically sized (square or rectangular) matrix: `DMat`. * A few methods for data analysis: `Cov`, `Mean`. @@ -150,7 +151,6 @@ pub use traits::{ PartialOrdering, PntAsVec, PntExt, - Projector, RMul, Rotate, Rotation, RotationMatrix, RotationWithTranslation, Row, @@ -174,7 +174,9 @@ pub use structs::{ Mat5, Mat6, Rot2, Rot3, Rot4, Vec0, Vec1, Vec2, Vec3, Vec4, Vec5, Vec6, - Pnt0, Pnt1, Pnt2, Pnt3, Pnt4, Pnt5, Pnt6 + Pnt0, Pnt1, Pnt2, Pnt3, Pnt4, Pnt5, Pnt6, + Persp3, PerspMat3, + Ortho3, OrthoMat3 }; pub use linalg::{ @@ -383,6 +385,7 @@ pub fn sqdist, V: Norm>(a: &P, b: &P) -> N { */ /// Computes a projection matrix given the frustrum near plane width, height, the field of /// view, and the distance to the clipping planes (`znear` and `zfar`). +#[deprecated = "Use `Persp3::new(width / height, fov, znear, zfar).as_mat()` instead"] pub fn perspective3d + Zero + One>(width: N, height: N, fov: N, znear: N, zfar: N) -> Mat4 { let aspect = width / height; diff --git a/src/structs/mod.rs b/src/structs/mod.rs index b5402041..091da160 100644 --- a/src/structs/mod.rs +++ b/src/structs/mod.rs @@ -7,6 +7,8 @@ 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::persp::{Persp3, PerspMat3}; +pub use self::ortho::{Ortho3, OrthoMat3}; pub use self::vec::{Vec1MulRhs, Vec2MulRhs, Vec3MulRhs, Vec4MulRhs, Vec5MulRhs, Vec6MulRhs, Vec1DivRhs, Vec2DivRhs, Vec3DivRhs, Vec4DivRhs, Vec5DivRhs, Vec6DivRhs, @@ -35,6 +37,8 @@ mod rot_macros; mod rot; mod iso_macros; mod iso; +mod persp; +mod ortho; // specialization for some 1d, 2d and 3d operations #[doc(hidden)] diff --git a/src/structs/ortho.rs b/src/structs/ortho.rs new file mode 100644 index 00000000..aae2fbef --- /dev/null +++ b/src/structs/ortho.rs @@ -0,0 +1,234 @@ +use std::num::{Zero, One}; +use std::num; +use structs::{Pnt3, Vec3, Mat4}; + +/// A 3D orthographic projection stored without any matrix. +/// +/// Reading or modifying its individual properties is cheap but applying the transformation is costly. +#[deriving(Eq, PartialEq, Encodable, Decodable, Clone, Show)] +pub struct Ortho3 { + width: N, + height: 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. +#[deriving(Eq, PartialEq, Encodable, Decodable, Clone, Show)] +pub struct OrthoMat3 { + mat: Mat4 +} + +impl Ortho3 { + /// Creates a new 3D orthographic projection. + pub fn new(width: N, height: N, znear: N, zfar: N) -> Ortho3 { + assert!(!(zfar - znear).is_zero()); + assert!(!width.is_zero()); + assert!(!height.is_zero()); + + Ortho3 { + width: width, + height: height, + znear: znear, + zfar: zfar + } + } + + /// Builds a 4D projection matrix (using homogeneous coordinates) for this projection. + pub fn to_mat(&self) -> Mat4 { + self.to_persp_mat().mat + } + + /// Build a `OrthoMat3` representing this projection. + pub fn to_persp_mat(&self) -> OrthoMat3 { + OrthoMat3::new(self.width, self.height, self.znear, self.zfar) + } +} + +impl Ortho3 { + /// The width of the view cuboid. + #[inline] + pub fn width(&self) -> N { + self.width.clone() + } + + /// The height of the view cuboid. + #[inline] + pub fn height(&self) -> N { + self.height.clone() + } + + /// The near plane offset of the view cuboid. + #[inline] + pub fn znear(&self) -> N { + self.znear.clone() + } + + /// The far plane offset of the view cuboid. + #[inline] + pub fn zfar(&self) -> N { + self.zfar.clone() + } + + /// Sets the width of the view cuboid. + #[inline] + pub fn set_width(&mut self, width: N) { + self.width = width + } + + /// Sets the height of the view cuboid. + #[inline] + pub fn set_height(&mut self, height: N) { + self.height = height + } + + /// Sets the near plane offset of the view cuboid. + #[inline] + pub fn set_znear(&mut self, znear: N) { + self.znear = znear + } + + /// Sets the far plane offset of the view cuboid. + #[inline] + pub fn set_zfar(&mut self, zfar: N) { + self.zfar = zfar + } + + /// Projects a point. + #[inline] + pub fn project_pnt(&self, p: &Pnt3) -> Pnt3 { + // FIXME: optimize that + self.to_persp_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) + } +} + +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!(!(zfar - znear).is_zero()); + assert!(!width.is_zero()); + assert!(!height.is_zero()); + + let mat: Mat4 = One::one(); + + let mut res = OrthoMat3 { mat: mat }; + res.set_width(width); + res.set_height(height); + res.set_znear_and_zfar(znear, zfar); + + res + } + + /// Creates a new orthographic matrix from a 4D matrix. + /// + /// This is unsafe because the input matrix is not checked to be a orthographic projection. + #[inline] + pub unsafe fn new_with_mat(mat: Mat4) -> OrthoMat3 { + OrthoMat3 { + mat: mat + } + } + + /// Returns a reference to the 4D matrix (using homogeneous coordinates) of this projection. + #[inline] + pub fn as_mat<'a>(&'a self) -> &'a Mat4 { + &self.mat + } + + /// The width of the view cuboid. + #[inline] + pub fn width(&self) -> N { + num::cast::(2.0).unwrap() / self.mat.m11 + } + + /// The height of the view cuboid. + #[inline] + pub fn height(&self) -> N { + num::cast::(2.0).unwrap() / self.mat.m22 + } + + /// The near plane offset of the view cuboid. + #[inline] + pub fn znear(&self) -> N { + (self.mat.m34 + One::one()) / self.mat.m33 + } + + /// The far plane offset of the view cuboid. + #[inline] + pub fn zfar(&self) -> N { + (self.mat.m34 - One::one()) / self.mat.m33 + } + + /// Sets the width of the view cuboid. + #[inline] + pub fn set_width(&mut self, width: N) { + assert!(!width.is_zero()); + self.mat.m11 = num::cast::(2.0).unwrap() / width; + } + + /// Sets the height of the view cuboid. + #[inline] + pub fn set_height(&mut self, height: N) { + assert!(!height.is_zero()); + self.mat.m22 = num::cast::(2.0).unwrap() / height; + } + + /// Sets the near plane offset of the view cuboid. + #[inline] + pub fn set_znear(&mut self, znear: N) { + let zfar = self.zfar(); + self.set_znear_and_zfar(znear, zfar); + } + + /// Sets the far plane offset of the view cuboid. + #[inline] + pub fn set_zfar(&mut self, zfar: N) { + let znear = self.znear(); + self.set_znear_and_zfar(znear, zfar); + } + + /// Sets the near and far plane offsets of the view cuboid. + #[inline] + pub fn set_znear_and_zfar(&mut self, znear: N, zfar: N) { + assert!(!(zfar - znear).is_zero()); + self.mat.m33 = -num::cast::(2.0).unwrap() / (zfar - znear); + self.mat.m34 = -(zfar + znear) / (zfar - znear); + } + + /// Projects a point. + #[inline] + pub fn project_pnt(&self, p: &Pnt3) -> Pnt3 { + Pnt3::new( + self.mat.m11 * p.x, + self.mat.m22 * p.y, + self.mat.m33 * p.z + self.mat.m34 + ) + } + + /// Projects a vector. + #[inline] + pub fn project_vec(&self, p: &Vec3) -> Vec3 { + Vec3::new( + self.mat.m11 * p.x, + self.mat.m22 * p.y, + self.mat.m33 * p.z + ) + } +} + +impl OrthoMat3 { + /// Returns the 4D matrix (using homogeneous coordinates) of this projection. + #[inline] + pub fn to_mat<'a>(&'a self) -> Mat4 { + self.mat.clone() + } +} diff --git a/src/structs/persp.rs b/src/structs/persp.rs new file mode 100644 index 00000000..618d5707 --- /dev/null +++ b/src/structs/persp.rs @@ -0,0 +1,265 @@ +use std::num::{Zero, One}; +use structs::{Pnt3, Vec3, Mat4}; + +/// A 3D perspective projection stored without any matrix. +/// +/// Reading or modifying its individual properties is cheap but applying the transformation is costly. +#[deriving(Eq, PartialEq, Encodable, Decodable, Clone, Show)] +pub struct Persp3 { + aspect: N, + fov: 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. +#[deriving(Eq, PartialEq, Encodable, Decodable, Clone, Show)] +pub struct PerspMat3 { + mat: Mat4 +} + +impl Persp3 { + /// Creates a new 3D perspective projection. + pub fn new(aspect: N, fov: N, znear: N, zfar: N) -> Persp3 { + assert!(!(zfar - znear).is_zero()); + assert!(!aspect.is_zero()); + + Persp3 { + aspect: aspect, + fov: fov, + znear: znear, + zfar: zfar + } + } + + /// Builds a 4D projection matrix (using homogeneous coordinates) for this projection. + pub fn to_mat(&self) -> Mat4 { + self.to_persp_mat().mat + } + + /// Build a `PerspMat3` representing this projection. + pub fn to_persp_mat(&self) -> PerspMat3 { + PerspMat3::new(self.aspect, self.fov, self.znear, self.zfar) + } +} + +impl Persp3 { + /// Gets the `width / height` aspect ratio. + #[inline] + pub fn aspect(&self) -> N { + self.aspect.clone() + } + + /// Gets the field of view of the view frustrum. + #[inline] + pub fn fov(&self) -> N { + self.fov.clone() + } + + /// Gets the near plane offset of the view frustrum. + #[inline] + pub fn znear(&self) -> N { + self.znear.clone() + } + + /// Gets the far plane offset of the view frustrum. + #[inline] + pub fn zfar(&self) -> N { + self.zfar.clone() + } + + /// Sets the `width / height` aspect ratio of the view frustrum. + /// + /// This method does not build any matrix. + #[inline] + pub fn set_aspect(&mut self, aspect: N) { + self.aspect = aspect; + } + + /// Sets the 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; + } + + /// Sets the near plane offset of the view frustrum. + /// + /// This method does not build any matrix. + #[inline] + pub fn set_znear(&mut self, znear: N) { + self.znear = znear; + } + + /// Sets the far plane offset of the view frustrum. + /// + /// This method does not build any matrix. + #[inline] + pub fn set_zfar(&mut self, zfar: N) { + self.zfar = zfar; + } + + /// Projects a point. + #[inline] + pub fn project_pnt(&self, p: &Pnt3) -> Pnt3 { + // FIXME: optimize that + self.to_persp_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) + } +} + +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 { + assert!(!(znear - zfar).is_zero()); + assert!(!aspect.is_zero()); + + let mat: Mat4 = One::one(); + + let mut res = PerspMat3 { mat: mat }; + res.set_fov(fov); + res.set_aspect(aspect); + res.set_znear_and_zfar(znear, zfar); + res.mat.m44 = Zero::zero(); + res.mat.m43 = One::one(); + + res + } + + /// Creates a new perspective projection matrix from a 4D matrix. + /// + /// This is unsafe because the input matrix is not checked to be a perspective projection. + #[inline] + pub unsafe fn new_with_mat(mat: Mat4) -> PerspMat3 { + PerspMat3 { + mat: mat + } + } + + /// Returns a reference to the 4D matrix (using homogeneous coordinates) of this projection. + #[inline] + pub fn as_mat<'a>(&'a self) -> &'a Mat4 { + &self.mat + } + + /// Gets the `width / height` aspect ratio of the view frustrum. + #[inline] + pub fn aspect(&self) -> N { + -self.mat.m22 / self.mat.m11 + } + + /// Gets the field of view of the view frustrum. + #[inline] + pub fn fov(&self) -> N { + let _1: N = One::one(); + let _2 = _1 + _1; + + (_1 / self.mat.m22).atan() * _2 + } + + /// Gets the near plane offset of the view frustrum. + #[inline] + pub fn znear(&self) -> N { + let _1: N = One::one(); + let _2 = _1 + _1; + let ratio = (self.mat.m33 + _1) / (self.mat.m33 - _1); + + self.mat.m34 / (_2 * ratio) - self.mat.m34 / _2 + } + + /// Gets the far plane offset of the view frustrum. + #[inline] + pub fn zfar(&self) -> N { + let _1: N = One::one(); + let _2 = _1 + _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 ? + + /// 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!(!aspect.is_zero()); + self.mat.m11 = -self.mat.m22 / aspect; + } + + /// Updates this projection with a new field of view of the view frustrum. + #[inline] + pub fn set_fov(&mut self, fov: N) { + let _1: N = One::one(); + let _2 = _1 + _1; + + let old_m22 = self.mat.m22.clone(); + self.mat.m22 = _1 / (fov / _2).tan(); + self.mat.m11 = self.mat.m11 * (self.mat.m22 / old_m22); + } + + /// Updates this projection matrix with a new near plane offset of the view frustrum. + #[inline] + pub fn set_znear(&mut self, znear: N) { + let zfar = self.zfar(); + self.set_znear_and_zfar(znear, zfar); + } + + /// Updates this projection matrix with a new far plane offset of the view frustrum. + #[inline] + pub fn set_zfar(&mut self, zfar: N) { + let znear = self.znear(); + self.set_znear_and_zfar(znear, zfar); + } + + /// Updates this projection matrix with new near and far plane offsets of the view frustrum. + #[inline] + pub fn set_znear_and_zfar(&mut self, znear: N, zfar: N) { + let _1: N = One::one(); + let _2 = _1 + _1; + + self.mat.m33 = -(zfar + znear) / (znear - zfar); + self.mat.m34 = zfar * znear * _2 / (znear - zfar); + } + + /// Projects a point. + #[inline] + pub fn project_pnt(&self, p: &Pnt3) -> Pnt3 { + let _1: N = One::one(); + let inv_denom = _1 / p.z; + Pnt3::new( + self.mat.m11 * p.x * inv_denom, + self.mat.m22 * p.y * inv_denom, + (self.mat.m33 * p.z + self.mat.m34) * inv_denom + ) + } + + /// Projects a vector. + #[inline] + pub fn project_vec(&self, p: &Vec3) -> Vec3 { + let _1: N = One::one(); + let inv_denom = _1 / p.z; + Vec3::new( + self.mat.m11 * p.x * inv_denom, + self.mat.m22 * p.y * inv_denom, + self.mat.m33 + ) + } +} + +impl PerspMat3 { + /// Returns the 4D matrix (using homogeneous coordinates) of this projection. + #[inline] + pub fn to_mat<'a>(&'a self) -> Mat4 { + self.mat.clone() + } +} diff --git a/src/traits/geometry.rs b/src/traits/geometry.rs index c8c6a214..afbc6421 100644 --- a/src/traits/geometry.rs +++ b/src/traits/geometry.rs @@ -276,12 +276,3 @@ pub trait Orig { /// Returns true if this points is exactly the trivial origin. fn is_orig(&self) -> bool; } - -/// Trait implemented by projectors. -// XXX: Vout should be an associated type instead of a type parameter. -pub trait Projector { - /// Projects an element of a vector or affine space to a subspace. - /// - /// This must be an indempotent operaton. - fn project(&self, &Vin) -> Vout; -} diff --git a/src/traits/mod.rs b/src/traits/mod.rs index 59b8e7ca..c4c60311 100644 --- a/src/traits/mod.rs +++ b/src/traits/mod.rs @@ -1,9 +1,8 @@ //! Mathematical traits. pub use self::geometry::{AbsoluteRotate, Cross, CrossMatrix, Dot, FromHomogeneous, Norm, Orig, - Projector, Rotate, Rotation, RotationMatrix, RotationWithTranslation, - ToHomogeneous, Transform, Transformation, Translate, Translation, - UniformSphereSample}; + Rotate, Rotation, RotationMatrix, RotationWithTranslation, ToHomogeneous, + Transform, Transformation, Translate, Translation, UniformSphereSample}; pub use self::structure::{FloatVec, FloatVecExt, FloatPnt, FloatPntExt, Basis, Cast, Col, Dim, Indexable, Iterable, IterableMut, Mat, Row, AnyVec, VecExt, AnyPnt, diff --git a/tests/mat.rs b/tests/mat.rs index 8526510c..dc0ed56b 100644 --- a/tests/mat.rs +++ b/tests/mat.rs @@ -6,7 +6,8 @@ extern crate "nalgebra" as na; use std::num::{Float, abs}; use std::rand::random; use std::cmp::{min, max}; -use na::{Vec1, Vec3, Mat1, Mat2, Mat3, Mat4, Mat5, Mat6, Rot3, DMat, DVec, Indexable, Row, Col}; +use na::{Vec1, Vec3, Mat1, Mat2, Mat3, Mat4, Mat5, Mat6, Rot3, Persp3, PerspMat3, Ortho3, OrthoMat3, + DMat, DVec, Indexable, Row, Col}; macro_rules! test_inv_mat_impl( ($t: ty) => ( @@ -296,7 +297,7 @@ fn test_qr_mat6() { test_qr_impl!(Mat6); } -// NOTE: deactivated untile we get a better convergence rate. +// NOTE: deactivated until we get a better convergence rate. // #[test] // fn test_eigen_qr_mat1() { // test_eigen_qr_impl!(Mat1); @@ -349,3 +350,83 @@ fn test_row_3() { assert!(second_row == Vec3::new(3.0, 4.0, 5.0)); assert!(second_col == Vec3::new(1.0, 4.0, 7.0)); } + +#[test] +fn test_persp() { + let mut p = Persp3::new(42.0f64, 0.5, 1.5, 10.0); + let mut pm = PerspMat3::new(42.0f64, 0.5, 1.5, 10.0); + assert!(p.to_mat() == pm.to_mat()); + assert!(p.aspect() == 42.0); + assert!(p.fov() == 0.5); + assert!(p.znear() == 1.5); + assert!(p.zfar() == 10.0); + assert!(na::approx_eq(&pm.aspect(), &42.0)); + assert!(na::approx_eq(&pm.fov(), &0.5)); + assert!(na::approx_eq(&pm.znear(), &1.5)); + assert!(na::approx_eq(&pm.zfar(), &10.0)); + + p.set_fov(0.1); + pm.set_fov(0.1); + assert!(na::approx_eq(&p.to_mat(), pm.as_mat())); + + p.set_znear(24.0); + pm.set_znear(24.0); + assert!(na::approx_eq(&p.to_mat(), pm.as_mat())); + + p.set_zfar(61.0); + pm.set_zfar(61.0); + assert!(na::approx_eq(&p.to_mat(), pm.as_mat())); + + p.set_aspect(23.0); + pm.set_aspect(23.0); + assert!(na::approx_eq(&p.to_mat(), pm.as_mat())); + + assert!(p.aspect() == 23.0); + assert!(p.fov() == 0.1); + assert!(p.znear() == 24.0); + assert!(p.zfar() == 61.0); + assert!(na::approx_eq(&pm.aspect(), &23.0)); + assert!(na::approx_eq(&pm.fov(), &0.1)); + assert!(na::approx_eq(&pm.znear(), &24.0)); + assert!(na::approx_eq(&pm.zfar(), &61.0)); +} + +#[test] +fn test_ortho() { + let mut p = Ortho3::new(42.0f64, 0.5, 1.5, 10.0); + let mut pm = OrthoMat3::new(42.0f64, 0.5, 1.5, 10.0); + assert!(p.to_mat() == pm.to_mat()); + assert!(p.width() == 42.0); + assert!(p.height() == 0.5); + assert!(p.znear() == 1.5); + assert!(p.zfar() == 10.0); + assert!(na::approx_eq(&pm.width(), &42.0)); + assert!(na::approx_eq(&pm.height(), &0.5)); + assert!(na::approx_eq(&pm.znear(), &1.5)); + assert!(na::approx_eq(&pm.zfar(), &10.0)); + + p.set_width(0.1); + pm.set_width(0.1); + assert!(na::approx_eq(&p.to_mat(), pm.as_mat())); + + p.set_znear(24.0); + pm.set_znear(24.0); + assert!(na::approx_eq(&p.to_mat(), pm.as_mat())); + + p.set_zfar(61.0); + pm.set_zfar(61.0); + assert!(na::approx_eq(&p.to_mat(), pm.as_mat())); + + p.set_height(23.0); + pm.set_height(23.0); + assert!(na::approx_eq(&p.to_mat(), pm.as_mat())); + + assert!(p.height() == 23.0); + assert!(p.width() == 0.1); + assert!(p.znear() == 24.0); + assert!(p.zfar() == 61.0); + assert!(na::approx_eq(&pm.height(), &23.0)); + assert!(na::approx_eq(&pm.width(), &0.1)); + assert!(na::approx_eq(&pm.znear(), &24.0)); + assert!(na::approx_eq(&pm.zfar(), &61.0)); +}