use rand::Rng;
use crate::mat2::Mat2;
use crate::utils;
use crate::utils::epsilon_eq;
use crate::utils::epsilon_eq_default;
use crate::utils::is_near_zero_default;
use crate::vec::Vec3;
use core::f32;
use std::fmt;
use std::cmp::PartialEq;
use std::ops::{
Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
};
#[derive(Debug, Clone, Copy)]
pub struct Vec2 {
pub x: f32,
pub y: f32,
}
impl Vec2 {
pub const fn new(x: f32, y: f32) -> Vec2 {
Vec2 { x, y }
}
pub fn to_array(self) -> [f32; 2] {
[self.x, self.y]
}
pub fn from_array(array: [f32; 2]) -> Vec2 {
Vec2::new(array[0], array[1])
}
pub fn to_tuple(self) -> (f32, f32) {
(self.x, self.y)
}
pub fn from_tuple(t: (f32, f32)) -> Vec2 {
Vec2::new(t.0, t.1)
}
pub const ZERO: Self = Self { x: 0.0, y: 0.0 };
pub const NAN: Self = Self {
x: f32::NAN,
y: f32::NAN,
};
pub const INFINITY: Self = Self {
x: f32::INFINITY,
y: f32::INFINITY,
};
pub const X: Self = Self { x: 1.0, y: 0.0 };
pub const Y: Self = Self { x: 0.0, y: 1.0 };
pub fn abs(self) -> Vec2 {
Vec2::new(self.x.abs(), self.y.abs())
}
pub fn signum(self) -> Self {
Vec2::new(
if self.x > 0.0 {
1.0
} else if self.x < 0.0 {
-1.0
} else {
0.0
},
if self.y > 0.0 {
1.0
} else if self.y < 0.0 {
-1.0
} else {
0.0
},
)
}
pub fn ieee_signum(self) -> Self {
Vec2::new(self.x.signum(), self.y.signum())
}
pub fn extend(&self, z: f32) -> Vec3 {
Vec3::new(self.x, self.y, z)
}
pub fn clamp(self, min: f32, max: f32) -> Vec2 {
Vec2::new(
utils::clamp(self.x, min, max),
utils::clamp(self.y, min, max),
)
}
pub fn min(self, other: Vec2) -> Vec2 {
Vec2::new(self.x.min(other.x), self.y.min(other.y))
}
pub fn max(self, other: Vec2) -> Vec2 {
Vec2::new(self.x.max(other.x), self.y.max(other.y))
}
pub fn length(&self) -> f32 {
(self.x * self.x + self.y * self.y).sqrt()
}
pub fn squared_length(&self) -> f32 {
self.x * self.x + self.y * self.y
}
pub fn normalize(&self) -> Vec2 {
let len = self.length();
assert!(len != 0.0, "Cannot normalize zero-length vector");
Vec2::new(self.x / len, self.y / len)
}
pub fn try_normalize(&self) -> Option<Vec2> {
let len = self.length();
if is_near_zero_default(len) {
None
} else {
Some(Vec2::new(self.x / len, self.y / len))
}
}
pub fn normalize_or_zero(&self) -> Vec2 {
let len = self.length();
if epsilon_eq_default(len, 0.0) {
Self::ZERO
} else {
*self / len
}
}
pub fn is_zero(&self) -> bool {
epsilon_eq_default(self.x, 0.0) && epsilon_eq_default(self.y, 0.0)
}
pub fn is_zero_eps(&self, epsilon: f32) -> bool {
epsilon_eq(self.x, 0.0, epsilon) && epsilon_eq(self.y, 0.0, epsilon)
}
pub fn is_normalized(self) -> bool {
epsilon_eq(self.length(), 1.0, 1e-6)
}
pub fn is_normalized_fast(self) -> bool {
epsilon_eq(self.squared_length(), 1.0, 1e-6)
}
pub fn dot(&self, other: Vec2) -> f32 {
self.x * other.x + self.y * other.y
}
pub fn cross(&self, other: Vec2) -> f32 {
self.x * other.y - self.y * other.x
}
pub fn angle_radians(&self) -> f32 {
utils::degrees_to_radians(self.y.atan2(self.x))
}
pub fn angle_degrees(&self) -> f32 {
self.y.atan2(self.x)
}
pub fn angle_between_radians(&self, other: Vec2) -> f32 {
let theta = (self.dot(other)) / (self.length() * other.length());
utils::clamp(theta, -1.0, 1.0).acos()
}
pub fn angle_between_degrees(&self, other: Vec2) -> f32 {
let theta = (self.dot(other)) / (self.length() * other.length());
utils::radians_to_degrees(utils::clamp(theta, -1.0, 1.0).acos())
}
pub fn angle_to_radians(&self, other: Vec2) -> f32 {
(self.x * other.y - self.y * other.x).atan2(self.dot(other))
}
pub fn angle_to_degrees(&self, other: Vec2) -> f32 {
utils::radians_to_degrees((self.x * other.y - self.y * other.x).atan2(self.dot(other)))
}
pub fn from_angle(angle: f32) -> Vec2 {
Vec2::new(angle.cos(), angle.sin())
}
pub fn lerp(&self, b: Vec2, t: f32) -> Vec2 {
*self * (1.0 - t) + b * t
}
pub fn lerp_clamped(&self, b: Vec2, t: f32) -> Vec2 {
let t = utils::clamp(t, 0.0, 1.0);
*self * (1.0 - t) + b * t
}
#[inline]
pub fn lerp_between(a: Vec2, b: Vec2, t: f32) -> Vec2 {
a * (1.0 - t) + b * t
}
#[inline]
pub fn lerp_between_clamped(a: Vec2, b: Vec2, t: f32) -> Vec2 {
let t = utils::clamp(t, 0.0, 1.0);
a * (1.0 - t) + b * t
}
pub fn slerp(a: Vec2, b: Vec2, t: f32) -> Vec2 {
if a.length() < 1e-6 {
return b * t;
}
if b.length() < 1e-6 {
return a * (1.0 - t);
}
let a_norm = a.normalize();
let b_norm = b.normalize();
let dot = a_norm.dot(b_norm).clamp(-1.0, 1.0);
if dot > 1.0 - 1e-6 {
return Vec2::lerp_between(a, b, t);
}
if dot < -1.0 + 1e-6 {
let perp = Vec2::new(-a_norm.y, a_norm.x);
let length = a.length() * (1.0 - t) + b.length() * t;
return if t < 0.5 {
perp.normalize() * length
} else {
(-perp).normalize() * length
};
}
let theta = dot.acos();
let sin_theta = theta.sin();
let wa = ((1.0 - t) * theta).sin() / sin_theta;
let wb = (t * theta).sin() / sin_theta;
(a_norm * wa + b_norm * wb) * (a.length() * (1.0 - t) + b.length() * t)
}
pub fn slerp_angle(a: Vec2, b: Vec2, t: f32) -> Vec2 {
if a.length() < 1e-6 || b.length() < 1e-6 {
return Vec2::lerp(&a, b, t);
}
let a_norm = a.normalize();
let b_norm = b.normalize();
let angle_a = a_norm.y.atan2(a_norm.x);
let angle_b = b_norm.y.atan2(b_norm.x);
let mut delta_angle = (angle_b - angle_a).rem_euclid(std::f32::consts::PI * 2.0);
if delta_angle > std::f32::consts::PI {
delta_angle -= std::f32::consts::PI * 2.0;
}
let angle = angle_a + t * delta_angle;
let length = a.length() * (1.0 - t) + b.length() * t;
Vec2::new(angle.cos(), angle.sin()) * length
}
pub fn project(&self, onto: Vec2) -> Vec2 {
let denominator = onto.dot(onto);
if denominator == 0.0 {
Vec2::new(0.0, 0.0)
} else {
onto * (self.dot(onto) / denominator)
}
}
pub fn reject(&self, other: Vec2) -> Vec2 {
*self - self.project(other)
}
pub fn reflect(&self, normal: Vec2) -> Vec2 {
*self - normal * (2.0 * self.dot(normal))
}
pub fn mirror(&self, normal: Vec2) -> Vec2 {
*self - normal * (2.0 * self.dot(normal))
}
pub fn distance(&self, other: Vec2) -> f32 {
(*self - other).length()
}
pub fn squared_distance(&self, other: Vec2) -> f32 {
(*self - other).squared_length()
}
pub fn direction_to(&self, other: Vec2) -> Vec2 {
let delta = other - *self;
delta.normalize()
}
pub fn direction_to_raw(&self, other: Vec2) -> Vec2 {
other - *self
}
pub fn perpendicular(self) -> Vec2 {
Vec2::new(-self.y, self.x)
}
pub fn normal(&self) -> Vec2 {
Vec2::new(self.y, -self.x)
}
pub fn move_towards(current: Vec2, target: Vec2, max_delta: f32) -> Vec2 {
let delta = target - current;
let distance = delta.length();
if distance <= max_delta || distance < f32::EPSILON {
target
} else {
current + delta / distance * max_delta
}
}
pub fn rotate(&mut self, angle_rad: f32) {
*self = Mat2::from_angle(angle_rad) * *self;
}
pub fn rotate_around(&self, center: Vec2, angle: f32) -> Vec2 {
let sin = angle.sin();
let cos = angle.cos();
let translated = *self - center;
let rotated = Vec2::new(
translated.x * cos - translated.y * sin,
translated.x * sin + translated.y * cos,
);
rotated + center
}
pub fn random_unit_vector() -> Vec2 {
let mut rng = rand::rng();
loop {
let x = rng.random_range(-1.0..=1.0);
let y = rng.random_range(-1.0..=1.0);
let v = Vec2::new(x, y);
let len_sq = v.squared_length();
if len_sq > 0.0 && len_sq <= 1.0 {
return v / len_sq.sqrt();
}
}
}
pub fn barycentric(p: Vec2, a: Vec2, b: Vec2, c: Vec2) -> (f32, f32, f32) {
let v0 = b - a;
let v1 = c - a;
let v2 = p - a;
let d00 = v0.dot(v0);
let d01 = v0.dot(v1);
let d11 = v1.dot(v1);
let d20 = v2.dot(v0);
let d21 = v2.dot(v1);
let denom = d00 * d11 - d01 * d01;
let v = (d11 * d20 - d01 * d21) / denom;
let w = (d00 * d21 - d01 * d20) / denom;
let u = 1.0 - v - w;
(u, v, w)
}
pub fn barycentric_simplified(a: Vec2, b: Vec2, c: Vec2, u: f32, v: f32, w: f32) -> Vec2 {
a * u + b * v + c * w
}
pub fn in_triangle(p: Vec2, a: Vec2, b: Vec2, c: Vec2) -> bool {
let (u, v, w) = Vec2::barycentric(p, a, b, c);
u >= 0.0 && v >= 0.0 && w >= 0.0
}
pub fn approx_eq(&self, other: Vec2) -> bool {
epsilon_eq_default(self.x, other.x) && epsilon_eq_default(self.y, other.y)
}
pub fn approx_eq_eps(&self, other: Vec2, epsilon: f32) -> bool {
epsilon_eq(self.x, other.x, epsilon) && epsilon_eq(self.y, other.y, epsilon)
}
pub fn is_finite(self) -> bool {
self.x.is_finite() && self.y.is_finite()
}
pub fn is_nan(self) -> bool {
self.x.is_nan() || self.y.is_nan()
}
}
impl Add for Vec2 {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Self::new(self.x + rhs.x, self.y + rhs.y)
}
}
impl Add<f32> for Vec2 {
type Output = Self;
#[inline]
fn add(self, rhs: f32) -> Self::Output {
Self::new(self.x + rhs, self.y + rhs)
}
}
impl Add<Vec2> for f32 {
type Output = Vec2;
#[inline]
fn add(self, rhs: Vec2) -> Self::Output {
Vec2::new(self + rhs.x, self + rhs.y)
}
}
impl Sub for Vec2 {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self::Output {
Self::new(self.x - rhs.x, self.y - rhs.y)
}
}
impl Sub<f32> for Vec2 {
type Output = Self;
#[inline]
fn sub(self, rhs: f32) -> Self::Output {
Self::new(self.x - rhs, self.y - rhs)
}
}
impl Sub<Vec2> for f32 {
type Output = Vec2;
#[inline]
fn sub(self, rhs: Vec2) -> Self::Output {
Vec2::new(self - rhs.x, self - rhs.y)
}
}
impl Mul for Vec2 {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self::Output {
Self::new(self.x * rhs.x, self.y * rhs.y)
}
}
impl Mul<f32> for Vec2 {
type Output = Self;
#[inline]
fn mul(self, rhs: f32) -> Self::Output {
Self::new(self.x * rhs, self.y * rhs)
}
}
impl Mul<Vec2> for f32 {
type Output = Vec2;
#[inline]
fn mul(self, rhs: Vec2) -> Self::Output {
Vec2::new(self * rhs.x, self * rhs.y)
}
}
impl Div for Vec2 {
type Output = Self;
#[inline]
fn div(self, rhs: Self) -> Self::Output {
Self::new(self.x / rhs.x, self.y / rhs.y)
}
}
impl Div<f32> for Vec2 {
type Output = Self;
#[inline]
fn div(self, rhs: f32) -> Self::Output {
Self::new(self.x / rhs, self.y / rhs)
}
}
impl Div<Vec2> for f32 {
type Output = Vec2;
#[inline]
fn div(self, rhs: Vec2) -> Self::Output {
Vec2::new(self / rhs.x, self / rhs.y)
}
}
impl Neg for Vec2 {
type Output = Self;
#[inline]
fn neg(self) -> Self::Output {
Self::new(-self.x, -self.y)
}
}
impl AddAssign for Vec2 {
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.x += rhs.x;
self.y += rhs.y;
}
}
impl AddAssign<f32> for Vec2 {
#[inline]
fn add_assign(&mut self, rhs: f32) {
self.x += rhs;
self.y += rhs;
}
}
impl SubAssign for Vec2 {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
self.x -= rhs.x;
self.y -= rhs.y;
}
}
impl SubAssign<f32> for Vec2 {
#[inline]
fn sub_assign(&mut self, rhs: f32) {
self.x -= rhs;
self.y -= rhs;
}
}
impl MulAssign for Vec2 {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
self.x *= rhs.x;
self.y *= rhs.y;
}
}
impl MulAssign<f32> for Vec2 {
#[inline]
fn mul_assign(&mut self, rhs: f32) {
self.x *= rhs;
self.y *= rhs;
}
}
impl DivAssign for Vec2 {
#[inline]
fn div_assign(&mut self, rhs: Self) {
self.x /= rhs.x;
self.y /= rhs.y;
}
}
impl DivAssign<f32> for Vec2 {
#[inline]
fn div_assign(&mut self, rhs: f32) {
self.x /= rhs;
self.y /= rhs;
}
}
impl Default for Vec2 {
#[inline]
fn default() -> Self {
Self::ZERO }
}
impl PartialEq for Vec2 {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.x == other.x && self.y == other.y
}
}
impl Index<usize> for Vec2 {
type Output = f32;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
match index {
0 => &self.x,
1 => &self.y,
_ => panic!("Vec2 index out of bounds: {}", index),
}
}
}
impl IndexMut<usize> for Vec2 {
#[inline]
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
match index {
0 => &mut self.x,
1 => &mut self.y,
_ => panic!("Vec2 index out of bounds: {}", index),
}
}
}
impl fmt::Display for Vec2 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Vec2({:.3}, {:.3})", self.x, self.y)
}
}
impl From<(f32, f32)> for Vec2 {
#[inline]
fn from(t: (f32, f32)) -> Self {
Self::new(t.0, t.1)
}
}
impl From<Vec2> for (f32, f32) {
#[inline]
fn from(v: Vec2) -> Self {
(v.x, v.y)
}
}
impl From<[f32; 2]> for Vec2 {
#[inline]
fn from(arr: [f32; 2]) -> Self {
Self::new(arr[0], arr[1])
}
}
impl From<Vec2> for [f32; 2] {
#[inline]
fn from(v: Vec2) -> Self {
[v.x, v.y]
}
}
impl approx::AbsDiffEq for Vec2 {
type Epsilon = f32;
#[inline]
fn default_epsilon() -> f32 {
f32::EPSILON
}
#[inline]
fn abs_diff_eq(&self, other: &Self, epsilon: f32) -> bool {
f32::abs_diff_eq(&self.x, &other.x, epsilon) && f32::abs_diff_eq(&self.y, &other.y, epsilon)
}
}
impl approx::RelativeEq for Vec2 {
#[inline]
fn default_max_relative() -> f32 {
f32::EPSILON
}
#[inline]
fn relative_eq(&self, other: &Self, epsilon: f32, max_relative: f32) -> bool {
f32::relative_eq(&self.x, &other.x, epsilon, max_relative)
&& f32::relative_eq(&self.y, &other.y, epsilon, max_relative)
}
}