#![allow(dead_code)] use std::ops::{Add, Sub, Mul, Div, Neg}; use std::cmp::PartialEq; use lerp::Lerp; use float4::Float4; use super::{DotProduct, CrossProduct}; use super::{Matrix4x4, Vector}; /// 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() } pub fn into_vector(self) -> Vector { Vector::new(self.co.get_0(), self.co.get_1(), self.co.get_2()) } pub fn get_n(&self, n: usize) -> f32 { match n { 0 => self.x(), 1 => self.y(), 2 => self.z(), _ => panic!("Attempt to access dimension beyond z."), } } pub fn x(&self) -> f32 { self.co.get_0() } pub fn y(&self) -> f32 { self.co.get_1() } pub fn z(&self) -> f32 { self.co.get_2() } pub fn set_x(&mut self, x: f32) { self.co.set_0(x); } pub fn set_y(&mut self, y: f32) { self.co.set_1(y); } pub fn set_z(&mut self, z: f32) { self.co.set_2(z); } } 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.co * mat[0]).h_sum(), (self.co * mat[1]).h_sum(), (self.co * mat[2]).h_sum(), 0.0), } } } impl Div for Normal { type Output = Normal; fn div(self, other: f32) -> Normal { Normal { co: self.co / other } } } impl Neg for Normal { type Output = Normal; fn neg(self) -> Normal { Normal { co: self.co * -1.0 } } } 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.co.get_1() * other.co.get_2()) - (self.co.get_2() * other.co.get_1()), (self.co.get_2() * other.co.get_0()) - (self.co.get_0() * other.co.get_2()), (self.co.get_0() * other.co.get_1()) - (self.co.get_1() * other.co.get_0()), 0.0), } } } #[cfg(test)] mod tests { use super::*; use super::super::{Matrix4x4, CrossProduct, DotProduct}; use lerp::Lerp; #[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.x() - n2.x()).abs() < 0.000001); assert!((n3.y() - n2.y()).abs() < 0.000001); assert!((n3.z() - n2.z()).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)); } }