From 3f78cacdd93036dbd51bae77d5d8e5430a0bc75f Mon Sep 17 00:00:00 2001
From: Julian T <julian@jtle.dk>
Date: Sat, 6 Mar 2021 16:15:26 +0100
Subject: Several changes to bounding boxes

For instance removed support for shapes without a bounding box, such as
planes
---
 src/core/bound3.rs          | 54 +++++++++++++++++++++++++++++++++++++++----
 src/lib.rs                  |  1 +
 src/world/container.rs      | 43 ----------------------------------
 src/world/container/list.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++
 src/world/container/mod.rs  |  3 +++
 src/world/hittable.rs       |  4 +---
 src/world/mod.rs            |  5 ++--
 src/world/scene.rs          | 14 ++++++++----
 src/world/shapes/sphere.rs  | 13 +++++------
 9 files changed, 128 insertions(+), 65 deletions(-)
 delete mode 100644 src/world/container.rs
 create mode 100644 src/world/container/list.rs
 create mode 100644 src/world/container/mod.rs

diff --git a/src/core/bound3.rs b/src/core/bound3.rs
index 7f54e03..ce6bb09 100644
--- a/src/core/bound3.rs
+++ b/src/core/bound3.rs
@@ -1,6 +1,6 @@
 //! Implements 3d axis aligned bounding box
 use crate::{Number, Float};
-use super::vector3::Vector3;
+use super::vector3::{Vector3, Vector3f};
 use crate::core::{min, max};
 use crate::core::Ray;
 
@@ -29,23 +29,64 @@ impl<T: Number> Bound3<T> {
 
         Self {min, max}
     }
