Finished vector/point/normal/matrix code.

Sans bugs, optimization, and possibly convenience functions down
the line.
This commit is contained in:
Nathan Vegdahl 2015-12-26 17:50:37 -08:00
parent aa6b72493b
commit ff10e696c8
6 changed files with 1189 additions and 6 deletions

View File

@ -16,7 +16,8 @@ pub fn lerp<T: Lerp>(a: T, b: T, alpha: f32) -> T {
}
/// Interpolates a slice of Lerp data.
/// Interpolates a slice of data as if each adjecent pair of elements
/// represent a linear segment.
pub fn lerp_slice<T: Lerp + Copy>(s: &[T], alpha: f32) -> T {
debug_assert!(s.len() > 0);
debug_assert!(alpha >= 0.0);
@ -37,13 +38,13 @@ pub fn lerp_slice<T: Lerp + Copy>(s: &[T], alpha: f32) -> T {
impl Lerp for f32 {
fn lerp(self, other: f32, alpha: f32) -> f32 {
self + ((other - self) * alpha)
(self * (1.0 - alpha)) + (other * alpha)
}
}
impl Lerp for f64 {
fn lerp(self, other: f64, alpha: f32) -> f64 {
self + ((other - self) * (alpha as f64))
(self * (1.0 - alpha as f64)) + (other * alpha as f64)
}
}

View File

@ -2,6 +2,9 @@ mod math;
mod lerp;
mod float4;
mod vector;
mod point;
mod normal;
mod matrix;
fn main() {
println!("Hello, world!");

537
src/matrix.rs Normal file
View File

@ -0,0 +1,537 @@
#![allow(dead_code)]
use std;
use std::ops::{Index, IndexMut, Mul};
use float4::Float4;
use lerp::Lerp;
/// A 4x4 matrix, used for transforms
#[derive(Debug, Copy, Clone)]
pub struct Matrix4x4 {
values: [Float4; 4],
}
impl Matrix4x4 {
/// Creates a new identity matrix
pub fn new() -> Matrix4x4 {
Matrix4x4 {
values: [Float4::new(1.0, 0.0, 0.0, 0.0),
Float4::new(0.0, 1.0, 0.0, 0.0),
Float4::new(0.0, 0.0, 1.0, 0.0),
Float4::new(0.0, 0.0, 0.0, 1.0)],
}
}
/// Creates a new matrix with the specified values:
/// a b c d
/// e f g h
/// i j k l
/// m n o p
pub fn new_from_values(a: f32,
b: f32,
c: f32,
d: f32,
e: f32,
f: f32,
g: f32,
h: f32,
i: f32,
j: f32,
k: f32,
l: f32,
m: f32,
n: f32,
o: f32,
p: f32)
-> Matrix4x4 {
Matrix4x4 {
values: [Float4::new(a, b, c, d),
Float4::new(e, f, g, h),
Float4::new(i, j, k, l),
Float4::new(m, n, o, p)],
}
}
/// Returns whether the matrices are approximately equal to each other.
/// Each corresponding element in the matrices cannot have a relative error
/// exceeding `epsilon`.
pub fn aprx_eq(&self, other: Matrix4x4, epsilon: f32) -> bool {
let mut result = true;
for y in 0..4 {
for x in 0..4 {
// All of this stuff is just an approximate comparison
// of floating point numbers. See:
// http://floating-point-gui.de/errors/comparison/
// It might be worth breaking this out into a separate funcion,
// but I'm not entirely sure where to put it.
let a = self[y][x];
let b = other[y][x];
let aabs = a.abs();
let babs = b.abs();
let diff = (a - b).abs();
if a == b {
} else if (aabs <= std::f32::EPSILON) || (babs <= std::f32::EPSILON) {
result = result && (diff < std::f32::EPSILON);
} else {
let rel = 2.0 * diff / (aabs + babs);
println!("{}", rel);
result = result && (rel < epsilon);
}
}
}
return result;
}
/// Returns the transpose of the matrix
pub fn transposed(&self) -> Matrix4x4 {
Matrix4x4 {
values: {
[Float4::new(self[0][0], self[1][0], self[2][0], self[3][0]),
Float4::new(self[0][1], self[1][1], self[2][1], self[3][1]),
Float4::new(self[0][2], self[1][2], self[2][2], self[3][2]),
Float4::new(self[0][3], self[1][3], self[2][3], self[3][3])]
},
}
}
/// Returns the inverse of the Matrix
pub fn inverse(&self) -> Matrix4x4 {
let s0 = (self[0][0] * self[1][1]) - (self[1][0] * self[0][1]);
let s1 = (self[0][0] * self[1][2]) - (self[1][0] * self[0][2]);
let s2 = (self[0][0] * self[1][3]) - (self[1][0] * self[0][3]);
let s3 = (self[0][1] * self[1][2]) - (self[1][1] * self[0][2]);
let s4 = (self[0][1] * self[1][3]) - (self[1][1] * self[0][3]);
let s5 = (self[0][2] * self[1][3]) - (self[1][2] * self[0][3]);
let c5 = (self[2][2] * self[3][3]) - (self[3][2] * self[2][3]);
let c4 = (self[2][1] * self[3][3]) - (self[3][1] * self[2][3]);
let c3 = (self[2][1] * self[3][2]) - (self[3][1] * self[2][2]);
let c2 = (self[2][0] * self[3][3]) - (self[3][0] * self[2][3]);
let c1 = (self[2][0] * self[3][2]) - (self[3][0] * self[2][2]);
let c0 = (self[2][0] * self[3][1]) - (self[3][0] * self[2][1]);
// TODO: handle 0.0 determinant
let det = (s0 * c5) - (s1 * c4) + (s2 * c3) + (s3 * c2) - (s4 * c1) + (s5 * c0);
let invdet = 1.0 / det;
Matrix4x4 {
values: {
[Float4::new(((self[1][1] * c5) - (self[1][2] * c4) + (self[1][3] * c3)) * invdet,
((-self[0][1] * c5) + (self[0][2] * c4) - (self[0][3] * c3)) * invdet,
((self[3][1] * s5) - (self[3][2] * s4) + (self[3][3] * s3)) * invdet,
((-self[2][1] * s5) + (self[2][2] * s4) - (self[2][3] * s3)) * invdet),
Float4::new(((-self[1][0] * c5) + (self[1][2] * c2) - (self[1][3] * c1)) * invdet,
((self[0][0] * c5) - (self[0][2] * c2) + (self[0][3] * c1)) * invdet,
((-self[3][0] * s5) + (self[3][2] * s2) - (self[3][3] * s1)) * invdet,
((self[2][0] * s5) - (self[2][2] * s2) + (self[2][3] * s1)) * invdet),
Float4::new(((self[1][0] * c4) - (self[1][1] * c2) + (self[1][3] * c0)) * invdet,
((-self[0][0] * c4) + (self[0][1] * c2) - (self[0][3] * c0)) * invdet,
((self[3][0] * s4) - (self[3][1] * s2) + (self[3][3] * s0)) * invdet,
((-self[2][0] * s4) + (self[2][1] * s2) - (self[2][3] * s0)) * invdet),
Float4::new(((-self[1][0] * c3) + (self[1][1] * c1) - (self[1][2] * c0)) * invdet,
((self[0][0] * c3) - (self[0][1] * c1) + (self[0][2] * c0)) * invdet,
((-self[3][0] * s3) + (self[3][1] * s1) - (self[3][2] * s0)) * invdet,
((self[2][0] * s3) - (self[2][1] * s1) + (self[2][2] * s0)) * invdet)]
},
}
}
}
impl Index<usize> for Matrix4x4 {
type Output = Float4;
fn index<'a>(&'a self, _index: usize) -> &'a Float4 {
&self.values[_index]
}
}
impl IndexMut<usize> for Matrix4x4 {
fn index_mut<'a>(&'a mut self, _index: usize) -> &'a mut Float4 {
&mut self.values[_index]
}
}
impl PartialEq for Matrix4x4 {
fn eq(&self, other: &Matrix4x4) -> bool {
let mut result = true;
for y in 0..4 {
for x in 0..4 {
result = result && (self[y][x] == other[y][x]);
}
}
return result;
}
}
/// Multiply two matrices together
impl Mul<Matrix4x4> for Matrix4x4 {
type Output = Matrix4x4;
fn mul(self, other: Matrix4x4) -> Matrix4x4 {
let m = self.transposed();
Matrix4x4 {
values: [Float4::new((m[0] * other[0]).h_sum(),
(m[1] * other[0]).h_sum(),
(m[2] * other[0]).h_sum(),
(m[3] * other[0]).h_sum()),
Float4::new((m[0] * other[1]).h_sum(),
(m[1] * other[1]).h_sum(),
(m[2] * other[1]).h_sum(),
(m[3] * other[1]).h_sum()),
Float4::new((m[0] * other[2]).h_sum(),
(m[1] * other[2]).h_sum(),
(m[2] * other[2]).h_sum(),
(m[3] * other[2]).h_sum()),
Float4::new((m[0] * other[3]).h_sum(),
(m[1] * other[3]).h_sum(),
(m[2] * other[3]).h_sum(),
(m[3] * other[3]).h_sum())],
}
}
}
impl Lerp for Matrix4x4 {
fn lerp(self, other: Matrix4x4, alpha: f32) -> Matrix4x4 {
let alpha_minus = 1.0 - alpha;
Matrix4x4 {
values: [Float4::new((self[0][0] * alpha_minus) + (other[0][0] * alpha),
(self[0][1] * alpha_minus) + (other[0][1] * alpha),
(self[0][2] * alpha_minus) + (other[0][2] * alpha),
(self[0][3] * alpha_minus) + (other[0][3] * alpha)),
Float4::new((self[1][0] * alpha_minus) + (other[1][0] * alpha),
(self[1][1] * alpha_minus) + (other[1][1] * alpha),
(self[1][2] * alpha_minus) + (other[1][2] * alpha),
(self[1][3] * alpha_minus) + (other[1][3] * alpha)),
Float4::new((self[2][0] * alpha_minus) + (other[2][0] * alpha),
(self[2][1] * alpha_minus) + (other[2][1] * alpha),
(self[2][2] * alpha_minus) + (other[2][2] * alpha),
(self[2][3] * alpha_minus) + (other[2][3] * alpha)),
Float4::new((self[3][0] * alpha_minus) + (other[3][0] * alpha),
(self[3][1] * alpha_minus) + (other[3][1] * alpha),
(self[3][2] * alpha_minus) + (other[3][2] * alpha),
(self[3][3] * alpha_minus) + (other[3][3] * alpha))],
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use lerp::Lerp;
#[test]
fn equality_test() {
let a = Matrix4x4::new();
let b = Matrix4x4::new();
let c = Matrix4x4::new_from_values(1.1,
0.0,
0.0,
0.0,
0.0,
1.1,
0.0,
0.0,
0.0,
0.0,
1.1,
0.0,
0.0,
0.0,
0.0,
1.1);
assert_eq!(a, b);
assert!(a != c);
}
#[test]
fn aproximate_equality_test() {
let a = Matrix4x4::new();
let b = Matrix4x4::new_from_values(1.001,
0.0,
0.0,
0.0,
0.0,
1.001,
0.0,
0.0,
0.0,
0.0,
1.001,
0.0,
0.0,
0.0,
0.0,
1.001);
let c = Matrix4x4::new_from_values(1.003,
0.0,
0.0,
0.0,
0.0,
1.003,
0.0,
0.0,
0.0,
0.0,
1.003,
0.0,
0.0,
0.0,
0.0,
1.003);
let d = Matrix4x4::new_from_values(-1.001,
0.0,
0.0,
0.0,
0.0,
-1.001,
0.0,
0.0,
0.0,
0.0,
-1.001,
0.0,
0.0,
0.0,
0.0,
-1.001);
assert!(a.aprx_eq(b, 0.002));
assert!(!a.aprx_eq(c, 0.002));
assert!(!a.aprx_eq(d, 0.002));
}
#[test]
fn multiply_test() {
let a = Matrix4x4::new_from_values(1.0,
2.0,
2.0,
1.5,
3.0,
6.0,
7.0,
8.0,
9.0,
2.0,
11.0,
12.0,
13.0,
7.0,
15.0,
3.0);
let b = Matrix4x4::new_from_values(1.0,
5.0,
9.0,
13.0,
2.0,
6.0,
10.0,
14.0,
3.0,
7.0,
11.0,
15.0,
4.0,
8.0,
12.0,
16.0);
let c = Matrix4x4::new_from_values(266.0,
141.0,
331.0,
188.5,
292.0,
158.0,
366.0,
213.0,
318.0,
175.0,
401.0,
237.5,
344.0,
192.0,
436.0,
262.0);
assert_eq!(a * b, c);
}
#[test]
fn inverse_test() {
let a = Matrix4x4::new_from_values(1.0,
0.33,
0.0,
-2.0,
0.0,
1.0,
0.0,
0.0,
2.1,
0.7,
1.3,
0.0,
0.0,
0.0,
0.0,
-1.0);
let b = a.inverse();
let c = Matrix4x4::new();
assert!((a * b).aprx_eq(c, 0.00001));
}
#[test]
fn transpose_test() {
let a = Matrix4x4::new_from_values(1.0,
2.0,
3.0,
4.0,
5.0,
6.0,
7.0,
8.0,
9.0,
10.0,
11.0,
12.0,
13.0,
14.0,
15.0,
16.0);
let b = Matrix4x4::new_from_values(1.0,
5.0,
9.0,
13.0,
2.0,
6.0,
10.0,
14.0,
3.0,
7.0,
11.0,
15.0,
4.0,
8.0,
12.0,
16.0);
let c = a.transposed();
assert_eq!(b, c);
}
#[test]
fn lerp_test() {
let a = Matrix4x4::new_from_values(0.0,
2.0,
2.0,
3.0,
4.0,
5.0,
6.0,
7.0,
8.0,
9.0,
10.0,
11.0,
12.0,
13.0,
14.0,
15.0);
let b = Matrix4x4::new_from_values(-1.0,
1.0,
3.0,
4.0,
5.0,
6.0,
7.0,
8.0,
9.0,
10.0,
11.0,
12.0,
13.0,
14.0,
15.0,
16.0);
let c1 = Matrix4x4::new_from_values(-0.25,
1.75,
2.25,
3.25,
4.25,
5.25,
6.25,
7.25,
8.25,
9.25,
10.25,
11.25,
12.25,
13.25,
14.25,
15.25);
let c2 = Matrix4x4::new_from_values(-0.5,
1.5,
2.5,
3.5,
4.5,
5.5,
6.5,
7.5,
8.5,
9.5,
10.5,
11.5,
12.5,
13.5,
14.5,
15.5);
let c3 = Matrix4x4::new_from_values(-0.75,
1.25,
2.75,
3.75,
4.75,
5.75,
6.75,
7.75,
8.75,
9.75,
10.75,
11.75,
12.75,
13.75,
14.75,
15.75);
assert_eq!(a.lerp(b, 0.0), a);
assert_eq!(a.lerp(b, 0.25), c1);
assert_eq!(a.lerp(b, 0.5), c2);
assert_eq!(a.lerp(b, 0.75), c3);
assert_eq!(a.lerp(b, 1.0), b);
}
}

270
src/normal.rs Normal file
View File

@ -0,0 +1,270 @@
#![allow(dead_code)]
use std::ops::{Index, IndexMut, Add, Sub, Mul, Div};
use std::cmp::PartialEq;
use lerp::Lerp;
use math::{DotProduct, CrossProduct};
use float4::Float4;
use matrix::Matrix4x4;
/// A surface normal in 3d homogeneous space.
#[derive(Debug, Copy, Clone)]
pub struct Normal {
pub co: Float4,
}
impl Normal {
pub fn new(x: f32, y: f32, z: f32) -> Normal {
Normal { co: Float4::new(x, y, z, 0.0) }
}
pub fn length(&self) -> f32 {
(self.co * self.co).h_sum().sqrt()
}
pub fn length2(&self) -> f32 {
(self.co * self.co).h_sum()
}
pub fn normalized(&self) -> Normal {
*self / self.length()
}
}
impl Index<usize> for Normal {
type Output = f32;
fn index(&self, index: usize) -> &f32 {
debug_assert!(index < 3);
&self.co[index]
}
}
impl IndexMut<usize> for Normal {
fn index_mut(&mut self, index: usize) -> &mut f32 {
debug_assert!(index < 3);
&mut self.co[index]
}
}
impl PartialEq for Normal {
fn eq(&self, other: &Normal) -> bool {
self.co == other.co
}
}
impl Add for Normal {
type Output = Normal;
fn add(self, other: Normal) -> Normal {
Normal { co: self.co + other.co }
}
}
impl Sub for Normal {
type Output = Normal;
fn sub(self, other: Normal) -> Normal {
Normal { co: self.co - other.co }
}
}
impl Mul<f32> for Normal {
type Output = Normal;
fn mul(self, other: f32) -> Normal {
Normal { co: self.co * other }
}
}
impl Mul<Matrix4x4> for Normal {
type Output = Normal;
fn mul(self, other: Matrix4x4) -> Normal {
let mat = other.inverse().transposed();
Normal {
co: Float4::new((self[0] * mat[0][0]) + (self[1] * mat[0][1]) + (self[2] * mat[0][2]),
(self[0] * mat[1][0]) + (self[1] * mat[1][1]) + (self[2] * mat[1][2]),
(self[0] * mat[2][0]) + (self[1] * mat[2][1]) + (self[2] * mat[2][2]),
0.0),
}
}
}
impl Div<f32> for Normal {
type Output = Normal;
fn div(self, other: f32) -> Normal {
Normal { co: self.co / other }
}
}
impl Lerp for Normal {
fn lerp(self, other: Normal, alpha: f32) -> Normal {
(self * (1.0 - alpha)) + (other * alpha)
}
}
impl DotProduct for Normal {
fn dot(self, other: Normal) -> f32 {
(self.co * other.co).h_sum()
}
}
impl CrossProduct for Normal {
fn cross(self, other: Normal) -> Normal {
Normal {
co: Float4::new((self[1] * other[2]) - (self[2] * other[1]),
(self[2] * other[0]) - (self[0] * other[2]),
(self[0] * other[1]) - (self[1] * other[0]),
0.0),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use math::*;
use lerp::Lerp;
use matrix::Matrix4x4;
#[test]
fn add() {
let v1 = Normal::new(1.0, 2.0, 3.0);
let v2 = Normal::new(1.5, 4.5, 2.5);
let v3 = Normal::new(2.5, 6.5, 5.5);
assert_eq!(v3, v1 + v2);
}
#[test]
fn sub() {
let v1 = Normal::new(1.0, 2.0, 3.0);
let v2 = Normal::new(1.5, 4.5, 2.5);
let v3 = Normal::new(-0.5, -2.5, 0.5);
assert_eq!(v3, v1 - v2);
}
#[test]
fn mul_scalar() {
let v1 = Normal::new(1.0, 2.0, 3.0);
let v2 = 2.0;
let v3 = Normal::new(2.0, 4.0, 6.0);
assert_eq!(v3, v1 * v2);
}
#[test]
fn mul_matrix_1() {
let n = Normal::new(1.0, 2.5, 4.0);
let m = Matrix4x4::new_from_values(1.0,
2.0,
2.0,
1.5,
3.0,
6.0,
7.0,
8.0,
9.0,
2.0,
11.0,
12.0,
13.0,
7.0,
15.0,
3.0);
let nm = Normal::new(-19.258825, 5.717648, -1.770588);
assert!(((n * m) - nm).length2() < 0.00001);
}
#[test]
fn div() {
let v1 = Normal::new(1.0, 2.0, 3.0);
let v2 = 2.0;
let v3 = Normal::new(0.5, 1.0, 1.5);
assert_eq!(v3, v1 / v2);
}
#[test]
fn length() {
let n = Normal::new(1.0, 2.0, 3.0);
assert!((n.length() - 3.7416573867739413).abs() < 0.000001);
}
#[test]
fn length2() {
let n = Normal::new(1.0, 2.0, 3.0);
assert_eq!(n.length2(), 14.0);
}
#[test]
fn normalized() {
let n1 = Normal::new(1.0, 2.0, 3.0);
let n2 = Normal::new(0.2672612419124244, 0.5345224838248488, 0.8017837257372732);
let n3 = n1.normalized();
assert!((n3[0] - n2[0]).abs() < 0.000001);
assert!((n3[1] - n2[1]).abs() < 0.000001);
assert!((n3[2] - n2[2]).abs() < 0.000001);
}
#[test]
fn dot_test() {
let v1 = Normal::new(1.0, 2.0, 3.0);
let v2 = Normal::new(1.5, 4.5, 2.5);
let v3 = 18.0f32;
assert_eq!(v3, v1.dot(v2));
}
#[test]
fn cross_test() {
let v1 = Normal::new(1.0, 0.0, 0.0);
let v2 = Normal::new(0.0, 1.0, 0.0);
let v3 = Normal::new(0.0, 0.0, 1.0);
assert_eq!(v3, v1.cross(v2));
}
#[test]
fn lerp1() {
let n1 = Normal::new(1.0, 2.0, 1.0);
let n2 = Normal::new(-2.0, 1.0, -1.0);
let n3 = Normal::new(1.0, 2.0, 1.0);
assert_eq!(n3, n1.lerp(n2, 0.0));
}
#[test]
fn lerp2() {
let n1 = Normal::new(1.0, 2.0, 1.0);
let n2 = Normal::new(-2.0, 1.0, -1.0);
let n3 = Normal::new(-2.0, 1.0, -1.0);
assert_eq!(n3, n1.lerp(n2, 1.0));
}
#[test]
fn lerp3() {
let n1 = Normal::new(1.0, 2.0, 1.0);
let n2 = Normal::new(-2.0, 1.0, -1.0);
let n3 = Normal::new(-0.5, 1.5, 0.0);
assert_eq!(n3, n1.lerp(n2, 0.5));
}
}

247
src/point.rs Normal file
View File

@ -0,0 +1,247 @@
#![allow(dead_code)]
use std::ops::{Index, IndexMut, Add, Sub, Mul};
use std::cmp::PartialEq;
use lerp::Lerp;
use float4::Float4;
use vector::Vector;
use matrix::Matrix4x4;
/// A position in 3d homogeneous space.
#[derive(Debug, Copy, Clone)]
pub struct Point {
pub co: Float4,
}
impl Point {
pub fn new(x: f32, y: f32, z: f32) -> Point {
Point { co: Float4::new(x, y, z, 1.0) }
}
/// Returns the point in standardized coordinates, where the
/// fourth homogeneous component has been normalized to 1.0.
pub fn norm(&self) -> Point {
Point { co: self.co / self.co[3] }
}
}
impl Index<usize> for Point {
type Output = f32;
fn index(&self, index: usize) -> &f32 {
debug_assert!(index < 3);
&self.co[index]
}
}
impl IndexMut<usize> for Point {
fn index_mut(&mut self, index: usize) -> &mut f32 {
debug_assert!(index < 3);
&mut self.co[index]
}
}
impl PartialEq for Point {
fn eq(&self, other: &Point) -> bool {
self.co == other.co
}
}
impl Add<Vector> for Point {
type Output = Point;
fn add(self, other: Vector) -> Point {
Point { co: self.co + other.co }
}
}
impl Sub for Point {
type Output = Vector;
fn sub(self, other: Point) -> Vector {
Vector { co: self.norm().co - other.norm().co }
}
}
impl Mul<Matrix4x4> for Point {
type Output = Point;
fn mul(self, other: Matrix4x4) -> Point {
Point {
co: Float4::new((self.co * other[0]).h_sum(),
(self.co * other[1]).h_sum(),
(self.co * other[2]).h_sum(),
(self.co * other[3]).h_sum()),
}
}
}
impl Lerp for Point {
fn lerp(self, other: Point, alpha: f32) -> Point {
let s = self.norm();
let o = other.norm();
Point { co: (s.co * (1.0 - alpha)) + (o.co * alpha) }
}
}
#[cfg(test)]
mod tests {
use super::*;
use vector::Vector;
use lerp::Lerp;
use matrix::Matrix4x4;
#[test]
fn norm() {
let mut p1 = Point::new(1.0, 2.0, 3.0);
let p2 = Point::new(2.0, 4.0, 6.0);
p1.co[3] = 0.5;
assert_eq!(p2, p1.norm());
}
#[test]
fn add() {
let p1 = Point::new(1.0, 2.0, 3.0);
let v1 = Vector::new(1.5, 4.5, 2.5);
let p2 = Point::new(2.5, 6.5, 5.5);
assert_eq!(p2, p1 + v1);
}
#[test]
fn sub() {
let p1 = Point::new(1.0, 2.0, 3.0);
let p2 = Point::new(1.5, 4.5, 2.5);
let v1 = Vector::new(-0.5, -2.5, 0.5);
assert_eq!(v1, p1 - p2);
}
#[test]
fn mul_matrix_1() {
let p = Point::new(1.0, 2.5, 4.0);
let m = Matrix4x4::new_from_values(1.0,
2.0,
2.0,
1.5,
3.0,
6.0,
7.0,
8.0,
9.0,
2.0,
11.0,
12.0,
0.0,
0.0,
0.0,
1.0);
let pm = Point::new(15.5, 54.0, 70.0);
assert_eq!(p * m, pm);
}
#[test]
fn mul_matrix_2() {
let p = Point::new(1.0, 2.5, 4.0);
let m = Matrix4x4::new_from_values(1.0,
2.0,
2.0,
1.5,
3.0,
6.0,
7.0,
8.0,
9.0,
2.0,
11.0,
12.0,
2.0,
3.0,
1.0,
5.0);
let mut pm = Point::new(15.5, 54.0, 70.0);
pm.co[3] = 18.5;
assert_eq!(p * m, pm);
}
#[test]
fn mul_matrix_3() {
// Make sure matrix multiplication composes the way one would expect
let p = Point::new(1.0, 2.5, 4.0);
let m1 = Matrix4x4::new_from_values(1.0,
2.0,
2.0,
1.5,
3.0,
6.0,
7.0,
8.0,
9.0,
2.0,
11.0,
12.0,
13.0,
7.0,
15.0,
3.0);
let m2 = Matrix4x4::new_from_values(4.0,
1.0,
2.0,
3.5,
3.0,
6.0,
5.0,
2.0,
2.0,
2.0,
4.0,
12.0,
5.0,
7.0,
8.0,
11.0);
println!("{:?}", m1 * m2);
let pmm1 = p * (m1 * m2);
let pmm2 = (p * m1) * m2;
assert!((pmm1 - pmm2).length2() <= 0.00001); // Assert pmm1 and pmm2 are roughly equal
}
#[test]
fn lerp1() {
let p1 = Point::new(1.0, 2.0, 1.0);
let p2 = Point::new(-2.0, 1.0, -1.0);
let p3 = Point::new(1.0, 2.0, 1.0);
assert_eq!(p3, p1.lerp(p2, 0.0));
}
#[test]
fn lerp2() {
let p1 = Point::new(1.0, 2.0, 1.0);
let p2 = Point::new(-2.0, 1.0, -1.0);
let p3 = Point::new(-2.0, 1.0, -1.0);
assert_eq!(p3, p1.lerp(p2, 1.0));
}
#[test]
fn lerp3() {
let p1 = Point::new(1.0, 2.0, 1.0);
let p2 = Point::new(-2.0, 1.0, -1.0);
let p3 = Point::new(-0.5, 1.5, 0.0);
assert_eq!(p3, p1.lerp(p2, 0.5));
}
}

View File

@ -6,17 +6,30 @@ use std::cmp::PartialEq;
use lerp::Lerp;
use math::{DotProduct, CrossProduct};
use float4::Float4;
use matrix::Matrix4x4;
/// A direction vector in 3d homogeneous space.
#[derive(Debug, Copy, Clone)]
pub struct Vector {
co: Float4,
pub co: Float4,
}
impl Vector {
pub fn new(x: f32, y: f32, z: f32) -> Vector {
Vector { co: Float4::new(x, y, z, 0.0) }
}
pub fn length(&self) -> f32 {
(self.co * self.co).h_sum().sqrt()
}
pub fn length2(&self) -> f32 {
(self.co * self.co).h_sum()
}
pub fn normalized(&self) -> Vector {
*self / self.length()
}
}
@ -73,6 +86,20 @@ impl Mul<f32> for Vector {
}
impl Mul<Matrix4x4> for Vector {
type Output = Vector;
fn mul(self, other: Matrix4x4) -> Vector {
Vector {
co: Float4::new((self.co * other[0]).h_sum(),
(self.co * other[1]).h_sum(),
(self.co * other[2]).h_sum(),
(self.co * other[3]).h_sum()),
}
}
}
impl Div<f32> for Vector {
type Output = Vector;
@ -84,7 +111,7 @@ impl Div<f32> for Vector {
impl Lerp for Vector {
fn lerp(self, other: Vector, alpha: f32) -> Vector {
self + ((other - self) * alpha)
(self * (1.0 - alpha)) + (other * alpha)
}
}
@ -112,6 +139,8 @@ impl CrossProduct for Vector {
mod tests {
use super::*;
use math::*;
use lerp::Lerp;
use matrix::Matrix4x4;
#[test]
fn add() {
@ -132,7 +161,7 @@ mod tests {
}
#[test]
fn mul() {
fn mul_scalar() {
let v1 = Vector::new(1.0, 2.0, 3.0);
let v2 = 2.0;
let v3 = Vector::new(2.0, 4.0, 6.0);
@ -140,6 +169,53 @@ mod tests {
assert_eq!(v3, v1 * v2);
}
#[test]
fn mul_matrix_1() {
let v = Vector::new(1.0, 2.5, 4.0);
let m = Matrix4x4::new_from_values(1.0,
2.0,
2.0,
1.5,
3.0,
6.0,
7.0,
8.0,
9.0,
2.0,
11.0,
12.0,
13.0,
7.0,
15.0,
3.0);
let mut vm = Vector::new(14.0, 46.0, 58.0);
vm.co[3] = 90.5;
assert_eq!(v * m, vm);
}
#[test]
fn mul_matrix_2() {
let v = Vector::new(1.0, 2.5, 4.0);
let m = Matrix4x4::new_from_values(1.0,
2.0,
2.0,
1.5,
3.0,
6.0,
7.0,
8.0,
9.0,
2.0,
11.0,
12.0,
0.0,
0.0,
0.0,
1.0);
let vm = Vector::new(14.0, 46.0, 58.0);
assert_eq!(v * m, vm);
}
#[test]
fn div() {
let v1 = Vector::new(1.0, 2.0, 3.0);
@ -149,6 +225,28 @@ mod tests {
assert_eq!(v3, v1 / v2);
}
#[test]
fn length() {
let v = Vector::new(1.0, 2.0, 3.0);
assert!((v.length() - 3.7416573867739413).abs() < 0.000001);
}
#[test]
fn length2() {
let v = Vector::new(1.0, 2.0, 3.0);
assert_eq!(v.length2(), 14.0);
}
#[test]
fn normalized() {
let v1 = Vector::new(1.0, 2.0, 3.0);
let v2 = Vector::new(0.2672612419124244, 0.5345224838248488, 0.8017837257372732);
let v3 = v1.normalized();
assert!((v3[0] - v2[0]).abs() < 0.000001);
assert!((v3[1] - v2[1]).abs() < 0.000001);
assert!((v3[2] - v2[2]).abs() < 0.000001);
}
#[test]
fn dot_test() {
let v1 = Vector::new(1.0, 2.0, 3.0);
@ -166,4 +264,31 @@ mod tests {
assert_eq!(v3, v1.cross(v2));
}
#[test]
fn lerp1() {
let v1 = Vector::new(1.0, 2.0, 1.0);
let v2 = Vector::new(-2.0, 1.0, -1.0);
let v3 = Vector::new(1.0, 2.0, 1.0);
assert_eq!(v3, v1.lerp(v2, 0.0));
}
#[test]
fn lerp2() {
let v1 = Vector::new(1.0, 2.0, 1.0);
let v2 = Vector::new(-2.0, 1.0, -1.0);
let v3 = Vector::new(-2.0, 1.0, -1.0);
assert_eq!(v3, v1.lerp(v2, 1.0));
}
#[test]
fn lerp3() {
let v1 = Vector::new(1.0, 2.0, 1.0);
let v2 = Vector::new(-2.0, 1.0, -1.0);
let v3 = Vector::new(-0.5, 1.5, 0.0);
assert_eq!(v3, v1.lerp(v2, 0.5));
}
}