1use std::ops::{
2 Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub,
3};
4
5#[derive(Debug, Clone, Copy, PartialEq)]
7pub struct Vec3 {
8 pub e: [f64; 3],
10}
11
12pub type Point3 = Vec3;
14pub type Color = Vec3;
16
17impl Vec3 {
20 pub fn new(e0: f64, e1: f64, e2: f64) -> Self {
22 Vec3 { e: [e0, e1, e2] }
23 }
24
25 pub fn x(self) -> f64 { self.e[0] }
27 pub fn y(self) -> f64 { self.e[1] }
29 pub fn z(self) -> f64 { self.e[2] }
31
32 pub fn length_squared(self) -> f64 {
34 self.e[0] * self.e[0] + self.e[1] * self.e[1] + self.e[2] * self.e[2]
35 }
36
37 pub fn length(self) -> f64 {
39 self.length_squared().sqrt()
40 }
41
42 pub fn near_zero(self) -> bool {
44 const S: f64 = 1e-8;
45 self.e[0].abs() < S && self.e[1].abs() < S && self.e[2].abs() < S
46 }
47}
48
49impl Default for Vec3 {
50 fn default() -> Self {
51 Vec3 { e: [0.0, 0.0, 0.0] }
52 }
53}
54
55impl std::fmt::Display for Vec3 {
58 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
59 write!(f, "{} {} {}", self.e[0], self.e[1], self.e[2])
60 }
61}
62
63impl Neg for Vec3 {
66 type Output = Vec3;
67 fn neg(self) -> Vec3 {
68 Vec3::new(-self.e[0], -self.e[1], -self.e[2])
69 }
70}
71
72impl Index<usize> for Vec3 {
73 type Output = f64;
74 fn index(&self, i: usize) -> &f64 { &self.e[i] }
75}
76
77impl IndexMut<usize> for Vec3 {
78 fn index_mut(&mut self, i: usize) -> &mut f64 { &mut self.e[i] }
79}
80
81impl AddAssign for Vec3 {
82 fn add_assign(&mut self, v: Vec3) {
83 self.e[0] += v.e[0];
84 self.e[1] += v.e[1];
85 self.e[2] += v.e[2];
86 }
87}
88
89impl MulAssign<f64> for Vec3 {
90 fn mul_assign(&mut self, t: f64) {
91 self.e[0] *= t;
92 self.e[1] *= t;
93 self.e[2] *= t;
94 }
95}
96
97impl DivAssign<f64> for Vec3 {
98 fn div_assign(&mut self, t: f64) {
99 *self *= 1.0 / t;
100 }
101}
102
103impl Add for Vec3 {
104 type Output = Vec3;
105 fn add(self, v: Vec3) -> Vec3 {
106 Vec3::new(self.e[0] + v.e[0], self.e[1] + v.e[1], self.e[2] + v.e[2])
107 }
108}
109
110impl Sub for Vec3 {
111 type Output = Vec3;
112 fn sub(self, v: Vec3) -> Vec3 {
113 Vec3::new(self.e[0] - v.e[0], self.e[1] - v.e[1], self.e[2] - v.e[2])
114 }
115}
116
117impl Mul for Vec3 {
118 type Output = Vec3;
119 fn mul(self, v: Vec3) -> Vec3 {
120 Vec3::new(self.e[0] * v.e[0], self.e[1] * v.e[1], self.e[2] * v.e[2])
121 }
122}
123
124impl Mul<f64> for Vec3 {
125 type Output = Vec3;
126 fn mul(self, t: f64) -> Vec3 {
127 Vec3::new(self.e[0] * t, self.e[1] * t, self.e[2] * t)
128 }
129}
130
131impl Mul<Vec3> for f64 {
132 type Output = Vec3;
133 fn mul(self, v: Vec3) -> Vec3 { v * self }
134}
135
136impl Div<f64> for Vec3 {
137 type Output = Vec3;
138 fn div(self, t: f64) -> Vec3 { self * (1.0 / t) }
139}
140
141pub fn dot(u: Vec3, v: Vec3) -> f64 {
145 u.e[0] * v.e[0] + u.e[1] * v.e[1] + u.e[2] * v.e[2]
146}
147
148pub fn cross(u: Vec3, v: Vec3) -> Vec3 {
150 Vec3::new(
151 u.e[1] * v.e[2] - u.e[2] * v.e[1],
152 u.e[2] * v.e[0] - u.e[0] * v.e[2],
153 u.e[0] * v.e[1] - u.e[1] * v.e[0],
154 )
155}
156
157pub fn unit_vector(v: Vec3) -> Vec3 {
159 v / v.length()
160}
161
162pub fn reflect(v: Vec3, n: Vec3) -> Vec3 {
166 v - 2.0 * dot(v, n) * n
167}
168
169pub fn refract(uv: Vec3, n: Vec3, etai_over_etat: f64) -> Vec3 {
173 let cos_theta = dot(-uv, n).min(1.0);
174 let r_out_parallel = etai_over_etat * (uv + cos_theta * n);
175 let r_out_perp = -(1.0 - r_out_parallel.length_squared()).sqrt() * n;
176 r_out_parallel + r_out_perp
177}
178
179pub fn random_in_unit_sphere() -> Vec3 {
181 use crate::utils::random_double_range;
182 loop {
183 let p = Vec3::new(
184 random_double_range(-1.0, 1.0),
185 random_double_range(-1.0, 1.0),
186 random_double_range(-1.0, 1.0),
187 );
188 if p.length_squared() < 1.0 {
189 return p;
190 }
191 }
192}
193
194pub fn random_unit_vector() -> Vec3 {
196 unit_vector(random_in_unit_sphere())
197}
198
199pub fn random_in_unit_disk() -> Vec3 {
201 use crate::utils::random_double_range;
202 loop {
203 let p = Vec3::new(
204 random_double_range(-1.0, 1.0),
205 random_double_range(-1.0, 1.0),
206 0.0,
207 );
208 if p.length_squared() < 1.0 {
209 return p;
210 }
211 }
212}
213
214pub fn random_in_hemisphere(normal: Vec3) -> Vec3 {
216 let in_unit_sphere = random_in_unit_sphere();
217 if dot(in_unit_sphere, normal) > 0.0 {
218 in_unit_sphere
219 } else {
220 -in_unit_sphere
221 }
222}
223
224pub fn write_color(pixel_color: Color, samples_per_pixel: i32) -> String {
228 let scale = 1.0 / samples_per_pixel as f64;
229 let r = (pixel_color.x() * scale).clamp(0.0, 0.999);
230 let g = (pixel_color.y() * scale).clamp(0.0, 0.999);
231 let b = (pixel_color.z() * scale).clamp(0.0, 0.999);
232 let ir = (256.0 * r) as i32;
233 let ig = (256.0 * g) as i32;
234 let ib = (256.0 * b) as i32;
235 format!("{} {} {}", ir, ig, ib)
236}
237
238pub fn write_color_gamma(pixel_color: Color, samples_per_pixel: i32) -> String {
240 let scale = 1.0 / samples_per_pixel as f64;
241 let r = (pixel_color.x() * scale).sqrt().clamp(0.0, 0.999);
242 let g = (pixel_color.y() * scale).sqrt().clamp(0.0, 0.999);
243 let b = (pixel_color.z() * scale).sqrt().clamp(0.0, 0.999);
244 let ir = (256.0 * r) as i32;
245 let ig = (256.0 * g) as i32;
246 let ib = (256.0 * b) as i32;
247 format!("{} {} {}", ir, ig, ib)
248}