use crate::surface3::*;
use crate::vector3::Vector3D;
use crate::transform3::Transform3;
use crate::bounding_box3::BoundingBox3D;
use crate::ray3::Ray3D;
use std::sync::{RwLock, Arc};
pub struct Sphere3 {
pub center: Vector3D,
pub radius: f64,
pub surface_data: Surface3Data,
}
impl Sphere3 {
pub fn new_default(transform: Option<Transform3>,
is_normal_flipped: Option<bool>) -> Sphere3 {
return Sphere3 {
center: Vector3D::new_default(),
radius: 0.0,
surface_data: Surface3Data::new(transform, is_normal_flipped),
};
}
pub fn new(center: Vector3D,
radius: f64,
transform: Option<Transform3>,
is_normal_flipped: Option<bool>) -> Sphere3 {
return Sphere3 {
center,
radius,
surface_data: Surface3Data::new(transform, is_normal_flipped),
};
}
pub fn builder() -> Builder {
return Builder::new();
}
}
impl Surface3 for Sphere3 {
fn closest_point_local(&self, other_point: &Vector3D) -> Vector3D {
return self.closest_normal_local(other_point) * self.radius + self.center;
}
fn bounding_box_local(&self) -> BoundingBox3D {
let r = Vector3D::new(self.radius, self.radius, self.radius);
return BoundingBox3D::new(self.center - r, self.center + r);
}
fn closest_intersection_local(&self, ray: &Ray3D) -> SurfaceRayIntersection3 {
let mut intersection = SurfaceRayIntersection3::new();
let r = ray.origin - self.center;
let b = ray.direction.dot(&r);
let c = r.length_squared() - crate::math_utils::square(self.radius);
let mut d = b * b - c;
if d > 0.0 {
d = f64::sqrt(d);
let mut t_min = -b - d;
let t_max = -b + d;
if t_min < 0.0 {
t_min = t_max;
}
if t_min < 0.0 {
intersection.is_intersecting = false;
} else {
intersection.is_intersecting = true;
intersection.distance = t_min;
intersection.point = ray.origin + ray.direction * t_min;
intersection.normal = (intersection.point - self.center).normalized();
}
} else {
intersection.is_intersecting = false;
}
return intersection;
}
fn closest_normal_local(&self, other_point: &Vector3D) -> Vector3D {
return if self.center.is_similar(&other_point, None) {
Vector3D::new(1.0, 0.0, 0.0)
} else {
(*other_point - self.center).normalized()
};
}
fn intersects_local(&self, ray_local: &Ray3D) -> bool {
let r = ray_local.origin - self.center;
let b = ray_local.direction.dot(&r);
let c = r.length_squared() - crate::math_utils::square(self.radius);
let mut d = b * b - c;
if d > 0.0 {
d = f64::sqrt(d);
let mut t_min = -b - d;
let t_max = -b + d;
if t_min < 0.0 {
t_min = t_max;
}
if t_min >= 0.0 {
return true;
}
}
return false;
}
fn closest_distance_local(&self, other_point_local: &Vector3D) -> f64 {
return f64::abs(self.center.distance_to(*other_point_local) - self.radius);
}
fn view(&self) -> &Surface3Data {
return &self.surface_data;
}
}
pub type Sphere3Ptr = Arc<RwLock<Sphere3>>;
pub struct Builder {
_center: Vector3D,
_radius: f64,
_surface_data: Surface3Data,
}
impl Builder {
pub fn with_center(&mut self, center: Vector3D) -> &mut Self {
self._center = center;
return self;
}
pub fn with_radius(&mut self, radius: f64) -> &mut Self {
self._radius = radius;
return self;
}
pub fn build(&mut self) -> Sphere3 {
return Sphere3::new(self._center,
self._radius,
Some(self._surface_data.transform.clone()),
Some(self._surface_data.is_normal_flipped),
);
}
pub fn make_shared(&mut self) -> Sphere3Ptr {
return Sphere3Ptr::new(RwLock::new(self.build()));
}
pub fn new() -> Builder {
return Builder {
_center: Vector3D::new_default(),
_radius: 0.0,
_surface_data: Surface3Data::new(None, None),
};
}
}
impl SurfaceBuilderBase3 for Builder {
fn view(&mut self) -> &mut Surface3Data {
return &mut self._surface_data;
}
}