Compare commits
12 Commits
b159820bad
...
f24a90b77b
| Author | SHA1 | Date | |
|---|---|---|---|
| f24a90b77b | |||
| c9ec19c3cd | |||
| 709465dafe | |||
| 7786aa99a1 | |||
| 536b6bed1f | |||
| d8f91a823e | |||
| 42455d593e | |||
| 21ac03acfb | |||
| df495feb57 | |||
| 21afbf8e7c | |||
| 1ea90770bc | |||
| 758f94acde |
155
rtchallenge/Cargo.lock
generated
155
rtchallenge/Cargo.lock
generated
@ -1,5 +1,160 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "adler32"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15af2628f6890fe2609a3b91bef4c83450512802e59489f9c1cb1fa5df064a61"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deflate"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174"
|
||||
dependencies = [
|
||||
"adler32",
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "float-cmp"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
|
||||
dependencies = [
|
||||
"adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.16.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crc32fast",
|
||||
"deflate",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rtchallenge"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"float-cmp",
|
||||
"png",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
@ -7,3 +7,7 @@ edition = "2018"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.41"
|
||||
float-cmp = "0.8.0"
|
||||
png = "0.16.8"
|
||||
thiserror = "1.0.25"
|
||||
|
||||
39
rtchallenge/examples/eoc1.rs
Normal file
39
rtchallenge/examples/eoc1.rs
Normal file
@ -0,0 +1,39 @@
|
||||
use rtchallenge::tuples::{point, vector, Tuple};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Environment {
|
||||
gravity: Tuple,
|
||||
wind: Tuple,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
struct Projectile {
|
||||
position: Tuple,
|
||||
velocity: Tuple,
|
||||
}
|
||||
|
||||
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 p = Projectile {
|
||||
position: point(0., 1., 0.),
|
||||
velocity: 4. * vector(1., 1., 0.).normalize(),
|
||||
};
|
||||
let e = Environment {
|
||||
gravity: vector(0., -0.1, 0.).normalize(),
|
||||
wind: vector(-0.01, 0., 0.),
|
||||
};
|
||||
|
||||
let mut i = 0;
|
||||
while p.position.y > 0. {
|
||||
p = tick(&e, &p);
|
||||
println!("tick {}: position {:?}", i, p.position);
|
||||
i += 1;
|
||||
if i > 100 {
|
||||
eprintln!("too many iterations");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
71
rtchallenge/examples/eoc2.rs
Normal file
71
rtchallenge/examples/eoc2.rs
Normal file
@ -0,0 +1,71 @@
|
||||
use anyhow::{bail, Result};
|
||||
|
||||
use rtchallenge::{
|
||||
canvas::{canvas, Canvas},
|
||||
tuples::{color, point, vector, Tuple},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Environment {
|
||||
gravity: Tuple,
|
||||
wind: Tuple,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
struct Projectile {
|
||||
position: Tuple,
|
||||
velocity: Tuple,
|
||||
}
|
||||
|
||||
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 draw_dot(c: &mut Canvas, x: usize, y: usize) {
|
||||
let red = color(1., 0., 0.);
|
||||
c.set(x.saturating_sub(1), y.saturating_sub(1), red);
|
||||
c.set(x, y.saturating_sub(1), red);
|
||||
c.set(x + 1, y.saturating_sub(1), red);
|
||||
c.set(x.saturating_sub(1), y, red);
|
||||
c.set(x, y, red);
|
||||
c.set(x + 1, y, red);
|
||||
c.set(x.saturating_sub(1), y + 1, red);
|
||||
c.set(x, y + 1, red);
|
||||
c.set(x + 1, y + 1, red);
|
||||
}
|
||||
fn main() -> Result<()> {
|
||||
let position = point(0., 1., 0.);
|
||||
let velocity = vector(1., 1.8, 0.).normalize() * 11.25;
|
||||
let mut p = Projectile { position, velocity };
|
||||
let gravity = vector(0., -0.1, 0.);
|
||||
let wind = vector(-0.01, 0., 0.);
|
||||
let e = Environment { gravity, wind };
|
||||
|
||||
let w = 800;
|
||||
let h = 600;
|
||||
let mut c = canvas(w, h);
|
||||
let mut i = 0;
|
||||
let w = w as f32;
|
||||
let h = h as f32;
|
||||
while p.position.y > 0. {
|
||||
p = tick(&e, &p);
|
||||
println!("tick {}: proj {:?}", i, p);
|
||||
let x = p.position.x;
|
||||
let y = p.position.y;
|
||||
if x > 0. && x < w && y > 0. && y < h {
|
||||
let x = x as usize;
|
||||
let y = (h - y) as usize;
|
||||
draw_dot(&mut c, x, y)
|
||||
}
|
||||
i += 1;
|
||||
if i > 1000 {
|
||||
bail!("too many iterations");
|
||||
}
|
||||
}
|
||||
|
||||
let path = "/tmp/output.png";
|
||||
println!("saving output to {}", path);
|
||||
c.write_to_file(path)?;
|
||||
Ok(())
|
||||
}
|
||||
97
rtchallenge/src/canvas.rs
Normal file
97
rtchallenge/src/canvas.rs
Normal file
@ -0,0 +1,97 @@
|
||||
use std::fs::File;
|
||||
use std::io::BufWriter;
|
||||
use std::path::Path;
|
||||
|
||||
use png;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::tuples::{color, Color};
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum CanvasError {
|
||||
#[error("faile to write canvas")]
|
||||
IOError(#[from] std::io::Error),
|
||||
#[error("faile to encode canvas to png")]
|
||||
EncodingError(#[from] png::EncodingError),
|
||||
}
|
||||
pub struct Canvas {
|
||||
pub height: usize,
|
||||
pub width: usize,
|
||||
pub pixels: Vec<Color>,
|
||||
}
|
||||
|
||||
pub fn canvas(width: usize, height: usize) -> Canvas {
|
||||
let pixels = vec![color(0., 0., 0.); width * height];
|
||||
Canvas {
|
||||
width,
|
||||
height,
|
||||
pixels,
|
||||
}
|
||||
}
|
||||
|
||||
impl Canvas {
|
||||
pub fn set(&mut self, x: usize, y: usize, c: Color) {
|
||||
if x > self.width {
|
||||
return;
|
||||
}
|
||||
if y > self.height {
|
||||
return;
|
||||
}
|
||||
self.pixels[x + y * self.width] = c;
|
||||
}
|
||||
pub fn get(&mut self, x: usize, y: usize) -> Color {
|
||||
self.pixels[x + y * self.width]
|
||||
}
|
||||
pub fn write_to_file<P>(&self, path: P) -> Result<(), CanvasError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let path = Path::new(path.as_ref());
|
||||
let file = File::create(path)?;
|
||||
let ref mut w = BufWriter::new(file);
|
||||
|
||||
let mut encoder = png::Encoder::new(w, self.width as u32, self.height as u32);
|
||||
encoder.set_color(png::ColorType::RGB);
|
||||
encoder.set_depth(png::BitDepth::Eight);
|
||||
let mut writer = encoder.write_header()?;
|
||||
|
||||
let data: Vec<u8> = self
|
||||
.pixels
|
||||
.iter()
|
||||
.flat_map(|p| {
|
||||
vec![
|
||||
(p.red.clamp(0., 1.) * 255.) as u8,
|
||||
(p.green.clamp(0., 1.) * 255.) as u8,
|
||||
(p.blue.clamp(0., 1.) * 255.) as u8,
|
||||
]
|
||||
})
|
||||
.collect();
|
||||
writer.write_image_data(&data)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::canvas;
|
||||
|
||||
use crate::tuples::color;
|
||||
|
||||
#[test]
|
||||
fn create_canvas() {
|
||||
let c = canvas(10, 20);
|
||||
assert_eq!(c.width, 10);
|
||||
assert_eq!(c.height, 20);
|
||||
let black = color(0., 0., 0.);
|
||||
for (i, p) in c.pixels.iter().enumerate() {
|
||||
assert_eq!(p, &black, "pixel {} not {:?}: {:?}", i, &black, p);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_to_canvas() {
|
||||
let mut c = canvas(10, 20);
|
||||
let red = color(1., 0., 0.);
|
||||
c.set(2, 3, red);
|
||||
assert_eq!(c.get(2, 3), red);
|
||||
}
|
||||
}
|
||||
@ -1 +1,2 @@
|
||||
mod tuples;
|
||||
pub mod canvas;
|
||||
pub mod tuples;
|
||||
|
||||
@ -1,36 +1,218 @@
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct Tuple {
|
||||
x: f32,
|
||||
y: f32,
|
||||
z: f32,
|
||||
w: f32,
|
||||
use std::ops::{Add, Div, Mul, Neg, Sub};
|
||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||
pub struct Tuple {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub z: f32,
|
||||
pub w: f32,
|
||||
}
|
||||
|
||||
impl Tuple {
|
||||
fn is_point(&self) -> bool {
|
||||
pub fn is_point(&self) -> bool {
|
||||
self.w == 1.0
|
||||
}
|
||||
|
||||
fn is_vector(&self) -> bool {
|
||||
pub fn is_vector(&self) -> bool {
|
||||
self.w == 0.0
|
||||
}
|
||||
|
||||
pub fn magnitude(&self) -> f32 {
|
||||
(self.x * self.x + self.y * self.y + self.z * self.z + self.w * self.w).sqrt()
|
||||
}
|
||||
pub fn normalize(&self) -> Tuple {
|
||||
let m = self.magnitude();
|
||||
Tuple {
|
||||
x: self.x / m,
|
||||
y: self.y / m,
|
||||
z: self.z / m,
|
||||
w: self.w / m,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn point(x: f32, y: f32, z: f32) -> Tuple {
|
||||
impl Add for Tuple {
|
||||
type Output = Self;
|
||||
fn add(self, other: Self) -> Self {
|
||||
Self {
|
||||
x: self.x + other.x,
|
||||
y: self.y + other.y,
|
||||
z: self.z + other.z,
|
||||
w: self.w + other.w,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<f32> for Tuple {
|
||||
type Output = Self;
|
||||
fn div(self, rhs: f32) -> Self::Output {
|
||||
Self::Output {
|
||||
x: self.x / rhs,
|
||||
y: self.y / rhs,
|
||||
z: self.z / rhs,
|
||||
w: self.w / rhs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<f32> for Tuple {
|
||||
type Output = Self;
|
||||
fn mul(self, rhs: f32) -> Self::Output {
|
||||
Self::Output {
|
||||
x: self.x * rhs,
|
||||
y: self.y * rhs,
|
||||
z: self.z * rhs,
|
||||
w: self.w * rhs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<Tuple> for f32 {
|
||||
type Output = Tuple;
|
||||
fn mul(self, rhs: Tuple) -> Self::Output {
|
||||
Self::Output {
|
||||
x: self * rhs.x,
|
||||
y: self * rhs.y,
|
||||
z: self * rhs.z,
|
||||
w: self * rhs.w,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Tuple {
|
||||
type Output = Self;
|
||||
fn neg(self) -> Self::Output {
|
||||
Self {
|
||||
x: -self.x,
|
||||
y: -self.y,
|
||||
z: -self.z,
|
||||
w: -self.w,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Sub for Tuple {
|
||||
type Output = Self;
|
||||
fn sub(self, other: Self) -> Self {
|
||||
Self {
|
||||
x: self.x - other.x,
|
||||
y: self.y - other.y,
|
||||
z: self.z - other.z,
|
||||
w: self.w - other.w,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn point(x: f32, y: f32, z: f32) -> Tuple {
|
||||
tuple(x, y, z, 1.0)
|
||||
}
|
||||
|
||||
fn vector(x: f32, y: f32, z: f32) -> Tuple {
|
||||
pub fn vector(x: f32, y: f32, z: f32) -> Tuple {
|
||||
tuple(x, y, z, 0.0)
|
||||
}
|
||||
|
||||
fn tuple(x: f32, y: f32, z: f32, w: f32) -> Tuple {
|
||||
pub fn tuple(x: f32, y: f32, z: f32, w: f32) -> Tuple {
|
||||
Tuple { x, y, z, w }
|
||||
}
|
||||
|
||||
pub fn dot(a: Tuple, b: Tuple) -> f32 {
|
||||
a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w
|
||||
}
|
||||
pub fn cross(a: Tuple, b: Tuple) -> Tuple {
|
||||
vector(
|
||||
a.y * b.z - a.z * b.y,
|
||||
a.z * b.x - a.x * b.z,
|
||||
a.x * b.y - a.y * b.x,
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct Color {
|
||||
pub red: f32,
|
||||
pub green: f32,
|
||||
pub blue: f32,
|
||||
}
|
||||
pub fn color(red: f32, green: f32, blue: f32) -> Color {
|
||||
Color { red, green, blue }
|
||||
}
|
||||
impl Add for Color {
|
||||
type Output = Self;
|
||||
fn add(self, other: Self) -> Self {
|
||||
Self {
|
||||
red: self.red + other.red,
|
||||
green: self.green + other.green,
|
||||
blue: self.blue + other.blue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<f32> for Color {
|
||||
type Output = Self;
|
||||
fn div(self, rhs: f32) -> Self::Output {
|
||||
Self::Output {
|
||||
red: self.red / rhs,
|
||||
green: self.green / rhs,
|
||||
blue: self.blue / rhs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<f32> for Color {
|
||||
type Output = Self;
|
||||
fn mul(self, rhs: f32) -> Self::Output {
|
||||
Self::Output {
|
||||
red: self.red * rhs,
|
||||
green: self.green * rhs,
|
||||
blue: self.blue * rhs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<Color> for f32 {
|
||||
type Output = Color;
|
||||
fn mul(self, rhs: Color) -> Self::Output {
|
||||
Self::Output {
|
||||
red: self * rhs.red,
|
||||
green: self * rhs.green,
|
||||
blue: self * rhs.blue,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Mul<Color> for Color {
|
||||
type Output = Color;
|
||||
fn mul(self, rhs: Color) -> Self::Output {
|
||||
Self::Output {
|
||||
red: self.red * rhs.red,
|
||||
green: self.green * rhs.green,
|
||||
blue: self.blue * rhs.blue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Color {
|
||||
type Output = Self;
|
||||
fn neg(self) -> Self::Output {
|
||||
Self {
|
||||
red: -self.red,
|
||||
green: -self.green,
|
||||
blue: -self.blue,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Sub for Color {
|
||||
type Output = Self;
|
||||
fn sub(self, other: Self) -> Self {
|
||||
Self {
|
||||
red: self.red - other.red,
|
||||
green: self.green - other.green,
|
||||
blue: self.blue - other.blue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{point, tuple, vector};
|
||||
use float_cmp::approx_eq;
|
||||
|
||||
use super::{color, cross, dot, point, tuple, vector};
|
||||
#[test]
|
||||
fn is_point() {
|
||||
// A tuple with w = 1 is a point
|
||||
@ -61,4 +243,126 @@ mod tests {
|
||||
fn vector_tuple() {
|
||||
assert_eq!(vector(4., -4., 3.), tuple(4., -4., 3., 0.))
|
||||
}
|
||||
#[test]
|
||||
fn add_two_tuples() {
|
||||
let a1 = tuple(3., -2., 5., 1.);
|
||||
let a2 = tuple(-2., 3., 1., 0.);
|
||||
assert_eq!(a1 + a2, tuple(1., 1., 6., 1.));
|
||||
}
|
||||
#[test]
|
||||
fn sub_two_points() {
|
||||
let p1 = point(3., 2., 1.);
|
||||
let p2 = point(5., 6., 7.);
|
||||
assert_eq!(p1 - p2, vector(-2., -4., -6.));
|
||||
}
|
||||
#[test]
|
||||
fn sub_vector_point() {
|
||||
let p = point(3., 2., 1.);
|
||||
let v = vector(5., 6., 7.);
|
||||
assert_eq!(p - v, point(-2., -4., -6.));
|
||||
}
|
||||
#[test]
|
||||
fn sub_two_vectors() {
|
||||
let v1 = vector(3., 2., 1.);
|
||||
let v2 = vector(5., 6., 7.);
|
||||
assert_eq!(v1 - v2, vector(-2., -4., -6.));
|
||||
}
|
||||
#[test]
|
||||
fn sub_zero_vector() {
|
||||
let zero = vector(0., 0., 0.);
|
||||
let v = vector(1., -2., 3.);
|
||||
assert_eq!(zero - v, vector(-1., 2., -3.));
|
||||
}
|
||||
#[test]
|
||||
fn negate_tuple() {
|
||||
let a = tuple(1., -2., 3., -4.);
|
||||
assert_eq!(-a, tuple(-1., 2., -3., 4.));
|
||||
}
|
||||
#[test]
|
||||
fn mul_tuple_scalar() {
|
||||
let a = tuple(1., -2., 3., -4.);
|
||||
assert_eq!(a * 3.5, tuple(3.5, -7., 10.5, -14.));
|
||||
assert_eq!(3.5 * a, tuple(3.5, -7., 10.5, -14.));
|
||||
}
|
||||
#[test]
|
||||
fn mul_tuple_fraction() {
|
||||
let a = tuple(1., -2., 3., -4.);
|
||||
assert_eq!(a * 0.5, tuple(0.5, -1., 1.5, -2.));
|
||||
assert_eq!(0.5 * a, tuple(0.5, -1., 1.5, -2.));
|
||||
}
|
||||
#[test]
|
||||
fn div_tuple_scalar() {
|
||||
let a = tuple(1., -2., 3., -4.);
|
||||
assert_eq!(a / 2., tuple(0.5, -1., 1.5, -2.));
|
||||
}
|
||||
#[test]
|
||||
fn vector_magnitude() {
|
||||
assert_eq!(1., vector(1., 0., 0.).magnitude());
|
||||
assert_eq!(1., vector(0., 1., 0.).magnitude());
|
||||
assert_eq!(1., vector(0., 0., 1.).magnitude());
|
||||
assert_eq!(14_f32.sqrt(), vector(1., 2., 3.).magnitude());
|
||||
assert_eq!(14_f32.sqrt(), vector(-1., -2., -3.).magnitude());
|
||||
}
|
||||
#[test]
|
||||
fn vector_normalize() {
|
||||
assert_eq!(vector(1., 0., 0.), vector(4., 0., 0.).normalize());
|
||||
assert_eq!(
|
||||
vector(1. / 14_f32.sqrt(), 2. / 14_f32.sqrt(), 3. / 14_f32.sqrt()),
|
||||
vector(1., 2., 3.).normalize()
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn vector_normalize_magnitude() {
|
||||
assert!(approx_eq!(
|
||||
f32,
|
||||
1.,
|
||||
vector(1., 2., 3.).normalize().magnitude(),
|
||||
ulps = 1
|
||||
));
|
||||
}
|
||||
#[test]
|
||||
fn dot_two_tuples() {
|
||||
let a = vector(1., 2., 3.);
|
||||
let b = vector(2., 3., 4.);
|
||||
assert_eq!(20., dot(a, b));
|
||||
}
|
||||
#[test]
|
||||
fn cross_two_tuples() {
|
||||
let a = vector(1., 2., 3.);
|
||||
let b = vector(2., 3., 4.);
|
||||
assert_eq!(vector(-1., 2., -1.), cross(a, b));
|
||||
assert_eq!(vector(1., -2., 1.), cross(b, a));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn color_rgb() {
|
||||
let c = color(-0.5, 0.4, 1.7);
|
||||
assert_eq!(c.red, -0.5);
|
||||
assert_eq!(c.green, 0.4);
|
||||
assert_eq!(c.blue, 1.7);
|
||||
}
|
||||
#[test]
|
||||
fn add_color() {
|
||||
let c1 = color(0.9, 0.6, 0.75);
|
||||
let c2 = color(0.7, 0.1, 0.25);
|
||||
assert_eq!(c1 + c2, color(0.9 + 0.7, 0.6 + 0.1, 0.75 + 0.25));
|
||||
}
|
||||
#[test]
|
||||
fn sub_color() {
|
||||
let c1 = color(0.9, 0.6, 0.75);
|
||||
let c2 = color(0.7, 0.1, 0.25);
|
||||
assert_eq!(c1 - c2, color(0.9 - 0.7, 0.6 - 0.1, 0.75 - 0.25));
|
||||
}
|
||||
#[test]
|
||||
fn mul_color_scalar() {
|
||||
let c = color(0.2, 0.3, 0.4);
|
||||
assert_eq!(c * 2., color(0.2 * 2., 0.3 * 2., 0.4 * 2.));
|
||||
assert_eq!(2. * c, color(0.2 * 2., 0.3 * 2., 0.4 * 2.));
|
||||
}
|
||||
#[test]
|
||||
fn mul_colors() {
|
||||
let c1 = color(1., 0.2, 0.4);
|
||||
let c2 = color(0.9, 1., 0.1);
|
||||
assert_eq!(c1 * c2, color(1.0 * 0.9, 0.2 * 1., 0.4 * 0.1));
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user