+
+    pub fn combine(&self, op: &Self) -> Self {
+        let min = Vector3::new_xyz(
+            min(self.min.x, op.min.x),
+            min(self.min.y, op.min.y),
+            min(self.min.z, op.min.z)
+            );
+        let max = Vector3::new_xyz(
+            max(self.max.x, op.max.x),
+            max(self.max.y, op.max.y),
+            max(self.max.z, op.max.z)
+            );
+
+        Self {min, max}
+    }
+
+    pub fn and(&self, op: &Self) -> Self {
+        let min_b = Vector3::new_xyz(
+            max(self.min.x, op.min.x),
+            max(self.min.y, op.min.y),
+            max(self.min.z, op.min.z)
+            );
+        let max_b = Vector3::new_xyz(
+            min(self.max.x, op.max.x),
+            min(self.max.y, op.max.y),
+            min(self.max.z, op.max.z)
+            );
+
+        Self {min: min_b, max: max_b}
+    }
+
+    pub fn area(&self) -> T {
+        let diag = self.max - self.min;
+        diag.x * diag.y * diag.z
+    }
 }
 
 impl Bound3f {
+    pub const EMPTY: Bound3f = Bound3f{min: Vector3f::ZERO, max: Vector3f::ZERO};
+
     /// Calculate whether there is a intersect between a bounding box and a ray
     ///
     /// # Examples:
     ///
     /// ```
     /// use rendering::core::{Bound3f, Vector3f, Ray};
+    /// use rendering::INFTY;
     /// let b = Bound3f::new(Vector3f::new(7.0), Vector3f::new(10.0));
     /// let r1 = Ray::new_to(Vector3f::new(0.0), Vector3f::new(5.0));
+    /// let r2 = Ray::new_to(Vector3f::new(-0.0), Vector3f::new(-5.0));
     /// let r3 = Ray::new(Vector3f::new_xyz(-1.0, 0.0, 0.0), Vector3f::new_xyz(1.0, 0.0, 0.0));
     ///
-    /// assert!(b.intersect(&r1));
-    /// assert!(!b.intersect(&r3));
+    /// assert!(b.intersect(&r1, 0.0, INFTY));
+    /// assert!(!b.intersect(&r2, 0.0, INFTY));
+    /// assert!(!b.intersect(&r3, 0.0, INFTY));
     /// ```
-    pub fn intersect(&self, ray: &Ray) -> bool {
+    pub fn intersect(&self, ray: &Ray, t_min: Float, t_max: Float) -> bool {
+        println!("BIN: {} -> {}", self.min, self.max);
         // Method stolen from Ray tracing the next week.
         // They mention its from pixar
         for i in 0..3 {
@@ -59,7 +100,10 @@ impl Bound3f {
                 t1 = tmp;
             }
 
-            if t1 <= t0 {
+            let t_min = max(t0, t_min);
+            let t_max = min(t1, t_max);
+
+            if t_max <= t_min {
                 return false;
             }
         }
diff --git a/src/lib.rs b/src/lib.rs
index a40e8e6..0e6674d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -38,3 +38,4 @@ pub type Float = f64;
 
 pub const M_PI: Float = PI;
 pub const NEAR_ZERO: Float = 1e-8;
+pub const INFTY: Float = f64::INFINITY;
diff --git a/src/world/container.rs b/src/world/container.rs
deleted file mode 100644
index 6f8c182..0000000
--- a/src/world/container.rs
+++ /dev/null
@@ -1,43 +0,0 @@
-use super::{Hittable, Intersection};
-use crate::core::Ray;
-
-pub struct HittableList {
-    elems: Vec<Box<dyn Hittable>>,
-}
-
-impl HittableList {
-    pub fn new() -> Self {
-        Self::default()
-    }
-
-    pub fn add(&mut self, h: Box<dyn Hittable>) {
-        self.elems.push(h);
-    }
-}
-
-impl Hittable for HittableList {
-    fn intersect(&self, ray: &Ray) -> Option<Intersection> {
-        let mut min: Option<Intersection> = None;
-
-        for e in self.elems.iter() {
-            if let Some(i) = e.intersect(&ray) {
-                match min {
-                    // Do nothing if distance is bigger than min
-                    Some(ref min_i) if min_i.t < i.t => {},
-                    // If no existing min or closer than
-                    _ => min = Some(i),
-                }
-            }
-        }
-
-        min
-    }
-}
-
-impl Default for HittableList {
-    fn default() -> Self {
-        Self {
-            elems: Vec::new(),
-        }
-    }
-}
diff --git a/src/world/container/list.rs b/src/world/container/list.rs
new file mode 100644
index 0000000..c8e3fdd
--- /dev/null
+++ b/src/world/container/list.rs
@@ -0,0 +1,56 @@
+use crate::world::{Hittable, Intersection};
+use crate::core::{Bound3f, Ray};
+
+
+pub struct HittableList {
+    elems: Vec<Box<dyn Hittable>>,
+}
+
+impl HittableList {
+    pub fn new() -> Self {
+        Self::default()
+    }
+
+    pub fn add(&mut self, h: Box<dyn Hittable>) {
+        self.elems.push(h);
+    }
+}
+
+impl Hittable for HittableList {
+    fn intersect(&self, ray: &Ray) -> Option<Intersection> {
+        let mut min: Option<Intersection> = None;
+
+        for e in self.elems.iter() {
+            if let Some(i) = e.intersect(&ray) {
+                match min {
+                    // Do nothing if distance is bigger than min
+                    Some(ref min_i) if min_i.t < i.t => {},
+                    // If no existing min or closer than
+                    _ => min = Some(i),
+                }
+            }
+        }
+
+        min
+    }
+
+    fn bounding_box(&self) -> Bound3f {
+        let mut bound: Bound3f = Bound3f::EMPTY;
+
+        for e in self.elems.iter() {
+            let eb = e.bounding_box();
+            bound = bound.combine(&eb);
+        }
+
+        bound
+    }
+}
+
+impl Default for HittableList {
+    fn default() -> Self {
+        Self {
+            elems: Vec::new(),
+        }
+    }
+}
+
diff --git a/src/world/container/mod.rs b/src/world/container/mod.rs
new file mode 100644
index 0000000..35d4693
--- /dev/null
+++ b/src/world/container/mod.rs
@@ -0,0 +1,3 @@
+mod list;
+
+pub use list::HittableList;
diff --git a/src/world/hittable.rs b/src/world/hittable.rs
index 720a019..c5a353e 100644
--- a/src/world/hittable.rs
+++ b/src/world/hittable.rs
@@ -27,7 +27,5 @@ pub trait Hittable: Sync + Send {
     fn intersect(&self, ray: &Ray) -> Option<Intersection>;
 
     /// Returns the axis alligned bounding box containing self
-    fn bounding_box(&self) -> Option<Bound3f> {
-        None
-    }
+    fn bounding_box(&self) -> Bound3f;
 }
diff --git a/src/world/mod.rs b/src/world/mod.rs
index 43f7530..53d8ad3 100644
--- a/src/world/mod.rs
+++ b/src/world/mod.rs
@@ -2,11 +2,10 @@
 pub mod shapes;
 
 mod scene;
-mod container;
+pub mod container;
 mod hittable;
 pub use scene::*;
 pub use hittable::{Intersection, Hittable};
-pub use container::HittableList;
 
 use std::sync::Arc;
 use crate::material::Material;
@@ -31,7 +30,7 @@ impl Hittable for Object {
         self.shape.intersect(ray).map(|mut i| {i.m = Some(self.mat.as_ref()); i})
     }
 
-    fn bounding_box(&self) -> Option<Bound3f> {
+    fn bounding_box(&self) -> Bound3f {
         self.shape.bounding_box()
     }
 }
diff --git a/src/world/scene.rs b/src/world/scene.rs
index 03578be..6d15fc1 100644
--- a/src/world/scene.rs
+++ b/src/world/scene.rs
@@ -1,9 +1,11 @@
-use crate::core::Ray;
+use crate::core::{Bound3f, Ray};
 
-use super::{Object, HittableList, Hittable, Intersection};
+use super::{Object, container, Hittable, Intersection};
+
+type Container = container::HittableList;
 
 pub struct Scene {
-    content: HittableList,
+    content: Container,
 }
 
 impl Scene {
@@ -26,12 +28,16 @@ impl Hittable for Scene {
     fn intersect(&self, ray: &Ray) -> Option<Intersection> {
         self.content.intersect(ray)
     }
+
+    fn bounding_box(&self) -> Bound3f {
+        self.content.bounding_box()
+    }
 }
 
 impl Default for Scene {
     fn default() -> Self {
         Self {
-            content: HittableList::new(),
+            content: Container::new(),
         }
     }
 }
diff --git a/src/world/shapes/sphere.rs b/src/world/shapes/sphere.rs
index 55fc8b4..e3348de 100644
--- a/src/world/shapes/sphere.rs
+++ b/src/world/shapes/sphere.rs
@@ -57,19 +57,18 @@ impl Hittable for Sphere {
     /// # Examples
     ///
     /// ```
-    /// use rendering::core::{Vector3f, Hittable};
-    /// use rendering::world::shapes::Sphere;
+    /// use rendering::core::Vector3f;
+    /// use rendering::world::{Hittable, shapes::Sphere};
     ///
     /// let sph = Sphere::new(1.0, Vector3f::new(0.0));
-    /// let b = sph.bounding_box().unwrap();
+    /// let b = sph.bounding_box();
     ///
     /// assert!(b.min.x == -1.0 && b.min.y == -1.0 && b.min.z == -1.0);
     /// assert!(b.max.x == 1.0 && b.max.y == 1.0 && b.max.z == 1.0);
-    fn bounding_box(&self) -> Option<Bound3f> {
+    fn bounding_box(&self) -> Bound3f {
         let offset = Vector3f::new(self.radius);
-        Some(
-            Bound3f::new(self.center - offset, self.center + offset)
-            )
+
+        Bound3f::new(self.center - offset, self.center + offset)
     }
 }
 
-- 
cgit v1.2.3