use crate::vec2::Vec2;
use crate::vec3::Vec3;
use crate::angle::{Degrees, Radians};
use crate::math;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Ray2<Unit: Copy = (), Space: Copy = ()> {
pub origin: Vec2<Unit, Space>,
pub dir: Vec2<Unit, Space>,
}
impl<Unit: Copy, Space: Copy> Ray2<Unit, Space> {
#[inline]
pub const fn new(origin: Vec2<Unit, Space>, dir: Vec2<Unit, Space>) -> Self {
Self { origin, dir }
}
#[inline]
pub fn point_at(self, t: f32) -> Vec2<Unit, Space> {
self.origin + self.dir * t
}
#[inline]
pub fn closest_t_to_point(self, p: Vec2<Unit, Space>) -> f32 {
let denom = self.dir.length_squared();
if denom == 0.0 {
return 0.0;
}
let t = (p - self.origin).dot(self.dir) / denom;
t.max(0.0)
}
#[inline]
pub fn closest_point(self, p: Vec2<Unit, Space>) -> Vec2<Unit, Space> {
self.point_at(self.closest_t_to_point(p))
}
#[inline]
pub fn distance_to_point(self, p: Vec2<Unit, Space>) -> f32 {
(p - self.closest_point(p)).length()
}
#[inline]
#[cfg(feature = "unit_vec")]
pub fn from_unit_dir(origin: Vec2<Unit, Space>, dir: crate::unit_vec::UnitVec2<Unit, Space>) -> Self {
Self::new(origin, dir.as_vec())
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Ray3<Unit: Copy = (), Space: Copy = ()> {
pub origin: Vec3<Unit, Space>,
pub dir: Vec3<Unit, Space>,
}
impl<Unit: Copy, Space: Copy> Ray3<Unit, Space> {
#[inline]
pub const fn new(origin: Vec3<Unit, Space>, dir: Vec3<Unit, Space>) -> Self {
Self { origin, dir }
}
#[inline]
pub fn point_at(self, t: f32) -> Vec3<Unit, Space> {
self.origin + self.dir * t
}
#[inline]
pub fn closest_t_to_point(self, p: Vec3<Unit, Space>) -> f32 {
let denom = self.dir.length_squared();
if denom == 0.0 {
return 0.0;
}
let t = (p - self.origin).dot(self.dir) / denom;
t.max(0.0)
}
#[inline]
pub fn closest_point(self, p: Vec3<Unit, Space>) -> Vec3<Unit, Space> {
self.point_at(self.closest_t_to_point(p))
}
#[inline]
pub fn distance_to_point(self, p: Vec3<Unit, Space>) -> f32 {
(p - self.closest_point(p)).length()
}
#[inline]
#[cfg(feature = "unit_vec")]
pub fn from_unit_dir(origin: Vec3<Unit, Space>, dir: crate::unit_vec::UnitVec3<Unit, Space>) -> Self {
Self::new(origin, dir.as_vec())
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Segment2<Unit: Copy = (), Space: Copy = ()> {
pub a: Vec2<Unit, Space>,
pub b: Vec2<Unit, Space>,
}
impl<Unit: Copy, Space: Copy> Segment2<Unit, Space> {
#[inline]
pub const fn new(a: Vec2<Unit, Space>, b: Vec2<Unit, Space>) -> Self {
Self { a, b }
}
#[inline]
pub fn direction(self) -> Vec2<Unit, Space> {
self.b - self.a
}
#[inline]
pub fn length_squared(self) -> f32 {
self.direction().length_squared()
}
#[inline]
pub fn length(self) -> f32 {
self.direction().length()
}
#[inline]
pub fn point_at(self, t: f32) -> Vec2<Unit, Space> {
self.a + (self.b - self.a) * t
}
#[inline]
pub fn closest_t_to_point(self, p: Vec2<Unit, Space>) -> f32 {
let ab = self.b - self.a;
let denom = ab.length_squared();
if denom == 0.0 {
return 0.0;
}
((p - self.a).dot(ab) / denom).clamp(0.0, 1.0)
}
#[inline]
pub fn closest_point(self, p: Vec2<Unit, Space>) -> Vec2<Unit, Space> {
self.point_at(self.closest_t_to_point(p))
}
#[inline]
pub fn distance_to_point(self, p: Vec2<Unit, Space>) -> f32 {
(p - self.closest_point(p)).length()
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Segment3<Unit: Copy = (), Space: Copy = ()> {
pub a: Vec3<Unit, Space>,
pub b: Vec3<Unit, Space>,
}
impl<Unit: Copy, Space: Copy> Segment3<Unit, Space> {
#[inline]
pub const fn new(a: Vec3<Unit, Space>, b: Vec3<Unit, Space>) -> Self {
Self { a, b }
}
#[inline]
pub fn direction(self) -> Vec3<Unit, Space> {
self.b - self.a
}
#[inline]
pub fn length_squared(self) -> f32 {
self.direction().length_squared()
}
#[inline]
pub fn length(self) -> f32 {
self.direction().length()
}
#[inline]
pub fn point_at(self, t: f32) -> Vec3<Unit, Space> {
self.a + (self.b - self.a) * t
}
#[inline]
pub fn closest_t_to_point(self, p: Vec3<Unit, Space>) -> f32 {
let ab = self.b - self.a;
let denom = ab.length_squared();
if denom == 0.0 {
return 0.0;
}
((p - self.a).dot(ab) / denom).clamp(0.0, 1.0)
}
#[inline]
pub fn closest_point(self, p: Vec3<Unit, Space>) -> Vec3<Unit, Space> {
self.point_at(self.closest_t_to_point(p))
}
#[inline]
pub fn distance_to_point(self, p: Vec3<Unit, Space>) -> f32 {
(p - self.closest_point(p)).length()
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Circle<Unit: Copy = (), Space: Copy = ()> {
pub center: Vec2<Unit, Space>,
pub radius: f32,
}
impl<Unit: Copy, Space: Copy> Circle<Unit, Space> {
#[inline]
pub const fn new(center: Vec2<Unit, Space>, radius: f32) -> Self {
Self { center, radius }
}
#[inline]
pub fn contains_point(self, p: Vec2<Unit, Space>) -> bool {
(p - self.center).length_squared() <= self.radius * self.radius
}
#[inline]
pub fn area(self) -> f32 {
core::f32::consts::PI * self.radius * self.radius
}
#[inline]
pub fn closest_point(self, p: Vec2<Unit, Space>) -> Vec2<Unit, Space> {
let v = p - self.center;
let len_sq = v.length_squared();
if len_sq == 0.0 {
return self.center + Vec2::new(self.radius, 0.0);
}
self.center + v * (self.radius / math::sqrt(len_sq))
}
#[inline]
pub fn distance_to_point(self, p: Vec2<Unit, Space>) -> f32 {
let d = (p - self.center).length() - self.radius;
d.max(0.0)
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Sphere<Unit: Copy = (), Space: Copy = ()> {
pub center: Vec3<Unit, Space>,
pub radius: f32,
}
impl<Unit: Copy, Space: Copy> Sphere<Unit, Space> {
#[inline]
pub const fn new(center: Vec3<Unit, Space>, radius: f32) -> Self {
Self { center, radius }
}
#[inline]
pub fn contains_point(self, p: Vec3<Unit, Space>) -> bool {
(p - self.center).length_squared() <= self.radius * self.radius
}
#[inline]
pub fn volume(self) -> f32 {
(4.0 / 3.0) * core::f32::consts::PI * self.radius * self.radius * self.radius
}
#[inline]
pub fn closest_point(self, p: Vec3<Unit, Space>) -> Vec3<Unit, Space> {
let v = p - self.center;
let len_sq = v.length_squared();
if len_sq == 0.0 {
return self.center + Vec3::new(self.radius, 0.0, 0.0);
}
self.center + v * (self.radius / math::sqrt(len_sq))
}
#[inline]
pub fn distance_to_point(self, p: Vec3<Unit, Space>) -> f32 {
let d = (p - self.center).length() - self.radius;
d.max(0.0)
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Plane<Unit: Copy = (), Space: Copy = ()> {
pub normal: Vec3<Unit, Space>,
pub d: f32,
}
impl<Unit: Copy, Space: Copy> Plane<Unit, Space> {
#[inline]
pub const fn from_normal_d(normal: Vec3<Unit, Space>, d: f32) -> Self {
Self { normal, d }
}
#[inline]
pub fn from_point_normal(point: Vec3<Unit, Space>, normal: Vec3<Unit, Space>) -> Option<Self> {
let n = normal.try_normalize()?;
let d = -n.dot(point);
Some(Self { normal: n, d })
}
#[inline]
pub fn signed_distance_to_point(self, p: Vec3<Unit, Space>) -> f32 {
self.normal.dot(p) + self.d
}
#[inline]
pub fn project_point(self, p: Vec3<Unit, Space>) -> Vec3<Unit, Space> {
p - self.normal * self.signed_distance_to_point(p)
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Capsule2<Unit: Copy = (), Space: Copy = ()> {
pub a: Vec2<Unit, Space>,
pub b: Vec2<Unit, Space>,
pub radius: f32,
}
impl<Unit: Copy, Space: Copy> Capsule2<Unit, Space> {
#[inline]
pub const fn new(a: Vec2<Unit, Space>, b: Vec2<Unit, Space>, radius: f32) -> Self {
Self { a, b, radius }
}
#[inline]
pub fn segment(self) -> Segment2<Unit, Space> {
Segment2::new(self.a, self.b)
}
#[inline]
pub fn contains_point(self, p: Vec2<Unit, Space>) -> bool {
self.segment().distance_to_point(p) <= self.radius
}
#[inline]
pub fn distance_to_point(self, p: Vec2<Unit, Space>) -> f32 {
let d = self.segment().distance_to_point(p) - self.radius;
d.max(0.0)
}
#[inline]
pub fn closest_point(self, p: Vec2<Unit, Space>) -> Vec2<Unit, Space> {
let c = self.segment().closest_point(p);
let v = p - c;
let len_sq = v.length_squared();
if len_sq == 0.0 {
return c + Vec2::new(self.radius, 0.0);
}
c + v * (self.radius / math::sqrt(len_sq))
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Capsule3<Unit: Copy = (), Space: Copy = ()> {
pub a: Vec3<Unit, Space>,
pub b: Vec3<Unit, Space>,
pub radius: f32,
}
impl<Unit: Copy, Space: Copy> Capsule3<Unit, Space> {
#[inline]
pub const fn new(a: Vec3<Unit, Space>, b: Vec3<Unit, Space>, radius: f32) -> Self {
Self { a, b, radius }
}
#[inline]
pub fn segment(self) -> Segment3<Unit, Space> {
Segment3::new(self.a, self.b)
}
#[inline]
pub fn contains_point(self, p: Vec3<Unit, Space>) -> bool {
self.segment().distance_to_point(p) <= self.radius
}
#[inline]
pub fn distance_to_point(self, p: Vec3<Unit, Space>) -> f32 {
let d = self.segment().distance_to_point(p) - self.radius;
d.max(0.0)
}
#[inline]
pub fn closest_point(self, p: Vec3<Unit, Space>) -> Vec3<Unit, Space> {
let c = self.segment().closest_point(p);
let v = p - c;
let len_sq = v.length_squared();
if len_sq == 0.0 {
return c + Vec3::new(self.radius, 0.0, 0.0);
}
c + v * (self.radius / math::sqrt(len_sq))
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Obb2<Unit: Copy = (), Space: Copy = ()> {
pub center: Vec2<Unit, Space>,
pub half_extents: Vec2<Unit, Space>,
pub axis_x: Vec2<Unit, Space>,
pub axis_y: Vec2<Unit, Space>,
}
impl<Unit: Copy, Space: Copy> Obb2<Unit, Space> {
const ORTHONORMAL_EPS: f32 = 1e-5;
#[inline]
pub const fn from_center_half_extents(center: Vec2<Unit, Space>, half_extents: Vec2<Unit, Space>) -> Self {
Self {
center,
half_extents,
axis_x: Vec2::new(1.0, 0.0),
axis_y: Vec2::new(0.0, 1.0),
}
}
#[inline]
pub fn from_center_half_extents_rotation_radians(
center: Vec2<Unit, Space>,
half_extents: Vec2<Unit, Space>,
angle: Radians,
) -> Self {
let axis_x = Vec2::new(1.0, 0.0).rotate(angle);
let axis_y = Vec2::new(0.0, 1.0).rotate(angle);
Self { center, half_extents, axis_x, axis_y }
}
#[inline]
pub fn from_center_half_extents_rotation_deg(
center: Vec2<Unit, Space>,
half_extents: Vec2<Unit, Space>,
angle: Degrees,
) -> Self {
Self::from_center_half_extents_rotation_radians(center, half_extents, angle.to_radians())
}
#[inline]
pub fn try_from_axes(
center: Vec2<Unit, Space>,
half_extents: Vec2<Unit, Space>,
axis_x: Vec2<Unit, Space>,
axis_y: Vec2<Unit, Space>,
) -> Option<Self> {
if !center.is_finite() || !half_extents.is_finite() || !axis_x.is_finite() || !axis_y.is_finite() {
return None;
}
if half_extents.x < 0.0 || half_extents.y < 0.0 {
return None;
}
let lx = axis_x.length_squared();
let ly = axis_y.length_squared();
if (lx - 1.0).abs() > Self::ORTHONORMAL_EPS || (ly - 1.0).abs() > Self::ORTHONORMAL_EPS {
return None;
}
if axis_x.dot(axis_y).abs() > Self::ORTHONORMAL_EPS {
return None;
}
Some(Self { center, half_extents, axis_x, axis_y })
}
#[inline]
pub fn contains_point(self, p: Vec2<Unit, Space>) -> bool {
let d = p - self.center;
let x = d.dot(self.axis_x);
let y = d.dot(self.axis_y);
x.abs() <= self.half_extents.x && y.abs() <= self.half_extents.y
}
#[inline]
pub fn closest_point(self, p: Vec2<Unit, Space>) -> Vec2<Unit, Space> {
let d = p - self.center;
let x = d.dot(self.axis_x).clamp(-self.half_extents.x, self.half_extents.x);
let y = d.dot(self.axis_y).clamp(-self.half_extents.y, self.half_extents.y);
self.center + self.axis_x * x + self.axis_y * y
}
#[inline]
pub fn corners(self) -> [Vec2<Unit, Space>; 4] {
let ex = self.axis_x * self.half_extents.x;
let ey = self.axis_y * self.half_extents.y;
[
self.center - ex - ey,
self.center - ex + ey,
self.center + ex - ey,
self.center + ex + ey,
]
}
#[cfg(feature = "aabb2")]
pub fn to_aabb(self) -> crate::aabb2::Aabb2<Unit, Space> {
let corners = self.corners();
let mut min = Vec2::new(f32::INFINITY, f32::INFINITY);
let mut max = Vec2::new(f32::NEG_INFINITY, f32::NEG_INFINITY);
for &c in &corners {
min = min.min(c);
max = max.max(c);
}
crate::aabb2::Aabb2::from_min_max(min, max)
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Obb3<Unit: Copy = (), Space: Copy = ()> {
pub center: Vec3<Unit, Space>,
pub half_extents: Vec3<Unit, Space>,
pub axis_x: Vec3<Unit, Space>,
pub axis_y: Vec3<Unit, Space>,
pub axis_z: Vec3<Unit, Space>,
}
impl<Unit: Copy, Space: Copy> Obb3<Unit, Space> {
const ORTHONORMAL_EPS: f32 = 1e-5;
#[inline]
pub const fn from_center_half_extents(center: Vec3<Unit, Space>, half_extents: Vec3<Unit, Space>) -> Self {
Self {
center,
half_extents,
axis_x: Vec3::new(1.0, 0.0, 0.0),
axis_y: Vec3::new(0.0, 1.0, 0.0),
axis_z: Vec3::new(0.0, 0.0, 1.0),
}
}
#[inline]
pub fn from_axis_angle_radians(
center: Vec3<Unit, Space>,
half_extents: Vec3<Unit, Space>,
axis: Vec3<Unit, Space>,
angle: Radians,
) -> Option<Self> {
let k = axis.try_normalize()?;
let axis_x = Vec3::new(1.0, 0.0, 0.0).rotate_axis(k, angle);
let axis_y = Vec3::new(0.0, 1.0, 0.0).rotate_axis(k, angle);
let axis_z = Vec3::new(0.0, 0.0, 1.0).rotate_axis(k, angle);
Some(Self { center, half_extents, axis_x, axis_y, axis_z })
}
#[inline]
pub fn from_axis_angle_deg(
center: Vec3<Unit, Space>,
half_extents: Vec3<Unit, Space>,
axis: Vec3<Unit, Space>,
angle: Degrees,
) -> Option<Self> {
Self::from_axis_angle_radians(center, half_extents, axis, angle.to_radians())
}
#[inline]
pub fn try_from_axes(
center: Vec3<Unit, Space>,
half_extents: Vec3<Unit, Space>,
axis_x: Vec3<Unit, Space>,
axis_y: Vec3<Unit, Space>,
axis_z: Vec3<Unit, Space>,
) -> Option<Self> {
if !center.is_finite()
|| !half_extents.is_finite()
|| !axis_x.is_finite()
|| !axis_y.is_finite()
|| !axis_z.is_finite()
{
return None;
}
if half_extents.x < 0.0 || half_extents.y < 0.0 || half_extents.z < 0.0 {
return None;
}
let lx = axis_x.length_squared();
let ly = axis_y.length_squared();
let lz = axis_z.length_squared();
if (lx - 1.0).abs() > Self::ORTHONORMAL_EPS
|| (ly - 1.0).abs() > Self::ORTHONORMAL_EPS
|| (lz - 1.0).abs() > Self::ORTHONORMAL_EPS
{
return None;
}
if axis_x.dot(axis_y).abs() > Self::ORTHONORMAL_EPS
|| axis_x.dot(axis_z).abs() > Self::ORTHONORMAL_EPS
|| axis_y.dot(axis_z).abs() > Self::ORTHONORMAL_EPS
{
return None;
}
Some(Self { center, half_extents, axis_x, axis_y, axis_z })
}
#[inline]
pub fn contains_point(self, p: Vec3<Unit, Space>) -> bool {
let d = p - self.center;
let x = d.dot(self.axis_x);
let y = d.dot(self.axis_y);
let z = d.dot(self.axis_z);
x.abs() <= self.half_extents.x && y.abs() <= self.half_extents.y && z.abs() <= self.half_extents.z
}
#[inline]
pub fn closest_point(self, p: Vec3<Unit, Space>) -> Vec3<Unit, Space> {
let d = p - self.center;
let x = d.dot(self.axis_x).clamp(-self.half_extents.x, self.half_extents.x);
let y = d.dot(self.axis_y).clamp(-self.half_extents.y, self.half_extents.y);
let z = d.dot(self.axis_z).clamp(-self.half_extents.z, self.half_extents.z);
self.center + self.axis_x * x + self.axis_y * y + self.axis_z * z
}
#[inline]
pub fn corners(self) -> [Vec3<Unit, Space>; 8] {
let ex = self.axis_x * self.half_extents.x;
let ey = self.axis_y * self.half_extents.y;
let ez = self.axis_z * self.half_extents.z;
[
self.center - ex - ey - ez,
self.center - ex - ey + ez,
self.center - ex + ey - ez,
self.center - ex + ey + ez,
self.center + ex - ey - ez,
self.center + ex - ey + ez,
self.center + ex + ey - ez,
self.center + ex + ey + ez,
]
}
#[cfg(feature = "aabb3")]
pub fn to_aabb(self) -> crate::aabb3::Aabb3<Unit, Space> {
let corners = self.corners();
let mut min = Vec3::new(f32::INFINITY, f32::INFINITY, f32::INFINITY);
let mut max = Vec3::new(f32::NEG_INFINITY, f32::NEG_INFINITY, f32::NEG_INFINITY);
for &c in &corners {
min = min.min(c);
max = max.max(c);
}
crate::aabb3::Aabb3::from_min_max(min, max)
}
}