use crate::surface2::*;
use crate::vector2::Vector2D;
use crate::transform2::Transform2;
use crate::bounding_box2::BoundingBox2D;
use crate::ray2::Ray2D;
use std::sync::{RwLock, Arc};
pub struct Sphere2 {
pub center: Vector2D,
pub radius: f64,
pub surface_data: Surface2Data,
}
impl Sphere2 {
pub fn new_default(transform: Option<Transform2>,
is_normal_flipped: Option<bool>) -> Sphere2 {
return Sphere2 {
center: Vector2D::new_default(),
radius: 0.0,
surface_data: Surface2Data::new(transform, is_normal_flipped),
};
}
pub fn new(center: Vector2D,
radius: f64,
transform: Option<Transform2>,
is_normal_flipped: Option<bool>) -> Sphere2 {
return Sphere2 {
center,
radius,
surface_data: Surface2Data::new(transform, is_normal_flipped),
};
}
pub fn builder() -> Builder {
return Builder::new();
}
}
impl Surface2 for Sphere2 {
fn closest_point_local(&self, other_point: &Vector2D) -> Vector2D {
return self.closest_normal_local(other_point) * self.radius + self.center;
}
fn bounding_box_local(&self) -> BoundingBox2D {
let r = Vector2D::new(self.radius, self.radius);
return BoundingBox2D::new(self.center - r, self.center + r);
}
fn closest_intersection_local(&self, ray: &Ray2D) -> SurfaceRayIntersection2 {
let mut intersection = SurfaceRayIntersection2::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: &Vector2D) -> Vector2D {
return if self.center.is_similar(&other_point, None) {
Vector2D::new(1.0, 0.0)
} else {
(*other_point - self.center).normalized()
};
}
fn intersects_local(&self, ray_local: &Ray2D) -> 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: &Vector2D) -> f64 {
return f64::abs(self.center.distance_to(*other_point_local) - self.radius);
}
fn view(&self) -> &Surface2Data {
return &self.surface_data;
}
}
pub type Sphere2Ptr = Arc<RwLock<Sphere2>>;
pub struct Builder {
_center: Vector2D,
_radius: f64,
_surface_data: Surface2Data,
}
impl Builder {
pub fn with_center(&mut self, center: Vector2D) -> &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) -> Sphere2 {
return Sphere2::new(self._center,
self._radius,
Some(self._surface_data.transform.clone()),
Some(self._surface_data.is_normal_flipped),
);
}
pub fn make_shared(&mut self) -> Sphere2Ptr {
return Sphere2Ptr::new(RwLock::new(self.build()));
}
pub fn new() -> Builder {
return Builder {
_center: Vector2D::new_default(),
_radius: 0.0,
_surface_data: Surface2Data::new(None, None),
};
}
}
impl SurfaceBuilderBase2 for Builder {
fn view(&mut self) -> &mut Surface2Data {
return &mut self._surface_data;
}
}