impacted 2.0.3

2d collision test for arbitrary convex shapes
Documentation
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};

#[derive(Debug, Copy, Clone, Default, PartialEq)]
pub struct Vec2 {
    pub(super) x: f32,
    pub(super) y: f32,
}

impl Vec2 {
    pub const ZERO: Self = Self::new(0.0, 0.0);
    pub const X: Self = Self::new(1.0, 0.0);
    pub const Y: Self = Self::new(0.0, 1.0);

    #[must_use]
    pub const fn new(x: f32, y: f32) -> Self {
        Self { x, y }
    }

    #[must_use]
    pub const fn splat(value: f32) -> Self {
        Self::new(value, value)
    }

    #[must_use]
    pub fn x(self) -> f32 {
        self.x
    }

    #[must_use]
    pub fn y(self) -> f32 {
        self.y
    }

    #[must_use]
    pub fn dot(self, other: Self) -> f32 {
        (self.x * other.x) + (self.y * other.y)
    }

    #[must_use]
    #[cfg(test)]
    pub(super) fn magnitude_squared(self) -> f32 {
        self.x * self.x + self.y * self.y
    }

    #[must_use]
    #[cfg(test)]
    pub(super) fn magnitude(self) -> f32 {
        self.magnitude_squared().sqrt()
    }

    #[must_use]
    #[cfg(test)]
    pub(super) fn normalize(self) -> Option<Self> {
        let normal = self / self.magnitude();
        if !normal.x.is_finite() {
            return None;
        }
        Some(normal)
    }

    #[must_use]
    #[cfg(test)]
    pub(super) fn perp(self) -> Self {
        Self {
            x: -self.y,
            y: self.x,
        }
    }
}

impl From<[f32; 2]> for Vec2 {
    fn from([x, y]: [f32; 2]) -> Self {
        Self::new(x, y)
    }
}

impl From<Vec2> for [f32; 2] {
    fn from(Vec2 { x, y }: Vec2) -> Self {
        [x, y]
    }
}

impl From<(f32, f32)> for Vec2 {
    fn from((x, y): (f32, f32)) -> Self {
        Vec2::new(x, y)
    }
}

impl From<Vec2> for (f32, f32) {
    fn from(Vec2 { x, y }: Vec2) -> Self {
        (x, y)
    }
}

impl AddAssign for Vec2 {
    fn add_assign(&mut self, rhs: Self) {
        self.x += rhs.x;
        self.y += rhs.y;
    }
}

impl Add for Vec2 {
    type Output = Self;
    fn add(mut self, rhs: Self) -> Self::Output {
        self += rhs;
        self
    }
}

impl SubAssign for Vec2 {
    fn sub_assign(&mut self, rhs: Self) {
        self.x -= rhs.x;
        self.y -= rhs.y;
    }
}

impl Sub for Vec2 {
    type Output = Self;
    fn sub(mut self, rhs: Self) -> Self::Output {
        self -= rhs;
        self
    }
}

impl MulAssign<f32> for Vec2 {
    fn mul_assign(&mut self, rhs: f32) {
        self.x *= rhs;
        self.y *= rhs;
    }
}

impl Mul<f32> for Vec2 {
    type Output = Self;
    fn mul(mut self, rhs: f32) -> Self::Output {
        self *= rhs;
        self
    }
}

impl DivAssign<f32> for Vec2 {
    fn div_assign(&mut self, rhs: f32) {
        self.x /= rhs;
        self.y /= rhs;
    }
}

impl Div<f32> for Vec2 {
    type Output = Self;
    fn div(mut self, rhs: f32) -> Self::Output {
        self /= rhs;
        self
    }
}

impl Neg for Vec2 {
    type Output = Self;
    fn neg(mut self) -> Self::Output {
        self.x = -self.x;
        self.y = -self.y;
        self
    }
}

#[cfg(test)]
impl approx::AbsDiffEq for Vec2 {
    type Epsilon = f32;
    fn default_epsilon() -> Self::Epsilon {
        f32::default_epsilon()
    }
    fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
        self.x.abs_diff_eq(&other.x, epsilon) && self.y.abs_diff_eq(&other.y, epsilon)
    }
}

#[cfg(test)]
mod tests {
    use approx::assert_abs_diff_eq;
    use rstest::rstest;

    use super::*;

    #[rstest]
    #[case(Vec2::ZERO, Vec2::ZERO, 0.0)]
    #[case(Vec2::X, Vec2::X, 1.0)]
    #[case(Vec2::X, Vec2::ZERO, 0.0)]
    #[case(Vec2::Y, Vec2::Y, 1.0)]
    #[case(Vec2::ZERO, Vec2::Y, 0.0)]
    #[case(Vec2::new(2.0, 3.0), Vec2::new(5.0, 7.0), 31.0)]
    #[case(Vec2::new(5.0, 7.0), Vec2::new(2.0, 3.0), 31.0)]
    fn test_dot(#[case] v1: Vec2, #[case] v2: Vec2, #[case] expected: f32) {
        assert_abs_diff_eq!(v1.dot(v2), expected);
    }

