Module lav::example

source ·
Expand description

Portably SIMD-optimized 3D rotator implementation generic over lane type f32 and f64.

#![allow(non_snake_case)]
#![feature(portable_simd)]

use core::{
	mem::transmute,
	ops::{
		Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Shl, ShlAssign, Sub, SubAssign,
	},
};
use lav::{swizzle, ApproxEq, Real, SimdMask, SimdReal};

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(transparent)]
pub struct Rotator3<R: Real> {
	wxyz: R::Simd<4>,
}

impl<R: Real> Rotator3<R> {
	pub fn new(alpha: R, x: R, y: R, z: R) -> Self {
		let (sin, cos) = (alpha * -R::FRAC_1_2).sin_cos();
		(Self::from([R::ZERO, x, y, z]).unit() * sin).set_w(cos)
	}
	pub fn from_wxyz(wxyz: [R; 4]) -> Self {
		Self { wxyz: wxyz.into() }
	}
	pub fn from_xyzw(xyzw: [R; 4]) -> Self {
		Self {
			wxyz: R::Simd::from_array(xyzw).simd_rotate_right::<1>(),
		}
	}
	pub fn norm(&self) -> R {
		self.norm_squared().sqrt()
	}
	pub fn norm_squared(&self) -> R {
		(self.wxyz * self.wxyz).reduce_sum()
	}
	pub fn unit(self) -> Self {
		self / self.norm()
	}
	pub fn inv(self) -> Self {
		self.rev() / self.norm_squared()
	}
	pub fn rev(self) -> Self {
		let tfff = R::Simd::mask_flag(0, true);
		Self {
			wxyz: tfff.select(self.wxyz, -self.wxyz),
		}
	}
	pub fn constrain(self) -> Self {
		if self.w().is_sign_negative() {
			-self
		} else {
			self
		}
	}
	pub fn to_wxyz(self) -> [R; 4] {
		self.wxyz.to_array()
	}
	pub fn to_xyzw(self) -> [R; 4] {
		self.wxyz.simd_rotate_left::<1>().to_array()
	}
	pub fn w(&self) -> R {
		self.wxyz[0]
	}
	pub fn x(&self) -> R {
		self.wxyz[1]
	}
	pub fn y(&self) -> R {
		self.wxyz[2]
	}
	pub fn z(&self) -> R {
		self.wxyz[3]
	}
	pub fn set_w(mut self, w: R) -> Self {
		self.wxyz[0] = w;
		self
	}
	pub fn set_x(mut self, x: R) -> Self {
		self.wxyz[1] = x;
		self
	}
	pub fn set_y(mut self, y: R) -> Self {
		self.wxyz[2] = y;
		self
	}
	pub fn set_z(mut self, z: R) -> Self {
		self.wxyz[3] = z;
		self
	}
	pub fn point_fn(self) -> impl Fn(&mut Point3<R>) {
		// +(+[+1ww+1zz+(+1xx+1yy)]~w+[+0zy+0wx]~w+[+0yw-0zx]~w)e123
		// +(+[+1xx+1ww-(+1yy+1zz)]~X+[+2wz+2xy]~Y+[+2zx-2wy]~Z)e032
		// +(+[+1yy+1ww-(+1zz+1xx)]~Y+[+2wx+2yz]~Z+[+2xy-2wz]~X)e013
		// +(+[+1zz+1ww-(+1xx+1yy)]~Z+[+2wy+2zx]~X+[+2yz-2wx]~Y)e021
		let wxyz = self.wxyz;
		let zwww = swizzle!(wxyz, [3, 0, 0, 0]);
		let xyzx = swizzle!(wxyz, [1, 2, 3, 1]);
		let yzxy = swizzle!(wxyz, [2, 3, 1, 2]);
		let zttt = R::Simd::from_array([R::ZERO, R::TWO, R::TWO, R::TWO]);
		let fttt = R::Simd::mask_flag(0, false);
		let pin0 = xyzx.mul_add(xyzx, yzxy * yzxy);
		let pin0 = zwww.mul_add(zwww, fttt.negate(pin0));
		let pin0 = wxyz.mul_add(wxyz, pin0);
		let pin1 = zwww.mul_add(yzxy, wxyz * xyzx) * zttt;
		let pin2 = yzxy.mul_add(wxyz, -(zwww * xyzx)) * zttt;
		move |point3| {
			let wXYZ = point3.wXYZ;
			let wYZX = swizzle!(wXYZ, [0, 2, 3, 1]);
			let wZXY = swizzle!(wXYZ, [0, 3, 1, 2]);
			let wXYZ = pin0.mul_add(wXYZ, pin1.mul_add(wYZX, pin2 * wZXY));
			point3.wXYZ = wXYZ;
		}
	}
	pub fn points_fn(self) -> impl Fn(&mut [Point3<R>]) {
		let rotate = self.point_fn();
		move |points| {
			for point3 in points {
				rotate(point3);
			}
		}
	}
}

