#![allow(mixed_script_confusables)]
#![warn(clippy::pedantic, clippy::dbg_macro, missing_docs)]
mod from;
mod ops;
#[doc(hidden)]
pub trait Kinda
where
Self: Sized,
{
fn kinda_eq(self, other: Self, tolerance: f32) -> bool;
fn approx_eq(self, other: Self) -> bool;
}
impl Kinda for f32 {
fn kinda_eq(self, other: Self, tolerance: f32) -> bool {
if self == other {
true
} else {
(self - other).abs() < tolerance
}
}
fn approx_eq(self, other: Self) -> bool {
self.kinda_eq(other, 0.00001)
}
}
impl Kinda for Vec2 {
fn kinda_eq(self, other: Self, tolerance: f32) -> bool {
self.x.kinda_eq(other.x, tolerance) && self.y.kinda_eq(other.y, tolerance)
}
fn approx_eq(self, other: Self) -> bool {
self.kinda_eq(other, 0.00001)
}
}
use umath::generic_float::{FloatAlone, Rounding};
pub type Vec2 = Vector2<f32>;
#[derive(Copy, Clone, PartialEq, PartialOrd, Default, Hash, Eq, Ord)]
#[repr(C)]
pub struct Vector2<T> {
pub x: T,
pub y: T,
}
impl<T: std::fmt::Debug> std::fmt::Debug for Vector2<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({:?}, {:?})", self.x, self.y)
}
}
impl<T> Vector2<T> {
pub const fn new(x: T, y: T) -> Self {
Self { x, y }
}
}
impl<T: Copy> Vector2<T> {
pub const fn splat(x: T) -> Self {
Self { x, y: x }
}
}
impl Vec2 {
pub const ZERO: Vec2 = Vec2::new(0.0, 0.0);
pub const RIGHT: Vec2 = Vec2::new(1.0, 0.0);
pub const LEFT: Vec2 = Vec2::new(-1.0, 0.0);
pub const UP: Vec2 = Vec2::new(0.0, -1.0);
pub const DOWN: Vec2 = Vec2::new(0.0, 1.0);
}
impl Vector2<f64> {
pub const ZERO: Vec2 = Vec2::new(0.0, 0.0);
pub const RIGHT: Vec2 = Vec2::new(1.0, 0.0);
pub const LEFT: Vec2 = Vec2::new(-1.0, 0.0);
pub const UP: Vec2 = Vec2::new(0.0, -1.0);
pub const DOWN: Vec2 = Vec2::new(0.0, 1.0);
}
impl<T: std::ops::Neg<Output = T>> Vector2<T> {
#[must_use = "Does not modify in place."]
pub fn orthogonal(self) -> Self {
Self::new(self.y, -self.x)
}
}
impl<T: FloatAlone> Vector2<T> {
pub fn from_angle(angle: T) -> Self {
Self::new(angle.cos(), angle.sin())
}
#[must_use = "Does not modify in place."]
pub fn abs(self) -> Self {
Self::new(self.x.abs(), self.y.abs())
}
pub fn angle(&self) -> T {
self.y.atan2(self.x)
}
pub fn cross(&self, with: &Self) -> T {
self.x * with.y - self.y * with.x
}
pub fn distance_to(&self, to: &Self) -> T {
((self.x - to.x) * (self.x - to.x) + (self.y - to.y) * (self.y - to.y)).sqrt()
}
pub fn dot(&self, with: &Self) -> T {
self.x * with.x + self.y * with.y
}
pub fn length(&self) -> T {
(self.x * self.x + self.y * self.y).sqrt()
}
pub fn length_squared(&self) -> T {
self.x * self.x + self.y * self.y
}
#[must_use = "Does not modify in place."]
pub fn limit_length(self, len: T) -> Self {
let l = self.length();
if l > unsafe { T::zero() } && len < l {
return (self / l) * len;
}
self
}
#[must_use = "Does not modify in place."]
pub fn normalized(self) -> Self {
let l = self.length_squared();
if l != unsafe { T::zero() } {
return self / l.sqrt();
}
self
}
#[must_use = "Does not modify in place."]
pub fn rotated(self, angle: T) -> Self {
Vector2::new(
self.x * angle.cos() - self.y * angle.sin(),
self.x * angle.sin() + self.y * angle.cos(),
)
}
}
impl<T: Rounding> Vector2<T> {
#[must_use = "Does not modify in place."]
pub fn ceil(self) -> Self {
Self::new(self.x.ceil(), self.y.ceil())
}
#[must_use = "Does not modify in place."]
pub fn floor(self) -> Self {
Self::new(self.x.floor(), self.y.floor())
}
}