1use crate::geom::{Matrix, Point, Rect, Vector};
6use crate::scene::Light;
7use crate::scene::Object;
8use crate::spectrum::{wavelength_to_colour, FIRST_WAVELENGTH, LAST_WAVELENGTH};
9
10use rand::prelude::*;
11use rand_distr::num_traits::real::Real;
12
13use std::f64::consts::PI;
14
15pub struct RayResult {
16 pub origin: Point,
17 pub termination: Point,
18 pub wavelength: f64,
19}
20
21impl RayResult {
22 #[cfg(test)]
23 pub fn new<A, B>(start: A, end: B, wavelen: f64) -> Self
24 where
25 A: Into<Point>,
26 B: Into<Point>,
27 {
28 Self {
29 origin: start.into(),
30 termination: end.into(),
31 wavelength: wavelen,
32 }
33 }
34
35 pub fn color<P>(&self) -> (P, P, P)
36 where
37 P: Copy + Real,
38 {
39 wavelength_to_colour(self.wavelength)
40 }
41}
42
43pub struct Ray<R: Rng + SeedableRng> {
44 origin: Point,
45 direction: Vector,
46 wavelength: f64,
47 bounces: u32,
48 ray_rng: R,
49}
50
51impl<R> Ray<R>
52where
53 R: Rng + SeedableRng,
54{
55 pub fn new(light: &Light<R>, rng: &mut R) -> Self {
59 let p = light.location.get(rng);
60 let cart_x = p.x;
61 let cart_y = p.y;
62 let polar_angle = light.polar_angle.sample(rng) * (PI / 180.0);
63 let polar_dist = light.polar_distance.sample(rng);
64 let origin = Point {
65 x: cart_x + f64::cos(polar_angle) * polar_dist,
66 y: cart_y + f64::sin(polar_angle) * polar_dist,
67 };
68 let ray_angle = light.ray_angle.sample(rng) * (PI / 180.0);
69 let direction = Vector {
71 x: f64::cos(ray_angle),
72 y: f64::sin(ray_angle),
73 };
74 let mut wavelength = 0.0;
76 while wavelength > LAST_WAVELENGTH - 1.0 || wavelength < FIRST_WAVELENGTH {
77 wavelength = light.wavelength.sample(rng);
78 }
79 Ray {
80 origin,
81 direction,
82 wavelength,
83 bounces: 1000,
84 ray_rng: R::seed_from_u64(rng.gen()),
85 }
86 }
87
88 pub fn collision_list(
89 mut self,
90 obj_list: &Vec<Object<R>>,
91 viewport: Rect,
92 ) -> (Option<RayResult>, Option<Self>) {
93 let ((hit, normal, alpha), _dist, obj) = obj_list
94 .iter()
95 .filter_map(|o| {
96 o.get_hit(&self.origin, &self.direction, &mut self.ray_rng)
97 .map(|r| (r, self.origin.distance(&r.0), o))
98 })
99 .filter(|(_, distance, _)| *distance > 3.0)
100 .fold(
101 (((0.0, 0.0).into(), (0.0, 0.0).into(), 0.0), f64::MAX, None),
102 |b, x| if b.1 > x.1 { (x.0, x.1, Some(x.2)) } else { b },
103 );
104
105 match obj {
106 None => {
107 match self.furthest_aabb(viewport) {
109 Some(vp_hit) => (
110 Some(RayResult {
111 origin: self.origin,
112 termination: vp_hit,
113 wavelength: self.wavelength,
114 }),
115 None,
116 ),
117 None => (None, None),
118 }
119 }
120 Some(obj) => {
121 let material_result = obj
123 .process_material(
124 &self.direction.normalized(),
125 &normal.normalized(),
126 self.wavelength,
127 alpha,
128 &mut self.ray_rng,
129 )
130 .map(|v| Ray {
131 origin: hit,
132 direction: v,
133 wavelength: self.wavelength,
134 bounces: self.bounces - 1,
135 ray_rng: R::seed_from_u64(self.ray_rng.gen()),
136 });
137
138 (
139 Some(RayResult {
140 origin: self.origin,
141 termination: hit,
142 wavelength: self.wavelength,
143 }),
144 material_result,
145 )
146 }
147 }
148 }
149
150 fn intersect_edge(&self, s1: Point, sd: Vector) -> Option<f64> {
151 let mat_a = Matrix {
152 a1: sd.x,
153 b1: -self.direction.x,
154 a2: sd.y,
155 b2: -self.direction.y,
156 };
157
158 let omega = self.origin - s1;
159
160 let result = match mat_a.inverse() {
161 Some(m) => m * omega,
162 None => {
163 return None; }
165 };
166 if (result.x >= 0.0) && (result.x <= 1.0) && (result.y > 0.0) {
167 Some(result.y)
168 } else {
169 None
170 }
171 }
172
173 pub fn furthest_aabb(&self, aabb: Rect) -> Option<Point> {
174 let mut max_dist: Option<f64> = None;
175
176 let horizontal = Vector {
177 x: aabb.top_right().x - aabb.top_left().x,
178 y: 0.0,
179 };
180 let vertical = Vector {
181 x: 0.0,
182 y: aabb.bottom_left().y - aabb.top_left().y,
183 };
184
185 match self.intersect_edge(aabb.top_left(), horizontal) {
187 None => (),
188 Some(d) => {
189 max_dist = match max_dist {
190 None => Some(d),
191 Some(md) => {
192 if d > md {
193 Some(d)
194 } else {
195 max_dist
196 }
197 }
198 };
199 }
200 }
201
202 match self.intersect_edge(aabb.bottom_left(), horizontal) {
204 None => (),
205 Some(d) => {
206 max_dist = match max_dist {
207 None => Some(d),
208 Some(md) => {
209 if d > md {
210 Some(d)
211 } else {
212 max_dist
213 }
214 }
215 };
216 }
217 }
218
219 match self.intersect_edge(aabb.top_left(), vertical) {
221 None => (),
222 Some(d) => {
223 max_dist = match max_dist {
224 None => Some(d),
225 Some(md) => {
226 if d > md {
227 Some(d)
228 } else {
229 max_dist
230 }
231 }
232 };
233 }
234 }
235
236 match self.intersect_edge(aabb.top_right(), vertical) {
238 None => (),
239 Some(d) => {
240 max_dist = match max_dist {
241 None => Some(d),
242 Some(md) => {
243 if d > md {
244 Some(d)
245 } else {
246 max_dist
247 }
248 }
249 };
250 }
251 }
252
253 match max_dist {
254 None => {
255 return None;
256 }
257 Some(d) => {
258 return Some(Point {
259 x: self.origin.x + d * self.direction.x,
260 y: self.origin.y + d * self.direction.y,
261 });
262 }
263 }
264 }
265}
266
267#[cfg(test)]
268mod test {
269 type RandGen = rand_pcg::Pcg64Mcg;
270
271 use super::Ray;
272 use crate::geom::{Point, Rect};
273 use crate::sampler::Sampler;
274 use crate::scene::Light;
275 use rand::prelude::*;
276
277 fn new_test_light<R>(l: (f64, f64), a: f64) -> Light<R>
278 where
279 R: Rng,
280 {
281 Light::new(l, 1.0, 0.0, 0.0, a, Sampler::new_blackbody(5800.0))
282 }
283
284 #[test]
285 fn new_works() {
286 let mut rng = RandGen::from_entropy();
287
288 let l = Light {
289 power: Sampler::new_const(1.0),
290 location: (100.0, 100.0).into(),
291 polar_angle: Sampler::new_const(360.0),
292 polar_distance: Sampler::new_const(1.0),
293 ray_angle: Sampler::new_const(0.0),
294 wavelength: Sampler::new_const(460.0),
295 };
296
297 let r = Ray::new(&l, &mut rng);
298 assert_eq!(r.origin.x.round(), 101.0);
299 assert_eq!(r.origin.y.round(), 100.0);
300 assert_eq!(r.direction.x.round(), 1.0);
301 assert_eq!(r.direction.y.round(), 0.0);
302 assert_eq!(r.wavelength.round(), 460.0);
303 assert_eq!(r.bounces, 1000);
304 }
305
306 #[test]
307 fn furthest_aabb_hits_horziontal() {
308 let mut rng = RandGen::from_entropy();
309
310 let x_plus_light = new_test_light((0.0, 0.0), 0.0);
311
312 let ray = Ray::new(&x_plus_light, &mut rng);
314
315 let p1 = Point { x: 1.0, y: -10.0 };
317 let p2 = Point { x: 11.0, y: 10.0 };
318 let aabb = Rect::from_points(&p1, &p2);
319
320 let result = ray.furthest_aabb(aabb);
321
322 let result = result.expect("Result should have been Some()");
324 assert_eq!(result.x, 11.0);
325 assert_eq!(result.y, 0.0);
326 }
327
328 #[test]
329 fn furthest_aabb_hits_vertical() {
330 let mut rng = RandGen::from_entropy();
331
332 let x_plus_light = new_test_light((0.0, 0.0), 90.0);
333
334 let ray = Ray::new(&x_plus_light, &mut rng);
336
337 let p1 = Point { x: -10.0, y: 1.0 };
339 let p2 = Point { x: 10.0, y: 11.0 };
340 let aabb = Rect::from_points(&p1, &p2);
341
342 let result = ray.furthest_aabb(aabb);
343
344 let result = result.expect("That shouldn't be None!");
346 assert_eq!(result.x.round(), 0.0);
347 assert_eq!(result.y.round(), 11.0);
348 }
349
350 #[test]
351 fn furthest_aabb_hits_almost_vertical() {
352 let mut rng = RandGen::from_entropy();
353
354 let x_plus_light = new_test_light((0.0, 0.0), 45.0);
355
356 let ray = Ray::new(&x_plus_light, &mut rng);
358
359 let p1 = Point { x: -10.0, y: 1.0 };
361 let p2 = Point { x: 20.0, y: 11.0 };
362 let aabb = Rect::from_points(&p1, &p2);
363
364 let result = ray.furthest_aabb(aabb);
365
366 assert!(result.is_some());
368 let result = result.expect("Something was meant to be there!");
369 assert_eq!(result.x.round(), 11.0);
370 assert_eq!(result.y.round(), 11.0);
371 }
372
373 #[test]
374 fn furthest_aabb_special_case() {
375 let mut rng = RandGen::from_entropy();
376
377 let x_plus_light = new_test_light((100.0, 700.0), -45.0);
378
379 let ray = Ray::new(&x_plus_light, &mut rng);
381
382 let p1 = Point { x: 0.0, y: 0.0 };
384 let p2 = Point {
385 x: 200.0,
386 y: 1000.0,
387 };
388 let aabb = Rect::from_points(&p1, &p2);
389
390 let result = ray.furthest_aabb(aabb);
391
392 let result = result.expect("None is not what we wanted");
394 assert_eq!(result.x.round(), 200.0);
395 assert_eq!(result.y.round(), 600.0);
396 }
397}