From aed74ae3c9a96e5ece4203da684ae504d5a5d039 Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Fri, 7 Sep 2012 20:48:47 +1000 Subject: [PATCH] initial commit --- .gitignore | 4 + Makefile | 10 + README.md | 27 ++ src/mat.rs | 700 +++++++++++++++++++++++++++++++++++ src/om3d.rc | 13 + src/projection.rs | 45 +++ src/quat.rs | 206 +++++++++++ src/vec.rs | 526 ++++++++++++++++++++++++++ sublime/om3d.sublime-project | 10 + test/mat-test.rs | 16 + test/quat-test.rs | 6 + test/vec-test.rs | 16 + 12 files changed, 1579 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 src/mat.rs create mode 100644 src/om3d.rc create mode 100644 src/projection.rs create mode 100644 src/quat.rs create mode 100644 src/vec.rs create mode 100644 sublime/om3d.sublime-project create mode 100644 test/mat-test.rs create mode 100644 test/quat-test.rs create mode 100644 test/vec-test.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..327a939 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.DS_Store +build +lib +*.sublime-workspace \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4c29c9d --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +# adapted from https://github.com/z0w0/rusty-math/blob/master/Makefile + +all: + rustc src/om3d.rc --lib --out-dir=lib +test: all + rustc --test -L lib test/*-test.rs -o test/build/test.elf + ./test/build/test.elf +clean: + rm -R -f ./lib/* + rm -R -f ./test/*.elf \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..1820abb --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +# om3D: A Linear Algebra Library for Rust + +Here's some linear algebra I've been working on. I've translated it over from my unpublished D library that I was using to teach myself 3D mathematics. + +For some reason my makefile isn't working - it give me the following error: + + rustc src/om3d.rc --lib --out-dir=lib + error: failed to resolve imports + src/quat.rs:5:7: 5:14 error: unresolved import + src/quat.rs:5 import mat::*; + ^~~~~~~ + src/mat.rs:5:7: 5:15 error: unresolved import + src/mat.rs:5 import quat::*; + ^~~~~~~~ + src/projection.rs:2:7: 2:14 error: unresolved import + src/projection.rs:2 import mat::*; + ^~~~~~~ + error: aborting due to 4 previous errors + make: *** [all] Error 101 + +Any assistance would be most appreciated! + +## Disclaimer: + +I'm new to Rust and a novice at linear algebra. I also haven't written any unittests yet (although I plan to soon). I've almost certainly made mistakes, so use this at your own risk! + +At the time of writing (September 2012) the Rust language is still in a state of flux. There is a good chance that the code will be soon out of date. \ No newline at end of file diff --git a/src/mat.rs b/src/mat.rs new file mode 100644 index 0000000..dd68e40 --- /dev/null +++ b/src/mat.rs @@ -0,0 +1,700 @@ +import std::cmp::FuzzyEq; +import cmp::Ord; +import num::Num; +// import to_str::ToStr; +import quat::*; +import vec::*; + +// TODO: Unittests! I've probably made lots of mistakes... + +// +// NxN Matrix +// +trait Matrix> { + pure fn rows() -> uint; + pure fn cols() -> uint; + pure fn is_col_major() -> bool; + + pure fn index(&&index:uint) -> V; + pure fn row(&&i:uint) -> V; + pure fn col(&&i:uint) -> V; + + pure fn neg() -> self; + + pure fn add_m(&&other:self) -> self; + pure fn sub_m(&&other:self) -> self; + pure fn mul_f(&&value:T) -> self; + pure fn mul_v(&&other:V) -> V; + pure fn mul_m(&&other:self) -> self; + + // pure fn invert(&&other:self) -> self; + pure fn transpose() -> self; + + pure fn exact_eq(&&other:self) -> bool; + pure fn fuzzy_eq(&&other:self) -> bool; + pure fn eq(&&other:self) -> bool; + + pure fn is_identity() -> bool; + pure fn is_symmetric() -> bool; + pure fn is_diagonal() -> bool; + pure fn is_rotated() -> bool; +} + +// +// 3x3 Matrix +// +trait Matrix3> { + pure fn scale(&&vec:V) -> self; + pure fn to_mat4() -> mat4; + pure fn to_quat() -> quat; +} + +// +// 4x4 Matrix +// +trait Matrix4> { + pure fn scale(&&vec:vec3) -> self; // I don't like the use of `vec3` here + pure fn translate(&&vec:vec3) -> self; +} + + + + + + +// +// Mat2: A 2x2, column major matrix +// +struct mat2 { data:[vec2 * 2] } + +// +// Mat2 Constructor +// +#[inline(always)] +pure fn mat2(m00:float, m01:float, + m10:float, m11:float) -> mat2 { + mat2 { data: [ vec2(m00, m01), + vec2(m10, m11) ] } +} + +// +// Construct Mat2 from column vectors +// +#[inline(always)] +pure fn mat2_v(col0:vec2, col1:vec2) -> mat2 { + mat2 { data: [ col0, col1 ] } +} + +#[inline(always)] +pure fn mat2_zero() -> mat2 { + mat2 (0.0, 0.0, + 0.0, 0.0) +} + +#[inline(always)] +pure fn mat2_identity() -> mat2 { + mat2 (1.0, 0.0, + 0.0, 1.0) +} + +// +// Matrix2x2 Implementation +// +impl mat2: Matrix { + #[inline(always)] + pure fn rows() -> uint { 2 } + + #[inline(always)] + pure fn cols() -> uint { 2 } + + #[inline(always)] + pure fn is_col_major() -> bool { true } + + #[inline(always)] + pure fn index(&&i: uint) -> vec2 { + self.data[i] + } + + #[inline(always)] + pure fn row(&&i:uint) -> vec2 { + vec2(self[0][i], + self[1][i]) + } + + #[inline(always)] + pure fn col(&&i:uint) -> vec2 { + self.data[i] + } + + #[inline(always)] + pure fn neg() -> mat2 { + mat2_v(-self[0], -self[1]) + } + + #[inline(always)] + pure fn add_m(&&other:mat2) -> mat2 { + mat2_v(self[0].add_v(other[0]), + self[1].add_v(other[1])) + } + + #[inline(always)] + pure fn sub_m(&&other:mat2) -> mat2 { + mat2_v(self[0].add_v(other[0]), + self[1].add_v(other[1])) + } + + #[inline(always)] + pure fn mul_f(&&value:float) -> mat2 { + mat2_v(self[0].mul_f(value), + self[1].mul_f(value)) + } + + #[inline(always)] + pure fn mul_v(&&other:vec2) -> vec2 { + vec2(self[0][0]*other[0] + self[1][0]*other[1], + self[0][1]*other[0] + self[1][1]*other[1]) + } + + #[inline(always)] + pure fn mul_m(&&other:mat2) -> mat2 { + mat2(self[0][0]*other[0][0] + self[1][0]*other[0][1], + self[0][1]*other[0][0] + self[1][1]*other[0][1], + + self[0][0]*other[1][0] + self[1][0]*other[1][1], + self[0][1]*other[1][0] + self[1][1]*other[1][1]) + } + + // TODO - inversion is harrrd D: + // #[inline(always)] + // pure fn invert(&&other:mat2) -> mat2 {} + + #[inline(always)] + pure fn transpose() -> mat2 { + mat2(self[0][0], self[1][0], + self[0][1], self[1][1]) + } + + #[inline(always)] + pure fn exact_eq(&&other:mat2) -> bool { + self[0].exact_eq(other[0]) && + self[1].exact_eq(other[1]) + } + + #[inline(always)] + pure fn fuzzy_eq(&&other:mat2) -> bool { + self[0].fuzzy_eq(other[0]) && + self[1].fuzzy_eq(other[1]) + } + + #[inline(always)] + pure fn eq(&&other:mat2) -> bool { + self.fuzzy_eq(other) + } + + #[inline(always)] + pure fn is_identity() -> bool { + self.fuzzy_eq(mat2_identity()) + } + + #[inline(always)] + pure fn is_symmetric() -> bool { + self[0][1].fuzzy_eq(&self[1][0]) && + self[1][0].fuzzy_eq(&self[0][1]) + } + + #[inline(always)] + pure fn is_diagonal() -> bool { + self[0][1].fuzzy_eq(&0.0) && + self[1][0].fuzzy_eq(&0.0) + } + + #[inline(always)] + pure fn is_rotated() -> bool { + !self.fuzzy_eq(mat2_identity()) + } +} + + + + + + +// +// Mat3: A 3x3, column major matrix +// +struct mat3 { data:[vec3 * 3] } + +// +// Mat3 Constructor +// +#[inline(always)] +pure fn mat3(m00:float, m01:float, m02:float, + m10:float, m11:float, m12:float, + m20:float, m21:float, m22:float) -> mat3 { + mat3 { data: [ vec3(m00, m01, m02), + vec3(m10, m11, m12), + vec3(m20, m21, m22) ] } +} + +// +// Construct Mat3 from column vectors +// +#[inline(always)] +pure fn mat3_v(col0:vec3, col1:vec3, col2:vec3) -> mat3 { + mat3 { data: [ col0, col1, col2 ] } +} + +#[inline(always)] +pure fn mat3_zero() -> mat3 { + mat3 (0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0) +} + +#[inline(always)] +pure fn mat3_identity() -> mat3 { + mat3 (1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0) +} + +// +// Matrix3x3 Implementation +// +impl mat3: Matrix { + #[inline(always)] + pure fn rows() -> uint { 3 } + + #[inline(always)] + pure fn cols() -> uint { 3 } + + #[inline(always)] + pure fn is_col_major() -> bool { true } + + #[inline(always)] + pure fn index(&&i: uint) -> vec3 { + self.data[i] + } + + #[inline(always)] + pure fn row(&&i:uint) -> vec3 { + vec3(self[0][i], + self[1][i], + self[2][i]) + } + + #[inline(always)] + pure fn col(&&i:uint) -> vec3 { + self.data[i] + } + + #[inline(always)] + pure fn neg() -> mat3 { + mat3_v(-self[0], -self[1], -self[2]) + } + + #[inline(always)] + pure fn add_m(&&other:mat3) -> mat3 { + mat3_v(self[0].add_v(other[0]), + self[1].add_v(other[1]), + self[2].add_v(other[2])) + } + + #[inline(always)] + pure fn sub_m(&&other:mat3) -> mat3 { + mat3_v(self[0].add_v(other[0]), + self[1].add_v(other[1]), + self[2].add_v(other[2])) + } + + #[inline(always)] + pure fn mul_f(&&value:float) -> mat3 { + mat3_v(self[0].mul_f(value), + self[1].mul_f(value), + self[2].mul_f(value)) + } + + #[inline(always)] + pure fn mul_v(&&other:vec3) -> vec3 { + vec3(self[0][0]*other[0] + self[1][0]*other[1] + self[2][0]*other[2], + self[0][1]*other[0] + self[1][1]*other[1] + self[2][1]*other[2], + self[0][2]*other[0] + self[1][2]*other[1] + self[2][2]*other[2]) + } + + #[inline(always)] + pure fn mul_m(&&other:mat3) -> mat3 { + mat3(self[0][0]*other[0][0] + self[1][0]*other[0][1] + self[2][0]*other[0][2], + self[0][1]*other[0][0] + self[1][1]*other[0][1] + self[2][1]*other[0][2], + self[0][2]*other[0][0] + self[1][2]*other[0][1] + self[2][2]*other[0][2], + + self[0][0]*other[1][0] + self[1][0]*other[1][1] + self[2][0]*other[1][2], + self[0][1]*other[1][0] + self[1][1]*other[1][1] + self[2][1]*other[1][2], + self[0][2]*other[1][0] + self[1][2]*other[1][1] + self[2][2]*other[1][2], + + self[0][0]*other[2][0] + self[1][0]*other[2][1] + self[2][0]*other[2][2], + self[0][1]*other[2][0] + self[1][1]*other[2][1] + self[2][1]*other[2][2], + self[0][2]*other[2][0] + self[1][2]*other[2][1] + self[2][2]*other[2][2]) + } + + // TODO - inversion is harrrd D: + // #[inline(always)] + // pure fn invert(&&other:mat3) -> mat3 {} + + #[inline(always)] + pure fn transpose() -> mat3 { + mat3(self[0][0], self[1][0], self[2][0], + self[0][1], self[1][1], self[2][1], + self[0][2], self[1][2], self[2][2]) + } + + #[inline(always)] + pure fn exact_eq(&&other:mat3) -> bool { + self[0].exact_eq(other[0]) && + self[1].exact_eq(other[1]) && + self[2].exact_eq(other[2]) + } + + #[inline(always)] + pure fn fuzzy_eq(&&other:mat3) -> bool { + self[0].fuzzy_eq(other[0]) && + self[1].fuzzy_eq(other[1]) && + self[2].fuzzy_eq(other[2]) + } + + #[inline(always)] + pure fn eq(&&other:mat3) -> bool { + self.fuzzy_eq(other) + } + + #[inline(always)] + pure fn is_identity() -> bool { + self.fuzzy_eq(mat3_identity()) + } + + #[inline(always)] + pure fn is_symmetric() -> bool { + self[0][1].fuzzy_eq(&self[1][0]) && + self[0][2].fuzzy_eq(&self[2][0]) && + + self[1][0].fuzzy_eq(&self[0][1]) && + self[1][2].fuzzy_eq(&self[2][1]) && + + self[2][0].fuzzy_eq(&self[0][2]) && + self[2][1].fuzzy_eq(&self[1][2]) + } + + #[inline(always)] + pure fn is_diagonal() -> bool { + self[0][1].fuzzy_eq(&0.0) && + self[0][2].fuzzy_eq(&0.0) && + + self[1][0].fuzzy_eq(&0.0) && + self[1][2].fuzzy_eq(&0.0) && + + self[2][0].fuzzy_eq(&0.0) && + self[2][1].fuzzy_eq(&0.0) + } + + #[inline(always)] + pure fn is_rotated() -> bool { + !self.fuzzy_eq(mat3_identity()) + } +} + +impl mat3: Matrix3 { + #[inline(always)] + pure fn scale(&&vec:V) -> mat3 { + self.mul_m(mat3(vec.x(), 0, 0, + 0, vec.y(), 0, + 0, 0, vec.z())) + } + + #[inline(always)] + pure fn to_mat4() -> mat4 { + mat4(self[0][0], self[0][1], self[0][2], 0, + self[1][0], self[1][1], self[1][2], 0, + self[2][0], self[2][1], self[2][2], 0, + 0, 0, 0, 1) + } + + pure fn to_quat() -> quat { + // Implemented using a mix of ideas from jMonkeyEngine and Ken Shoemake's + // paper on Quaternions: http://www.cs.ucr.edu/~vbz/resources/quatut.pdf + + let w:float, x:float, y:float, z:float, s:float; + let trace:float = self[0][0] + self[1][1] + self[2][2]; + + if trace >= 0 { + s = sqrt(trace + 1); + w = 0.5 * s; + s = 0.5 / s; + x = self[1][2] - self[2][1] * s; + y = self[2][0] - self[0][2] * s; + z = self[0][1] - self[1][0] * s; + } else if (self[0][0] > self[1][1]) && (self[0][0] > self[2][2]) { + s = sqrt(1 + self[0][0] - self[1][1] - self[2][2]); + w = 0.5 * s; + s = 0.5 / s; + x = self[0][1] - self[1][0] * s; + y = self[2][0] - self[0][2] * s; + z = self[1][2] - self[2][1] * s; + } else if self[1][1] > self[2][2] { + s = sqrt(1 + self[1][1] - self[0][0] - self[2][2]); + w = 0.5 * s; + s = 0.5 / s; + x = self[0][1] - self[1][0] * s; + y = self[1][2] - self[2][1] * s; + z = self[2][0] - self[0][2] * s; + } else { + s = sqrt(1 + self[2][2] - self[0][0] - self[1][1]); + w = 0.5 * s; + s = 0.5 / s; + x = self[2][0] - self[0][2] * s; + y = self[1][2] - self[2][1] * s; + z = self[0][1] - self[1][0] * s; + } + return quat(w, x, y, z); + } +} + + + + + + +// +// Mat4: A 4x4, column major matrix +// +struct mat4 { data:[vec4 * 4] } + +// +// Mat4 Constructor +// +#[inline(always)] +pure fn mat4(m00:float, m01:float, m02:float, m03:float, + m10:float, m11:float, m12:float, m13:float, + m20:float, m21:float, m22:float, m23:float, + m30:float, m31:float, m32:float, m33:float) -> mat4 { + mat4 { data: [ vec4(m00, m01, m02, m03), + vec4(m10, m11, m12, m13), + vec4(m20, m21, m22, m23), + vec4(m30, m31, m32, m33) ] } +} + +// +// Construct Mat4 from column vectors +// +#[inline(always)] +pure fn mat4_v(col0:vec4, col1:vec4, col2:vec4, col3:vec4) -> mat4 { + mat4 { data: [ col0, col1, col2, col3 ] } +} + +#[inline(always)] +pure fn mat4_zero() -> mat4 { + mat4 (0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0) +} + +#[inline(always)] +pure fn mat4_identity() -> mat4 { + mat4 (1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0) +} + +// +// Matrix4x4 Implementation +// +impl mat4: Matrix { + #[inline(always)] + pure fn rows() -> uint { 4 } + + #[inline(always)] + pure fn cols() -> uint { 4 } + + #[inline(always)] + pure fn is_col_major() -> bool { true } + + #[inline(always)] + pure fn index(&&i: uint) -> vec4 { + self.data[i] + } + + #[inline(always)] + pure fn row(&&i:uint) -> vec4 { + vec4(self[0][i], + self[1][i], + self[2][i], + self[3][i]) + } + + #[inline(always)] + pure fn col(&&i:uint) -> vec4 { + self.data[i] + } + + #[inline(always)] + pure fn neg() -> mat4 { + mat4_v(-self[0], -self[1], -self[2], -self[3]) + } + + #[inline(always)] + pure fn add_m(&&other:mat4) -> mat4 { + mat4_v(self[0].add_v(other[0]), + self[1].add_v(other[1]), + self[2].add_v(other[2]), + self[3].add_v(other[3])) + } + + #[inline(always)] + pure fn sub_m(&&other:mat4) -> mat4 { + mat4_v(self[0].add_v(other[0]), + self[1].add_v(other[1]), + self[2].add_v(other[2]), + self[3].add_v(other[3])) + } + + #[inline(always)] + pure fn mul_f(&&value:float) -> mat4 { + mat4_v(self[0].mul_f(value), + self[1].mul_f(value), + self[2].mul_f(value), + self[3].mul_f(value)) + } + + #[inline(always)] + pure fn mul_v(&&other:vec4) -> vec4 { + vec4(self[0][0]*other[0] + self[1][0]*other[1] + self[2][0]*other[2] + self[3][0]*other[3], + self[0][1]*other[0] + self[1][1]*other[1] + self[2][1]*other[2] + self[3][1]*other[3], + self[0][2]*other[0] + self[1][2]*other[1] + self[2][2]*other[2] + self[3][2]*other[3], + self[0][3]*other[0] + self[1][3]*other[1] + self[2][3]*other[2] + self[3][3]*other[3]) + } + + #[inline(always)] + pure fn mul_m(&&other:mat4) -> mat4 { + mat4(self[0][0]*other[0][0] + self[1][0]*other[0][1] + self[2][0]*other[0][2] + self[3][0]*other[0][3], + self[0][1]*other[0][0] + self[1][1]*other[0][1] + self[2][1]*other[0][2] + self[3][1]*other[0][3], + self[0][2]*other[0][0] + self[1][2]*other[0][1] + self[2][2]*other[0][2] + self[3][2]*other[0][3], + self[0][3]*other[0][0] + self[1][3]*other[0][1] + self[2][3]*other[0][2] + self[3][3]*other[0][3], + + self[0][0]*other[1][0] + self[1][0]*other[1][1] + self[2][0]*other[1][2] + self[3][0]*other[1][3], + self[0][1]*other[1][0] + self[1][1]*other[1][1] + self[2][1]*other[1][2] + self[3][1]*other[1][3], + self[0][2]*other[1][0] + self[1][2]*other[1][1] + self[2][2]*other[1][2] + self[3][2]*other[1][3], + self[0][3]*other[1][0] + self[1][3]*other[1][1] + self[2][3]*other[1][2] + self[3][3]*other[1][3], + + self[0][0]*other[2][0] + self[1][0]*other[2][1] + self[2][0]*other[2][2] + self[3][0]*other[2][3], + self[0][1]*other[2][0] + self[1][1]*other[2][1] + self[2][1]*other[2][2] + self[3][1]*other[2][3], + self[0][2]*other[2][0] + self[1][2]*other[2][1] + self[2][2]*other[2][2] + self[3][2]*other[2][3], + self[0][3]*other[2][0] + self[1][3]*other[2][1] + self[2][3]*other[2][2] + self[3][3]*other[2][3], + + self[0][0]*other[3][0] + self[1][0]*other[3][1] + self[2][0]*other[3][2] + self[3][0]*other[3][3], + self[0][1]*other[3][0] + self[1][1]*other[3][1] + self[2][1]*other[3][2] + self[3][1]*other[3][3], + self[0][2]*other[3][0] + self[1][2]*other[3][1] + self[2][2]*other[3][2] + self[3][2]*other[3][3], + self[0][3]*other[3][0] + self[1][3]*other[3][1] + self[2][3]*other[3][2] + self[3][3]*other[3][3]) + } + + // TODO - inversion is harrrd D: + // #[inline(always)] + // pure fn invert(&&other:mat4) -> mat4 {} + + #[inline(always)] + pure fn transpose() -> mat4 { + mat4(self[0][0], self[1][0], self[2][0], self[3][0], + self[0][1], self[1][1], self[2][1], self[3][1], + self[0][2], self[1][2], self[2][2], self[3][2], + self[0][3], self[1][3], self[2][3], self[3][3]) + } + + #[inline(always)] + pure fn exact_eq(&&other:mat4) -> bool { + self[0].exact_eq(other[0]) && + self[1].exact_eq(other[1]) && + self[2].exact_eq(other[2]) && + self[3].exact_eq(other[3]) + } + + #[inline(always)] + pure fn fuzzy_eq(&&other:mat4) -> bool { + self[0].fuzzy_eq(other[0]) && + self[1].fuzzy_eq(other[1]) && + self[2].fuzzy_eq(other[2]) && + self[3].fuzzy_eq(other[3]) + } + + #[inline(always)] + pure fn eq(&&other:mat4) -> bool { + self.fuzzy_eq(other) + } + + #[inline(always)] + pure fn is_identity() -> bool { + self.fuzzy_eq(mat4_identity()) + } + + #[inline(always)] + pure fn is_symmetric() -> bool { + self[0][1].fuzzy_eq(&self[1][0]) && + self[0][2].fuzzy_eq(&self[2][0]) && + self[0][3].fuzzy_eq(&self[3][0]) && + + self[1][0].fuzzy_eq(&self[0][1]) && + self[1][2].fuzzy_eq(&self[2][1]) && + self[1][3].fuzzy_eq(&self[3][1]) && + + self[2][0].fuzzy_eq(&self[0][2]) && + self[2][1].fuzzy_eq(&self[1][2]) && + self[2][3].fuzzy_eq(&self[3][2]) && + + self[3][0].fuzzy_eq(&self[0][3]) && + self[3][1].fuzzy_eq(&self[1][3]) && + self[3][2].fuzzy_eq(&self[2][3]) + } + + #[inline(always)] + pure fn is_diagonal() -> bool { + self[0][1].fuzzy_eq(&0.0) && + self[0][2].fuzzy_eq(&0.0) && + self[0][3].fuzzy_eq(&0.0) && + + self[1][0].fuzzy_eq(&0.0) && + self[1][2].fuzzy_eq(&0.0) && + self[1][3].fuzzy_eq(&0.0) && + + self[2][0].fuzzy_eq(&0.0) && + self[2][1].fuzzy_eq(&0.0) && + self[2][3].fuzzy_eq(&0.0) && + + self[3][0].fuzzy_eq(&0.0) && + self[3][1].fuzzy_eq(&0.0) && + self[3][2].fuzzy_eq(&0.0) + } + + #[inline(always)] + pure fn is_rotated() -> bool { + !self.fuzzy_eq(mat4_identity()) + } +} + +impl mat4: Matrix4 { + #[inline(always)] + pure fn scale(&&vec:vec3) -> mat4 { + self.mul_m(mat3(vec.x(), 0, 0, 0, + 0, vec.y(), 0, 0, + 0, 0, vec.z(), 0, + 0, 0, 0, 1)) + } + + #[inline(always)] + pure fn translate(&&vec:vec3) -> mat4 { + mat4(self[0], + self[1], + self[2], + vec4(self[3][0] + vec.x(), + self[3][1] + vec.y(), + self[3][2] + vec.z(), + self[3][3])) + } +} \ No newline at end of file diff --git a/src/om3d.rc b/src/om3d.rc new file mode 100644 index 0000000..5cb8f97 --- /dev/null +++ b/src/om3d.rc @@ -0,0 +1,13 @@ +#[link(name = "om3d", + vers = "0.1", + author = "Brendan Zabarauskas")]; + +#[comment = "Linear algebra library for Rust. Incomplete and probably buggy."]; +#[crate_type = "lib"]; + +use std; + +mod mat; +mod projection; +mod quat; +mod vec; \ No newline at end of file diff --git a/src/projection.rs b/src/projection.rs new file mode 100644 index 0000000..ec3fefd --- /dev/null +++ b/src/projection.rs @@ -0,0 +1,45 @@ +import float::consts::pi; +import mat::*; + +// +// Create a perspective projection matrix +// +// fov is in degrees +// http://www.opengl.org/wiki/GluPerspective_code +// +pure fn perspective(fovy:float, aspectRatio:float, near:float, fa:float) -> mat4 { + let ymax = near * tan(fovy * PI / 360.0); + let xmax = ymax * aspectRatio; + return frustum(-xmax, xmax, -ymax, ymax, near, far); +} + +// +// Define a view frustrum +// http://www.felixgers.de/teaching/jogl/perspectiveProjection.html +// http://www.opengl.org/wiki/GluPerspective_code +// +// TODO: double check algorithm +// +pure fn frustum(left:float, right:float, bottom:float, top:float, near:float, far:float) -> mat4 { + let m00:float = (2*near)/(right-left); + let m01:float = 0.0; + let m02:float = 0.0; + let m03:float = 0.0; + let m10:float = 0.0; + let m11:float = (2*near)/(top-bottom); + let m12:float = 0.0; + let m13:float = 0.0; + let m20:float = (right+left)/(right-left); + let m21:float = (top+bottom)/(top-bottom); + let m22:float = -(far+near)/(far-near); + let m23:float = -1.0; + let m30:float = 0.0; + let m31:float = 0.0; + let m32:float = -(2*far*near)/(far-near); + let m33:float = 0.0; + + return mat4(m00, m01, m02, m03, + m10, m11, m12, m13, + m20, m21, m22, m23, + m30, m31, m32, m33); +} \ No newline at end of file diff --git a/src/quat.rs b/src/quat.rs new file mode 100644 index 0000000..4ec2168 --- /dev/null +++ b/src/quat.rs @@ -0,0 +1,206 @@ +import std::cmp::FuzzyEq; +import cmp::Ord; +import num::Num; +import to_str::ToStr; +import mat::*; +import vec::*; + +// TODO: Unittests! I've probably made lots of mistakes... + +// +// Quaternion +// +trait Quaternion { + pure fn dim() -> uint; + + pure fn index(&&index:uint) -> T; + fn w() -> T; + fn x() -> T; + fn y() -> T; + fn z() -> T; + + pure fn neg() -> self; + + pure fn mul_f(&&value:T) -> self; + pure fn div_f(&&value:T) -> self; + + // pure fn mul_v(&&vec3) -> vec3; + + pure fn add_q(&&other:self) -> self; + pure fn sub_q(&&other:self) -> self; + pure fn mul_q(&&other:self) -> self; + + pure fn exact_eq(&&other:self) -> bool; + pure fn fuzzy_eq(&&other:self) -> bool; + pure fn eq(&&other:self) -> bool; + + pure fn conjugate() -> self; + pure fn inverse() -> self; + pure fn magnitude2() -> T; + pure fn magnitude() -> T; + + pure fn to_mat3() -> mat3; + pure fn to_mat4() -> mat4; +} + + + + + + +// +// Quat struct definition +// +struct quat { data:[float * 4] } + +// +// Quat Constructor +// +#[inline(always)] +pure fn quat(w:float, x:float, y:float, z:float) -> quat { + quat { data: [ w, x, y, z ] } +} + +// +// Quaternion Implementation +// +impl quat { + #[inline(always)] + pure fn dim() -> uint { 4 } + + #[inline(always)] + pure fn index(&&i: uint) -> float { + self.data[i] + } + + #[inline(always)] + pure fn neg() -> quat { + quat(-self[0], -self[1], -self[2], -self[3]) + } + + #[inline(always)] fn w() -> float { self.data[0] } + #[inline(always)] fn x() -> float { self.data[1] } + #[inline(always)] fn y() -> float { self.data[2] } + #[inline(always)] fn z() -> float { self.data[3] } + + #[inline(always)] + pure fn mul_f(&&value:float) -> quat { + quat(self[0] * value, + self[1] * value, + self[2] * value, + self[3] * value) + } + + #[inline(always)] + pure fn div_f(&&value:float) -> quat { + quat(self[0] / value, + self[1] / value, + self[2] / value, + self[3] / value) + } + + #[inline(always)] + pure fn add_q(&&other:quat) -> quat{ + quat(self[0] + other[0], + self[1] + other[1], + self[2] + other[2], + self[3] + other[3]) + } + + #[inline(always)] + pure fn sub_q(&&other:quat) -> quat{ + quat(self[0] - other[0], + self[1] - other[1], + self[2] - other[2], + self[3] - other[3]) + } + + #[inline(always)] + pure fn mul_q(&&other:quat) -> quat { + quat(self.w()*other.w() - self.x()*other.x() - self.y()*other.y() - self.z()*other.z(), + self.w()*other.x() + self.x()*other.w() + self.y()*other.z() - self.z()*other.y(), + self.w()*other.y() + self.y()*other.w() + self.z()*other.x() - self.x()*other.z(), + self.w()*other.z() + self.z()*other.w() + self.x()*other.y() - self.y()*other.x()) + } + + #[inline(always)] + pure fn exact_eq(&&other:quat) -> bool { + self[0] == other[0] && + self[1] == other[1] && + self[2] == other[2] && + self[3] == other[3] + } + + #[inline(always)] + pure fn fuzzy_eq(&&other: quat) -> bool { + self[0].fuzzy_eq(&other[0]) && + self[1].fuzzy_eq(&other[1]) && + self[2].fuzzy_eq(&other[2]) && + self[3].fuzzy_eq(&other[3]) + } + + #[inline(always)] + pure fn eq(&&other:quat) -> bool { + self.fuzzy_eq(other) + } + + #[inline(always)] + pure fn conjugate() -> quat { + quat(self.w(), -self.x(), -self.y(), -self.z()); + } + + #[inline(always)] + pure fn inverse() -> quat { + + } + + #[inline(always)] + pure fn magnitude2() -> float { + self.w() * self.w() + + self.x() * self.x() + + self.y() * self.y() + + self.z() * self.z() + } + + #[inline(always)] + pure fn magnitude() -> float { + sqrt(self.magnitude2) + } + + #[inline(always)] + pure fn to_mat3() -> mat3 { + let x2 = self.x() + self.x(); + let y2 = self.y() + self.y(); + let z2 = self.z() + self.z(); + + let xx2 = x2 * self.x(); + let xy2 = x2 * self.y(); + let xz2 = x2 * self.z(); + + let yy2 = y2 * self.y(); + let yz2 = y2 * self.z(); + let zz2 = z2 * self.z(); + + let wy2 = y2 * self.w(); + let wz2 = z2 * self.w(); + let wx2 = x2 * self.w(); + + return mat3(1 - yy2 - zz2, xy2 - wz2, xz2 + wy2, + xy2 + wz2, 1 - xx2 - zz2, yz2 - wx2, + xz2 - wy2, yz2 + wx2, 1 - xx2 - yy2); + } + + #[inline(always)] + pure fn to_mat4() -> mat4 { + self.to_mat3().to_mat4() + } +} + +// +// Convert To String +// +impl quat: ToStr { + fn to_str() -> ~str { + fmt!("quat[ %f, %f, %f, %f ]", self.w(), self.x(), self.y(), self.z()) + } +} \ No newline at end of file diff --git a/src/vec.rs b/src/vec.rs new file mode 100644 index 0000000..5268577 --- /dev/null +++ b/src/vec.rs @@ -0,0 +1,526 @@ +import std::cmp::FuzzyEq; +import cmp::Ord; +import num::Num; +import float::sqrt; +import to_str::ToStr; + +// TODO: Unittests! I've probably made lots of mistakes... + +// +// N-dimensional Vector +// +trait Vector { + pure fn dim() -> uint; + + pure fn index(&&index:uint) -> T; + + pure fn neg() -> self; + + pure fn add_f(&&value:T) -> self; + pure fn sub_f(&&value:T) -> self; + pure fn mul_f(&&value:T) -> self; + pure fn div_f(&&value:T) -> self; + + pure fn add_v(&&other: self) -> self; + pure fn sub_v(&&other: self) -> self; + + pure fn exact_eq(&&other:self) -> bool; + pure fn fuzzy_eq(&&other:self) -> bool; + pure fn eq(&&other:self) -> bool; + + pure fn dot(&&other: self) -> T; + + pure fn magnitude2() -> T; + pure fn magnitude() -> T; + pure fn normalize() -> self; + + // pure fn lerp(&&other:self, &&value:T) -> self; + // pure fn abs(&&other:self) -> self; + // pure fn min(&&other:self) -> self; + // pure fn max(&&other:self) -> self; +} + +// +// 2-Dimensional Vector +// +trait Vector2 { + // This is where I wish rust had properties ;) + fn x() -> T; + fn y() -> T; +} + +// +// 3-Dimensional Vector +// +trait Vector3 { + fn x() -> T; + fn y() -> T; + fn z() -> T; + + fn cross(&&other:self) -> self; +} + +// +// 4-Dimensional Vector +// +trait Vector4 { + fn x() -> T; + fn y() -> T; + fn z() -> T; + fn w() -> T; +} + + + + + + +// +// Vec2 struct definition +// +struct vec2 { data:[float * 2] } + +// +// Vec2 Constructor +// +#[inline(always)] +pure fn vec2(x:float, y:float) -> vec2 { + vec2 { data: [ x, y ] } +} + +// +// Constants +// +#[inline(always)] pure fn vec2_zero() -> vec2 { vec2 (0.0, 0.0) } +#[inline(always)] pure fn vec2_unit_x() -> vec2 { vec2 (1.0, 0.0) } +#[inline(always)] pure fn vec2_unit_y() -> vec2 { vec2 (0.0, 1.0) } +#[inline(always)] pure fn vec2_identity() -> vec2 { vec2 (1.0, 1.0) } + +// +// Vector2 Implementation +// +impl vec2: Vector2 { + #[inline(always)] fn x() -> float { self.data[0] } + #[inline(always)] fn y() -> float { self.data[1] } +} + +// +// Vector Implementation +// +impl vec2: Vector { + #[inline(always)] + pure fn dim() -> uint { 2 } + + #[inline(always)] + pure fn index(&&i: uint) -> float { + self.data[i] + } + + #[inline(always)] + pure fn neg() -> vec2 { + vec2(-self[0], -self[1]) + } + + #[inline(always)] + pure fn add_f(&&value:float) -> vec2 { + vec2(self[0] + value, + self[1] + value) + } + + #[inline(always)] + pure fn sub_f(&&value:float) -> vec2 { + vec2(self[0] - value, + self[1] - value) + } + + #[inline(always)] + pure fn mul_f(&&value:float) -> vec2 { + vec2(self[0] * value, + self[1] * value) + } + + #[inline(always)] + pure fn div_f(&&value:float) -> vec2 { + vec2(self[0] / value, + self[1] / value) + } + + #[inline(always)] + pure fn add_v(&&other: vec2) -> vec2{ + vec2(self[0] + other[0], + self[1] + other[1]) + } + + #[inline(always)] + pure fn sub_v(&&other: vec2) -> vec2{ + vec2(self[0] - other[0], + self[1] - other[1]) + } + + #[inline(always)] + pure fn exact_eq(&&other:vec2) -> bool { + self[0] == other[0] && + self[1] == other[1] + } + + #[inline(always)] + pure fn fuzzy_eq(&&other: vec2) -> bool { + self[0].fuzzy_eq(&other[0]) && + self[1].fuzzy_eq(&other[1]) + } + + #[inline(always)] + pure fn eq(&&other:vec2) -> bool { + self.fuzzy_eq(other) + } + + #[inline(always)] + pure fn dot(&&other: vec2) -> float { + self[0] * other[0] + + self[1] * other[1] + } + + #[inline(always)] + pure fn magnitude2() -> float { + self[0] * self[0] + + self[1] * self[1] + } + + #[inline(always)] + pure fn magnitude() -> float { + sqrt(self.magnitude2()) + } + + #[inline(always)] + pure fn normalize() -> vec2 { + let n = 1.0 / self.magnitude(); + return self.mul_f(n); + } +} + +// +// Convert To String +// +impl vec2: ToStr { + fn to_str() -> ~str { + fmt!("vec2[ %f, %f ]", self[0], self[1]) + } +} + + + + + + +// +// Vec3 struct definition +// +struct vec3 { data:[float * 3] } + +// +// Constants +// +#[inline(always)] pure fn vec3_zero() -> vec3 { vec3(0.0, 0.0, 0.0) } +#[inline(always)] pure fn vec3_unit_x() -> vec3 { vec3(1.0, 0.0, 0.0) } +#[inline(always)] pure fn vec3_unit_y() -> vec3 { vec3(0.0, 1.0, 0.0) } +#[inline(always)] pure fn vec3_unit_z() -> vec3 { vec3(0.0, 0.0, 1.0) } +#[inline(always)] pure fn vec3_identity() -> vec3 { vec3(1.0, 1.0, 1.0) } + +// +// Vec3 Constructor +// +#[inline(always)] +pure fn vec3(x:float, y:float, z:float) -> vec3 { + vec3 { data: [ x, y, z ] } +} + + +// +// Vector3 Implementation +// +impl vec3: Vector3 { + #[inline(always)] fn x() -> float { self.data[0] } + #[inline(always)] fn y() -> float { self.data[1] } + #[inline(always)] fn z() -> float { self.data[2] } + + #[inline(always)] + fn cross(&&other:vec3) -> vec3 { + vec3((self[1] * other[2]) - (self[2] * other[1]), + (self[2] * other[0]) - (self[0] * other[2]), + (self[0] * other[1]) - (self[1] * other[0])) + } +} + +// +// Vector Implementation +// +impl vec3: Vector { + #[inline(always)] + pure fn dim() -> uint { 3 } + + #[inline(always)] + pure fn index(&&i: uint) -> float { + self.data[i] + } + + #[inline(always)] + pure fn neg() -> vec3 { + vec3(-self[0], -self[1], -self[2]) + } + + #[inline(always)] + pure fn add_f(&&value:float) -> vec3 { + vec3(self[0] + value, + self[1] + value, + self[2] + value) + } + + #[inline(always)] + pure fn sub_f(&&value:float) -> vec3 { + vec3(self[0] - value, + self[1] - value, + self[2] - value) + } + + #[inline(always)] + pure fn mul_f(&&value:float) -> vec3 { + vec3(self[0] * value, + self[1] * value, + self[2] * value) + } + + #[inline(always)] + pure fn div_f(&&value:float) -> vec3 { + vec3(self[0] / value, + self[1] / value, + self[2] / value) + } + + #[inline(always)] + pure fn add_v(&&other: vec3) -> vec3{ + vec3(self[0] + other[0], + self[1] + other[1], + self[2] + other[2]) + } + + #[inline(always)] + pure fn sub_v(&&other: vec3) -> vec3{ + vec3(self[0] - other[0], + self[1] - other[1], + self[2] - other[2]) + } + + #[inline(always)] + pure fn exact_eq(&&other:vec3) -> bool { + self[0] == other[0] && + self[1] == other[1] && + self[2] == other[2] + } + + #[inline(always)] + pure fn fuzzy_eq(&&other: vec3) -> bool { + self[0].fuzzy_eq(&other[0]) && + self[1].fuzzy_eq(&other[1]) && + self[2].fuzzy_eq(&other[2]) + } + + #[inline(always)] + pure fn eq(&&other:vec3) -> bool { + self.fuzzy_eq(other) + } + + #[inline(always)] + pure fn dot(&&other: vec3) -> float { + self[0] * other[0] + + self[1] * other[1] + + self[2] * other[2] + } + + #[inline(always)] + pure fn magnitude2() -> float { + self[0] * self[0] + + self[1] * self[1] + + self[2] * self[2] + } + + #[inline(always)] + pure fn magnitude() -> float { + sqrt(self.magnitude2()) + } + + #[inline(always)] + pure fn normalize() -> vec3 { + let n = 1.0 / self.magnitude(); + return self.mul_f(n); + } +} + +// +// Convert To String +// +impl vec3: ToStr { + fn to_str() -> ~str { + fmt!("vec3[ %f, %f, %f ]", self[0], self[1], self[2]) + } +} + + + + + + +// +// Vec4 struct definition +// +struct vec4 { data:[float * 4] } + +// +// Constants +// +#[inline(always)] pure fn vec4_zero() -> vec4 { vec4(0.0, 0.0, 0.0, 0.0) } +#[inline(always)] pure fn vec4_unit_x() -> vec4 { vec4(1.0, 0.0, 0.0, 0.0) } +#[inline(always)] pure fn vec4_unit_y() -> vec4 { vec4(0.0, 1.0, 0.0, 0.0) } +#[inline(always)] pure fn vec4_unit_z() -> vec4 { vec4(0.0, 0.0, 1.0, 0.0) } +#[inline(always)] pure fn vec4_unit_w() -> vec4 { vec4(0.0, 0.0, 0.0, 1.0) } +#[inline(always)] pure fn vec4_identity() -> vec4 { vec4(1.0, 1.0, 1.0, 1.0) } + +// +// Vec4 Constructor +// +#[inline(always)] +pure fn vec4(x:float, y:float, z:float, w:float) -> vec4 { + vec4 { data: [ x, y, z, w ] } +} + +// +// Vector4 Implementation +// +impl vec4: Vector4 { + #[inline(always)] fn x() -> float { self.data[0] } + #[inline(always)] fn y() -> float { self.data[1] } + #[inline(always)] fn z() -> float { self.data[2] } + #[inline(always)] fn w() -> float { self.data[3] } +} + +// +// Vector Implementation +// +impl vec4: Vector { + #[inline(always)] + pure fn dim() -> uint { 4 } + + #[inline(always)] + pure fn index(&&i: uint) -> float { + self.data[i] + } + + #[inline(always)] + pure fn neg() -> vec4 { + vec4(-self[0], -self[1], -self[2], -self[3]) + } + + #[inline(always)] + pure fn add_f(&&value:float) -> vec4 { + vec4(self[0] + value, + self[1] + value, + self[2] + value, + self[3] + value) + } + + #[inline(always)] + pure fn sub_f(&&value:float) -> vec4 { + vec4(self[0] - value, + self[1] - value, + self[2] - value, + self[3] - value) + } + + #[inline(always)] + pure fn mul_f(&&value:float) -> vec4 { + vec4(self[0] * value, + self[1] * value, + self[2] * value, + self[3] * value) + } + + #[inline(always)] + pure fn div_f(&&value:float) -> vec4 { + vec4(self[0] / value, + self[1] / value, + self[2] / value, + self[3] / value) + } + + #[inline(always)] + pure fn add_v(&&other: vec4) -> vec4{ + vec4(self[0] + other[0], + self[1] + other[1], + self[2] + other[2], + self[3] + other[3]) + } + + #[inline(always)] + pure fn sub_v(&&other: vec4) -> vec4{ + vec4(self[0] - other[0], + self[1] - other[1], + self[2] - other[2], + self[3] - other[3]) + } + + #[inline(always)] + pure fn exact_eq(&&other:vec4) -> bool { + self[0] == other[0] && + self[1] == other[1] && + self[2] == other[2] && + self[3] == other[3] + } + + #[inline(always)] + pure fn fuzzy_eq(&&other: vec4) -> bool { + self[0].fuzzy_eq(&other[0]) && + self[1].fuzzy_eq(&other[1]) && + self[2].fuzzy_eq(&other[2]) && + self[3].fuzzy_eq(&other[3]) + } + + #[inline(always)] + pure fn eq(&&other:vec4) -> bool { + self.fuzzy_eq(other) + } + + #[inline(always)] + pure fn dot(&&other:vec4) -> float { + self[0] * other[0] + + self[1] * other[1] + + self[2] * other[2] + + self[3] * other[3] + } + + #[inline(always)] + pure fn magnitude2() -> float { + self[0] * self[0] + + self[1] * self[1] + + self[2] * self[2] + + self[3] + self[3] + } + + #[inline(always)] + pure fn magnitude() -> float { + sqrt(self.magnitude2()) + } + + #[inline(always)] + pure fn normalize() -> vec4 { + let n = 1.0 / self.magnitude(); + return self.mul_f(n); + } +} + +// +// Convert To String +// +impl vec4: ToStr { + fn to_str() -> ~str { + fmt!("vec4[ %f, %f, %f, %f ]", self[0], self[1], self[2], self[3]) + } +} \ No newline at end of file diff --git a/sublime/om3d.sublime-project b/sublime/om3d.sublime-project new file mode 100644 index 0000000..95f76b1 --- /dev/null +++ b/sublime/om3d.sublime-project @@ -0,0 +1,10 @@ +{ + "folders": + [ + { + "path": "../", + "file_exclude_patterns": ["*.sublime-workspace"], + "folder_exclude_patterns": ["*.dSYM"] + } + ] +} diff --git a/test/mat-test.rs b/test/mat-test.rs new file mode 100644 index 0000000..7312d4c --- /dev/null +++ b/test/mat-test.rs @@ -0,0 +1,16 @@ +// TODO + +#[test] +fn test_mat2() { + +} + +#[test] +fn test_mat3() { + +} + +#[test] +fn test_mat4() { + +} \ No newline at end of file diff --git a/test/quat-test.rs b/test/quat-test.rs new file mode 100644 index 0000000..9dc045f --- /dev/null +++ b/test/quat-test.rs @@ -0,0 +1,6 @@ +// TODO + +#[test] +fn test_quat() { + +} \ No newline at end of file diff --git a/test/vec-test.rs b/test/vec-test.rs new file mode 100644 index 0000000..7312d4c --- /dev/null +++ b/test/vec-test.rs @@ -0,0 +1,16 @@ +// TODO + +#[test] +fn test_mat2() { + +} + +#[test] +fn test_mat3() { + +} + +#[test] +fn test_mat4() { + +} \ No newline at end of file