use crate::surface3::*;
use crate::vector3::Vector3D;
use crate::transform3::Transform3;
use crate::bounding_box3::BoundingBox3D;
use crate::ray3::Ray3D;
use crate::vector2::Vector2D;
use crate::box2::Box2;
use crate::surface2::Surface2;
use crate::plane3::Plane3;
use std::sync::{RwLock, Arc};
pub struct Cylinder3 {
center: Vector3D,
radius: f64,
height: f64,
pub surface_data: Surface3Data,
}
impl Cylinder3 {
pub fn new_default(transform: Option<Transform3>,
is_normal_flipped: Option<bool>) -> Cylinder3 {
return Cylinder3 {
center: Vector3D::new_default(),
radius: 1.0,
height: 1.0,
surface_data: Surface3Data::new(transform, is_normal_flipped),
};
}
pub fn new(center: Vector3D,
radius: f64,
height: f64,
transform: Option<Transform3>,
is_normal_flipped: Option<bool>) -> Cylinder3 {
return Cylinder3 {
center,
radius,
height,
surface_data: Surface3Data::new(transform, is_normal_flipped),
};
}
pub fn builder() -> Builder {
return Builder::new();
}
}
impl Surface3 for Cylinder3 {
fn closest_point_local(&self, other_point: &Vector3D) -> Vector3D {
let r = *other_point - self.center;
let rr = Vector2D::new(f64::sqrt(r.x * r.x + r.z * r.z), r.y);
let aabb = Box2::new(Vector2D::new(-self.radius, -0.5 * self.height),
Vector2D::new(self.radius, 0.5 * self.height), None, None);
let cp = aabb.closest_point(&rr);
let angle = f64::atan2(r.z, r.x);
return Vector3D::new(cp.x * f64::cos(angle), cp.y, cp.x * f64::sin(angle)) + self.center;
}
fn bounding_box_local(&self) -> BoundingBox3D {
return BoundingBox3D::new(self.center - Vector3D::new(self.radius, 0.5 * self.height, self.radius),
self.center + Vector3D::new(self.radius, 0.5 * self.height, self.radius));
}
fn closest_intersection_local(&self, ray: &Ray3D) -> SurfaceRayIntersection3 {
let mut intersection = SurfaceRayIntersection3::new();
let mut d = ray.direction;
d.y = 0.0;
let mut o = ray.origin - self.center;
o.y = 0.0;
let aaa = d.length_squared();
let bbb = d.dot(&o);
let ccc = o.length_squared() - crate::math_utils::square(self.radius);
let bbox = self.bounding_box_local();
let upper_plane = Plane3::new(Vector3D::new(0.0, 1.0, 0.0),
bbox.upper_corner, None, None);
let lower_plane = Plane3::new(Vector3D::new(0.0, -1.0, 0.0),
bbox.lower_corner, None, None);
let mut upper_intersection = upper_plane.closest_intersection(&ray);
let mut lower_intersection = lower_plane.closest_intersection(&ray);
intersection.distance = f64::MAX;
intersection.is_intersecting = false;
if aaa < f64::EPSILON || bbb * bbb - aaa * ccc < 0.0 {
let r = ray.origin - self.center;
let rr = Vector2D::new(r.x, r.z);
if rr.length_squared() <= crate::math_utils::square(self.radius) {
if upper_intersection.is_intersecting {
intersection = upper_intersection;
}
if lower_intersection.is_intersecting &&
lower_intersection.distance < intersection.distance {
intersection = lower_intersection;
}
}
return intersection;
}
let t1 = (-bbb + f64::sqrt(bbb * bbb - aaa * ccc)) / aaa;
let t2 = (-bbb - f64::sqrt(bbb * bbb - aaa * ccc)) / aaa;
let mut t_cylinder = t2;
if t2 < 0.0 {
t_cylinder = t1;
}
let point_on_cylinder = ray.point_at(t_cylinder);
if point_on_cylinder.y >= self.center.y - 0.5 * self.height &&
point_on_cylinder.y <= self.center.y + 0.5 * self.height {
intersection.is_intersecting = true;
intersection.distance = t_cylinder;
intersection.point = point_on_cylinder;
intersection.normal = point_on_cylinder - self.center;
intersection.normal.y = 0.0;
intersection.normal.normalize();
}
if upper_intersection.is_intersecting {
let mut r = upper_intersection.point - self.center;
r.y = 0.0;
if r.length_squared() > crate::math_utils::square(self.radius) {
upper_intersection.is_intersecting = false;
} else if upper_intersection.distance < intersection.distance {
intersection = upper_intersection;
}
}
if lower_intersection.is_intersecting {
let mut r = lower_intersection.point - self.center;
r.y = 0.0;
if r.length_squared() > crate::math_utils::square(self.radius) {
lower_intersection.is_intersecting = false;
} else if lower_intersection.distance < intersection.distance {
intersection = lower_intersection;
}
}
return intersection;
}
fn closest_normal_local(&self, other_point: &Vector3D) -> Vector3D {
let r = *other_point - self.center;
let rr = Vector2D::new(f64::sqrt(r.x * r.x + r.z * r.z), r.y);
let aabb = Box2::new(Vector2D::new(-self.radius, -0.5 * self.height),
Vector2D::new(self.radius, 0.5 * self.height), None, None);
let cn = aabb.closest_normal(&rr);
return if cn.y > 0.0 {
Vector3D::new(0.0, 1.0, 0.0)
} else if cn.y < 0.0 {
Vector3D::new(0.0, -1.0, 0.0)
} else {
Vector3D::new(r.x, 0.0, r.z).normalized()
};
}
fn intersects_local(&self, ray: &Ray3D) -> bool {
let mut d = ray.direction;
d.y = 0.0;
let mut o = ray.origin - self.center;
o.y = 0.0;
let aaa = d.length_squared();
let bbb = d.dot(&o);
let ccc = o.length_squared() - crate::math_utils::square(self.radius);
let bbox = self.bounding_box();
let upper_plane = Plane3::new(Vector3D::new(0.0, 1.0, 0.0),
bbox.upper_corner, None, None);
let lower_plane = Plane3::new(Vector3D::new(0.0, -1.0, 0.0),
bbox.lower_corner, None, None);
let upper_intersection = upper_plane.closest_intersection(&ray);
let lower_intersection = lower_plane.closest_intersection(&ray);
if aaa < f64::EPSILON || bbb * bbb - aaa * ccc < 0.0 {
let r = ray.origin - self.center;
let rr = Vector2D::new(r.x, r.z);
if rr.length_squared() <= crate::math_utils::square(self.radius) {
if upper_intersection.is_intersecting ||
lower_intersection.is_intersecting {
return true;
}
}
return false;
}
let t1 = (-bbb + f64::sqrt(bbb * bbb - aaa * ccc)) / aaa;
let t2 = (-bbb - f64::sqrt(bbb * bbb - aaa * ccc)) / aaa;
let mut t_cylinder = t2;
if t2 < 0.0 {
t_cylinder = t1;
}
let point_on_cylinder = ray.point_at(t_cylinder);
if point_on_cylinder.y >= self.center.y - 0.5 * self.height &&
point_on_cylinder.y <= self.center.y + 0.5 * self.height {
return true;
}
if upper_intersection.is_intersecting {
let mut r = upper_intersection.point - self.center;
r.y = 0.0;
if r.length_squared() <= crate::math_utils::square(self.radius) {
return true;
}
}
if lower_intersection.is_intersecting {
let mut r = lower_intersection.point - self.center;
r.y = 0.0;
if r.length_squared() <= crate::math_utils::square(self.radius) {
return true;
}
}
return false;
}
fn closest_distance_local(&self, other_point_local: &Vector3D) -> f64 {
let r = *other_point_local - self.center;
let rr = Vector2D::new(f64::sqrt(r.x * r.x + r.z * r.z), r.y);
let aabb = Box2::new(Vector2D::new(-self.radius, -0.5 * self.height),
Vector2D::new(self.radius, 0.5 * self.height), None, None);
return aabb.closest_distance(&rr);
}
fn view(&self) -> &Surface3Data {
return &self.surface_data;
}
}
pub type Cylinder3Ptr = Arc<RwLock<Cylinder3>>;
pub struct Builder {
_center: Vector3D,
_radius: f64,
_height: 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 with_height(&mut self, height: f64) -> &mut Self {
self._height = height;
return self;
}
pub fn build(&mut self) -> Cylinder3 {
return Cylinder3::new(
self._center,
self._radius,
self._height,
Some(self._surface_data.transform.clone()),
Some(self._surface_data.is_normal_flipped),
);
}
pub fn make_shared(&mut self) -> Cylinder3Ptr {
return Cylinder3Ptr::new(RwLock::new(self.build()));
}
pub fn new() -> Builder {
return Builder {
_center: Vector3D::new_default(),
_radius: 1.0,
_height: 1.0,
_surface_data: Surface3Data::new(None, None),
};
}
}
impl SurfaceBuilderBase3 for Builder {
fn view(&mut self) -> &mut Surface3Data {
return &mut self._surface_data;
}
}