impl<R: Real> Default for Rotator3<R> {
	fn default() -> Self {
		Self::from([R::ONE, R::ZERO, R::ZERO, R::ZERO])
	}
}

impl<R: Real> From<[R; 4]> for Rotator3<R> {
	fn from(wxyz: [R; 4]) -> Self {
		Self::from_wxyz(wxyz)
	}
}

impl<R: Real> Into<[R; 4]> for Rotator3<R> {
	fn into(self) -> [R; 4] {
		self.to_wxyz()
	}
}

impl<R: Real> ApproxEq<R> for Rotator3<R> {
	fn approx_eq(&self, other: &Self, epsilon: R, ulp: R::Bits) -> bool {
		self.wxyz.approx_eq(&other.wxyz, epsilon, ulp)
	}
}

impl<R: Real> Add for Rotator3<R> {
	type Output = Self;

	fn add(self, other: Self) -> Self::Output {
		Self {
			wxyz: self.wxyz + other.wxyz,
		}
	}
}

impl<R: Real> AddAssign for Rotator3<R> {
	fn add_assign(&mut self, other: Self) {
		*self = *self + other;
	}
}

impl<R: Real> Div<R> for Rotator3<R> {
	type Output = Self;

	fn div(self, other: R) -> Self::Output {
		Self {
			wxyz: self.wxyz / other.splat(),
		}
	}
}

impl<R: Real> DivAssign<R> for Rotator3<R> {
	fn div_assign(&mut self, other: R) {
		*self = *self / other;
	}
}

impl<R: Real> Mul for Rotator3<R> {
	type Output = Self;

	fn mul(self, other: Self) -> Self::Output {
		// +(+1LwRw-1LxRx-(+1LyRy+1LzRz))e
		// +(+1LwRx-1LyRz+(+1LxRw+1LzRy))e23
		// +(+1LwRy-1LzRx+(+1LxRz+1LyRw))e31
		// +(+1LwRz-1LxRy+(+1LyRx+1LzRw))e12
		let tfff = R::Simd::mask_flag(0, true);
		let wxyz = swizzle!(self.wxyz, [0, 0, 0, 0]).mul_add(
			other.wxyz,
			swizzle!(self.wxyz, [1, 2, 3, 1]).mul_add(
				-swizzle!(other.wxyz, [1, 3, 1, 2]),
				tfff.negate(swizzle!(self.wxyz, [2, 1, 1, 2]).mul_add(
					swizzle!(other.wxyz, [2, 0, 3, 1]),
					swizzle!(self.wxyz, [3, 3, 2, 3]) * swizzle!(other.wxyz, [3, 2, 0, 0]),
				)),
			),
		);
		Self { wxyz }
	}
}

impl<R: Real> Mul<R> for Rotator3<R> {
	type Output = Self;

