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
116
117
118
|
use crate::{NEAR_ZERO, Float};
use crate::core::{Ray, Vector3f, Bound3f};
use crate::world::{Hittable, DynHittable, Intersection, Instancable};
const OUT_XY: Vector3f = Vector3f { x: 0.0, y: 0.0, z: 1.0 };
pub enum Plane {
XY,
XZ,
YZ,
}
pub struct Rect {
// Size of rectangle in first dimension
d1: Float,
// Size in second
d2: Float,
// Offset of plane plane. Should only be used when composing other types
offset: Float,
plane: Plane,
}
impl Rect {
pub fn new(width: Float, height: Float, plane: Plane) -> Self {
Rect {d1: width, d2: height, offset: 0.0, plane}
}
pub fn new_with_offset(d1: Float, d2: Float, offset: Float, plane: Plane) -> Self {
Rect {
d1, d2,
offset,
plane
}
}
}
impl Hittable for Rect {
fn intersect(&self, ray: &Ray) -> Option<Intersection> {
let t = (self.offset - self.plane.translate(ray.origin).z)
/ self.plane.translate(ray.direction).z;
if t <= NEAR_ZERO {
return None;
}
let poi = self.plane.translate(ray.at(t));
// Check if at is inside rectangle on plane
if poi.x.abs() > (self.d1/2.0) || poi.y.abs() > (self.d2/2.0) {
// No collision
return None;
}
Some(Intersection::new(self.plane.translate_inv(OUT_XY), self.plane.translate_inv(poi), ray, t))
}
fn bounding_box(&self) -> Bound3f {
// A rectangle has no area, but we will give it some for its bounding box
Bound3f::new(
Vector3f::new_xyz(-self.d1, -self.d2, self.offset - 0.0001),
Vector3f::new_xyz(self.d1, self.d2, self.offset + 0.0001)
)
}
}
impl Into<DynHittable> for Rect {
fn into(self) -> DynHittable {
DynHittable::new(Box::new(self))
}
}
impl Instancable for Rect {}
impl Plane {
pub fn translate(&self, v: Vector3f) -> Vector3f {
match self {
Plane::XY => v,
Plane::XZ => Vector3f::new_xyz(v.x, v.z, v.y),
Plane::YZ => Vector3f::new_xyz(v.y, v.z, v.x),
}
}
pub fn translate_inv(&self, v: Vector3f) -> Vector3f {
match self {
Plane::XY => v,
Plane::XZ => Vector3f::new_xyz(v.x, v.z, v.y),
Plane::YZ => Vector3f::new_xyz(v.z, v.x, v.y),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn xy_rect() {
let rect = Rect::new_xy(5.0, 5.0);
let ray = Ray {
origin: Vector3f::new_xyz(0.0, 1.0, 1.5),
direction: Vector3f::new_xyz(0.0, 0.0, -1.0),
};
let t = rect.intersect(&ray).unwrap().t;
assert!((t - 1.5).abs() < 0.01);
}
#[test]
fn xy_rect_offset() {
let rect = Rect::new_with_offset(5.0, 5.0, -1.0, Plane::XY);
let ray = Ray {
origin: Vector3f::new_xyz(0.0, 1.0, 1.5),
direction: Vector3f::new_xyz(0.0, 0.0, -1.0),
};
let t = rect.intersect(&ray).unwrap().t;
assert!((t - 2.5).abs() < 0.01);
}
}
|