use crate::surface3::*;
use crate::vector3::Vector3D;
use crate::transform3::Transform3;
use crate::bounding_box3::BoundingBox3D;
use crate::ray3::Ray3D;
use crate::plane3::Plane3;
use std::sync::{RwLock, Arc};
pub struct Box3 {
bound: BoundingBox3D,
pub surface_data: Surface3Data,
}
impl Box3 {
pub fn new_default(transform: Option<Transform3>,
is_normal_flipped: Option<bool>) -> Box3 {
return Box3 {
bound: BoundingBox3D::new(Vector3D::new_default(), Vector3D::new(1.0, 1.0, 1.0)),
surface_data: Surface3Data::new(transform, is_normal_flipped),
};
}
pub fn new(lower_corner: Vector3D,
upper_corner: Vector3D,
transform: Option<Transform3>,
is_normal_flipped: Option<bool>) -> Box3 {
return Box3 {
bound: BoundingBox3D::new(lower_corner, upper_corner),
surface_data: Surface3Data::new(transform, is_normal_flipped),
};
}
pub fn new_aabb(bounding_box: BoundingBox3D,
transform: Option<Transform3>,
is_normal_flipped: Option<bool>) -> Box3 {
return Box3 {
bound: bounding_box,
surface_data: Surface3Data::new(transform, is_normal_flipped),
};
}
pub fn builder() -> Builder {
return Builder::new();
}
}
impl Surface3 for Box3 {
fn closest_point_local(&self, other_point: &Vector3D) -> Vector3D {
return if self.bound.contains(other_point) {
let planes = [Plane3::new(Vector3D::new(1.0, 0.0, 0.0), self.bound.upper_corner, None, None),
Plane3::new(Vector3D::new(0.0, 1.0, 0.0), self.bound.upper_corner, None, None),
Plane3::new(Vector3D::new(0.0, 0.0, 1.0), self.bound.upper_corner, None, None),
Plane3::new(Vector3D::new(-1.0, 0.0, 0.0), self.bound.lower_corner, None, None),
Plane3::new(Vector3D::new(0.0, -1.0, 0.0), self.bound.lower_corner, None, None),
Plane3::new(Vector3D::new(0.0, 0.0, -1.0), self.bound.lower_corner, None, None)];
let mut result = planes[0].closest_point(other_point);
let mut distance_squared = result.distance_squared_to(*other_point);
for i in 1..6 {
let local_result = planes[i].closest_point(other_point);
let local_distance_squared =
local_result.distance_squared_to(*other_point);
if local_distance_squared < distance_squared {
result = local_result;
distance_squared = local_distance_squared;
}
}
result
} else {
crate::vector3::clamp(&other_point, &self.bound.lower_corner, &self.bound.upper_corner)
};
}
fn bounding_box_local(&self) -> BoundingBox3D {
return self.bound.clone();
}
fn closest_intersection_local(&self, ray: &Ray3D) -> SurfaceRayIntersection3 {
let mut intersection = SurfaceRayIntersection3::new();
let bb_ray_intersection = self.bound.closest_intersection(&ray);
intersection.is_intersecting = bb_ray_intersection.is_intersecting;
if intersection.is_intersecting {
intersection.distance = bb_ray_intersection.t_near;
intersection.point = ray.point_at(bb_ray_intersection.t_near);
intersection.normal = self.closest_normal_local(&intersection.point);
}
return intersection;
}
fn closest_normal_local(&self, other_point: &Vector3D) -> Vector3D {
let planes = [Plane3::new(Vector3D::new(1.0, 0.0, 0.0), self.bound.upper_corner, None, None),
Plane3::new(Vector3D::new(0.0, 1.0, 0.0), self.bound.upper_corner, None, None),
Plane3::new(Vector3D::new(0.0, 0.0, 1.0), self.bound.upper_corner, None, None),
Plane3::new(Vector3D::new(-1.0, 0.0, 0.0), self.bound.lower_corner, None, None),
Plane3::new(Vector3D::new(0.0, -1.0, 0.0), self.bound.lower_corner, None, None),
Plane3::new(Vector3D::new(0.0, 0.0, -1.0), self.bound.lower_corner, None, None)];
return if self.bound.contains(other_point) {
let mut closest_normal = planes[0].normal;
let closest_point = planes[0].closest_point(other_point);
let mut min_distance_squared = (closest_point - *other_point).length_squared();
for i in 1..6 {
let local_closest_point = planes[i].closest_point(other_point);
let local_distance_squared =
(local_closest_point - *other_point).length_squared();
if local_distance_squared < min_distance_squared {
closest_normal = planes[i].normal;
min_distance_squared = local_distance_squared;
}
}
closest_normal
} else {
let closest_point =
crate::vector3::clamp(&other_point, &self.bound.lower_corner, &self.bound.upper_corner);
let closest_point_to_input_point = *other_point - closest_point;
let mut closest_normal = planes[0].normal;
let mut max_cosine_angle = closest_normal.dot(&closest_point_to_input_point);
for i in 1..6 {
let cosine_angle = planes[i].normal.dot(&closest_point_to_input_point);
if cosine_angle > max_cosine_angle {
closest_normal = planes[i].normal;
max_cosine_angle = cosine_angle;
}
}
closest_normal
};
}
fn intersects_local(&self, ray_local: &Ray3D) -> bool {
return self.bound.intersects(ray_local);
}
fn view(&self) -> &Surface3Data {
return &self.surface_data;
}
}
pub type Box3Ptr = Arc<RwLock<Box3>>;
pub struct Builder {
_lower_corner: Vector3D,
_upper_corner: Vector3D,
_surface_data: Surface3Data,
}
impl Builder {
pub fn with_lower_corner(&mut self, pt: Vector3D) -> &mut Self {
self._lower_corner = pt;
return self;
}
pub fn with_upper_corner(&mut self, pt: Vector3D) -> &mut Self {
self._upper_corner = pt;
return self;
}
pub fn with_bounding_box(&mut self, bbox: BoundingBox3D) -> &mut Self {
self._lower_corner = bbox.lower_corner;
self._upper_corner = bbox.upper_corner;
return self;
}
pub fn build(&mut self) -> Box3 {
return Box3::new_aabb(
BoundingBox3D::new(self._lower_corner, self._upper_corner),
Some(self._surface_data.transform.clone()),
Some(self._surface_data.is_normal_flipped),
);
}
pub fn make_shared(&mut self) -> Box3Ptr {
return Box3Ptr::new(RwLock::new(self.build()));
}
pub fn new() -> Builder {
return Builder {
_lower_corner: Vector3D::new_default(),
_upper_corner: Vector3D::new(1.0, 1.0, 1.0),
_surface_data: Surface3Data::new(None, None),
};
}
}
impl SurfaceBuilderBase3 for Builder {
fn view(&mut self) -> &mut Surface3Data {
return &mut self._surface_data;
}
}