aboutsummaryrefslogtreecommitdiff
path: root/src/world/shapes/rectangle.rs
diff options
context:
space:
mode:
authorJulian T <julian@jtle.dk>2021-08-05 15:42:43 +0200
committerJulian T <julian@jtle.dk>2021-08-05 15:42:43 +0200
commitc5505ab84820248c6dba35fc06aef9e0ced183de (patch)
treebb1d508c31c6a5aa5d57aed86daaeb6f9cffc88e /src/world/shapes/rectangle.rs
parent02fedfc9653cb37219849de7dae14ae13d2f6c36 (diff)
Add XY rectangle
Diffstat (limited to 'src/world/shapes/rectangle.rs')
-rw-r--r--src/world/shapes/rectangle.rs105
1 files changed, 105 insertions, 0 deletions
diff --git a/src/world/shapes/rectangle.rs b/src/world/shapes/rectangle.rs
new file mode 100644
index 0000000..313ebb8
--- /dev/null
+++ b/src/world/shapes/rectangle.rs
@@ -0,0 +1,105 @@
+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,
+}
+
+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-ray.origin.z) / ray.direction.z;
+ if t <= NEAR_ZERO {
+ return None;
+ }
+
+ let poi = 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(OUT_XY, 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 switch(&self, v: Vector3f) -> Vector3f {
+ match self {
+ Plane::XY => v,
+ }
+ }
+}
+
+#[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);
+ }
+}