aboutsummaryrefslogtreecommitdiff
path: root/src/hittable/instance/rotate_z.rs
blob: 38601ce5db87610868da3ea7bd400b350d575f1f (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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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()
    }
}