diff --git a/rtiow/src/lib.rs b/rtiow/src/lib.rs index 31e1bb2..9b789be 100644 --- a/rtiow/src/lib.rs +++ b/rtiow/src/lib.rs @@ -1,7 +1 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } -} +pub mod vec3; diff --git a/rtiow/src/vec3.rs b/rtiow/src/vec3.rs new file mode 100644 index 0000000..7ac2f07 --- /dev/null +++ b/rtiow/src/vec3.rs @@ -0,0 +1,217 @@ +use std::f32::consts::PI; +use std::fmt; +use std::num::ParseFloatError; +use std::ops::Add; +use std::ops::Div; +use std::ops::Index; +use std::ops::Mul; +use std::ops::Sub; +use std::str; + +#[derive(Default, Debug, PartialEq, Copy, Clone)] +pub struct Vec3 { + x: f32, + y: f32, + z: f32, +} + +fn cross(v1: Vec3, v2: Vec3) -> Vec3 { + Vec3 { + x: v1.y * v2.z - v1.z * v2.y, + y: v1.x * v2.z - v1.z * v2.x, + z: v1.x * v2.y - v1.y * v2.x, + } +} + +fn dot(v1: Vec3, v2: Vec3) -> f32 { + v1.x * v2.x + v1.y * v2.y + v1.z * v2.z +} + +impl Vec3 { + pub fn new(x: f32, y: f32, z: f32) -> Vec3 { + Vec3 { x, y, z } + } + + pub fn length(self) -> f32 { + self.squared_length().sqrt() + } + + pub fn squared_length(self) -> f32 { + self.x * self.x + self.y * self.y + self.z * self.z + } + + pub fn unit_vector(self) -> Vec3 { + let k = 1. / self.length(); + Vec3 { + x: self.x / k, + y: self.y / k, + z: self.z / k, + } + } + + pub fn make_unit_vector(&mut self) { + *self = self.unit_vector(); + } +} + +impl fmt::Display for Vec3 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{} {} {}", self.x, self.y, self.z) + } +} + +impl str::FromStr for Vec3 { + type Err = ParseFloatError; + fn from_str(s: &str) -> Result { + let coords: Vec<&str> = s.split(" ").collect(); + Ok(Vec3 { + x: coords[0].parse::()?, + y: coords[1].parse::()?, + z: coords[2].parse::()?, + }) + } +} + +impl Add for Vec3 { + type Output = Vec3; + + fn add(self, r: Vec3) -> Vec3 { + Vec3 { + x: self.x + r.x, + y: self.y + r.y, + z: self.z + r.z, + } + } +} + +impl Div for Vec3 { + type Output = Vec3; + + fn div(self, r: f32) -> Vec3 { + Vec3 { + x: self.x / r, + y: self.y / r, + z: self.z / r, + } + } +} + +impl Index for Vec3 { + type Output = f32; + fn index(&self, idx: usize) -> &f32 { + match idx { + 0 => &self.x, + 1 => &self.y, + 2 => &self.z, + _ => panic!(format!("idx {} out of range for vec3", idx)), + } + } +} + +impl Mul for Vec3 { + type Output = Vec3; + + fn mul(self, r: Vec3) -> Vec3 { + Vec3 { + x: self.x * r.x, + y: self.y * r.y, + z: self.z * r.z, + } + } +} + +impl Mul for Vec3 { + type Output = Vec3; + + fn mul(self, r: f32) -> Vec3 { + Vec3 { + x: self.x * r, + y: self.y * r, + z: self.z * r, + } + } +} + +impl Sub for Vec3 { + type Output = Vec3; + + fn sub(self, r: Vec3) -> Vec3 { + Vec3 { + x: self.x - r.x, + y: self.y - r.y, + z: self.z - r.z, + } + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use super::*; + + #[test] + fn vec_add() { + let v0 = Vec3::new(1., 2., 3.); + let v1 = Vec3::new(1., 1., 1.); + assert_eq!(v0 + v1, Vec3::new(2., 3., 4.)); + } + + #[test] + fn vec_div() { + let v0 = Vec3::new(1., 2., 4.); + assert_eq!(v0 / 2., Vec3::new(0.5, 1., 2.)); + } + + #[test] + fn vec_mul() { + let v0 = Vec3::new(1., 2., 4.); + assert_eq!(v0 * 0.5, Vec3::new(0.5, 1., 2.)); + assert_eq!(v0 * v0, Vec3::new(1., 4., 16.)); + } + + #[test] + fn vec_sub() { + let v0 = Vec3::new(1., 2., 3.); + let v1 = Vec3::new(1., 1., 1.); + assert_eq!(v0 - v1, Vec3::new(0., 1., 2.)); + } + + #[test] + fn vec_idx() { + let v0 = Vec3::new(1., 2., 3.); + assert_eq!(v0[2], 3.); + } + + #[test] + fn vec_display() { + let v0 = Vec3::new(1., 2., 3.); + assert_eq!(format!("{}", v0), "1 2 3".to_owned()); + } + + #[test] + fn vec_from_str() { + assert_eq!(Vec3::from_str("1. 2. 3.").unwrap(), Vec3::new(1., 2., 3.)); + } + + #[test] + fn vec_str_roundtrip() { + let v = Vec3::from_str("1 2 3").unwrap(); + let s = format!("{}", v); + assert_eq!(v, Vec3::from_str(&s).unwrap()); + } + + #[test] + fn vec_dot() { + let v0 = Vec3::new(1., 0., 0.); + let v1 = Vec3::new(-1., 0., 0.); + assert_eq!(dot(v0, v1), v0.length() * v1.length() * PI.cos()); + } + + #[test] + fn vec_cross() { + let v0 = Vec3::new(1., 0., 0.); + let v1 = Vec3::new(0., 1., 0.); + assert_eq!(cross(v0, v1), Vec3::new(0., 0., 1.)); + } +}