commit 8f21b8640274351d594fd25ca9d1e47b295fa116
parent 3a2c64c30f0b2fc03b986a67a36d689ac3feb4d4
Author: Bradley Gannon <bradley@bradleygannon.com>
Date: Fri, 12 Dec 2025 23:04:48 -0500
Add vec3
Complete Chapter 3
Diffstat:
| M | src/main.rs | | | 36 | ++++++++++++++++++++---------------- |
| A | src/vec3.rs | | | 181 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
2 files changed, 201 insertions(+), 16 deletions(-)
diff --git a/src/main.rs b/src/main.rs
@@ -1,22 +1,26 @@
-#[derive(Clone, Copy, Default)]
-struct Pixel {
- red: f32,
- green: f32,
- blue: f32,
-}
+use crate::vec3::Vec3;
+use std::{
+ fmt::{Debug, Display},
+ ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub},
+};
+
+mod vec3;
+
+vec3!(Color, f64, red, green, blue);
+vec3!(Point3, f64, x, y, z);
struct Image {
- data: Vec<Vec<Pixel>>,
+ data: Vec<Vec<Color>>,
}
-fn scale_f32_to_u8(value: f32) -> u8 {
+fn scale_f64_to_u8(value: f64) -> u8 {
(value * 255.) as u8
}
impl Image {
fn new(width: usize, height: usize) -> Self {
Image {
- data: vec![vec![Pixel::default(); width]; height],
+ data: vec![vec![Color::default(); width]; height],
}
}
fn to_ppm_string(&self) -> String {
@@ -27,12 +31,12 @@ impl Image {
output += "255\n";
for (row_idx, row_data) in self.data.iter().enumerate() {
eprintln!("Scanlines remaining: {}", height - row_idx);
- for Pixel { red, green, blue } in row_data.iter() {
+ for Color { red, green, blue } in row_data.iter() {
output += &format!(
"{} {} {}\n",
- scale_f32_to_u8(*red),
- scale_f32_to_u8(*green),
- scale_f32_to_u8(*blue)
+ scale_f64_to_u8(*red),
+ scale_f64_to_u8(*green),
+ scale_f64_to_u8(*blue)
);
}
}
@@ -46,10 +50,10 @@ fn main() {
let mut image = Image::new(width, height);
for i in 0..width {
for j in 0..height {
- let red = i as f32 / (width - 1) as f32;
- let green = j as f32 / (height - 1) as f32;
+ 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] = Pixel { red, green, blue };
+ image.data[j][i] = Color { red, green, blue };
}
}
println!("{}", image.to_ppm_string());
diff --git a/src/vec3.rs b/src/vec3.rs
@@ -0,0 +1,181 @@
+use std::{
+ fmt::{Debug, Display},
+ 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
+{
+}
+
+#[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 $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()
+ }
+ }
+
+ impl Vec3 for $name {}
+
+ impl Add for $name {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self::Output {
+ $name {
+ $dim0: self.$dim0 + rhs.$dim0,
+ $dim1: self.$dim1 + rhs.$dim1,
+ $dim2: self.$dim2 + rhs.$dim2,
+ }
+ }
+ }
+
+ impl AddAssign for $name {
+ fn add_assign(&mut self, rhs: Self) {
+ self.$dim0 += rhs.$dim0;
+ self.$dim1 += rhs.$dim1;
+ self.$dim2 += rhs.$dim2;
+ }
+ }
+
+ impl Display for $name {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{} {} {}", self.$dim0, self.$dim1, self.$dim2)
+ }
+ }
+
+ impl DivAssign<$type> for $name {
+ fn div_assign(&mut self, rhs: $type) {
+ self.$dim0 /= rhs;
+ self.$dim1 /= rhs;
+ self.$dim2 /= rhs;
+ }
+ }
+
+ impl Div<$type> for $name {
+ type Output = $name;
+
+ fn div(self, rhs: $type) -> Self::Output {
+ $name {
+ $dim0: self.$dim0 / rhs,
+ $dim1: self.$dim1 / rhs,
+ $dim2: self.$dim2 / rhs,
+ }
+ }
+ }
+
+ impl MulAssign<$type> for $name {
+ fn mul_assign(&mut self, rhs: $type) {
+ self.$dim0 *= rhs;
+ self.$dim1 *= rhs;
+ self.$dim2 *= rhs;
+ }
+ }
+
+ impl Mul<$type> for $name {
+ type Output = $name;
+
+ fn mul(self, rhs: $type) -> Self::Output {
+ $name {
+ $dim0: self.$dim0 * rhs,
+ $dim1: self.$dim1 * rhs,
+ $dim2: self.$dim2 * rhs,
+ }
+ }
+ }
+
+ impl Mul<$name> for $type {
+ type Output = $name;
+
+ fn mul(self, rhs: $name) -> Self::Output {
+ rhs * self
+ }
+ }
+
+ impl Neg for $name {
+ type Output = Self;
+
+ fn neg(self) -> Self::Output {
+ $name {
+ $dim0: -self.$dim0,
+ $dim1: -self.$dim1,
+ $dim2: -self.$dim2,
+ }
+ }
+ }
+
+ impl Sub for $name {
+ type Output = $name;
+
+ fn sub(self, rhs: Self) -> Self::Output {
+ $name {
+ $dim0: self.$dim0 - rhs.$dim0,
+ $dim1: self.$dim1 - rhs.$dim1,
+ $dim2: self.$dim2 - rhs.$dim2,
+ }
+ }
+ }
+ };
+}