From 39c947b461f2c7b2f9b0b04c57f78b35a095a943 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Sun, 9 Jun 2024 14:08:01 -0400 Subject: [PATCH] Implement the remaining operations and the projectile simulator --- ray-tracer/Cargo.toml | 3 + ray-tracer/src/bin/projectile.rs | 37 +++++++ ray-tracer/src/lib.rs | 2 +- ray-tracer/src/types.rs | 179 ++++++++++++++++++++++++++++--- 4 files changed, 206 insertions(+), 15 deletions(-) create mode 100644 ray-tracer/src/bin/projectile.rs diff --git a/ray-tracer/Cargo.toml b/ray-tracer/Cargo.toml index a367ea5..bdd6b82 100644 --- a/ray-tracer/Cargo.toml +++ b/ray-tracer/Cargo.toml @@ -6,3 +6,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] + +[[bin]] +name = "projectile" diff --git a/ray-tracer/src/bin/projectile.rs b/ray-tracer/src/bin/projectile.rs new file mode 100644 index 0000000..3cb5c6f --- /dev/null +++ b/ray-tracer/src/bin/projectile.rs @@ -0,0 +1,37 @@ +use ray_tracer::types::*; + +#[derive(Clone, Copy)] +struct Projectile { + position: Point, + velocity: Vector, +} + +struct Environment { + gravity: Vector, + wind: Vector, +} + +fn tick(env: &Environment, proj: &Projectile) -> Projectile { + let position = proj.position + proj.velocity; + let velocity = proj.velocity + env.gravity + env.wind; + Projectile { position, velocity } +} + +fn main() { + let start = Projectile { + position: Point::new(0., 1., 0.), + velocity: Vector::new(1., 1., 0.).normalize() * 5., + }; + + let e = Environment { + gravity: Vector::new(0., -0.1, 0.), + wind: Vector::new(-0.1, 0., 0.), + }; + + let mut p = start; + while p.position.y > 0. { + p = tick(&e, &p); + } + + println!("distance travelled: [{}] {} {} {}", (p.position - start.position).magnitude(), p.position.x, p.position.y, p.position.z); +} diff --git a/ray-tracer/src/lib.rs b/ray-tracer/src/lib.rs index fc1a2f5..97fe92f 100644 --- a/ray-tracer/src/lib.rs +++ b/ray-tracer/src/lib.rs @@ -1,2 +1,2 @@ -mod types; +pub mod types; diff --git a/ray-tracer/src/types.rs b/ray-tracer/src/types.rs index 5174656..04e260e 100644 --- a/ray-tracer/src/types.rs +++ b/ray-tracer/src/types.rs @@ -4,14 +4,20 @@ fn eq_f64(l: f64, r: f64) -> bool { (l - r).abs() < EPSILON } -#[derive(Debug)] -struct Tuple { - x: f64, - y: f64, - z: f64, - w: f64, // Used for very low-level math. w = 1.0 indicates a point, w = 0.0 indicates a vector. - // Theoretically the type system should make this redundant, so operations on points - // and vectors can always assert the correct value. +#[derive(Debug, Clone, Copy)] +pub struct Tuple { + pub x: f64, + pub y: f64, + pub z: f64, + pub w: f64, // Used for very low-level math. w = 1.0 indicates a point, w = 0.0 indicates a vector. + // Theoretically the type system should make this redundant, so operations on points + // and vectors can always assert the correct value. +} + +impl Tuple { + fn dot(&self, r: &Tuple) -> f64 { + self.x * r.x + self.y * r.y + self.z * r.z + self.w * r.w + } } impl PartialEq for Tuple { @@ -56,15 +62,46 @@ impl std::ops::Neg for Tuple { } } -#[derive(Debug, PartialEq)] -struct Point(Tuple); +impl std::ops::Mul for Tuple { + type Output = Tuple; + fn mul(self, scalar: f64) -> Self::Output { + return Self::Output { + x: self.x * scalar, + y: self.y * scalar, + z: self.z * scalar, + w: self.w * scalar, + }; + } +} + +impl std::ops::Div for Tuple { + type Output = Tuple; + fn div(self, scalar: f64) -> Self::Output { + return Self::Output { + x: self.x / scalar, + y: self.y / scalar, + z: self.z / scalar, + w: self.w / scalar, + }; + } +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct Point(Tuple); impl Point { - fn new(x: f64, y: f64, z: f64) -> Self { + pub fn new(x: f64, y: f64, z: f64) -> Self { Self(Tuple { x, y, z, w: 1.0 }) } } +impl std::ops::Deref for Point { + type Target = Tuple; + fn deref(&self) -> &Tuple { + &self.0 + } +} + impl From for Point { fn from(tuple: Tuple) -> Self { assert_eq!(tuple.w, 1.0); @@ -108,13 +145,40 @@ impl std::ops::Neg for Point { } } -#[derive(Debug, PartialEq)] -struct Vector(Tuple); +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct Vector(Tuple); impl Vector { - fn new(x: f64, y: f64, z: f64) -> Self { + pub fn new(x: f64, y: f64, z: f64) -> Self { Self(Tuple { x, y, z, w: 0.0 }) } + + pub fn magnitude(&self) -> f64 { + (self.x * self.x + self.y * self.y + self.z * self.z).sqrt() + } + + pub fn normalize(&self) -> Self { + let mag = self.magnitude(); + Self::new(self.x / mag, self.y / mag, self.z / mag) + } + + pub fn dot(&self, r: &Vector) -> f64 { + self.0.dot(r) + } + + pub fn cross(&self, r: &Vector) -> Self { + let x = self.y * r.z - self.z * r.y; + let y = self.z * r.x - self.x * r.z; + let z = self.x * r.y - self.y * r.x; + Self::new(x, y, z) + } +} + +impl std::ops::Deref for Vector { + type Target = Tuple; + fn deref(&self) -> &Tuple { + &self.0 + } } impl Default for Vector { @@ -130,6 +194,13 @@ impl From for Vector { } } +impl std::ops::Add for Vector { + type Output = Vector; + fn add(self, r: Self) -> Self { + Vector::from(self.0 + r.0) + } +} + impl std::ops::Sub for Vector { type Output = Vector; fn sub(self, r: Self) -> Self { @@ -137,6 +208,14 @@ impl std::ops::Sub for Vector { } } +impl std::ops::Mul for Vector { + type Output = Vector; + fn mul(self, r: f64) -> Self { + Vector::from(self.0 * r) + } +} + + impl std::ops::Neg for Vector { type Output = Vector; fn neg(self) -> Self::Output { @@ -253,4 +332,76 @@ mod tests { }, ); } + + #[test] + fn multiply_tuple_by_scalar() { + assert_eq!( + Tuple { + x: 1., + y: -2., + z: 3., + w: -4. + } * 3.5, + Tuple { + x: 3.5, + y: -7., + z: 10.5, + w: -14. + } + ); + } + + #[test] + fn divide_tuple_by_scalar() { + assert_eq!( + Tuple { + x: 1., + y: -2., + z: 3., + w: -4. + } / 2., + Tuple { + x: 0.5, + y: -1., + z: 1.5, + w: -2. + } + ); + } + + #[test] + fn magnitude_of_vector() { + assert_eq!(Vector::new(1., 0., 0.).magnitude(), 1.); + assert_eq!(Vector::new(0., 1., 0.).magnitude(), 1.); + assert_eq!(Vector::new(0., 0., 1.).magnitude(), 1.); + assert_eq!(Vector::new(1., 2., 3.).magnitude(), 14_f64.sqrt()); + assert_eq!(Vector::new(-1., -2., -3.).magnitude(), 14_f64.sqrt()); + } + + #[test] + fn normalize_vector() { + assert_eq!(Vector::new(4., 0., 0.).normalize(), Vector::new(1., 0., 0.)); + assert_eq!( + Vector::new(1., 2., 3.).normalize(), + Vector::new(0.26726, 0.53452, 0.80178) + ); + assert_eq!(Vector::new(1., 2., 3.).normalize().magnitude(), 1.); + } + + #[test] + fn dot_product() { + assert_eq!(Vector::new(1., 2., 3.).dot(&Vector::new(2., 3., 4.)), 20.); + } + + #[test] + fn cross_product() { + assert_eq!( + Vector::new(1., 2., 3.).cross(&Vector::new(2., 3., 4.)), + Vector::new(-1., 2., -1.) + ); + assert_eq!( + Vector::new(2., 3., 4.).cross(&Vector::new(1., 2., 3.)), + Vector::new(1., -2., 1.) + ); + } }