    #[rstest]
    #[case(Vec2::ZERO, Vec2::ZERO, Vec2::ZERO)]
    #[case(Vec2::X, Vec2::ZERO, Vec2::X)]
    #[case(Vec2::ZERO, Vec2::X, Vec2::X)]
    #[case(Vec2::ZERO, Vec2::Y, Vec2::Y)]
    #[case(Vec2::Y, Vec2::ZERO, Vec2::Y)]
    #[case(Vec2::new(1.0, 2.0), Vec2::new(3.0, 4.0), Vec2::new(4.0, 6.0))]
    #[case(Vec2::new(-1.0, 2.0), Vec2::new(3.0, -4.0), Vec2::new(2.0, -2.0))]
    fn test_add(#[case] v1: Vec2, #[case] v2: Vec2, #[case] expected: Vec2) {
        assert_abs_diff_eq!(v1 + v2, expected);
        let mut sum = v1;
        sum += v2;
        assert_abs_diff_eq!(sum, expected);
    }

    #[rstest]
    #[case(Vec2::ZERO, Vec2::ZERO, Vec2::ZERO)]
    #[case(Vec2::X, Vec2::ZERO, Vec2::X)]
    #[case(Vec2::ZERO, Vec2::X, Vec2::new(-1.0, 0.0))]
    #[case(Vec2::Y, Vec2::ZERO, Vec2::Y)]
    #[case(Vec2::ZERO, Vec2::Y, Vec2::new(0.0, -1.0))]
    #[case(Vec2::new(10.0, 11.0), Vec2::new(3.0, 5.0), Vec2::new(7.0, 6.0))]
    #[case(Vec2::new(3.0, 5.0), Vec2::new(10.0, 11.0), Vec2::new(-7.0, -6.0))]
    fn test_sub(#[case] v1: Vec2, #[case] v2: Vec2, #[case] expected: Vec2) {
        assert_abs_diff_eq!(v1 - v2, expected);
        let mut res = v1;
        res -= v2;
        assert_abs_diff_eq!(res, expected);
    }

    #[rstest]
    #[case(Vec2::ZERO, 1.0, Vec2::ZERO)]
    #[case(Vec2::X, 2.0, Vec2::new(2.0, 0.0))]
    #[case(Vec2::Y, 2.0, Vec2::new(0.0, 2.0))]
    #[case(Vec2::new(2.0, 3.0), 4.0, Vec2::new(8.0, 12.0))]
    fn test_mul_div(#[case] v: Vec2, #[case] f: f32, #[case] expected: Vec2) {
        assert_abs_diff_eq!(v * f, expected);
        assert_abs_diff_eq!(v / f.recip(), expected);
        let mut res = v;
        res *= f;
        assert_abs_diff_eq!(res, expected);
        res = v;
        res /= f.recip();
        assert_abs_diff_eq!(res, expected);
    }

    #[rstest]
    #[case(Vec2::ZERO, Vec2::ZERO)]
    #[case(Vec2::X, Vec2::new(-1.0, 0.0))]
    #[case(Vec2::Y, Vec2::new(0.0, -1.0))]
    #[case(Vec2::new(-2.3, 4.5), Vec2::new(2.3, -4.5))]
    fn test_negate(#[case] v: Vec2, #[case] expected: Vec2) {
        assert_abs_diff_eq!(-v, expected);
    }

    #[rstest]
    #[case(Vec2::ZERO, 0.0)]
    #[case(Vec2::X, 1.0)]
    #[case(-Vec2::X, 1.0)]
    #[case(Vec2::Y, 1.0)]
    #[case(-Vec2::Y, 1.0)]
    #[case(Vec2::new(3.0, 4.0), 5.0)]
    fn test_magnitude(#[case] vector: Vec2, #[case] expected_magnitude: f32) {
        assert_abs_diff_eq!(vector.magnitude(), expected_magnitude);
        assert_abs_diff_eq!(
            vector.magnitude_squared(),
            expected_magnitude * expected_magnitude
        );
    }

    #[rstest]
    fn normalize_zero_returns_none() {
        assert_eq!(Vec2::ZERO.normalize(), None);
    }

    #[rstest]
    #[case(Vec2::X, Vec2::X)]
    #[case(-Vec2::X, -Vec2::X)]
    #[case(Vec2::Y, Vec2::Y)]
    #[case(Vec2::X * 2.0, Vec2::X)]
    #[case(Vec2::Y * 2.0, Vec2::Y)]
    #[case(Vec2::new(3.0, 4.0), Vec2::new(0.6, 0.8))]
    fn normalize_returns_expected(#[case] vector: Vec2, #[case] expected: Vec2) {
        assert_abs_diff_eq!(vector.normalize().unwrap(), expected);
    }

    #[rstest]
    #[case(Vec2::X, Vec2::Y)]
    #[case(Vec2::Y, -Vec2::X)]
    #[case(-Vec2::X, -Vec2::Y)]
    #[case(-Vec2::Y, Vec2::X)]
    #[case(Vec2::new(1.0, 2.0), Vec2::new(-2.0, 1.0))]
    #[case(Vec2::new(-3.0, -4.0), Vec2::new(4.0, -3.0))]
    fn test_perp(#[case] vector: Vec2, #[case] expected: Vec2) {
        assert_eq!(vector.perp(), expected);
    }
}