use std::{
fmt::Display,
hash::{Hash, Hasher},
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign},
};
use super::{Point, Size};
#[repr(C)]
#[derive(Copy, Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Vector {
pub x: f32,
pub y: f32,
}
impl Vector {
pub const ZERO: Self = Self::new(0.0, 0.0);
pub const ONE: Self = Self::new(1.0, 1.0);
pub const X: Self = Self::new(1.0, 0.0);
pub const Y: Self = Self::new(0.0, 1.0);
pub const NEG_X: Self = Self::new(-1.0, 0.0);
pub const NEG_Y: Self = Self::new(0.0, -1.0);
pub const fn new(x: f32, y: f32) -> Self {
Self { x, y }
}
pub const fn all(value: f32) -> Self {
Self::new(value, value)
}
pub fn from_angle(angle: f32) -> Self {
let (sin, cos) = angle.sin_cos();
Self::new(cos, sin)
}
pub fn min(self, other: Self) -> Self {
Self::new(self.x.min(other.x), self.y.min(other.y))
}
pub fn max(self, other: Self) -> Self {
Self::new(self.x.max(other.x), self.y.max(other.y))
}
pub fn clamp(self, min: Self, max: Self) -> Self {
Self::new(self.x.clamp(min.x, max.x), self.y.clamp(min.y, max.y))
}
pub fn floor(self) -> Self {
Self::new(self.x.floor(), self.y.floor())
}
pub fn ceil(self) -> Self {
Self::new(self.x.ceil(), self.y.ceil())
}
pub fn round(self) -> Self {
Self::new(self.x.round(), self.y.round())
}
pub fn signum(self) -> Self {
Self::new(self.x.signum(), self.y.signum())
}
pub fn length_squared(self) -> f32 {
self.x * self.x + self.y * self.y
}
pub fn length(self) -> f32 {
self.length_squared().sqrt()
}
pub fn normalize(self) -> Self {
let length = self.length();
if length == 0.0 {
Self::ZERO
} else {
self / length
}
}
pub fn dot(self, other: Self) -> f32 {
self.x * other.x + self.y * other.y
}
pub fn cross(self, other: Self) -> f32 {
self.x * other.y - self.y * other.x
}
pub fn hat(self) -> Self {
Self::new(-self.y, self.x)
}
pub fn angle_between(self, other: Self) -> f32 {
let dot = self.dot(other) / f32::sqrt(self.length_squared() * other.length_squared());
dot.acos()
}
pub fn angle(self) -> f32 {
self.y.atan2(self.x)
}
pub const fn to_point(self) -> Point {
Point::new(self.x, self.y)
}
pub const fn to_size(self) -> Size {
Size::new(self.x, self.y)
}
}
impl Display for Vector {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[{}, {}]", self.x, self.y)
}
}
impl From<(f32, f32)> for Vector {
fn from((x, y): (f32, f32)) -> Self {
Self::new(x, y)
}
}
impl From<[f32; 2]> for Vector {
fn from([x, y]: [f32; 2]) -> Self {
Self::new(x, y)
}
}
impl From<Point> for Vector {
fn from(point: Point) -> Self {
point.to_vector()
}
}
impl From<Size> for Vector {
fn from(size: Size) -> Self {
size.to_vector()
}
}
impl From<f32> for Vector {
fn from(value: f32) -> Self {
Self::all(value)
}
}
impl From<Vector> for (f32, f32) {
fn from(vector: Vector) -> Self {
(vector.x, vector.y)
}
}
impl From<Vector> for [f32; 2] {
fn from(vector: Vector) -> Self {
[vector.x, vector.y]
}
}
impl Neg for Vector {
type Output = Self;
fn neg(self) -> Self::Output {
Self::new(-self.x, -self.y)
}
}
macro_rules! impl_math_op {
($op_trait:ident, $op_assign_trait:ident, $op_fn:ident, $op_assign_fn:ident, $op:tt) => {
impl $op_trait for Vector {
type Output = Self;
fn $op_fn(self, rhs: Self) -> Self::Output {
Self::new(self.x $op rhs.x, self.y $op rhs.y)
}
}
impl $op_assign_trait for Vector {
fn $op_assign_fn(&mut self, rhs: Self) {
*self = *self $op rhs;
}
}
impl $op_trait<Vector> for Size {
type Output = Self;
fn $op_fn(self, rhs: Vector) -> Self::Output {
Self::new(self.width $op rhs.x, self.width $op rhs.y)
}
}
impl $op_assign_trait<Vector> for Size {
fn $op_assign_fn(&mut self, rhs: Vector) {
*self = *self $op rhs;
}
}
impl $op_trait<f32> for Vector {
type Output = Self;
fn $op_fn(self, rhs: f32) -> Self::Output {
Self::new(self.x $op rhs, self.y $op rhs)
}
}
impl $op_assign_trait<f32> for Vector {
fn $op_assign_fn(&mut self, rhs: f32) {
*self = *self $op rhs;
}
}
};
}
impl_math_op!(Add, AddAssign, add, add_assign, +);
impl_math_op!(Sub, SubAssign, sub, sub_assign, -);
impl_math_op!(Mul, MulAssign, mul, mul_assign, *);
impl_math_op!(Div, DivAssign, div, div_assign, /);
impl_math_op!(Rem, RemAssign, rem, rem_assign, %);
impl Hash for Vector {
fn hash<H: Hasher>(&self, state: &mut H) {
self.x.to_bits().hash(state);
self.y.to_bits().hash(state);
}
}