use crate::geometry::{Box2D, Box3D, Point2D, Point3D};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Ray2D {
pub origin: Point2D,
pub dir_x: f64,
pub dir_y: f64,
pub(crate) inv_dir_x: f64,
pub(crate) inv_dir_y: f64,
pub max_distance: f64,
}
impl Ray2D {
#[inline]
pub const fn new(origin: Point2D, dir_x: f64, dir_y: f64, max_distance: f64) -> Self {
Self {
origin,
dir_x,
dir_y,
inv_dir_x: 1.0 / dir_x,
inv_dir_y: 1.0 / dir_y,
max_distance,
}
}
#[inline]
pub(crate) fn has_zero_direction(self) -> bool {
self.dir_x == 0.0 || self.dir_y == 0.0
}
#[inline]
pub fn intersects_box(self, bounds: Box2D) -> bool {
if self.max_distance < 0.0 || self.max_distance.is_nan() {
return false;
}
let mut t_min: f64 = 0.0;
let mut t_max = self.max_distance;
slab(
self.origin.x,
self.dir_x,
self.inv_dir_x,
bounds.min_x,
bounds.max_x,
&mut t_min,
&mut t_max,
) && slab(
self.origin.y,
self.dir_y,
self.inv_dir_y,
bounds.min_y,
bounds.max_y,
&mut t_min,
&mut t_max,
)
}
#[inline]
pub fn enter_t(self, bounds: Box2D) -> Option<f64> {
if self.max_distance < 0.0 || self.max_distance.is_nan() {
return None;
}
let mut t_min: f64 = 0.0;
let mut t_max = self.max_distance;
let hit = slab(
self.origin.x,
self.dir_x,
self.inv_dir_x,
bounds.min_x,
bounds.max_x,
&mut t_min,
&mut t_max,
) && slab(
self.origin.y,
self.dir_y,
self.inv_dir_y,
bounds.min_y,
bounds.max_y,
&mut t_min,
&mut t_max,
);
hit.then_some(t_min)
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Ray3D {
pub origin: Point3D,
pub dir_x: f64,
pub dir_y: f64,
pub dir_z: f64,
pub(crate) inv_dir_x: f64,
pub(crate) inv_dir_y: f64,
pub(crate) inv_dir_z: f64,
pub max_distance: f64,
}
impl Ray3D {
#[inline]
pub const fn new(
origin: Point3D,
dir_x: f64,
dir_y: f64,
dir_z: f64,
max_distance: f64,
) -> Self {
Self {
origin,
dir_x,
dir_y,
dir_z,
inv_dir_x: 1.0 / dir_x,
inv_dir_y: 1.0 / dir_y,
inv_dir_z: 1.0 / dir_z,
max_distance,
}
}
#[inline]
pub(crate) fn has_zero_direction(self) -> bool {
self.dir_x == 0.0 || self.dir_y == 0.0 || self.dir_z == 0.0
}
#[inline]
pub fn intersects_box(self, bounds: Box3D) -> bool {
if self.max_distance < 0.0 || self.max_distance.is_nan() {
return false;
}
let mut t_min: f64 = 0.0;
let mut t_max = self.max_distance;
slab(
self.origin.x,
self.dir_x,
self.inv_dir_x,
bounds.min_x,
bounds.max_x,
&mut t_min,
&mut t_max,
) && slab(
self.origin.y,
self.dir_y,
self.inv_dir_y,
bounds.min_y,
bounds.max_y,
&mut t_min,
&mut t_max,
) && slab(
self.origin.z,
self.dir_z,
self.inv_dir_z,
bounds.min_z,
bounds.max_z,
&mut t_min,
&mut t_max,
)
}
#[inline]
pub fn enter_t(self, bounds: Box3D) -> Option<f64> {
if self.max_distance < 0.0 || self.max_distance.is_nan() {
return None;
}
let mut t_min: f64 = 0.0;
let mut t_max = self.max_distance;
let hit = slab(
self.origin.x,
self.dir_x,
self.inv_dir_x,
bounds.min_x,
bounds.max_x,
&mut t_min,
&mut t_max,
) && slab(
self.origin.y,
self.dir_y,
self.inv_dir_y,
bounds.min_y,
bounds.max_y,
&mut t_min,
&mut t_max,
) && slab(
self.origin.z,
self.dir_z,
self.inv_dir_z,
bounds.min_z,
bounds.max_z,
&mut t_min,
&mut t_max,
);
hit.then_some(t_min)
}
}
#[inline]
fn slab(
origin: f64,
direction: f64,
inverse: f64,
min: f64,
max: f64,
t_min: &mut f64,
t_max: &mut f64,
) -> bool {
if direction == 0.0 {
return origin >= min && origin <= max;
}
let mut near = (min - origin) * inverse;
let mut far = (max - origin) * inverse;
if near > far {
core::mem::swap(&mut near, &mut far);
}
*t_min = (*t_min).max(near);
*t_max = (*t_max).min(far);
*t_min <= *t_max
}