use crate::geom::about_equal;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::{
cmp::{Eq, PartialEq},
fmt,
iter::Sum,
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign},
};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Default, Debug)]
pub struct Vector {
pub x: f32,
pub y: f32,
}
impl Vector {
pub const ZERO: Vector = Vector { x: 0f32, y: 0f32 };
pub const X: Vector = Vector { x: 1f32, y: 0f32 };
pub const Y: Vector = Vector { x: 0f32, y: 1f32 };
pub const ONE: Vector = Vector { x: 1f32, y: 1f32 };
}
#[allow(clippy::len_without_is_empty)]
impl Vector {
pub fn new(x: f32, y: f32) -> Vector {
Vector { x, y }
}
pub fn from_angle(angle: f32) -> Vector {
Vector::new(angle.to_radians().cos(), angle.to_radians().sin())
}
pub fn len2(self) -> f32 {
self.x * self.x + self.y * self.y
}
pub fn len(self) -> f32 {
self.len2().sqrt()
}
#[must_use]
pub fn clamp(self, min_bound: Vector, max_bound: Vector) -> Vector {
Vector::new(
max_bound.x.min(min_bound.x.max(self.x)),
max_bound.y.min(min_bound.y.max(self.y)),
)
}
pub fn cross(self, other: Vector) -> f32 {
self.x * other.y - self.y * other.x
}
pub fn dot(self, other: Vector) -> f32 {
self.x * other.x + self.y * other.y
}
#[must_use]
pub fn normalize(self) -> Vector {
self / self.len()
}
#[must_use]
pub fn x_comp(self) -> Vector {
Vector::new(self.x, 0f32)
}
#[must_use]
pub fn y_comp(self) -> Vector {
Vector::new(0f32, self.y)
}
#[must_use]
pub fn recip(self) -> Vector {
Vector::new(self.x.recip(), self.y.recip())
}
#[must_use]
pub fn times(self, other: Vector) -> Vector {
Vector::new(self.x * other.x, self.y * other.y)
}
pub fn angle(self) -> f32 {
self.y.atan2(self.x).to_degrees()
}
#[must_use]
pub fn with_len(self, length: f32) -> Vector {
self.normalize() * length
}
pub fn distance(self, other: Vector) -> f32 {
((other.x - self.x).powi(2) + (other.y - self.y).powi(2)).sqrt()
}
pub fn min(self, other: Vector) -> Vector {
Vector::new(self.x.min(other.x), self.y.min(other.y))
}
pub fn max(self, other: Vector) -> Vector {
Vector::new(self.x.max(other.x), self.y.max(other.y))
}
}
impl Neg for Vector {
type Output = Vector;
fn neg(self) -> Vector {
Vector::new(-self.x, -self.y)
}
}
impl Add for Vector {
type Output = Vector;
fn add(self, rhs: Vector) -> Vector {
Vector::new(self.x + rhs.x, self.y + rhs.y)
}
}
impl AddAssign for Vector {
fn add_assign(&mut self, rhs: Vector) {
*self = *self + rhs;
}
}
impl Sub for Vector {
type Output = Vector;
fn sub(self, rhs: Vector) -> Vector {
self + (-rhs)
}
}
impl SubAssign for Vector {
fn sub_assign(&mut self, rhs: Vector) {
*self = *self - rhs;
}
}
impl Div<f32> for Vector {
type Output = Vector;
fn div(self, rhs: f32) -> Vector {
Vector::new(self.x / rhs, self.y / rhs)
}
}
impl DivAssign<f32> for Vector {
fn div_assign(&mut self, rhs: f32) {
*self = *self / rhs;
}
}
impl Mul<f32> for Vector {
type Output = Vector;
fn mul(self, rhs: f32) -> Vector {
Vector::new(self.x * rhs, self.y * rhs)
}
}
impl MulAssign<f32> for Vector {
fn mul_assign(&mut self, rhs: f32) {
*self = *self * rhs;
}
}
impl Sum for Vector {
fn sum<I>(iter: I) -> Vector
where
I: Iterator<Item = Vector>,
{
iter.fold(Vector::ZERO, |a, b| a + b)
}
}
impl PartialEq for Vector {
fn eq(&self, other: &Vector) -> bool {
about_equal(self.x, other.x) && about_equal(self.y, other.y)
}
}
impl Eq for Vector {}
impl fmt::Display for Vector {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "<{}, {}>", self.x, self.y)
}
}
impl From<mint::Vector2<f32>> for Vector {
fn from(other: mint::Vector2<f32>) -> Vector {
Vector::new(other.x, other.y)
}
}
impl From<Vector> for mint::Vector2<f32> {
fn from(vec: Vector) -> mint::Vector2<f32> {
mint::Vector2 { x: vec.x, y: vec.y }
}
}
impl From<(f32, f32)> for Vector {
fn from(other: (f32, f32)) -> Vector {
Vector::new(other.0, other.1)
}
}
#[cfg(test)]
mod tests {
use crate::geom::*;
#[test]
fn arithmetic() {
let a = Vector::new(5.0, 10.0);
let b = Vector::new(1.0, -2.0);
assert!((a + b).x == 6f32);
assert!((a - b).y == 12f32);
}
#[test]
fn equality() {
assert_eq!(Vector::new(5.0, 5.0), Vector::new(5.0, 5.0));
assert_ne!(Vector::new(0.0, 5.0), Vector::new(5.0, 5.0));
}
#[test]
fn inverse() {
let vec = Vector::new(3.0, 5.0);
let inverse = vec.recip();
assert_eq!(Vector::new(1.0 / 3.0, 1.0 / 5.0), inverse);
}
#[test]
fn length() {
let vec = Vector::X * 5.0;
assert!(about_equal(vec.len2(), 25f32));
assert!(about_equal(vec.len(), 5f32));
}
#[test]
fn scale() {
let vec = Vector::new(1.0, 1.0);
let doubled = Vector::new(2.0, 2.0);
assert_eq!(vec * 2.0, doubled);
let halved = Vector::new(0.5, 0.5);
assert_eq!(vec / 2.0, halved);
}
#[test]
fn clamp() {
let min = Vector::new(-10.0, -2.0);
let max = Vector::new(5.0, 6.0);
let vec = Vector::new(-11.0, 3.0);
let clamped = vec.clamp(min, max);
let expected = Vector::new(-10.0, 3.0);
assert_eq!(clamped, expected);
}
#[test]
fn dot() {
assert!(about_equal(
Vector::new(6.0, 5.0).dot(Vector::new(2.0, -8.0)),
-28f32
));
}
#[test]
fn times() {
let vec = Vector::new(3.0, -2.0);
let two = Vector::ONE * 2.0;
assert_eq!(vec.times(two), vec * 2.0);
}
#[test]
fn angle() {
let a = Vector::X;
let b = Vector::Y;
let c = a + b;
assert_eq!(a.angle(), 0.0);
assert_eq!(b.angle(), 90.0);
assert_eq!(c.angle(), 45.0);
}
#[test]
fn distance() {
let a = Vector::X;
let b = Vector::Y;
let c = a + b;
assert_eq!(a.distance(a), 0.0);
assert_eq!(a.distance(Vector::ZERO), 1.0);
assert_eq!(b.distance(a), 2_f32.sqrt());
assert_eq!(c.distance(Vector::ZERO), 2_f32.sqrt());
}
#[test]
fn sum() {
let input = vec![
Vector::new(1.0, 2.0),
Vector::new(2.0, 3.0),
Vector::new(3.0, 4.0),
];
let sum = input.into_iter().sum();
assert_eq!(Vector::new(6.0, 9.0), sum);
}
}