	fn mul(self, other: R) -> Self::Output {
		Self {
			wxyz: self.wxyz * other.splat(),
		}
	}
}

impl<R: Real> MulAssign<R> for Rotator3<R> {
	fn mul_assign(&mut self, other: R) {
		*self = *self * other;
	}
}

impl<R: Real> Neg for Rotator3<R> {
	type Output = Self;

	fn neg(self) -> Self::Output {
		Self { wxyz: -self.wxyz }
	}
}

impl<R: Real> Sub for Rotator3<R> {
	type Output = Self;

	fn sub(self, other: Self) -> Self::Output {
		Self {
			wxyz: self.wxyz - other.wxyz,
		}
	}
}

impl<R: Real> SubAssign for Rotator3<R> {
	fn sub_assign(&mut self, other: Self) {
		*self = *self - other;
	}
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(transparent)]
pub struct Point3<R: Real> {
	wXYZ: R::Simd<4>,
}

impl<R: Real> Point3<R> {
	pub fn new(w: R, X: R, Y: R, Z: R) -> Self {
		Self::from([w, X, Y, Z])
	}
	pub fn from_wXYZ(wXYZ: [R; 4]) -> Self {
		Self { wXYZ: wXYZ.into() }
	}
	pub fn from_XYZw(XYZw: [R; 4]) -> Self {
		Self {
			wXYZ: R::Simd::from_array(XYZw).simd_rotate_right::<1>(),
		}
	}
	pub fn as_points(points: &[R]) -> &[Self] {
		let (prefix, points, suffix) = R::as_simd::<4>(points);
		assert!(prefix.is_empty() && suffix.is_empty(), "misaligned");
		// Safe due to `#[repr(transparent)]`.
		unsafe { transmute::<&[R::Simd<4>], &[Point3<R>]>(points) }
	}
	pub fn as_points_mut(points: &mut [R]) -> &mut [Self] {
		let (prefix, points, suffix) = R::as_simd_mut::<4>(points);
		assert!(prefix.is_empty() && suffix.is_empty(), "misaligned");
		// Safe due to `#[repr(transparent)]`.
		unsafe { transmute::<&mut [R::Simd<4>], &mut [Point3<R>]>(points) }
	}
	pub fn norm(&self) -> R {
		self.w().abs()
	}
	pub fn norm_squared(&self) -> R {
		let w = self.w();
		w * w
	}
	pub fn unit(self) -> Self {
		self / self.norm()
	}
	pub fn inv(self) -> Self {
		self.rev() / self.norm_squared()
	}
	pub fn rev(self) -> Self {
		-self
	}
	pub fn to_wXYZ(self) -> [R; 4] {
		self.wXYZ.to_array()
	}
	pub fn to_XYZw(self) -> [R; 4] {
		self.wXYZ.simd_rotate_left::<1>().to_array()
	}
	pub fn w(&self) -> R {
		self.wXYZ[0]
	}
	pub fn X(&self) -> R {
		self.wXYZ[1]
	}
	pub fn Y(&self) -> R {
		self.wXYZ[2]
	}
	pub fn Z(&self) -> R {
		self.wXYZ[3]
	}
	pub fn set_w(mut self, w: R) -> Self {
		self.wXYZ[0] = w;
		self
	}
	pub fn set_X(mut self, X: R) -> Self {
		self.wXYZ[1] = X;
		self
	}
	pub fn set_Y(mut self, Y: R) -> Self {
		self.wXYZ[2] = Y;
		self
	}
	pub fn set_Z(mut self, Z: R) -> Self {
		self.wXYZ[3] = Z;
		self
	}
}

impl<R: Real> Default for Point3<R> {
	fn default() -> Self {
		Self::from([R::ONE, R::ZERO, R::ZERO, R::ZERO])
	}
}

impl<R: Real> From<[R; 4]> for Point3<R> {
	fn from(wXYZ: [R; 4]) -> Self {
		Self::from_wXYZ(wXYZ)
	}
}

impl<R: Real> Into<[R; 4]> for Point3<R> {
	fn into(self) -> [R; 4] {
		self.to_wXYZ()
	}
}

impl<R: Real> ApproxEq<R> for Point3<R> {
	fn approx_eq(&self, other: &Self, epsilon: R, ulp: R::Bits) -> bool {
		self.wXYZ.approx_eq(&other.wXYZ, epsilon, ulp)
	}
}

impl<R: Real> Add for Point3<R> {
	type Output = Self;

	fn add(self, other: Self) -> Self::Output {
		Self {
			wXYZ: self.wXYZ + other.wXYZ,
		}
	}
}

impl<R: Real> AddAssign for Point3<R> {
	fn add_assign(&mut self, other: Self) {
		*self = *self + other;
	}
}

impl<R: Real> Div<R> for Point3<R> {
	type Output = Self;

	fn div(self, other: R) -> Self::Output {
		Self {
			wXYZ: self.wXYZ / other.splat(),
		}
	}
}

impl<R: Real> DivAssign<R> for Point3<R> {
	fn div_assign(&mut self, other: R) {
		*self = *self / other;
	}
}

impl<R: Real> Mul<R> for Point3<R> {
	type Output = Self;

	fn mul(self, other: R) -> Self::Output {
		Self {
			wXYZ: self.wXYZ * other.splat(),
		}
	}
}

impl<R: Real> MulAssign<R> for Point3<R> {
	fn mul_assign(&mut self, other: R) {
		*self = *self * other;
	}
}

impl<R: Real> Neg for Point3<R> {
	type Output = Self;

	fn neg(self) -> Self::Output {
		Self { wXYZ: -self.wXYZ }
	}
}

impl<R: Real> Shl<Rotator3<R>> for Point3<R> {
	type Output = Self;

	fn shl(mut self, other: Rotator3<R>) -> Self::Output {
		other.point_fn()(&mut self);
		self
	}
}

impl<R: Real> ShlAssign<Rotator3<R>> for Point3<R> {
	fn shl_assign(&mut self, other: Rotator3<R>) {
		*self = *self << other
	}
}

impl<R: Real> Sub for Point3<R> {
	type Output = Self;

	fn sub(self, other: Self) -> Self::Output {
		Self {
			wXYZ: self.wXYZ - other.wXYZ,
		}
	}
}

impl<R: Real> SubAssign for Point3<R> {
	fn sub_assign(&mut self, other: Self) {
		*self = *self - other;
	}
}

let r000_ = Rotator3::default();
let r030x = Rotator3::new(030f64.to_radians(), 1.0, 0.0, 0.0);
let r060x = Rotator3::new(060f64.to_radians(), 1.0, 0.0, 0.0);
let r330x = Rotator3::new(330f64.to_radians(), 1.0, 0.0, 0.0);
assert!((r030x * r030x).approx_eq(&r060x, 0.0, 0));
assert!((r030x * 42.0).unit().approx_eq(&r030x, 0.0, 0));
assert!(((r030x * 42.0) * (r030x * 42.0).inv()).approx_eq(&r000_, f64::EPSILON, 0));
assert!((r030x * r030x.rev()).approx_eq(&Rotator3::default(), f64::EPSILON, 0));
assert!(r330x.constrain().approx_eq(&r030x.rev(), 0.0, 5));

let r090x = Rotator3::new(090f64.to_radians(), 1.0, 0.0, 0.0);
let x5 = Point3::new(1.0, 5.0, 0.0, 0.0);
let y5 = Point3::new(1.0, 0.0, 5.0, 0.0);
let z5 = Point3::new(1.0, 0.0, 0.0, 5.0);
assert!((x5 << r090x).approx_eq(&x5, 0.0, 0));
assert!((y5 << r090x).approx_eq(&z5, 5.0 * f64::EPSILON, 0));