Compare commits
8 Commits
15c4ae9bad
...
2569a48792
Author | SHA1 | Date |
---|---|---|
|
2569a48792 | |
|
15d87fbde6 | |
|
3c8536deb6 | |
|
d0a8be63e9 | |
|
2a38ca38e1 | |
|
39c947b461 | |
|
e23a4aacab | |
|
01f3e05235 |
|
@ -3331,6 +3331,10 @@ dependencies = [
|
|||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ray-tracer"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.8.0"
|
||||
|
|
|
@ -27,5 +27,5 @@ members = [
|
|||
"sgf",
|
||||
"timezone-testing",
|
||||
"tree",
|
||||
"visions/server",
|
||||
"visions/server", "ray-tracer",
|
||||
]
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
name = "ray-tracer"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
||||
[[bin]]
|
||||
name = "projectile"
|
|
@ -0,0 +1,53 @@
|
|||
use ray_tracer::{types::*, PPM};
|
||||
use std::{fs::File, io::Write};
|
||||
|
||||
#[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 mut canvas = Canvas::new(900, 550);
|
||||
|
||||
let start = Projectile {
|
||||
position: Point::new(0., 1., 0.),
|
||||
velocity: Vector::new(1., 5., 0.).normalize() * 5.,
|
||||
};
|
||||
|
||||
let e = Environment {
|
||||
gravity: Vector::new(0., -0.1, 0.),
|
||||
wind: Vector::new(-0.01, 0., 0.),
|
||||
};
|
||||
|
||||
let mut p = start;
|
||||
while p.position.y() > 0. {
|
||||
p = tick(&e, &p);
|
||||
|
||||
let x = p.position.x().round() as usize;
|
||||
let y = p.position.y().round() as usize;
|
||||
if x > 1 && x < 900 && y > 1 && y < 550 {
|
||||
*canvas.pixel_mut(x, 550 - y - 1) = Color::new(1., 1., 0.);
|
||||
*canvas.pixel_mut(x+1, 550 - y - 1) = Color::new(1., 1., 0.);
|
||||
*canvas.pixel_mut(x-1, 550 - y - 1) = Color::new(1., 1., 0.);
|
||||
*canvas.pixel_mut(x, 550 - y) = Color::new(1., 1., 0.);
|
||||
*canvas.pixel_mut(x, 550 - y - 2) = Color::new(1., 1., 0.);
|
||||
}
|
||||
}
|
||||
|
||||
let ppm = PPM::from(canvas);
|
||||
|
||||
let mut file = File::create("projectile.ppm").unwrap();
|
||||
file.write(ppm.as_bytes()).unwrap();
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
mod ppm;
|
||||
|
||||
pub mod types;
|
||||
|
||||
pub use ppm::PPM;
|
|
@ -0,0 +1,119 @@
|
|||
use crate::types::Canvas;
|
||||
|
||||
fn color_float_to_int(val: f64) -> u8 {
|
||||
(val * 256.).clamp(0., 255.) as u8
|
||||
}
|
||||
|
||||
fn join_to_line_limit(data: impl IntoIterator<Item = String>) -> Vec<String> {
|
||||
let mut lines = vec![];
|
||||
|
||||
let mut line = String::new();
|
||||
let mut iter = data.into_iter();
|
||||
while let Some(element) = iter.next() {
|
||||
if line.is_empty() {
|
||||
line = line + &element;
|
||||
} else if line.len() + 1 + element.len() < 70 {
|
||||
line = line + " " + &element;
|
||||
} else {
|
||||
lines.push(line);
|
||||
line = element;
|
||||
}
|
||||
}
|
||||
|
||||
if !line.is_empty() {
|
||||
lines.push(line);
|
||||
}
|
||||
|
||||
lines
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PPM(String);
|
||||
|
||||
impl From<Canvas> for PPM {
|
||||
fn from(c: Canvas) -> Self {
|
||||
// let v = vec![0.; c.width() * c.height() * 3];
|
||||
let header = format!("P3\n{} {}\n255\n", c.width(), c.height());
|
||||
|
||||
let mut data = vec![];
|
||||
for y in 0..c.height() {
|
||||
let mut row = vec![];
|
||||
for x in 0..c.width() {
|
||||
let pixel = c.pixel(x, y);
|
||||
row.push(color_float_to_int(pixel.red()).to_string());
|
||||
row.push(color_float_to_int(pixel.green()).to_string());
|
||||
row.push(color_float_to_int(pixel.blue()).to_string());
|
||||
}
|
||||
let mut lines = join_to_line_limit(row);
|
||||
data.append(&mut lines);
|
||||
}
|
||||
|
||||
let data = data.join("\n");
|
||||
|
||||
Self(format!("{header}{data}\n"))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for PPM {
|
||||
type Target = String;
|
||||
fn deref(&self) -> &String {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::types::Color;
|
||||
|
||||
#[test]
|
||||
fn construct_ppm_header() {
|
||||
let c = Canvas::new(5, 3);
|
||||
let ppm = PPM::from(c);
|
||||
|
||||
assert!(ppm.starts_with(
|
||||
"P3\n\
|
||||
5 3\n\
|
||||
255\n"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn construct_full_ppm_data() {
|
||||
let mut c = Canvas::new(5, 3);
|
||||
*c.pixel_mut(0, 0) = Color::new(1.5, 0., 0.);
|
||||
*c.pixel_mut(2, 1) = Color::new(0., 0.5, 0.);
|
||||
*c.pixel_mut(4, 2) = Color::new(-0.5, 0., 1.);
|
||||
|
||||
let expected = "P3\n\
|
||||
5 3\n\
|
||||
255\n\
|
||||
255 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n\
|
||||
0 0 0 0 0 0 0 128 0 0 0 0 0 0 0\n\
|
||||
0 0 0 0 0 0 0 0 0 0 0 0 0 0 255\n";
|
||||
|
||||
let ppm = PPM::from(c);
|
||||
assert_eq!(*ppm, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ppm_line_length_limit() {
|
||||
let mut c = Canvas::new(10, 2);
|
||||
for y in 0..2 {
|
||||
for x in 0..10 {
|
||||
*c.pixel_mut(x, y) = Color::new(1., 0.8, 0.6);
|
||||
}
|
||||
}
|
||||
|
||||
let expected = "P3\n\
|
||||
10 2\n\
|
||||
255\n\
|
||||
255 204 153 255 204 153 255 204 153 255 204 153 255 204 153 255 204\n\
|
||||
153 255 204 153 255 204 153 255 204 153 255 204 153\n\
|
||||
255 204 153 255 204 153 255 204 153 255 204 153 255 204 153 255 204\n\
|
||||
153 255 204 153 255 204 153 255 204 153 255 204 153\n";
|
||||
|
||||
let ppm = PPM::from(c);
|
||||
assert_eq!(*ppm, expected);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
use crate::types::Color;
|
||||
|
||||
pub struct Canvas {
|
||||
width: usize,
|
||||
height: usize,
|
||||
pixels: Vec<Color>,
|
||||
}
|
||||
|
||||
impl Canvas {
|
||||
pub fn new(width: usize, height: usize) -> Self {
|
||||
Self {
|
||||
width,
|
||||
height,
|
||||
pixels: vec![Color::default(); width * height],
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn width(&self) -> usize {
|
||||
self.width
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn height(&self) -> usize {
|
||||
self.height
|
||||
}
|
||||
|
||||
pub fn pixel(&self, x: usize, y: usize) -> &Color {
|
||||
&self.pixels[self.addr(y, x)]
|
||||
}
|
||||
|
||||
pub fn pixel_mut<'a>(&'a mut self, x: usize, y: usize) -> &'a mut Color {
|
||||
let addr = self.addr(y, x);
|
||||
&mut self.pixels[addr]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn addr(&self, y: usize, x: usize) -> usize {
|
||||
let val = y * self.width() + x;
|
||||
if val >= self.pixels.len() {
|
||||
eprintln!("[{val}] out of range: [{x}, {y}]");
|
||||
}
|
||||
val
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn addresses() {
|
||||
let c = Canvas::new(5, 3);
|
||||
|
||||
assert_eq!(c.addr(0, 0), 0);
|
||||
assert_eq!(c.addr(1, 0), 5);
|
||||
assert_eq!(c.addr(2, 0), 10);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
use crate::types::Tuple;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Color(Tuple);
|
||||
|
||||
impl Color {
|
||||
pub fn new(red: f64, green: f64, blue: f64) -> Self {
|
||||
Self(Tuple(red, green, blue, 0.))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn red(&self) -> f64 {
|
||||
self.0.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn green(&self) -> f64 {
|
||||
self.0.1
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn blue(&self) -> f64 {
|
||||
self.0.2
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Default for Color {
|
||||
fn default() -> Self {
|
||||
Self::new(0., 0., 0.)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Tuple> for Color {
|
||||
fn from(tuple: Tuple) -> Self {
|
||||
assert_eq!(tuple.3, 0.0);
|
||||
Self(tuple)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for Color {
|
||||
type Target = Tuple;
|
||||
fn deref(&self) -> &Tuple {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Add for Color {
|
||||
type Output = Color;
|
||||
fn add(self, r: Self) -> Self {
|
||||
Color::from(self.0 + r.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Sub for Color {
|
||||
type Output = Color;
|
||||
fn sub(self, r: Self) -> Self {
|
||||
Color::from(self.0 - r.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Neg for Color {
|
||||
type Output = Color;
|
||||
fn neg(self) -> Self::Output {
|
||||
let mut t = -self.0;
|
||||
t.0 = 0.;
|
||||
Color::from(t)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Mul for Color {
|
||||
type Output = Color;
|
||||
fn mul(self, r: Color) -> Self {
|
||||
let red = self.red() * r.red();
|
||||
let green = self.green() * r.green();
|
||||
let blue = self.blue() * r.blue();
|
||||
Self::new(red, green, blue)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Mul<f64> for Color {
|
||||
type Output = Color;
|
||||
fn mul(self, r: f64) -> Self {
|
||||
Color::from(self.0 * r)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
mod canvas;
|
||||
mod color;
|
||||
mod point;
|
||||
mod tuple;
|
||||
mod vector;
|
||||
|
||||
pub use canvas::Canvas;
|
||||
pub use color::Color;
|
||||
pub use point::Point;
|
||||
pub use tuple::Tuple;
|
||||
pub use vector::Vector;
|
||||
|
||||
const EPSILON: f64 = 0.00001;
|
||||
|
||||
fn eq_f64(l: f64, r: f64) -> bool {
|
||||
(l - r).abs() < EPSILON
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn eq_f64_compares_values() {
|
||||
assert!(eq_f64(1.0, 1.0));
|
||||
assert!(eq_f64(0.9994, 0.9994));
|
||||
assert!(eq_f64(0.9999994, 0.9999995));
|
||||
assert!(!eq_f64(0.9995, 0.9994));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_two_tuples() {
|
||||
let a = Tuple(3., -2., 5., 1.);
|
||||
let b = Tuple(-2., 3., 1., 0.);
|
||||
assert_eq!(a + b, Tuple(1., 1., 6., 1.));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subtracts_two_tuples() {
|
||||
let a = Tuple(3., 2., 1., 1.);
|
||||
let b = Tuple(5., 6., 7., 1.);
|
||||
assert_eq!(a - b, Tuple(-2., -4., -6., 0.));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adds_point_and_vector() {
|
||||
let a = Point::new(3., -2., 5.);
|
||||
let b = Vector::new(-2., 3., 1.);
|
||||
assert_eq!(a + b, Point::new(1., 1., 6.,));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subtracts_two_points() {
|
||||
let a = Point::new(3., 2., 1.);
|
||||
let b = Point::new(5., 6., 7.);
|
||||
assert_eq!(a - b, Vector::new(-2., -4., -6.,));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subtracts_vector_from_point() {
|
||||
let a = Point::new(3., 2., 1.);
|
||||
let b = Vector::new(5., 6., 7.);
|
||||
assert_eq!(a - b, Point::new(-2., -4., -6.));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subtracts_two_vectors() {
|
||||
let a = Vector::new(3., 2., 1.);
|
||||
let b = Vector::new(5., 6., 7.);
|
||||
assert_eq!(a - b, Vector::new(-2., -4., -6.));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_negates_primitives() {
|
||||
assert_eq!(-Tuple(1., 2., 3., 4.), Tuple(-1., -2., -3., -4.),);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiply_tuple_by_scalar() {
|
||||
assert_eq!(Tuple(1., -2., 3., -4.) * 3.5, Tuple(3.5, -7., 10.5, -14.));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn divide_tuple_by_scalar() {
|
||||
assert_eq!(Tuple(1., -2., 3., -4.) / 2., Tuple(0.5, -1., 1.5, -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.)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiply_colors() {
|
||||
let c1 = Color::new(1., 0.2, 0.4);
|
||||
let c2 = Color::new(0.9, 1., 0.1);
|
||||
|
||||
assert_eq!(c1 * c2, Color::new(0.9, 0.2, 0.04));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_creates_a_canvas() {
|
||||
let c = Canvas::new(10, 20);
|
||||
assert_eq!(c.width(), 10);
|
||||
assert_eq!(c.height(), 20);
|
||||
for row in 0..20 {
|
||||
for col in 0..10 {
|
||||
assert_eq!(*c.pixel(row, col), Color::default());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_write_pixel() {
|
||||
let mut c = Canvas::new(10, 20);
|
||||
let red = Color::new(1., 0., 0.);
|
||||
*c.pixel_mut(2, 3) = red;
|
||||
assert_eq!(*c.pixel(2, 3), red);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
use crate::types::{Tuple, Vector};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Point(Tuple);
|
||||
|
||||
impl Point {
|
||||
pub fn new(x: f64, y: f64, z: f64) -> Self {
|
||||
Self(Tuple(x, y, z, 1.0))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn x(&self) -> f64 {
|
||||
self.0.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn y(&self) -> f64 {
|
||||
self.0.1
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn z(&self) -> f64 {
|
||||
self.0.2
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Point {
|
||||
fn default() -> Self {
|
||||
Self::new(0., 0., 0.)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Tuple> for Point {
|
||||
fn from(tuple: Tuple) -> Self {
|
||||
assert_eq!(tuple.3, 1.0);
|
||||
Self(tuple)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for Point {
|
||||
type Target = Tuple;
|
||||
fn deref(&self) -> &Tuple {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Add<Vector> for Point {
|
||||
type Output = Point;
|
||||
fn add(self, r: Vector) -> Self::Output {
|
||||
Point::from(self.0 + *r)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Sub for Point {
|
||||
type Output = Vector;
|
||||
fn sub(self, r: Point) -> Self::Output {
|
||||
Vector::from(self.0 - r.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Sub<Vector> for Point {
|
||||
type Output = Point;
|
||||
fn sub(self, r: Vector) -> Self::Output {
|
||||
Point::from(self.0 - *r)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Neg for Point {
|
||||
type Output = Point;
|
||||
fn neg(self) -> Self::Output {
|
||||
let mut t = -self.0;
|
||||
t.3 = 1.;
|
||||
Point::from(t)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
use crate::types::eq_f64;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Tuple(
|
||||
pub f64, // x or red
|
||||
pub f64, // y or green
|
||||
pub f64, // z or blue
|
||||
pub f64, // w, the flag which
|
||||
// indicates point vs vec, or alpha
|
||||
);
|
||||
|
||||
impl Tuple {
|
||||
pub fn dot(&self, r: &Tuple) -> f64 {
|
||||
self.0 * r.0 + self.1 * r.1 + self.2 * r.2 + self.3 * r.3
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Tuple {
|
||||
fn eq(&self, r: &Tuple) -> bool {
|
||||
eq_f64(self.0, r.0) && eq_f64(self.1, r.1) && eq_f64(self.2, r.2) && eq_f64(self.3, r.3)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Add for Tuple {
|
||||
type Output = Tuple;
|
||||
fn add(self, r: Tuple) -> Self::Output {
|
||||
Tuple(self.0 + r.0, self.1 + r.1, self.2 + r.2, self.3 + r.3)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Sub for Tuple {
|
||||
type Output = Tuple;
|
||||
fn sub(self, r: Tuple) -> Self::Output {
|
||||
Tuple(self.0 - r.0, self.1 - r.1, self.2 - r.2, self.3 - r.3)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Neg for Tuple {
|
||||
type Output = Tuple;
|
||||
fn neg(self) -> Self::Output {
|
||||
return Self::Output {
|
||||
0: -self.0,
|
||||
1: -self.1,
|
||||
2: -self.2,
|
||||
3: -self.3,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Mul<f64> for Tuple {
|
||||
type Output = Tuple;
|
||||
fn mul(self, scalar: f64) -> Self::Output {
|
||||
Tuple(
|
||||
self.0 * scalar,
|
||||
self.1 * scalar,
|
||||
self.2 * scalar,
|
||||
self.3 * scalar,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Div<f64> for Tuple {
|
||||
type Output = Tuple;
|
||||
fn div(self, scalar: f64) -> Self::Output {
|
||||
Tuple(
|
||||
self.0 / scalar,
|
||||
self.1 / scalar,
|
||||
self.2 / scalar,
|
||||
self.3 / scalar,
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
use crate::types::Tuple;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Vector(Tuple);
|
||||
|
||||
impl Vector {
|
||||
pub fn new(x: f64, y: f64, z: f64) -> Self {
|
||||
Self(Tuple(x, y, z, 0.0))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn x(&self) -> f64 {
|
||||
self.0.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn y(&self) -> f64 {
|
||||
self.0.1
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn z(&self) -> f64 {
|
||||
self.0.2
|
||||
}
|
||||
|
||||
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 Default for Vector {
|
||||
fn default() -> Self {
|
||||
Self::new(0., 0., 0.)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Tuple> for Vector {
|
||||
fn from(tuple: Tuple) -> Self {
|
||||
assert_eq!(tuple.3, 0.0);
|
||||
Self(tuple)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for Vector {
|
||||
type Target = Tuple;
|
||||
fn deref(&self) -> &Tuple {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
Vector::from(self.0 - r.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Neg for Vector {
|
||||
type Output = Vector;
|
||||
fn neg(self) -> Self::Output {
|
||||
let mut t = -self.0;
|
||||
t.0 = 0.;
|
||||
Vector::from(t)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Mul<f64> for Vector {
|
||||
type Output = Vector;
|
||||
fn mul(self, r: f64) -> Self {
|
||||
Vector::from(self.0 * r)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue