Add 3D orthogonal and perspective projection structures.

Fix #26.
This commit is contained in:
Sébastien Crozet 2014-10-12 20:21:06 +02:00
parent 03eda09084
commit 0cf725f5f2
8 changed files with 619 additions and 41 deletions

View File

@ -1,31 +1,30 @@
# nalgebra # 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 graphics.
* real time computer physics. * 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).
## Using **nalgebra** ## 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 This module re-exports everything and includes free functions for all traits methods doing
out-of-place modifications. out-of-place modifications.
* You can import the whole prelude using: * You can import the whole prelude using:
``` ```.ignore
use nalgebra::na::*; use nalgebra::*;
``` ```
The preferred way to use **nalgebra** is to import types and traits explicitly, and call The preferred way to use **nalgebra** is to import types and traits explicitly, and call
free-functions using the `na::` prefix: free-functions using the `na::` prefix:
```.rust ```.rust
extern crate nalgebra; extern crate "nalgebra" as na;
use nalgebra::na::{Vec3, Rot3, Rotation}; use na::{Vec3, Rot3, Rotation};
use nalgebra::na;
fn main() { fn main() {
let a = Vec3::new(1.0f64, 1.0, 1.0); let a = Vec3::new(1.0f64, 1.0, 1.0);
@ -38,26 +37,27 @@ fn main() {
``` ```
## Features ## Features
**nalgebra** is meant to be a general-purpose linear algebra library (but is very far from that…), **nalgebra** is meant to be a general-purpose, low-dimensional, linear algebra
and keeps an optimized set of tools for computational graphics and physics. Those features include: 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`. * 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 `. * Square matrices with static sizes: `Mat1`, `Mat2`, `Mat3`, `Mat4`, `Mat5`, `Mat6 `.
* Rotation matrices: `Rot2`, `Rot3`, `Rot4`. * Rotation matrices: `Rot2`, `Rot3`, `Rot4`.
* Isometries: `Iso2`, `Iso3`, `Iso4`. * Isometries: `Iso2`, `Iso3`, `Iso4`.
* 3D projections for computer graphics: `Persp3`, `PerspMat3`, `Ortho3`, `OrthoMat3`.
* Dynamically sized vector: `DVec`. * Dynamically sized vector: `DVec`.
* Dynamically sized (square or rectangular) matrix: `DMat`. * Dynamically sized (square or rectangular) matrix: `DMat`.
* A few methods for data analysis: `Cov`, `Mean`. * A few methods for data analysis: `Cov`, `Mean`.
* Some matrix factorization algorithms: QR decomposition, ...
* Almost one trait per functionality: useful for generic programming. * Almost one trait per functionality: useful for generic programming.
* Operator overloading using the double trait dispatch * Operator overloading using the double trait dispatch
[trick](http://smallcultfollowing.com/babysteps/blog/2012/10/04/refining-traits-slash-impls/). [trick](http://smallcultfollowing.com/babysteps/blog/2012/10/04/refining-traits-slash-impls/).
For example, the following works: For example, the following works:
```rust ```rust
extern crate nalgebra; extern crate "nalgebra" as na;
use nalgebra::na::{Vec3, Mat3}; use na::{Vec3, Mat3};
use nalgebra::na;
fn main() { fn main() {
let v: Vec3<f64> = na::zero(); let v: Vec3<f64> = na::zero();
@ -66,7 +66,7 @@ fn main() {
let _ = m * v; // matrix-vector multiplication. let _ = m * v; // matrix-vector multiplication.
let _ = v * m; // vector-matrix multiplication. let _ = v * m; // vector-matrix multiplication.
let _ = m * m; // matrix-matrix multiplication. let _ = m * m; // matrix-matrix multiplication.
let _ = v * 2.0; // vector-scalar 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: Simply add the following to your `Cargo.toml` file:
``` ```.ignore
[dependencies.nalgebra] [dependencies.nalgebra]
git = "https://github.com/sebcrozet/nalgebra" git = "https://github.com/sebcrozet/nalgebra"
``` ```
@ -86,6 +86,7 @@ git = "https://github.com/sebcrozet/nalgebra"
Here are some projects using **nalgebra**. Here are some projects using **nalgebra**.
Feel free to add your project to this list if you happen to use **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. * [nphysics](https://github.com/sebcrozet/nphysics): a real-time physics engine.
* [ncollide](http://ncollide.org): a collision detection library. * [ncollide](https://github.com/sebcrozet/ncollide): a collision detection library.
* [kiss3d](http://kiss3d.org): a minimalistic graphics engine. * [kiss3d](https://github.com/sebcrozet/kiss3d): a minimalistic graphics engine.
* [nrays](https://github.com/sebcrozet/nrays): a ray tracer.

View File

@ -1,7 +1,7 @@
/*! /*!
# nalgebra # 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). * general-purpose linear algebra (still lacks a lot of features).
* real time computer graphics. * real time computer graphics.
@ -38,14 +38,15 @@ fn main() {
``` ```
## Features ## Features
**nalgebra** is meant to be a general-purpose linear algebra library (but is very far from that), **nalgebra** is meant to be a general-purpose, low-dimensional, linear algebra library, and keeps
and keeps an optimized set of tools for computational graphics and physics. Those features include: an optimized set of tools for computational graphics and physics. Those features include:
* Vectors with static sizes: `Vec0`, `Vec1`, `Vec2`, `Vec3`, `Vec4`, `Vec5`, `Vec6`. * Vectors with static sizes: `Vec0`, `Vec1`, `Vec2`, `Vec3`, `Vec4`, `Vec5`, `Vec6`.
* Points with static sizes: `Pnt0`, `Pnt1`, `Pnt2`, `Pnt3`, `Pnt4`, `Pnt5`, `Pnt6`. * Points with static sizes: `Pnt0`, `Pnt1`, `Pnt2`, `Pnt3`, `Pnt4`, `Pnt5`, `Pnt6`.
* Square matrices with static sizes: `Mat1`, `Mat2`, `Mat3`, `Mat4`, `Mat5`, `Mat6 `. * Square matrices with static sizes: `Mat1`, `Mat2`, `Mat3`, `Mat4`, `Mat5`, `Mat6 `.
* Rotation matrices: `Rot2`, `Rot3`, `Rot4`. * Rotation matrices: `Rot2`, `Rot3`, `Rot4`.
* Isometries: `Iso2`, `Iso3`, `Iso4`. * Isometries: `Iso2`, `Iso3`, `Iso4`.
* 3D projections for computer graphics: `Persp3`, `PerspMat3`, `Ortho3`, `OrthoMat3`.
* Dynamically sized vector: `DVec`. * Dynamically sized vector: `DVec`.
* Dynamically sized (square or rectangular) matrix: `DMat`. * Dynamically sized (square or rectangular) matrix: `DMat`.
* A few methods for data analysis: `Cov`, `Mean`. * A few methods for data analysis: `Cov`, `Mean`.
@ -150,7 +151,6 @@ pub use traits::{
PartialOrdering, PartialOrdering,
PntAsVec, PntAsVec,
PntExt, PntExt,
Projector,
RMul, RMul,
Rotate, Rotation, RotationMatrix, RotationWithTranslation, Rotate, Rotation, RotationMatrix, RotationWithTranslation,
Row, Row,
@ -174,7 +174,9 @@ pub use structs::{
Mat5, Mat6, Mat5, Mat6,
Rot2, Rot3, Rot4, Rot2, Rot3, Rot4,
Vec0, Vec1, Vec2, Vec3, Vec4, Vec5, Vec6, 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::{ pub use linalg::{
@ -383,6 +385,7 @@ pub fn sqdist<N: Float, P: FloatPnt<N, V>, V: Norm<N>>(a: &P, b: &P) -> N {
*/ */
/// Computes a projection matrix given the frustrum near plane width, height, the field of /// 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`). /// 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<N: FloatMath + Cast<f32> + Zero + One>(width: N, height: N, fov: N, znear: N, zfar: N) -> Mat4<N> { pub fn perspective3d<N: FloatMath + Cast<f32> + Zero + One>(width: N, height: N, fov: N, znear: N, zfar: N) -> Mat4<N> {
let aspect = width / height; let aspect = width / height;

View File

@ -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::mat::{Identity, Mat1, Mat2, Mat3, Mat4, Mat5, Mat6};
pub use self::rot::{Rot2, Rot3, Rot4}; pub use self::rot::{Rot2, Rot3, Rot4};
pub use self::iso::{Iso2, Iso3, Iso4}; 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, pub use self::vec::{Vec1MulRhs, Vec2MulRhs, Vec3MulRhs, Vec4MulRhs, Vec5MulRhs, Vec6MulRhs,
Vec1DivRhs, Vec2DivRhs, Vec3DivRhs, Vec4DivRhs, Vec5DivRhs, Vec6DivRhs, Vec1DivRhs, Vec2DivRhs, Vec3DivRhs, Vec4DivRhs, Vec5DivRhs, Vec6DivRhs,
@ -35,6 +37,8 @@ mod rot_macros;
mod rot; mod rot;
mod iso_macros; mod iso_macros;
mod iso; mod iso;
mod persp;
mod ortho;
// specialization for some 1d, 2d and 3d operations // specialization for some 1d, 2d and 3d operations
#[doc(hidden)] #[doc(hidden)]

234
src/structs/ortho.rs Normal file
View File

@ -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<N> {
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<N> {
mat: Mat4<N>
}
impl<N: FloatMath> Ortho3<N> {
/// Creates a new 3D orthographic projection.
pub fn new(width: N, height: N, znear: N, zfar: N) -> Ortho3<N> {
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<N> {
self.to_persp_mat().mat
}
/// Build a `OrthoMat3` representing this projection.
pub fn to_persp_mat(&self) -> OrthoMat3<N> {
OrthoMat3::new(self.width, self.height, self.znear, self.zfar)
}
}
impl<N: FloatMath + Clone> Ortho3<N> {
/// 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<N>) -> Pnt3<N> {
// FIXME: optimize that
self.to_persp_mat().project_pnt(p)
}
/// Projects a vector.
#[inline]
pub fn project_vec(&self, p: &Vec3<N>) -> Vec3<N> {
// FIXME: optimize that
self.to_persp_mat().project_vec(p)
}
}
impl<N: FloatMath> OrthoMat3<N> {
/// 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<N> {
assert!(!(zfar - znear).is_zero());
assert!(!width.is_zero());
assert!(!height.is_zero());
let mat: Mat4<N> = 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<N>) -> OrthoMat3<N> {
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<N> {
&self.mat
}
/// The width of the view cuboid.
#[inline]
pub fn width(&self) -> N {
num::cast::<f64, N>(2.0).unwrap() / self.mat.m11
}
/// The height of the view cuboid.
#[inline]
pub fn height(&self) -> N {
num::cast::<f64, N>(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::<f64, N>(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::<f64, N>(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::<f64, N>(2.0).unwrap() / (zfar - znear);
self.mat.m34 = -(zfar + znear) / (zfar - znear);
}
/// Projects a point.
#[inline]
pub fn project_pnt(&self, p: &Pnt3<N>) -> Pnt3<N> {
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<N>) -> Vec3<N> {
Vec3::new(
self.mat.m11 * p.x,
self.mat.m22 * p.y,
self.mat.m33 * p.z
)
}
}
impl<N: FloatMath + Clone> OrthoMat3<N> {
/// Returns the 4D matrix (using homogeneous coordinates) of this projection.
#[inline]
pub fn to_mat<'a>(&'a self) -> Mat4<N> {
self.mat.clone()
}
}

265
src/structs/persp.rs Normal file
View File

@ -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<N> {
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<N> {
mat: Mat4<N>
}
impl<N: FloatMath> Persp3<N> {
/// Creates a new 3D perspective projection.
pub fn new(aspect: N, fov: N, znear: N, zfar: N) -> Persp3<N> {
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<N> {
self.to_persp_mat().mat
}
/// Build a `PerspMat3` representing this projection.
pub fn to_persp_mat(&self) -> PerspMat3<N> {
PerspMat3::new(self.aspect, self.fov, self.znear, self.zfar)
}
}
impl<N: FloatMath + Clone> Persp3<N> {
/// 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<N>) -> Pnt3<N> {
// FIXME: optimize that
self.to_persp_mat().project_pnt(p)
}
/// Projects a vector.
#[inline]
pub fn project_vec(&self, p: &Vec3<N>) -> Vec3<N> {
// FIXME: optimize that
self.to_persp_mat().project_vec(p)
}
}
impl<N: FloatMath> PerspMat3<N> {
/// 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<N> {
assert!(!(znear - zfar).is_zero());
assert!(!aspect.is_zero());
let mat: Mat4<N> = 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<N>) -> PerspMat3<N> {
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<N> {
&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<N>) -> Pnt3<N> {
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<N>) -> Vec3<N> {
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<N: FloatMath + Clone> PerspMat3<N> {
/// Returns the 4D matrix (using homogeneous coordinates) of this projection.
#[inline]
pub fn to_mat<'a>(&'a self) -> Mat4<N> {
self.mat.clone()
}
}

View File

@ -276,12 +276,3 @@ pub trait Orig {
/// Returns true if this points is exactly the trivial origin. /// Returns true if this points is exactly the trivial origin.
fn is_orig(&self) -> bool; fn is_orig(&self) -> bool;
} }
/// Trait implemented by projectors.
// XXX: Vout should be an associated type instead of a type parameter.
pub trait Projector<Vin, Vout> {
/// Projects an element of a vector or affine space to a subspace.
///
/// This must be an indempotent operaton.
fn project(&self, &Vin) -> Vout;
}

View File

@ -1,9 +1,8 @@
//! Mathematical traits. //! Mathematical traits.
pub use self::geometry::{AbsoluteRotate, Cross, CrossMatrix, Dot, FromHomogeneous, Norm, Orig, pub use self::geometry::{AbsoluteRotate, Cross, CrossMatrix, Dot, FromHomogeneous, Norm, Orig,
Projector, Rotate, Rotation, RotationMatrix, RotationWithTranslation, Rotate, Rotation, RotationMatrix, RotationWithTranslation, ToHomogeneous,
ToHomogeneous, Transform, Transformation, Translate, Translation, Transform, Transformation, Translate, Translation, UniformSphereSample};
UniformSphereSample};
pub use self::structure::{FloatVec, FloatVecExt, FloatPnt, FloatPntExt, Basis, Cast, Col, Dim, pub use self::structure::{FloatVec, FloatVecExt, FloatPnt, FloatPntExt, Basis, Cast, Col, Dim,
Indexable, Iterable, IterableMut, Mat, Row, AnyVec, VecExt, AnyPnt, Indexable, Iterable, IterableMut, Mat, Row, AnyVec, VecExt, AnyPnt,

View File

@ -6,7 +6,8 @@ extern crate "nalgebra" as na;
use std::num::{Float, abs}; use std::num::{Float, abs};
use std::rand::random; use std::rand::random;
use std::cmp::{min, max}; 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( macro_rules! test_inv_mat_impl(
($t: ty) => ( ($t: ty) => (
@ -296,7 +297,7 @@ fn test_qr_mat6() {
test_qr_impl!(Mat6<f64>); test_qr_impl!(Mat6<f64>);
} }
// NOTE: deactivated untile we get a better convergence rate. // NOTE: deactivated until we get a better convergence rate.
// #[test] // #[test]
// fn test_eigen_qr_mat1() { // fn test_eigen_qr_mat1() {
// test_eigen_qr_impl!(Mat1<f64>); // test_eigen_qr_impl!(Mat1<f64>);
@ -349,3 +350,83 @@ fn test_row_3() {
assert!(second_row == Vec3::new(3.0, 4.0, 5.0)); assert!(second_row == Vec3::new(3.0, 4.0, 5.0));
assert!(second_col == Vec3::new(1.0, 4.0, 7.0)); assert!(second_col == Vec3::new(1.0, 4.0, 7.0));
} }
#[test]
fn test_persp() {
let mut p = Persp3::new(42.0f64, 0.5, 1.5, 10.0);
let mut pm = PerspMat3::new(42.0f64, 0.5, 1.5, 10.0);
assert!(p.to_mat() == pm.to_mat());
assert!(p.aspect() == 42.0);
assert!(p.fov() == 0.5);
assert!(p.znear() == 1.5);
assert!(p.zfar() == 10.0);
assert!(na::approx_eq(&pm.aspect(), &42.0));
assert!(na::approx_eq(&pm.fov(), &0.5));
assert!(na::approx_eq(&pm.znear(), &1.5));
assert!(na::approx_eq(&pm.zfar(), &10.0));
p.set_fov(0.1);
pm.set_fov(0.1);
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
p.set_znear(24.0);
pm.set_znear(24.0);
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
p.set_zfar(61.0);
pm.set_zfar(61.0);
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
p.set_aspect(23.0);
pm.set_aspect(23.0);
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
assert!(p.aspect() == 23.0);
assert!(p.fov() == 0.1);
assert!(p.znear() == 24.0);
assert!(p.zfar() == 61.0);
assert!(na::approx_eq(&pm.aspect(), &23.0));
assert!(na::approx_eq(&pm.fov(), &0.1));
assert!(na::approx_eq(&pm.znear(), &24.0));
assert!(na::approx_eq(&pm.zfar(), &61.0));
}
#[test]
fn test_ortho() {
let mut p = Ortho3::new(42.0f64, 0.5, 1.5, 10.0);
let mut pm = OrthoMat3::new(42.0f64, 0.5, 1.5, 10.0);
assert!(p.to_mat() == pm.to_mat());
assert!(p.width() == 42.0);
assert!(p.height() == 0.5);
assert!(p.znear() == 1.5);
assert!(p.zfar() == 10.0);
assert!(na::approx_eq(&pm.width(), &42.0));
assert!(na::approx_eq(&pm.height(), &0.5));
assert!(na::approx_eq(&pm.znear(), &1.5));
assert!(na::approx_eq(&pm.zfar(), &10.0));
p.set_width(0.1);
pm.set_width(0.1);
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
p.set_znear(24.0);
pm.set_znear(24.0);
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
p.set_zfar(61.0);
pm.set_zfar(61.0);
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
p.set_height(23.0);
pm.set_height(23.0);
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
assert!(p.height() == 23.0);
assert!(p.width() == 0.1);
assert!(p.znear() == 24.0);
assert!(p.zfar() == 61.0);
assert!(na::approx_eq(&pm.height(), &23.0));
assert!(na::approx_eq(&pm.width(), &0.1));
assert!(na::approx_eq(&pm.znear(), &24.0));
assert!(na::approx_eq(&pm.zfar(), &61.0));
}