use core::ops::{Add, Mul, Sub};
use num_traits::ToPrimitive;
pub trait VectorArithmetic:
Clone + Default + Send + 'static + Add<Output = Self> + Sub<Output = Self> + Mul<f64, Output = Self>
{
#[must_use]
fn lerp(&self, other: &Self, t: f64) -> Self {
self.clone() + (other.clone() - self.clone()) * t
}
}
impl<T> VectorArithmetic for T where
T: Clone + Default + Send + 'static + Add<Output = T> + Sub<Output = T> + Mul<f64, Output = T>
{
}
#[derive(Clone, Default, Debug, PartialEq, Eq)]
pub struct AnimatablePair<A, B>(pub A, pub B);
impl<A: Add<Output = A>, B: Add<Output = B>> Add for AnimatablePair<A, B> {
type Output = Self;
fn add(self, other: Self) -> Self {
Self(self.0 + other.0, self.1 + other.1)
}
}
impl<A: Sub<Output = A>, B: Sub<Output = B>> Sub for AnimatablePair<A, B> {
type Output = Self;
fn sub(self, other: Self) -> Self {
Self(self.0 - other.0, self.1 - other.1)
}
}
impl<A: Mul<f64, Output = A>, B: Mul<f64, Output = B>> Mul<f64> for AnimatablePair<A, B> {
type Output = Self;
fn mul(self, scalar: f64) -> Self {
Self(self.0 * scalar, self.1 * scalar)
}
}
#[derive(Clone, Copy, Default, Debug, PartialEq)]
pub struct Point2(pub [f32; 2]);
impl Point2 {
#[must_use]
pub const fn new(x: f32, y: f32) -> Self {
Self([x, y])
}
#[must_use]
pub const fn x(&self) -> f32 {
self.0[0]
}
#[must_use]
pub const fn y(&self) -> f32 {
self.0[1]
}
}
impl From<[f32; 2]> for Point2 {
fn from(arr: [f32; 2]) -> Self {
Self(arr)
}
}
impl From<Point2> for [f32; 2] {
fn from(p: Point2) -> Self {
p.0
}
}
impl Add for Point2 {
type Output = Self;
fn add(self, other: Self) -> Self {
Self([self.0[0] + other.0[0], self.0[1] + other.0[1]])
}
}
impl Sub for Point2 {
type Output = Self;
fn sub(self, other: Self) -> Self {
Self([self.0[0] - other.0[0], self.0[1] - other.0[1]])
}
}
impl Mul<f64> for Point2 {
type Output = Self;
fn mul(self, scalar: f64) -> Self {
let s = scalar
.to_f32()
.expect("Point2 scaling requires an f64 representable as f32");
Self([self.0[0] * s, self.0[1] * s])
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_f64_lerp() {
let a = 0.0_f64;
let b = 1.0_f64;
assert!((a.lerp(&b, 0.0) - 0.0).abs() < 0.001);
assert!((a.lerp(&b, 0.5) - 0.5).abs() < 0.001);
assert!((a.lerp(&b, 1.0) - 1.0).abs() < 0.001);
}
#[test]
fn test_point2_lerp() {
let a = Point2::new(0.0, 0.0);
let b = Point2::new(1.0, 2.0);
let mid = a.lerp(&b, 0.5);
assert!((mid.x() - 0.5).abs() < 0.001);
assert!((mid.y() - 1.0).abs() < 0.001);
}
#[test]
fn test_animatable_pair() {
let a = AnimatablePair(0.0_f64, Point2::new(0.0, 0.0));
let b = AnimatablePair(1.0_f64, Point2::new(2.0, 4.0));
let mid = a.lerp(&b, 0.5);
assert!((mid.0 - 0.5).abs() < 0.001);
assert!((mid.1.x() - 1.0).abs() < 0.001);
assert!((mid.1.y() - 2.0).abs() < 0.001);
}
}