use crate::aabb2::Aabb2;
use crate::aabb3::Aabb3;
use crate::geometry::{Circle, Ray2, Ray3, Segment2, Segment3, Sphere};
use crate::math;
use crate::rect2::Rect2;
use crate::rect3::Rect3;
use crate::vec2::Vec2;
use crate::vec3::Vec3;
const EPS: f32 = 1e-6;
#[inline]
fn approx_zero(x: f32) -> bool {
x.abs() <= EPS
}
pub fn segment2_intersects<Unit: Copy, Space: Copy>(a: Segment2<Unit, Space>, b: Segment2<Unit, Space>) -> bool {
segment2_intersection_kind(a, b).is_some()
}
#[derive(Copy, Clone, Debug, PartialEq)]
enum Segment2IntersectionKind<Unit: Copy, Space: Copy> {
Point(Vec2<Unit, Space>),
Overlap,
}
fn segment2_intersection_kind<Unit: Copy, Space: Copy>(
a: Segment2<Unit, Space>,
b: Segment2<Unit, Space>,
) -> Option<Segment2IntersectionKind<Unit, Space>> {
let p = a.a;
let r = a.b - a.a;
let q = b.a;
let s = b.b - b.a;
if approx_zero(r.length_squared()) && approx_zero(s.length_squared()) {
return if (p - q).length_squared() <= EPS * EPS {
Some(Segment2IntersectionKind::Point(p))
} else {
None
};
}
if approx_zero(r.length_squared()) {
return if point_on_segment2(p, b) {
Some(Segment2IntersectionKind::Point(p))
} else {
None
};
}
if approx_zero(s.length_squared()) {
return if point_on_segment2(q, a) {
Some(Segment2IntersectionKind::Point(q))
} else {
None
};
}
let rxs = r.cross(s);
let q_p = q - p;
let q_pxr = q_p.cross(r);
if approx_zero(rxs) && approx_zero(q_pxr) {
let (r_comp, qp_comp, qs_comp) = if r.x.abs() >= r.y.abs() {
(r.x, q_p.x, (q + s - p).x)
} else {
(r.y, q_p.y, (q + s - p).y)
};
let t0 = qp_comp / r_comp;
let t1 = qs_comp / r_comp;
let tmin = t0.min(t1).max(0.0);
let tmax = t0.max(t1).min(1.0);
if tmin > tmax + EPS {
return None;
}
if (tmin - tmax).abs() <= EPS {
return Some(Segment2IntersectionKind::Point(p + r * tmin));
}
return Some(Segment2IntersectionKind::Overlap);
}
if approx_zero(rxs) && !approx_zero(q_pxr) {
return None;
}
let t = q_p.cross(s) / rxs;
let u = q_p.cross(r) / rxs;
if t >= -EPS && t <= 1.0 + EPS && u >= -EPS && u <= 1.0 + EPS {
Some(Segment2IntersectionKind::Point(p + r * t))
} else {
None
}
}
#[inline]
fn point_on_segment2<Unit: Copy, Space: Copy>(p: Vec2<Unit, Space>, s: Segment2<Unit, Space>) -> bool {
let a = s.a;
let b = s.b;
let ab = b - a;
let ap = p - a;
if !approx_zero(ab.cross(ap)) {
return false;
}
let min_x = a.x.min(b.x) - EPS;
let max_x = a.x.max(b.x) + EPS;
let min_y = a.y.min(b.y) - EPS;
let max_y = a.y.max(b.y) + EPS;
p.x >= min_x && p.x <= max_x && p.y >= min_y && p.y <= max_y
}
pub fn segment2_intersection_point<Unit: Copy, Space: Copy>(
a: Segment2<Unit, Space>,
b: Segment2<Unit, Space>,
) -> Option<Vec2<Unit, Space>> {
match segment2_intersection_kind(a, b)? {
Segment2IntersectionKind::Point(p) => Some(p),
Segment2IntersectionKind::Overlap => None,
}
}
#[inline]
pub fn circle_rect2_intersects<Unit: Copy, Space: Copy>(circle: Circle<Unit, Space>, rect: &Rect2<Unit, Space>) -> bool {
let c = rect.closest_point(circle.center);
(c - circle.center).length_squared() <= circle.radius * circle.radius
}
#[inline]
pub fn circle_aabb2_intersects<Unit: Copy, Space: Copy>(circle: Circle<Unit, Space>, aabb: &Aabb2<Unit, Space>) -> bool {
let c = aabb.closest_point(circle.center);
(c - circle.center).length_squared() <= circle.radius * circle.radius
}
#[inline]
pub fn sphere_aabb3_intersects<Unit: Copy, Space: Copy>(sphere: Sphere<Unit, Space>, aabb: &Aabb3<Unit, Space>) -> bool {
let c = aabb.closest_point(sphere.center);
(c - sphere.center).length_squared() <= sphere.radius * sphere.radius
}
#[inline]
pub fn sphere_rect3_intersects<Unit: Copy, Space: Copy>(sphere: Sphere<Unit, Space>, rect: &Rect3<Unit, Space>) -> bool {
let c = rect.closest_point(sphere.center);
(c - sphere.center).length_squared() <= sphere.radius * sphere.radius
}
#[inline]
pub fn segment2_circle_intersects<Unit: Copy, Space: Copy>(seg: Segment2<Unit, Space>, circle: Circle<Unit, Space>) -> bool {
seg.distance_to_point(circle.center) <= circle.radius
}
#[inline]
pub fn segment3_sphere_intersects<Unit: Copy, Space: Copy>(seg: Segment3<Unit, Space>, sphere: Sphere<Unit, Space>) -> bool {
seg.distance_to_point(sphere.center) <= sphere.radius
}
pub fn ray2_circle_intersect<Unit: Copy, Space: Copy>(ray: Ray2<Unit, Space>, circle: Circle<Unit, Space>) -> Option<f32> {
let m = ray.origin - circle.center;
let d = ray.dir;
let a = d.dot(d);
let c = m.dot(m) - circle.radius * circle.radius;
if approx_zero(a) {
return if c <= 0.0 { Some(0.0) } else { None };
}
let b = m.dot(d);
let disc = b * b - a * c;
if disc < 0.0 {
return None;
}
let sqrt_disc = math::sqrt(disc);
let t0 = (-b - sqrt_disc) / a;
let t1 = (-b + sqrt_disc) / a;
if t0 >= 0.0 {
Some(t0)
} else if t1 >= 0.0 {
Some(t1)
} else {
None
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct RayHit2<Unit: Copy = (), Space: Copy = ()> {
pub t: f32,
pub point: Vec2<Unit, Space>,
pub normal: Vec2<Unit, Space>,
}
#[inline]
pub fn ray2_circle_cast<Unit: Copy, Space: Copy>(ray: Ray2<Unit, Space>, circle: Circle<Unit, Space>) -> Option<RayHit2<Unit, Space>> {
let t = ray2_circle_intersect(ray, circle)?;
let point = ray.point_at(t);
let normal = (point - circle.center)
.try_normalize()
.unwrap_or_else(|| Vec2::new(1.0, 0.0));
Some(RayHit2 { t, point, normal })
}
pub fn ray3_sphere_intersect<Unit: Copy, Space: Copy>(ray: Ray3<Unit, Space>, sphere: Sphere<Unit, Space>) -> Option<f32> {
let m = ray.origin - sphere.center;
let d = ray.dir;
let a = d.dot(d);
let c = m.dot(m) - sphere.radius * sphere.radius;
if approx_zero(a) {
return if c <= 0.0 { Some(0.0) } else { None };
}
let b = m.dot(d);
let disc = b * b - a * c;
if disc < 0.0 {
return None;
}
let sqrt_disc = math::sqrt(disc);
let t0 = (-b - sqrt_disc) / a;
let t1 = (-b + sqrt_disc) / a;
if t0 >= 0.0 {
Some(t0)
} else if t1 >= 0.0 {
Some(t1)
} else {
None
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct RayHit3<Unit: Copy = (), Space: Copy = ()> {
pub t: f32,
pub point: Vec3<Unit, Space>,
pub normal: Vec3<Unit, Space>,
}
#[inline]
pub fn ray3_sphere_cast<Unit: Copy, Space: Copy>(ray: Ray3<Unit, Space>, sphere: Sphere<Unit, Space>) -> Option<RayHit3<Unit, Space>> {
let t = ray3_sphere_intersect(ray, sphere)?;
let point = ray.point_at(t);
let normal = (point - sphere.center)
.try_normalize()
.unwrap_or_else(|| Vec3::new(1.0, 0.0, 0.0));
Some(RayHit3 { t, point, normal })
}