aboutsummaryrefslogtreecommitdiff
path: root/src/hittable/instance/rotate_z.rs
blob: 119baca8eed4f2da769d3859df45d45cf370edfb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
use std::sync::Arc;

use crate::{hittable::{HitRecord, Hittable, AABB}, ray::Ray, util::degrees_to_radians, vec3::{Point3, Vec3}};

pub struct RotateZ {
    hittable: Arc<dyn Hittable>,
    sin_theta: f64,
    cos_theta: f64,
    aabb: Option<AABB>,
}

impl RotateZ {
    pub fn new(hittable: Arc<dyn Hittable>, angle: f64) -> Self {
        let radians = degrees_to_radians(angle);
        let sin_theta = radians.sin();
        let cos_theta = radians.cos();
        match hittable.bounding_box(0.0, 1.0) { // TODO: passing in 0.0 and 1.0 for time seems suspicious.
            None => Self { hittable, sin_theta, cos_theta, aabb: None },
            Some(aabb) => {
                let mut min = Point3 { x: f64::INFINITY, y: f64::INFINITY, z: f64::INFINITY };
                let mut max = Point3 { x: -f64::INFINITY, y: -f64::INFINITY, z: -f64::INFINITY };
                for i in 0..2 {
                    for j in 0..2 {
                        for k in 0..2 {
                            let x = i as f64 * aabb.maximum.x + (1.0 - i as f64) * aabb.minimum.x;
                            let y = j as f64 * aabb.maximum.y + (1.0 - j as f64) * aabb.minimum.y;
                            let z = k as f64 * aabb.maximum.z + (1.0 - k as f64) * aabb.minimum.z;
                            let new_x = cos_theta * x + sin_theta * y;
                            let new_y = -sin_theta * x + cos_theta * y;

                            let tester = Vec3 { x: new_x, z, y: new_y };
                            for c in 0..3 {
                                *min.get_mut(c).unwrap() = min.get(c).unwrap().min(*tester.get(c).unwrap());
                                *max.get_mut(c).unwrap() = max.get(c).unwrap().max(*tester.get(c).unwrap());
                            }
                        }
                    }
                }
                let aabb = AABB { minimum: min, maximum: max };

                Self {
                    hittable,
                    sin_theta,
                    cos_theta,
                    aabb: Some(aabb),
                }
            }
        }
    }
}

impl Hittable for RotateZ {
    fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
        let mut origin = ray.origin.clone();
        let mut direction = ray.direction.clone();

        origin.x = self.cos_theta * ray.origin.x - self.sin_theta * ray.origin.y;
        origin.y = self.sin_theta * ray.origin.x + self.cos_theta * ray.origin.y;
        
        direction.x = self.cos_theta * ray.direction.x - self.sin_theta * ray.direction.y;
        direction.y = self.sin_theta * ray.direction.x + self.cos_theta * ray.direction.y;

        let rotated_ray = Ray { origin, direction, time: ray.time };
        let mut hit_record = self.hittable.hit(&rotated_ray, t_min, t_max)?;

        let mut p = hit_record.p.clone();
        let mut normal = hit_record.normal.clone();

        p.x = self.cos_theta * hit_record.p.x + self.sin_theta * hit_record.p.y;
        p.y = -self.sin_theta * hit_record.p.x + self.cos_theta * hit_record.p.y;

        normal.x = self.cos_theta * hit_record.normal.x + self.sin_theta * hit_record.normal.y;
        normal.y = -self.sin_theta * hit_record.normal.x + self.cos_theta * hit_record.normal.y;

        hit_record.p = p;
        hit_record.set_face_normal(&ray, &normal);
        Some(hit_record)
    }

    fn bounding_box(&self, _: f64, _: f64) -> Option<AABB> {
        self.aabb.clone()
    }
}