Implement the remaining operations and the projectile simulator
This commit is contained in:
parent
e23a4aacab
commit
39c947b461
|
@ -6,3 +6,6 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "projectile"
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -1,2 +1,2 @@
|
||||||
mod types;
|
pub mod types;
|
||||||
|
|
||||||
|
|
|
@ -4,14 +4,20 @@ fn eq_f64(l: f64, r: f64) -> bool {
|
||||||
(l - r).abs() < EPSILON
|
(l - r).abs() < EPSILON
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
struct Tuple {
|
pub struct Tuple {
|
||||||
x: f64,
|
pub x: f64,
|
||||||
y: f64,
|
pub y: f64,
|
||||||
z: f64,
|
pub z: f64,
|
||||||
w: f64, // Used for very low-level math. w = 1.0 indicates a point, w = 0.0 indicates a vector.
|
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
|
// Theoretically the type system should make this redundant, so operations on points
|
||||||
// and vectors can always assert the correct value.
|
// 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 {
|
impl PartialEq for Tuple {
|
||||||
|
@ -56,15 +62,46 @@ impl std::ops::Neg for Tuple {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
impl std::ops::Mul<f64> for Tuple {
|
||||||
struct Point(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<f64> 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 {
|
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 })
|
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<Tuple> for Point {
|
impl From<Tuple> for Point {
|
||||||
fn from(tuple: Tuple) -> Self {
|
fn from(tuple: Tuple) -> Self {
|
||||||
assert_eq!(tuple.w, 1.0);
|
assert_eq!(tuple.w, 1.0);
|
||||||
|
@ -108,13 +145,40 @@ impl std::ops::Neg for Point {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
struct Vector(Tuple);
|
pub struct Vector(Tuple);
|
||||||
|
|
||||||
impl Vector {
|
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 })
|
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 {
|
impl Default for Vector {
|
||||||
|
@ -130,6 +194,13 @@ impl From<Tuple> 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 {
|
impl std::ops::Sub for Vector {
|
||||||
type Output = Vector;
|
type Output = Vector;
|
||||||
fn sub(self, r: Self) -> Self {
|
fn sub(self, r: Self) -> Self {
|
||||||
|
@ -137,6 +208,14 @@ impl std::ops::Sub for Vector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::ops::Mul<f64> for Vector {
|
||||||
|
type Output = Vector;
|
||||||
|
fn mul(self, r: f64) -> Self {
|
||||||
|
Vector::from(self.0 * r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
impl std::ops::Neg for Vector {
|
impl std::ops::Neg for Vector {
|
||||||
type Output = Vector;
|
type Output = Vector;
|
||||||
fn neg(self) -> Self::Output {
|
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.)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue