diff --git a/README.md b/README.md index 61f83627..af014edf 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,10 @@ programming language. It is mainly focused on features needed for real-time physics. It should be usable for graphics too. +## Disclaimer + +As of today, nalgebra is largely untested. + ## Licence nalgebra is provided "as is", under the BSD 3-Clause License. diff --git a/src/dim2/mat2.rs b/src/dim2/mat2.rs index 831f99fa..4b0ddeb8 100644 --- a/src/dim2/mat2.rs +++ b/src/dim2/mat2.rs @@ -1,7 +1,10 @@ use core::num::{One, Zero}; +use traits::dim::Dim; use traits::inv::Inv; +use traits::transpose::Transpose; use dim2::vec2::Vec2; +#[deriving(Eq)] pub struct Mat2 { m11: T, m12: T, @@ -17,6 +20,12 @@ pub fn Mat2(m11: T, m12: T, m21: T, m22: T) -> Mat2 } } +impl Dim for Mat2 +{ + fn dim() -> uint + { 2 } +} + impl One for Mat2 { fn one() -> Mat2 @@ -45,7 +54,7 @@ impl Zero for Mat2 impl + Add> Mul, Mat2> for Mat2 { - fn mul(&self, other : &Mat2) -> Mat2 + fn mul(&self, other: &Mat2) -> Mat2 { Mat2 (self.m11 * other.m11 + self.m12 * other.m21, @@ -60,18 +69,18 @@ impl + Add> Mul, Mat2> for Mat2 // // impl + Add> Mul, Vec2> for Mat2 // { -// fn mul(&self, v : &Vec2) -> Vec2 +// fn mul(&self, v: &Vec2) -> Vec2 // { Vec2(self.m11 * v.x + self.m12 * v.y, self.m21 * v.x + self.m22 * v.y) } // } impl + Add> Mul, Vec2> for Vec2 { - fn mul(&self, m : &Mat2) -> Vec2 + fn mul(&self, m: &Mat2) -> Vec2 { Vec2(self.x * m.m11 + self.y * m.m21, self.x * m.m12 + self.y * m.m22) } } impl + Div + Sub + Neg + Eq + Zero> -Inv for Mat2 +Inv for Mat2 { fn inv(&self) -> Mat2 { @@ -84,6 +93,18 @@ Inv for Mat2 } } +impl Transpose for Mat2 +{ + fn transposed(&self) -> Mat2 + { + Mat2(self.m11, self.m21, + self.m12, self.m22) + } + + fn transpose(&mut self) + { self.m21 <-> self.m12; } +} + impl ToStr for Mat2 { fn to_str(&self) -> ~str diff --git a/src/dim2/rotmat2.rs b/src/dim2/rotmat2.rs index 469d8d98..56a3af43 100644 --- a/src/dim2/rotmat2.rs +++ b/src/dim2/rotmat2.rs @@ -9,7 +9,7 @@ use dim2::mat2::Mat2; #[deriving(Eq)] pub struct Rotmat2 { - submat: Mat2 + priv submat: Mat2 } pub fn Rotmat2>(angle: T) -> Rotmat2 diff --git a/src/dim2/vec2.rs b/src/dim2/vec2.rs index bb4b4f8a..30522083 100644 --- a/src/dim2/vec2.rs +++ b/src/dim2/vec2.rs @@ -1,5 +1,8 @@ +use core::num::Zero; use traits::dot::Dot; -use traits::sqrt::Sqrt; +use traits::dim::Dim; +use traits::cross::Cross; +use traits::workarounds::sqrt::Sqrt; #[deriving(Eq)] pub struct Vec2 @@ -11,6 +14,11 @@ pub struct Vec2 pub fn Vec2(x: T, y: T) -> Vec2 { Vec2 {x: x, y: y} } +impl Dim for Vec2 +{ + fn dim() -> uint + { 2 } +} impl> Add, Vec2> for Vec2 { @@ -36,6 +44,21 @@ impl + Add + Sqrt> Dot for Vec2 { self.sqnorm().sqrt() } } +impl + Sub> Cross for Vec2 +{ + fn cross(&self, other : &Vec2) -> T + { self.x * other.y - self.y * other.x } +} + +impl Zero for Vec2 +{ + fn zero() -> Vec2 + { + let _0 = Zero::zero(); + Vec2(_0, _0) + } +} + impl ToStr for Vec2 { fn to_str(&self) -> ~str diff --git a/src/dim3/mat3.rs b/src/dim3/mat3.rs index 2df20705..9a29d24a 100644 --- a/src/dim3/mat3.rs +++ b/src/dim3/mat3.rs @@ -1,7 +1,11 @@ use core::num::{One, Zero}; +// use core::rand::{Rand, Rng}; +use traits::dim::Dim; use traits::inv::Inv; +use traits::transpose::Transpose; use dim3::vec3::Vec3; +#[deriving(Eq)] pub struct Mat3 { m11: T, m12: T, m13: T, @@ -21,6 +25,12 @@ pub fn Mat3(m11: T, m12: T, m13: T, } } +impl Dim for Mat3 +{ + fn dim() -> uint + { 3 } +} + impl One for Mat3 { fn one() -> Mat3 @@ -95,7 +105,7 @@ impl + Add> Mul, Vec3> for Vec3 impl + Div + Sub + Add + Neg + Eq + Zero> -Inv for Mat3 +Inv for Mat3 { fn inv(&self) -> Mat3 { @@ -125,6 +135,36 @@ Inv for Mat3 } } +impl Transpose for Mat3 +{ + fn transposed(&self) -> Mat3 + { + Mat3(self.m11, self.m21, self.m31, + self.m12, self.m22, self.m32, + self.m13, self.m23, self.m33) + } + + fn transpose(&mut self) + { + self.m12 <-> self.m21; + self.m13 <-> self.m31; + self.m23 <-> self.m32; + } +} + +// impl Rand for Mat3 +// { +// fn rand(rng: &mut R) -> Mat3 +// { +// Mat3 +// { +// m11: rng.next(), m12: rng.next(), m13: rng.next(), +// m21: rng.next(), m22: rng.next(), m23: rng.next(), +// m31: rng.next(), m32: rng.next(), m33: rng.next() +// } +// } +// } + impl ToStr for Mat3 { fn to_str(&self) -> ~str diff --git a/src/dim3/vec3.rs b/src/dim3/vec3.rs index 9f210c93..35bf557c 100644 --- a/src/dim3/vec3.rs +++ b/src/dim3/vec3.rs @@ -1,5 +1,8 @@ +use core::num::Zero; +use traits::dim::Dim; use traits::dot::Dot; -use traits::sqrt::Sqrt; +use traits::cross::Cross; +use traits::workarounds::sqrt::Sqrt; #[deriving(Eq)] pub struct Vec3 @@ -12,6 +15,11 @@ pub struct Vec3 pub fn Vec3(x: T, y: T, z: T) -> Vec3 { Vec3 {x: x, y: y, z: z} } +impl Dim for Vec3 +{ + fn dim() -> uint + { 3 } +} impl> Add, Vec3> for Vec3 { @@ -25,18 +33,6 @@ impl> Sub, Vec3> for Vec3 { Vec3(self.x - other.x, self.y - other.y, self.z - other.z) } } -impl ToStr for Vec3 -{ - fn to_str(&self) -> ~str - { - ~"Vec3 " - + "{ x : " + self.x.to_str() - + ", y : " + self.y.to_str() - + ", z : " + self.z.to_str() - + " }" - } -} - impl + Add + Sqrt> Dot for Vec3 { fn dot(&self, other : &Vec3) -> T @@ -48,3 +44,36 @@ impl + Add + Sqrt> Dot for Vec3 fn norm(&self) -> T { self.sqnorm().sqrt() } } + +impl + Sub> Cross> for Vec3 +{ + fn cross(&self, other : &Vec3) -> Vec3 + { + Vec3( + self.y * other.z - self.z * other.y, + self.z * other.x - self.x * other.z, + self.x * other.y - self.y * other.x + ) + } +} + +impl Zero for Vec3 +{ + fn zero() -> Vec3 + { + let _0 = Zero::zero(); + Vec3(_0, _0, _0) + } +} + +impl ToStr for Vec3 +{ + fn to_str(&self) -> ~str + { + ~"Vec3 " + + "{ x : " + self.x.to_str() + + ", y : " + self.y.to_str() + + ", z : " + self.z.to_str() + + " }" + } +} diff --git a/src/nalgebra.rc b/src/nalgebra.rc index d30f326e..82c639a4 100644 --- a/src/nalgebra.rc +++ b/src/nalgebra.rc @@ -7,11 +7,16 @@ extern mod std; -pub use dim2::vec2::Vec2; pub use dim3::vec3::Vec3; +pub use dim3::mat3::Mat3; + +pub use dim2::vec2::Vec2; pub use dim2::mat2::Mat2; pub use dim2::rotmat2::Rotmat2; +pub use ndim::nvec::NVec; +pub use ndim::nmat::NMat; + pub use traits::dot::Dot; pub use traits::cross::Cross; pub use traits::dim::Dim; @@ -37,6 +42,8 @@ mod dim3 mod ndim { + mod nvec; + mod nmat; } mod traits diff --git a/src/ndim/nmat.rs b/src/ndim/nmat.rs new file mode 100644 index 00000000..e399e0d3 --- /dev/null +++ b/src/ndim/nmat.rs @@ -0,0 +1,187 @@ +use core::num::{One, Zero}; +use core::vec::{from_elem, swap}; +use traits::dim::Dim; +use traits::inv::Inv; +use traits::transpose::Transpose; +// use ndim::nvec::NVec; + +// D is a phantom type parameter, used only as a dimensional token. +// Its allows use to encode the vector dimension at the type-level. +// It can be anything implementing the Dim trait. However, to avoid confusion, +// using d0, d1, d2, d3 and d4 tokens are prefered. +#[deriving(Eq)] +pub struct NMat +{ + mij: ~[T] +} + +impl NMat +{ + fn offset(i: uint, j: uint) -> uint + { i * Dim::dim::() + j } + + fn set(&mut self, i: uint, j: uint, t: &T) + { self.mij[NMat::offset::(i, j)] = *t } +} + +impl Dim for NMat +{ + fn dim() -> uint + { Dim::dim::() } +} + +impl Index<(uint, uint), T> for NMat +{ + fn index(&self, &(i, j): &(uint, uint)) -> T + { self.mij[NMat::offset::(i, j)] } +} + +impl One for NMat +{ + fn one() -> NMat + { + let dim = Dim::dim::(); + let mut res = NMat{ mij: from_elem(dim * dim, Zero::zero()) }; + let _1 = One::one::(); + + for uint::range(0u, dim) |i| + { res.set(i, i, &_1); } + + res + } +} + +impl Zero for NMat +{ + fn zero() -> NMat + { + let dim = Dim::dim::(); + + NMat{ mij: from_elem(dim * dim, Zero::zero()) } + } +} + +impl + Add + Zero> +Mul, NMat> for NMat +{ + fn mul(&self, other: &NMat) -> NMat + { + let dim = Dim::dim::(); + let mut res = Zero::zero::>(); + + for uint::range(0u, dim) |i| + { + for uint::range(0u, dim) |j| + { + let mut acc: T = Zero::zero(); + + for uint::range(0u, dim) |k| + { acc += self[(i, k)] * other[(k, j)]; } + + res.set(i, j, &acc); + } + } + + res + } +} + +impl + Div + Sub + Neg + Eq + One + Zero> +Inv for NMat +{ + fn inv(&self) -> NMat + { + let mut cpy = copy *self; + let dim = Dim::dim::(); + let mut res = One::one::>(); + let _0T = Zero::zero::(); + + // inversion using Gauss-Jordan elimination + for uint::range(0u, dim) |k| + { + // search a non-zero value on the k-th column + // FIXME: is it worth it to spend some more time searching for the max + // instead? + + // FIXME: this is kind of uggly… + // … but we cannot use position_betwee since we are iterating on one + // columns + let mut n0 = dim; // index of a non-zero entry + + for uint::range(k, dim) |i| + { + n0 = k; + + if (cpy[(i, k)] != _0T) + { break; } + } + + assert!(n0 != dim); // non inversible matrix + + // swap pivot line + for uint::range(0u, dim) |j| + { + swap(cpy.mij, NMat::offset::(n0, j), NMat::offset::(k, j)); + swap(res.mij, NMat::offset::(n0, j), NMat::offset::(k, j)); + } + + let pivot = cpy[(k, k)]; + + for uint::range(k, dim) |j| + { + cpy.set(k, j, &(cpy[(k, j)] / pivot)); + res.set(k, j, &(res[(k, j)] / pivot)); + } + + for uint::range(0u, dim) |l| + { + if (l != k) + { + let normalizer = cpy[(l, k)] / pivot; + + for uint::range(k, dim) |j| + { + cpy.set(k, j, &(cpy[(l, j)] - cpy[(k, j)] * normalizer)); + res.set(k, j, &(res[(l, j)] - res[(k, j)] * normalizer)); + } + } + } + } + + res + } +} + +impl Transpose for NMat +{ + fn transposed(&self) -> NMat + { + let mut res = copy *self; + + res.transpose(); + + res + } + + fn transpose(&mut self) + { + let dim = Dim::dim::(); + + for uint::range(1u, dim) |i| + { + for uint::range(0u, dim - 1) |j| + { + swap(self.mij, + NMat::offset::(i, j), + NMat::offset::(j, i)); + } + } + } +} + +impl ToStr for NMat +{ + fn to_str(&self) -> ~str + { ~"Mat" + Dim::dim::().to_str() + " {" + self.mij.to_str() + " }" } +} diff --git a/src/ndim/nvec.rs b/src/ndim/nvec.rs new file mode 100644 index 00000000..cd490b8d --- /dev/null +++ b/src/ndim/nvec.rs @@ -0,0 +1,79 @@ +use core::vec::{map2, from_elem}; +use core::num::Zero; +use traits::dim::Dim; +use traits::dot::Dot; +use traits::workarounds::sqrt::Sqrt; + +// D is a phantom parameter, used only as a dimensional token. +// Its allows use to encode the vector dimension at the type-level. +// It can be anything implementing the Dim trait. However, to avoid confusion, +// using d0, d1, d2, d3 and d4 tokens are prefered. +// FIXME: it might be possible to implement type-level integers and use them +// here? +#[deriving(Eq)] +pub struct NVec +{ + at: ~[T] +} + + +impl Dim for NVec +{ + fn dim() -> uint + { Dim::dim::() } +} + +impl> Add, NVec> for NVec +{ + fn add(&self, other: &NVec) -> NVec + { NVec { at: map2(self.at, other.at, | a, b | { *a + *b }) } } +} + +impl> Sub, NVec> for NVec +{ + fn sub(&self, other: &NVec) -> NVec + { NVec { at: map2(self.at, other.at, | a, b | *a - *b) } } +} + +impl + Add + Sqrt + Zero> Dot for NVec +{ + fn dot(&self, other: &NVec) -> T + { + let mut res = Zero::zero::(); + + for uint::range(0u, Dim::dim::()) |i| + { res += self.at[i] * other.at[i]; } + + res + } + + fn sqnorm(&self) -> T + { self.dot(self) } + + fn norm(&self) -> T + { self.sqnorm().sqrt() } +} + +// FIXME: I dont really know how te generalize the cross product int +// n-dimensions… +// impl + Sub> Cross for NVec +// { +// fn cross(&self, other: &NVec) -> T +// { self.x * other.y - self.y * other.x } +// } + +impl Zero for NVec +{ + fn zero() -> NVec + { + let _0 = Zero::zero(); + + NVec { at: from_elem(Dim::dim::(), _0) } + } +} + +impl ToStr for NVec +{ + fn to_str(&self) -> ~str + { ~"Vec" + Dim::dim::().to_str() + self.at.to_str() } +} diff --git a/src/traits/dim.rs b/src/traits/dim.rs index 5d8f68c9..00364f34 100644 --- a/src/traits/dim.rs +++ b/src/traits/dim.rs @@ -2,3 +2,26 @@ pub trait Dim { fn dim() -> uint; } + +// Some dimension token. Useful to restrict the dimension of n-dimensional +// object at the type-level. +pub struct d0; +pub struct d1; +pub struct d2; +pub struct d3; +pub struct d4; + +impl Dim for d0 +{ fn dim() -> uint { 0 } } + +impl Dim for d1 +{ fn dim() -> uint { 1 } } + +impl Dim for d2 +{ fn dim() -> uint { 2 } } + +impl Dim for d3 +{ fn dim() -> uint { 3 } } + +impl Dim for d4 +{ fn dim() -> uint { 4 } } diff --git a/src/traits/inv.rs b/src/traits/inv.rs index 0ce3ffe8..907d7452 100644 --- a/src/traits/inv.rs +++ b/src/traits/inv.rs @@ -1,4 +1,4 @@ -pub trait Inv +pub trait Inv { fn inv(&self) -> Self; } diff --git a/src/traits/sqrt.rs b/src/traits/sqrt.rs deleted file mode 100644 index 9da09ca6..00000000 --- a/src/traits/sqrt.rs +++ /dev/null @@ -1,17 +0,0 @@ -// FIXME: this does not seem to exist already -// but it will surely be added someday. - -pub trait Sqrt -{ - fn sqrt(&self) -> Self; -} - -impl Sqrt for f64 -{ - fn sqrt(&self) -> f64 { f64::sqrt(*self) } -} - -impl Sqrt for f32 -{ - fn sqrt(&self) -> f32 { f32::sqrt(*self) } -}