use core::{
array,
fmt::{Debug, Formatter},
iter::Sum,
marker::PhantomData as Pd,
ops::{Add, Div, Index, IndexMut, Mul, Neg, Sub},
ops::{AddAssign, DivAssign, MulAssign, SubAssign},
};
use crate::math::{
Affine, ApproxEq, Linear, Point,
space::{Proj3, Real},
vary::ZDiv,
};
#[repr(transparent)]
pub struct Vector<Repr, Space = ()>(pub Repr, Pd<Space>);
pub type Vec2<Basis = ()> = Vector<[f32; 2], Real<2, Basis>>;
pub type Vec3<Basis = ()> = Vector<[f32; 3], Real<3, Basis>>;
pub type ProjVec3 = Vector<[f32; 4], Proj3>;
pub type Vec2i<Basis = ()> = Vector<[i32; 2], Real<2, Basis>>;
pub type Vec3i<Basis = ()> = Vector<[i32; 3], Real<3, Basis>>;
pub const fn vec2<Sc, B>(x: Sc, y: Sc) -> Vector<[Sc; 2], Real<2, B>> {
Vector([x, y], Pd)
}
pub const fn vec3<Sc, B>(x: Sc, y: Sc, z: Sc) -> Vector<[Sc; 3], Real<3, B>> {
Vector([x, y, z], Pd)
}
#[inline]
pub fn splat<Sp, Sc: Clone, const DIM: usize>(s: Sc) -> Vector<[Sc; DIM], Sp> {
array::from_fn(|_| s.clone()).into() }
impl<R, Sp> Vector<R, Sp> {
#[inline]
pub const fn new(repr: R) -> Self {
Self(repr, Pd)
}
#[inline]
pub fn to<S>(self) -> Vector<R, S> {
Vector::new(self.0)
}
#[inline]
pub fn to_pt(self) -> Point<R, Sp> {
Point::new(self.0)
}
}
impl<Sp, const N: usize> Vector<[f32; N], Sp> {
#[cfg(feature = "fp")]
#[inline]
pub fn len(&self) -> f32 {
super::float::f32::sqrt(self.dot(self))
}
#[inline]
#[must_use]
pub fn normalize(&self) -> Self {
#[cfg(feature = "std")]
use super::float::RecipSqrt;
use super::float::f32;
let len_sqr = self.len_sqr();
assert!(
len_sqr.is_finite() && !len_sqr.approx_eq_eps(&0.0, &1e-12),
"cannot normalize a near-zero or non-finite vector: {:?}",
self.0
);
*self * f32::recip_sqrt(len_sqr)
}
#[must_use]
pub fn clamp(&self, min: &Self, max: &Self) -> Self {
array::from_fn(|i| self[i].clamp(min[i], max[i])).into()
}
pub fn is_finite(&self) -> bool {
self.0.iter().all(|c| c.is_finite())
}
}
impl<Sc, Sp, const N: usize> Vector<[Sc; N], Sp>
where
Self: Linear<Scalar = Sc>,
Sc: Linear<Scalar = Sc> + Copy,
{
#[inline]
pub fn len_sqr(&self) -> Sc {
self.dot(self)
}
#[inline]
pub fn dot(&self, other: &Self) -> Sc {
self.0
.iter()
.zip(&other.0)
.map(|(a, b)| a.mul(*b))
.fold(Sc::zero(), |acc, x| acc.add(&x))
}
#[must_use]
pub fn scalar_project(&self, other: &Self) -> Sc
where
Sc: Div<Sc, Output = Sc>,
{
self.dot(other) / other.dot(other)
}
#[must_use]
pub fn vector_project(&self, other: &Self) -> Self
where
Sc: Div<Sc, Output = Sc>,
{
other.mul(self.scalar_project(other))
}
pub fn reflect(self, other: Self) -> Self
where
Sc: Div<Sc, Output = Sc>,
{
let proj_on_other = self.vector_project(&other);
proj_on_other + proj_on_other - self
}
}
impl<Sc: Copy, Sp, const N: usize> Vector<[Sc; N], Sp> {
#[inline]
#[must_use]
pub fn map<T>(self, mut f: impl FnMut(Sc) -> T) -> Vector<[T; N], Sp> {
array::from_fn(|i| f(self.0[i])).into()
}
#[inline]
#[must_use]
pub fn zip_map<T: Copy, U>(
self,
other: Vector<[T; N], Sp>,
mut f: impl FnMut(Sc, T) -> U,
) -> Vector<[U; N], Sp> {
array::from_fn(|i| f(self.0[i], other.0[i])).into()
}
}
impl<R, Sc, B> Vector<R, Real<2, B>>
where
R: Index<usize, Output = Sc>,
Sc: Copy,
{
#[inline]
pub fn x(&self) -> Sc {
self.0[0]
}
#[inline]
pub fn y(&self) -> Sc {
self.0[1]
}
}
impl<B> Vec2<B> {
pub const X: Self = vec2(1.0, 0.0);
pub const Y: Self = vec2(0.0, 1.0);
pub fn to_vec3(self) -> Vec3<B> {
vec3(self.x(), self.y(), 0.0)
}
#[inline]
pub fn perp(self) -> Self {
vec2(-self.y(), self.x())
}
#[inline]
pub fn perp_dot(self, other: Self) -> f32 {
self.perp().dot(&other)
}
}
impl<R, Sc, B> Vector<R, Real<3, B>>
where
R: Index<usize, Output = Sc>,
Sc: Copy,
{
#[inline]
pub fn x(&self) -> Sc {
self.0[0]
}
#[inline]
pub fn y(&self) -> Sc {
self.0[1]
}
#[inline]
pub fn z(&self) -> Sc {
self.0[2]
}
pub fn cross(&self, other: &Self) -> Self
where
Sc: Linear<Scalar = Sc>,
[Sc; 3]: Into<Self>,
{
let (s, o) = (self, other);
[
s.y().mul(o.z()).sub(&s.z().mul(o.y())),
s.z().mul(o.x()).sub(&s.x().mul(o.z())),
s.x().mul(o.y()).sub(&s.y().mul(o.x())),
]
.into()
}
}
impl<B> Vec3<B> {
pub const X: Self = vec3(1.0, 0.0, 0.0);
pub const Y: Self = vec3(0.0, 1.0, 0.0);
pub const Z: Self = vec3(0.0, 0.0, 1.0);
}
impl<R, Sc> Vector<R, Proj3>
where
R: Index<usize, Output = Sc>,
Sc: Copy,
{
#[inline]
pub fn x(&self) -> Sc {
self.0[0]
}
#[inline]
pub fn y(&self) -> Sc {
self.0[1]
}
#[inline]
pub fn z(&self) -> Sc {
self.0[2]
}
#[inline]
pub fn w(&self) -> Sc {
self.0[3]
}
}
impl<Sc, Sp, const DIM: usize> Affine for Vector<[Sc; DIM], Sp>
where
Sc: Linear<Scalar = Sc> + Copy,
{
type Space = Sp;
type Diff = Self;
const DIM: usize = DIM;
#[inline]
fn add(&self, other: &Self) -> Self {
Self(array::from_fn(|i| self.0[i].add(&other.0[i])), Pd)
}
#[inline]
fn sub(&self, other: &Self) -> Self {
Self(array::from_fn(|i| self.0[i].sub(&other.0[i])), Pd)
}
}
impl<Sc, Sp, const DIM: usize> Linear for Vector<[Sc; DIM], Sp>
where
Sc: Linear<Scalar = Sc> + Copy,
{
type Scalar = Sc;
#[inline]
fn zero() -> Self {
Self([Sc::zero(); DIM], Pd)
}
#[inline]
fn mul(&self, scalar: Sc) -> Self {
self.map(|c| c.mul(scalar))
}
}
impl<Sc, Sp, const N: usize> ZDiv for Vector<[Sc; N], Sp>
where
Sc: ZDiv + Copy,
{
fn z_div(self, z: f32) -> Self {
self.map(|c| c.z_div(z))
}
}
impl<Sc: ApproxEq, Sp, const N: usize> ApproxEq<Self, Sc>
for Vector<[Sc; N], Sp>
{
fn approx_eq_eps(&self, other: &Self, eps: &Sc) -> bool {
self.0.approx_eq_eps(&other.0, eps)
}
fn relative_epsilon() -> Sc {
Sc::relative_epsilon()
}
}
impl<R: Copy, S> Copy for Vector<R, S> {}
impl<R: Clone, S> Clone for Vector<R, S> {
fn clone(&self) -> Self {
Self(self.0.clone(), Pd)
}
}
impl<R: Default, B, const DIM: usize> Default for Vector<R, Real<DIM, B>> {
fn default() -> Self {
Self(R::default(), Pd)
}
}
impl<R: Eq, S> Eq for Vector<R, S> {}
impl<R: PartialEq, S> PartialEq for Vector<R, S> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<R: Debug, Sp: Debug + Default> Debug for Vector<R, Sp> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "Vec<{:?}>", Sp::default())?;
Debug::fmt(&self.0, f)
}
}
impl<R, Sp> From<R> for Vector<R, Sp> {
#[inline]
fn from(repr: R) -> Self {
Self(repr, Pd)
}
}
impl<Sp, Sc: Clone, const DIM: usize> From<Sc> for Vector<[Sc; DIM], Sp> {
#[inline]
fn from(scalar: Sc) -> Self {
splat(scalar)
}
}
impl<R, Sp> Index<usize> for Vector<R, Sp>
where
Self: Affine,
R: Index<usize>,
{
type Output = R::Output;
#[inline]
fn index(&self, i: usize) -> &Self::Output {
assert!(i < Self::DIM, "index {i} out of bounds ({})", Self::DIM);
&self.0[i]
}
}
impl<R, Sp> IndexMut<usize> for Vector<R, Sp>
where
Self: Affine,
R: IndexMut<usize>,
{
#[inline]
fn index_mut(&mut self, i: usize) -> &mut Self::Output {
assert!(i < Self::DIM, "index {i} out of bounds ({})", Self::DIM);
&mut self.0[i]
}
}
impl<R, Sp> Sum for Vector<R, Sp>
where
Self: Linear,
{
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::zero(), |acc, v| Affine::add(&acc, &v))
}
}
impl<R, Sp> AddAssign<<Self as Affine>::Diff> for Vector<R, Sp>
where
Self: Affine,
{
#[inline]
fn add_assign(&mut self, rhs: <Self as Affine>::Diff) {
*self = Affine::add(&*self, &rhs);
}
}
impl<R, Sp> SubAssign<<Self as Affine>::Diff> for Vector<R, Sp>
where
Self: Affine,
{
#[inline]
fn sub_assign(&mut self, rhs: <Self as Affine>::Diff) {
*self = Affine::add(&*self, &rhs.neg());
}
}
impl<R, Sp> MulAssign<<Self as Linear>::Scalar> for Vector<R, Sp>
where
Self: Linear,
{
#[inline]
fn mul_assign(&mut self, rhs: <Self as Linear>::Scalar) {
*self = Linear::mul(&*self, rhs);
}
}
impl<R, Sp> DivAssign<f32> for Vector<R, Sp>
where
Self: Linear<Scalar = f32>,
{
#[inline]
fn div_assign(&mut self, rhs: f32) {
debug_assert!(!rhs.approx_eq(&0.0), "divisor {rhs} < epsilon");
*self = Linear::mul(&*self, rhs.recip());
}
}
impl<R, Sp> Neg for Vector<R, Sp>
where
Self: Linear,
{
type Output = Self;
#[inline]
fn neg(self) -> Self::Output {
<Self as Linear>::neg(&self)
}
}
impl<R, Sp> Mul<Vector<R, Sp>> for f32
where
Vector<R, Sp>: Linear<Scalar = f32>,
{
type Output = Vector<R, Sp>;
#[inline]
fn mul(self, rhs: Vector<R, Sp>) -> Self::Output {
rhs * self
}
}
impl<R, Sp> Mul<Vector<R, Sp>> for i32
where
Vector<R, Sp>: Linear<Scalar = i32>,
{
type Output = Vector<R, Sp>;
#[inline]
fn mul(self, rhs: Vector<R, Sp>) -> Self::Output {
rhs * self
}
}
impl<R, Sp> Mul<Vector<R, Sp>> for u32
where
Vector<R, Sp>: Linear<Scalar = u32>,
{
type Output = Vector<R, Sp>;
#[inline]
fn mul(self, rhs: Vector<R, Sp>) -> Self::Output {
rhs * self
}
}
impl_op!(Add::add, Vector, <Self as Affine>::Diff, +=, bound=Affine);
impl_op!(Sub::sub, Vector, <Self as Affine>::Diff, -=, bound=Affine);
impl_op!(Mul::mul, Vector, <Self as Linear>::Scalar, *=);
impl_op!(Div::div, Vector, f32, /=, bound=Linear<Scalar = f32>);
#[cfg(test)]
mod tests {
use core::f32::consts::*;
use crate::assert_approx_eq;
use super::*;
pub const fn vec2<S>(x: S, y: S) -> Vector<[S; 2], Real<2>> {
super::vec2(x, y)
}
pub const fn vec3<S>(x: S, y: S, z: S) -> Vector<[S; 3], Real<3>> {
super::vec3(x, y, z)
}
pub const fn vec4<S>(x: S, y: S, z: S, w: S) -> Vector<[S; 4], Real<4>> {
Vector::new([x, y, z, w])
}
mod f32 {
use super::*;
#[cfg(feature = "fp")]
#[test]
fn length() {
assert_approx_eq!(vec2(1.0, 1.0).len(), SQRT_2);
assert_approx_eq!(vec2(-3.0, 4.0).len(), 5.0);
assert_approx_eq!(vec3(1.0, -2.0, 3.0).len(), 14.0f32.sqrt());
}
#[test]
fn length_squared() {
assert_eq!(vec2(1.0, 1.0).len_sqr(), 2.0);
assert_eq!(vec2(-4.0, 3.0).len_sqr(), 25.0);
assert_eq!(vec3(1.0, -2.0, 3.0).len_sqr(), 14.0);
}
#[test]
fn normalize() {
assert_approx_eq!(vec2(3.0, 4.0).normalize(), vec2(0.6, 0.8));
let sqrt_14 = 14.0f32.sqrt();
assert_approx_eq!(
vec3(1.0, 2.0, 3.0).normalize(),
vec3(1.0 / sqrt_14, 2.0 / sqrt_14, 3.0 / sqrt_14)
);
}
#[test]
fn vector_addition() {
assert_eq!(vec2(1.0, 2.0) + vec2(-2.0, 1.0), vec2(-1.0, 3.0));
assert_eq!(
vec3(1.0, 2.0, 0.0) + vec3(-2.0, 1.0, -1.0),
vec3(-1.0, 3.0, -1.0)
);
}
#[test]
fn scalar_multiplication() {
assert_eq!(vec2(1.0, -2.0) * 0.0, vec2(0.0, 0.0));
assert_eq!(vec3(1.0, -2.0, 3.0) * 3.0, vec3(3.0, -6.0, 9.0));
assert_eq!(3.0 * vec3(1.0, -2.0, 3.0), vec3(3.0, -6.0, 9.0));
assert_eq!(
vec4(1.0, -2.0, 0.0, -3.0) * 3.0,
vec4(3.0, -6.0, 0.0, -9.0)
);
assert_eq!(
3.0 * vec4(1.0, -2.0, 0.0, -3.0),
vec4(3.0, -6.0, 0.0, -9.0)
);
}
#[test]
fn scalar_division() {
assert_eq!(vec2(1.0, -2.0) / 1.0, vec2(1.0, -2.0));
assert_eq!(vec3(3.0, -6.0, 9.0) / 3.0, vec3(1.0, -2.0, 3.0));
assert_eq!(
vec4(3.0, -6.0, 0.0, -9.0) / 3.0,
vec4(1.0, -2.0, 0.0, -3.0)
);
}
#[test]
fn dot_product() {
assert_eq!(vec2(1.0, -2.0).dot(&vec2(2.0, 3.0)), -4.0);
assert_eq!(vec3(1.0, -2.0, 3.0).dot(&vec3(2.0, 3.0, -1.0)), -7.0);
}
#[test]
fn indexing() {
let mut v = vec2(1.0, 2.0);
assert_eq!(v[1], 2.0);
v[0] = 3.0;
assert_eq!(v.0, [3.0, 2.0]);
let mut v = vec3(1.0, 2.0, 3.0);
assert_eq!(v[1], 2.0);
v[2] = 4.0;
assert_eq!(v.0, [1.0, 2.0, 4.0]);
}
#[test]
fn from_array() {
assert_eq!(Vec2::from([1.0, -2.0]), vec2(1.0, -2.0));
assert_eq!(Vec3::from([1.0, -2.0, 4.0]), vec3(1.0, -2.0, 4.0));
assert_eq!(
Vector::from([1.0, -2.0, 4.0, -3.0]),
vec4(1.0, -2.0, 4.0, -3.0)
);
}
#[test]
fn perp() {
assert_eq!(Vec2::<()>::zero().perp(), Vec2::zero());
assert_eq!(Vec2::<()>::X.perp(), Vec2::Y);
assert_eq!(vec2(-0.2, -1.5).perp(), vec2(1.5, -0.2));
}
#[test]
fn perp_dot() {
const X: Vec2 = Vec2::X;
const Y: Vec2 = Vec2::Y;
assert_eq!(X.perp_dot(X), 0.0);
assert_eq!(X.perp_dot(Y), 1.0);
assert_eq!((2.0 * Y).perp_dot(3.0 * X), -6.0);
}
}
mod i32 {
use super::*;
#[test]
fn vector_addition() {
assert_eq!(vec2(1, 2) + vec2(-2, 1), vec2(-1, 3));
assert_eq!(vec3(1, 2, 0) + vec3(-2, 1, -1), vec3(-1, 3, -1));
}
#[test]
fn vector_subtraction() {
assert_eq!(vec2(1, 2) - vec2(-2, 3), vec2(3, -1));
assert_eq!(vec3(1, 2, 0) - vec3(-2, 1, 2), vec3(3, 1, -2));
}
#[test]
#[allow(clippy::erasing_op)]
fn scalar_multiplication() {
assert_eq!(vec2(1, -2) * 0, vec2(0, 0));
assert_eq!(vec3(1, -2, 3) * 3, vec3(3, -6, 9));
assert_eq!(3 * vec3(1, -2, 3), vec3(3, -6, 9));
assert_eq!(vec4(1, -2, 0, -3) * 3, vec4(3, -6, 0, -9));
assert_eq!(3 * vec4(1, -2, 0, -3), vec4(3, -6, 0, -9));
}
#[test]
fn dot_product() {
assert_eq!(vec2(1, -2).dot(&vec2(2, 3)), -4);
assert_eq!(vec3(1, -2, 3).dot(&vec3(2, 3, -1)), -7);
}
#[test]
fn indexing() {
let mut v = vec2(1, 2);
assert_eq!(v[1], 2);
v[0] = 3;
assert_eq!(v.0, [3, 2]);
let mut v = vec3(1, 2, 3);
assert_eq!(v[1], 2);
v[2] = 4;
assert_eq!(v.0, [1, 2, 4]);
}
#[test]
fn from_array() {
assert_eq!(Vec2i::from([1, -2]), vec2(1, -2));
assert_eq!(Vec3i::from([1, -2, 3]), vec3(1, -2, 3));
}
}
const X: Vec3 = Vec3::X;
const Y: Vec3 = Vec3::Y;
const Z: Vec3 = Vec3::Z;
#[test]
fn cross_product_basis_vectors() {
assert_eq!(X.cross(&Y), Z);
assert_eq!(Y.cross(&X), -Z);
assert_eq!(Y.cross(&Z), X);
assert_eq!(Z.cross(&Y), -X);
assert_eq!(Z.cross(&X), Y);
assert_eq!(X.cross(&Z), -Y);
}
#[test]
fn cross_product_parallelogram_area() {
let a = 3.0 * Y;
let b = 2.0 * X - Y;
assert_eq!(a.cross(&b).len_sqr(), 6.0 * 6.0);
}
#[test]
fn iterator_sum() {
let vs = [vec2(-1.0, 2.0), vec2(0.0, 2.0), vec2(3.0, -1.0)];
assert_eq!(vs.into_iter().sum::<Vec2>(), vec2(2.0, 3.0));
}
#[test]
fn approx_equal_pass() {
assert_approx_eq!(vec2(1.0, -10.0), vec2(1.01, -9.9), eps = 0.011);
}
#[test]
#[should_panic]
fn approx_equal_fail() {
let eps = 2.0 * f32::relative_epsilon();
assert_approx_eq!(vec2(1.0, -10.0), vec2(1.0 + eps, -10.0 - eps));
}
#[test]
fn debug() {
assert_eq!(
alloc::format!("{:?}", vec2(1.0, -E)),
"Vec<ℝ²<()>>[1.0, -2.7182817]"
);
assert_eq!(
alloc::format!("{:?}", vec3(1.0, -2.0, 3.0)),
"Vec<ℝ³<()>>[1.0, -2.0, 3.0]"
);
assert_eq!(
alloc::format!("{:?}", vec4(1.0, -2.0, PI, -4.0)),
"Vec<ℝ⁴<()>>[1.0, -2.0, 3.1415927, -4.0]"
);
}
}