From ff10e696c853afaad8319f88473374915117c6d9 Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Sat, 26 Dec 2015 17:50:37 -0800 Subject: [PATCH] Finished vector/point/normal/matrix code. Sans bugs, optimization, and possibly convenience functions down the line. --- src/lerp.rs | 7 +- src/main.rs | 3 + src/matrix.rs | 537 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/normal.rs | 270 +++++++++++++++++++++++++ src/point.rs | 247 +++++++++++++++++++++++ src/vector.rs | 131 +++++++++++- 6 files changed, 1189 insertions(+), 6 deletions(-) create mode 100644 src/matrix.rs create mode 100644 src/normal.rs create mode 100644 src/point.rs diff --git a/src/lerp.rs b/src/lerp.rs index 00558a1..87c6067 100644 --- a/src/lerp.rs +++ b/src/lerp.rs @@ -16,7 +16,8 @@ pub fn 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(s: &[T], alpha: f32) -> T { debug_assert!(s.len() > 0); debug_assert!(alpha >= 0.0); @@ -37,13 +38,13 @@ pub fn lerp_slice(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) } } diff --git a/src/main.rs b/src/main.rs index 317d754..d598f32 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,9 @@ mod math; mod lerp; mod float4; mod vector; +mod point; +mod normal; +mod matrix; fn main() { println!("Hello, world!"); diff --git a/src/matrix.rs b/src/matrix.rs new file mode 100644 index 0000000..f3a85dd --- /dev/null +++ b/src/matrix.rs @@ -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 for Matrix4x4 { + type Output = Float4; + + fn index<'a>(&'a self, _index: usize) -> &'a Float4 { + &self.values[_index] + } +} + + +impl IndexMut 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 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); + } +} diff --git a/src/normal.rs b/src/normal.rs new file mode 100644 index 0000000..1a16f8d --- /dev/null +++ b/src/normal.rs @@ -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 for Normal { + type Output = f32; + + fn index(&self, index: usize) -> &f32 { + debug_assert!(index < 3); + + &self.co[index] + } +} + +impl IndexMut 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 for Normal { + type Output = Normal; + + fn mul(self, other: f32) -> Normal { + Normal { co: self.co * other } + } +} + +impl Mul 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 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)); + } +} diff --git a/src/point.rs b/src/point.rs new file mode 100644 index 0000000..57f921e --- /dev/null +++ b/src/point.rs @@ -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 for Point { + type Output = f32; + + fn index(&self, index: usize) -> &f32 { + debug_assert!(index < 3); + + &self.co[index] + } +} + +impl IndexMut 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 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 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)); + } +} diff --git a/src/vector.rs b/src/vector.rs index fb1d292..91caeb1 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -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 for Vector { } +impl Mul 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 for Vector { type Output = Vector; @@ -84,7 +111,7 @@ impl Div 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)); + } }