stocking

git clone https://git.bradleygannon.com/stocking.git
Log | Files | Refs

commit 01ef5128cb055813bc648c8f36374f2a041c406e
parent 8f21b8640274351d594fd25ca9d1e47b295fa116
Author: Bradley Gannon <bradley@bradleygannon.com>
Date:   Sat, 13 Dec 2025 15:27:51 -0500

Replace vec3! macro with Vec3 struct, add separate Color and Ray structs

Complete Chapter 4

Diffstat:
Asrc/color.rs | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/main.rs | 61+++++++++++++++++++++++++++++++++++++++++--------------------
Asrc/ray.rs | 27+++++++++++++++++++++++++++
Msrc/vec3.rs | 269+++++++++++++++++++++++++++++++++++--------------------------------------------
4 files changed, 253 insertions(+), 169 deletions(-)

diff --git a/src/color.rs b/src/color.rs @@ -0,0 +1,65 @@ +use std::ops::{Add, Mul}; + +#[derive(Clone, Copy, Debug, Default)] +pub struct Color { + red: f64, + green: f64, + blue: f64, +} + +impl Color { + const MIN: f64 = 0.; + const MAX: f64 = 1.; + + pub const fn new(red: f64, green: f64, blue: f64) -> Self { + Color { + red: red.clamp(Color::MIN, Color::MAX), + green: green.clamp(Color::MIN, Color::MAX), + blue: blue.clamp(Color::MIN, Color::MAX), + } + } + + pub const fn red(&self) -> f64 { + self.red + } + + pub const fn green(&self) -> f64 { + self.green + } + + pub const fn blue(&self) -> f64 { + self.blue + } +} + +impl Add<Color> for Color { + type Output = Color; + + fn add(self, rhs: Color) -> Self::Output { + Color { + red: (self.red + rhs.red).clamp(Color::MIN, Color::MAX), + green: (self.green + rhs.green).clamp(Color::MIN, Color::MAX), + blue: (self.blue + rhs.blue).clamp(Color::MIN, Color::MAX), + } + } +} + +impl Mul<f64> for Color { + type Output = Color; + + fn mul(self, rhs: f64) -> Self::Output { + Color { + red: (self.red * rhs).clamp(Color::MIN, Color::MAX), + green: (self.green * rhs).clamp(Color::MIN, Color::MAX), + blue: (self.blue * rhs).clamp(Color::MIN, Color::MAX), + } + } +} + +impl Mul<Color> for f64 { + type Output = Color; + + fn mul(self, rhs: Color) -> Self::Output { + rhs * self + } +} diff --git a/src/main.rs b/src/main.rs @@ -1,14 +1,9 @@ -use crate::vec3::Vec3; -use std::{ - fmt::{Debug, Display}, - ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub}, -}; +use crate::{color::Color, ray::Ray, vec3::Vec3}; +mod color; +mod ray; mod vec3; -vec3!(Color, f64, red, green, blue); -vec3!(Point3, f64, x, y, z); - struct Image { data: Vec<Vec<Color>>, } @@ -31,12 +26,12 @@ impl Image { output += "255\n"; for (row_idx, row_data) in self.data.iter().enumerate() { eprintln!("Scanlines remaining: {}", height - row_idx); - for Color { red, green, blue } in row_data.iter() { + for color in row_data.iter() { output += &format!( "{} {} {}\n", - scale_f64_to_u8(*red), - scale_f64_to_u8(*green), - scale_f64_to_u8(*blue) + scale_f64_to_u8(color.red()), + scale_f64_to_u8(color.green()), + scale_f64_to_u8(color.blue()) ); } } @@ -45,15 +40,41 @@ impl Image { } } +const ASPECT_RATIO: f64 = 16. / 9.; +const IMAGE_WIDTH: usize = 400; +const IMAGE_HEIGHT: usize = { + let height = (IMAGE_WIDTH as f64 / ASPECT_RATIO) as usize; + if height < 1 { 1 } else { height } +}; + +const VIEWPORT_HEIGHT: f64 = 2.; +const VIEWPORT_WIDTH: f64 = VIEWPORT_HEIGHT * (IMAGE_WIDTH as f64 / IMAGE_HEIGHT as f64); +const VIEWPORT_U: Vec3 = Vec3::new(VIEWPORT_WIDTH, 0., 0.); +const VIEWPORT_V: Vec3 = Vec3::new(0., -VIEWPORT_HEIGHT, 0.); + +const FOCAL_LENGTH: f64 = 1.; +const CAMERA_CENTER: Vec3 = Vec3::new(0., 0., 0.); + fn main() { - let (width, height) = (256, 256); - let mut image = Image::new(width, height); - for i in 0..width { - for j in 0..height { - let red = i as f64 / (width - 1) as f64; - let green = j as f64 / (height - 1) as f64; - let blue = 0.; - image.data[j][i] = Color { red, green, blue }; + let pixel_delta_u: Vec3 = VIEWPORT_U / IMAGE_WIDTH as f64; + let pixel_delta_v: Vec3 = VIEWPORT_V / IMAGE_HEIGHT as f64; + let viewport_upper_left: Vec3 = + CAMERA_CENTER - Vec3::new(0., 0., FOCAL_LENGTH) - VIEWPORT_U / 2. - VIEWPORT_V / 2.; + let pixel_origin: Vec3 = viewport_upper_left + 0.5 * (pixel_delta_u + pixel_delta_v); + + let mut image = Image::new(IMAGE_WIDTH, IMAGE_HEIGHT); + for i in 0..IMAGE_WIDTH { + for j in 0..IMAGE_HEIGHT { + let pixel_center = + pixel_origin + (i as f64 * pixel_delta_u) + (j as f64 * pixel_delta_v); + let ray_direction = pixel_center - CAMERA_CENTER; + let ray = Ray::new(CAMERA_CENTER, ray_direction); + let color = { + let unit_direction = ray.direction().unit(); + let a = 0.5 * (unit_direction.y() + 1.); + (1. - a) * Color::new(1., 1., 1.) + a * Color::new(0.5, 0.7, 1.) + }; + image.data[j][i] = color; } } println!("{}", image.to_ppm_string()); diff --git a/src/ray.rs b/src/ray.rs @@ -0,0 +1,27 @@ +use crate::vec3::Vec3; + +pub struct Ray { + _origin: Vec3, + direction: Vec3, +} + +impl Ray { + pub const fn new(origin: Vec3, direction: Vec3) -> Self { + Ray { + _origin: origin, + direction, + } + } + + pub const fn _origin(&self) -> Vec3 { + self._origin + } + + pub const fn direction(&self) -> Vec3 { + self.direction + } + + pub fn _at(&self, t: f64) -> Vec3 { + self._origin + t * self.direction + } +} diff --git a/src/vec3.rs b/src/vec3.rs @@ -3,179 +3,150 @@ use std::{ ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub}, }; -#[allow(dead_code)] -pub trait Vec3: - Add - + AddAssign - + Clone - + Copy - + Debug - + Default - + Display - + DivAssign<f64> - + Div<f64> - + MulAssign<f64> - + Mul<f64> - + Neg - + Sub -{ +#[derive(Copy, Clone, Debug, Default)] +pub struct Vec3 { + x: f64, + y: f64, + z: f64, } -#[macro_export] -macro_rules! vec3 { - ( $name:ident, $type:ty, $dim0:ident, $dim1:ident, $dim2:ident ) => { - #[derive(Copy, Clone, Debug, Default)] - struct $name { - $dim0: $type, - $dim1: $type, - $dim2: $type, +#[allow(dead_code)] +impl Vec3 { + pub const fn new(x: f64, y: f64, z: f64) -> Self { + Vec3 { x, y, z } + } + + pub const fn x(&self) -> f64 { + self.x + } + + pub const fn y(&self) -> f64 { + self.y + } + + pub const fn z(&self) -> f64 { + self.z + } + + pub const fn cross(&self, other: &Vec3) -> Vec3 { + Vec3 { + x: self.y * other.z - self.z * other.y, + y: self.z * other.x - self.x * other.z, + z: self.x * other.y - self.y * other.x, } + } - #[allow(dead_code)] - impl $name { - pub fn new($dim0: $type, $dim1: $type, $dim2: $type) -> Self { - $name { - $dim0, - $dim1, - $dim2, - } - } - - pub fn $dim0(&self) -> $type { - self.$dim0 - } - - pub fn $dim1(&self) -> $type { - self.$dim1 - } - - pub fn $dim2(&self) -> $type { - self.$dim2 - } - - pub fn cross(&self, other: &$name) -> $name { - $name { - $dim0: self.$dim1 * other.$dim2 - self.$dim2 * other.$dim1, - $dim1: self.$dim2 * other.$dim0 - self.$dim0 * other.$dim2, - $dim2: self.$dim0 * other.$dim1 - self.$dim1 * other.$dim0, - } - } - - pub fn dot(&self, other: &$name) -> $type { - self.$dim0 * other.$dim0 + self.$dim1 * other.$dim1 + self.$dim2 * other.$dim2 - } - - pub fn length(&self) -> $type { - self.length_squared().sqrt() - } - - pub fn length_squared(&self) -> $type { - self.$dim0.powi(2) + self.$dim1.powi(2) + self.$dim2.powi(2) - } - - pub fn unit(&self) -> $name { - *self / self.length() - } - } + pub const fn dot(&self, other: &Vec3) -> f64 { + self.x * other.x + self.y * other.y + self.z * other.z + } - impl Vec3 for $name {} + pub fn length(&self) -> f64 { + self.length_squared().sqrt() + } - impl Add for $name { - type Output = Self; + pub fn length_squared(&self) -> f64 { + self.x.powi(2) + self.y.powi(2) + self.z.powi(2) + } - fn add(self, rhs: Self) -> Self::Output { - $name { - $dim0: self.$dim0 + rhs.$dim0, - $dim1: self.$dim1 + rhs.$dim1, - $dim2: self.$dim2 + rhs.$dim2, - } - } - } + pub fn unit(&self) -> Vec3 { + *self / self.length() + } +} - impl AddAssign for $name { - fn add_assign(&mut self, rhs: Self) { - self.$dim0 += rhs.$dim0; - self.$dim1 += rhs.$dim1; - self.$dim2 += rhs.$dim2; - } - } +impl Add for Vec3 { + type Output = Self; - impl Display for $name { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{} {} {}", self.$dim0, self.$dim1, self.$dim2) - } + fn add(self, rhs: Self) -> Self::Output { + Vec3 { + x: self.x + rhs.x, + y: self.y + rhs.y, + z: self.z + rhs.z, } + } +} - impl DivAssign<$type> for $name { - fn div_assign(&mut self, rhs: $type) { - self.$dim0 /= rhs; - self.$dim1 /= rhs; - self.$dim2 /= rhs; - } - } +impl AddAssign for Vec3 { + fn add_assign(&mut self, rhs: Self) { + self.x += rhs.x; + self.y += rhs.y; + self.z += rhs.z; + } +} + +impl Display for Vec3 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {} {}", self.x, self.y, self.z) + } +} - impl Div<$type> for $name { - type Output = $name; +impl DivAssign<f64> for Vec3 { + fn div_assign(&mut self, rhs: f64) { + self.x /= rhs; + self.y /= rhs; + self.z /= rhs; + } +} - fn div(self, rhs: $type) -> Self::Output { - $name { - $dim0: self.$dim0 / rhs, - $dim1: self.$dim1 / rhs, - $dim2: self.$dim2 / rhs, - } - } - } +impl Div<f64> for Vec3 { + type Output = Vec3; - impl MulAssign<$type> for $name { - fn mul_assign(&mut self, rhs: $type) { - self.$dim0 *= rhs; - self.$dim1 *= rhs; - self.$dim2 *= rhs; - } + fn div(self, rhs: f64) -> Self::Output { + Vec3 { + x: self.x / rhs, + y: self.y / rhs, + z: self.z / rhs, } + } +} + +impl MulAssign<f64> for Vec3 { + fn mul_assign(&mut self, rhs: f64) { + self.x *= rhs; + self.y *= rhs; + self.z *= rhs; + } +} - impl Mul<$type> for $name { - type Output = $name; +impl Mul<f64> for Vec3 { + type Output = Vec3; - fn mul(self, rhs: $type) -> Self::Output { - $name { - $dim0: self.$dim0 * rhs, - $dim1: self.$dim1 * rhs, - $dim2: self.$dim2 * rhs, - } - } + fn mul(self, rhs: f64) -> Self::Output { + Vec3 { + x: self.x * rhs, + y: self.y * rhs, + z: self.z * rhs, } + } +} - impl Mul<$name> for $type { - type Output = $name; +impl Mul<Vec3> for f64 { + type Output = Vec3; - fn mul(self, rhs: $name) -> Self::Output { - rhs * self - } - } + fn mul(self, rhs: Vec3) -> Self::Output { + rhs * self + } +} - impl Neg for $name { - type Output = Self; +impl Neg for Vec3 { + type Output = Self; - fn neg(self) -> Self::Output { - $name { - $dim0: -self.$dim0, - $dim1: -self.$dim1, - $dim2: -self.$dim2, - } - } + fn neg(self) -> Self::Output { + Vec3 { + x: -self.x, + y: -self.y, + z: -self.z, } + } +} - impl Sub for $name { - type Output = $name; +impl Sub for Vec3 { + type Output = Vec3; - fn sub(self, rhs: Self) -> Self::Output { - $name { - $dim0: self.$dim0 - rhs.$dim0, - $dim1: self.$dim1 - rhs.$dim1, - $dim2: self.$dim2 - rhs.$dim2, - } - } + fn sub(self, rhs: Self) -> Self::Output { + Vec3 { + x: self.x - rhs.x, + y: self.y - rhs.y, + z: self.z - rhs.z, } - }; + } }