use glam::{Quat, Vec2, Vec3};
use std::ops::{Add, Div, Mul, Neg, Sub};
use wide::{f32x8, CmpGe, CmpGt, CmpLe, CmpLt};
pub trait Real:
Copy
+ Send
+ Sync
+ 'static
+ Add<Output = Self>
+ Sub<Output = Self>
+ Mul<Output = Self>
+ Div<Output = Self>
+ Neg<Output = Self>
{
type Mask: Copy;
const LANES: usize;
fn splat(v: f32) -> Self;
fn zero() -> Self {
Self::splat(0.0)
}
fn one() -> Self {
Self::splat(1.0)
}
fn sqrt(self) -> Self;
fn abs(self) -> Self;
fn floor(self) -> Self;
fn round(self) -> Self;
fn min(self, other: Self) -> Self;
fn max(self, other: Self) -> Self;
fn sin_cos(self) -> (Self, Self);
fn atan2(self, x: Self) -> Self;
fn exp(self) -> Self;
fn ln(self) -> Self;
fn signum(self) -> Self;
fn lt(self, other: Self) -> Self::Mask;
fn gt(self, other: Self) -> Self::Mask;
fn le(self, other: Self) -> Self::Mask;
fn ge(self, other: Self) -> Self::Mask;
fn select(mask: Self::Mask, a: Self, b: Self) -> Self;
fn mask_and(a: Self::Mask, b: Self::Mask) -> Self::Mask;
fn mask_or(a: Self::Mask, b: Self::Mask) -> Self::Mask;
fn map(self, f: impl Fn(f32) -> f32) -> Self;
fn map2(self, other: Self, f: impl Fn(f32, f32) -> f32) -> Self;
fn map3(x: Self, y: Self, z: Self, f: impl Fn(Vec3) -> f32) -> Self;
fn map3v(x: Self, y: Self, z: Self, f: impl Fn(Vec3) -> Vec3) -> Vec3R<Self>;
fn map3vs(x: Self, y: Self, z: Self, f: impl Fn(Vec3) -> (Vec3, f32)) -> (Vec3R<Self>, Self);
fn map_dp(d: Self, p: Vec3R<Self>, f: impl Fn(f32, Vec3) -> f32) -> Self;
#[inline(always)]
fn clamp(self, lo: Self, hi: Self) -> Self {
self.max(lo).min(hi)
}
#[inline(always)]
fn mul_add(self, m: Self, a: Self) -> Self {
self * m + a
}
#[inline(always)]
fn round_half_up(self) -> Self {
(self + Self::splat(0.5)).floor()
}
#[inline(always)]
fn lerp(a: Self, b: Self, t: Self) -> Self {
a + (b - a) * t
}
#[inline(always)]
fn twist(p: Vec3R<Self>, strength: f32) -> Vec3R<Self> {
let (s, c) = (p.y * Self::splat(strength)).sin_cos();
Vec3R::new(p.x * c - p.z * s, p.y, p.x * s + p.z * c)
}
#[inline(always)]
fn rotate_inverse(q: Quat, p: Vec3R<Self>) -> Vec3R<Self> {
let qv = Vec3R::<Self>::splat(-Vec3::new(q.x, q.y, q.z));
let w = Self::splat(q.w);
let t = qv.cross(p) * Self::splat(2.0);
p + t * w + qv.cross(t)
}
}
impl Real for f32 {
type Mask = bool;
const LANES: usize = 1;
#[inline(always)]
fn splat(v: f32) -> Self {
v
}
#[inline(always)]
fn sqrt(self) -> Self {
Self::sqrt(self)
}
#[inline(always)]
fn abs(self) -> Self {
Self::abs(self)
}
#[inline(always)]
fn floor(self) -> Self {
Self::floor(self)
}
#[inline(always)]
fn round(self) -> Self {
Self::round(self)
}
#[inline(always)]
fn min(self, other: Self) -> Self {
Self::min(self, other)
}
#[inline(always)]
fn max(self, other: Self) -> Self {
Self::max(self, other)
}
#[inline(always)]
fn sin_cos(self) -> (Self, Self) {
Self::sin_cos(self)
}
#[inline(always)]
fn atan2(self, x: Self) -> Self {
Self::atan2(self, x)
}
#[inline(always)]
fn exp(self) -> Self {
Self::exp(self)
}
#[inline(always)]
fn ln(self) -> Self {
Self::ln(self)
}
#[inline(always)]
fn signum(self) -> Self {
if self < 0.0 {
-1.0
} else {
1.0
}
}
#[inline(always)]
fn lt(self, other: Self) -> bool {
self < other
}
#[inline(always)]
fn gt(self, other: Self) -> bool {
self > other
}
#[inline(always)]
fn le(self, other: Self) -> bool {
self <= other
}
#[inline(always)]
fn ge(self, other: Self) -> bool {
self >= other
}
#[inline(always)]
fn select(mask: bool, a: Self, b: Self) -> Self {
if mask {
a
} else {
b
}
}
#[inline(always)]
fn mask_and(a: bool, b: bool) -> bool {
a && b
}
#[inline(always)]
fn mask_or(a: bool, b: bool) -> bool {
a || b
}
#[inline(always)]
fn map(self, f: impl Fn(Self) -> Self) -> Self {
f(self)
}
#[inline(always)]
fn map2(self, other: Self, f: impl Fn(Self, Self) -> Self) -> Self {
f(self, other)
}
#[inline(always)]
fn map3(x: Self, y: Self, z: Self, f: impl Fn(Vec3) -> Self) -> Self {
f(Vec3::new(x, y, z))
}
#[inline(always)]
fn map3v(x: Self, y: Self, z: Self, f: impl Fn(Vec3) -> Vec3) -> Vec3R<Self> {
let v = f(Vec3::new(x, y, z));
Vec3R::new(v.x, v.y, v.z)
}
#[inline(always)]
fn map3vs(x: Self, y: Self, z: Self, f: impl Fn(Vec3) -> (Vec3, Self)) -> (Vec3R<Self>, Self) {
let (v, s) = f(Vec3::new(x, y, z));
(Vec3R::new(v.x, v.y, v.z), s)
}
#[inline(always)]
fn map_dp(d: Self, p: Vec3R<Self>, f: impl Fn(Self, Vec3) -> Self) -> Self {
f(d, Vec3::new(p.x, p.y, p.z))
}
#[inline(always)]
fn mul_add(self, m: Self, a: Self) -> Self {
Self::mul_add(self, m, a)
}
#[inline(always)]
fn twist(p: Vec3R<Self>, strength: f32) -> Vec3R<Self> {
crate::modifiers::modifier_twist(Vec3::from(p), strength).into()
}
}
impl Real for f32x8 {
type Mask = Self;
const LANES: usize = 8;
#[inline(always)]
fn splat(v: f32) -> Self {
Self::splat(v)
}
#[inline(always)]
fn sqrt(self) -> Self {
Self::sqrt(self)
}
#[inline(always)]
fn abs(self) -> Self {
Self::abs(self)
}
#[inline(always)]
fn floor(self) -> Self {
Self::floor(self)
}
#[inline(always)]
fn round(self) -> Self {
Self::round(self)
}
#[inline(always)]
fn min(self, other: Self) -> Self {
Self::min(self, other)
}
#[inline(always)]
fn max(self, other: Self) -> Self {
Self::max(self, other)
}
#[inline(always)]
fn sin_cos(self) -> (Self, Self) {
Self::sin_cos(self)
}
#[inline(always)]
fn atan2(self, x: Self) -> Self {
self.map2(x, f32::atan2)
}
#[inline(always)]
fn exp(self) -> Self {
Self::exp(self)
}
#[inline(always)]
fn ln(self) -> Self {
Self::ln(self)
}
#[inline(always)]
fn signum(self) -> Self {
self.cmp_lt(Self::ZERO).blend(Self::splat(-1.0), Self::ONE)
}
#[inline(always)]
fn lt(self, other: Self) -> Self {
self.cmp_lt(other)
}
#[inline(always)]
fn gt(self, other: Self) -> Self {
self.cmp_gt(other)
}
#[inline(always)]
fn le(self, other: Self) -> Self {
self.cmp_le(other)
}
#[inline(always)]
fn ge(self, other: Self) -> Self {
self.cmp_ge(other)
}
#[inline(always)]
fn select(mask: Self, a: Self, b: Self) -> Self {
mask.blend(a, b)
}
#[inline(always)]
fn mask_and(a: Self, b: Self) -> Self {
a & b
}
#[inline(always)]
fn mask_or(a: Self, b: Self) -> Self {
a | b
}
#[inline(always)]
fn map(self, f: impl Fn(f32) -> f32) -> Self {
let a = self.as_array_ref();
Self::new([
f(a[0]),
f(a[1]),
f(a[2]),
f(a[3]),
f(a[4]),
f(a[5]),
f(a[6]),
f(a[7]),
])
}
#[inline(always)]
fn map2(self, other: Self, f: impl Fn(f32, f32) -> f32) -> Self {
let a = self.as_array_ref();
let b = other.as_array_ref();
Self::new([
f(a[0], b[0]),
f(a[1], b[1]),
f(a[2], b[2]),
f(a[3], b[3]),
f(a[4], b[4]),
f(a[5], b[5]),
f(a[6], b[6]),
f(a[7], b[7]),
])
}
#[inline(always)]
fn map3(x: Self, y: Self, z: Self, f: impl Fn(Vec3) -> f32) -> Self {
let (xa, ya, za) = (x.as_array_ref(), y.as_array_ref(), z.as_array_ref());
let mut out = [0.0f32; 8];
for i in 0..8 {
out[i] = f(Vec3::new(xa[i], ya[i], za[i]));
}
Self::new(out)
}
#[inline(always)]
fn map3v(x: Self, y: Self, z: Self, f: impl Fn(Vec3) -> Vec3) -> Vec3R<Self> {
let (xa, ya, za) = (x.as_array_ref(), y.as_array_ref(), z.as_array_ref());
let (mut ox, mut oy, mut oz) = ([0.0f32; 8], [0.0f32; 8], [0.0f32; 8]);
for i in 0..8 {
let v = f(Vec3::new(xa[i], ya[i], za[i]));
ox[i] = v.x;
oy[i] = v.y;
oz[i] = v.z;
}
Vec3R::new(Self::new(ox), Self::new(oy), Self::new(oz))
}
#[inline(always)]
fn map3vs(x: Self, y: Self, z: Self, f: impl Fn(Vec3) -> (Vec3, f32)) -> (Vec3R<Self>, Self) {
let (xa, ya, za) = (x.as_array_ref(), y.as_array_ref(), z.as_array_ref());
let (mut ox, mut oy, mut oz, mut os) = ([0.0f32; 8], [0.0f32; 8], [0.0f32; 8], [0.0f32; 8]);
for i in 0..8 {
let (v, s) = f(Vec3::new(xa[i], ya[i], za[i]));
ox[i] = v.x;
oy[i] = v.y;
oz[i] = v.z;
os[i] = s;
}
(
Vec3R::new(Self::new(ox), Self::new(oy), Self::new(oz)),
Self::new(os),
)
}
#[inline(always)]
fn map_dp(d: Self, p: Vec3R<Self>, f: impl Fn(f32, Vec3) -> f32) -> Self {
let (da, xa, ya, za) = (
d.as_array_ref(),
p.x.as_array_ref(),
p.y.as_array_ref(),
p.z.as_array_ref(),
);
let mut out = [0.0f32; 8];
for i in 0..8 {
out[i] = f(da[i], Vec3::new(xa[i], ya[i], za[i]));
}
Self::new(out)
}
#[inline(always)]
fn mul_add(self, m: Self, a: Self) -> Self {
Self::mul_add(self, m, a)
}
}
#[derive(Clone, Copy, Debug)]
pub struct Vec3R<R: Real> {
pub x: R,
pub y: R,
pub z: R,
}
impl<R: Real> Vec3R<R> {
#[inline(always)]
pub const fn new(x: R, y: R, z: R) -> Self {
Self { x, y, z }
}
#[inline(always)]
pub fn splat(v: Vec3) -> Self {
Self {
x: R::splat(v.x),
y: R::splat(v.y),
z: R::splat(v.z),
}
}
#[inline(always)]
pub fn zero() -> Self {
Self::new(R::zero(), R::zero(), R::zero())
}
#[inline(always)]
pub fn dot(self, o: Self) -> R {
self.x * o.x + self.y * o.y + self.z * o.z
}
#[inline(always)]
pub fn cross(self, o: Self) -> Self {
Self::new(
self.y * o.z - self.z * o.y,
self.z * o.x - self.x * o.z,
self.x * o.y - self.y * o.x,
)
}
#[inline(always)]
pub fn length_squared(self) -> R {
self.dot(self)
}
#[inline(always)]
pub fn length(self) -> R {
self.length_squared().sqrt()
}
#[inline(always)]
pub fn abs(self) -> Self {
Self::new(self.x.abs(), self.y.abs(), self.z.abs())
}
#[inline(always)]
pub fn min(self, o: Self) -> Self {
Self::new(self.x.min(o.x), self.y.min(o.y), self.z.min(o.z))
}
#[inline(always)]
pub fn max(self, o: Self) -> Self {
Self::new(self.x.max(o.x), self.y.max(o.y), self.z.max(o.z))
}
#[inline(always)]
pub fn clamp(self, lo: Self, hi: Self) -> Self {
self.max(lo).min(hi)
}
#[inline(always)]
pub fn round(self) -> Self {
Self::new(self.x.round(), self.y.round(), self.z.round())
}
#[inline(always)]
pub fn round_half_up(self) -> Self {
Self::new(
self.x.round_half_up(),
self.y.round_half_up(),
self.z.round_half_up(),
)
}
#[inline(always)]
pub fn max_element(self) -> R {
self.x.max(self.y).max(self.z)
}
#[inline(always)]
pub fn mul_vec(self, o: Self) -> Self {
Self::new(self.x * o.x, self.y * o.y, self.z * o.z)
}
#[inline(always)]
pub fn map(self, f: impl Fn(Vec3) -> Vec3) -> Self {
R::map3v(self.x, self.y, self.z, f)
}
#[inline(always)]
pub fn map_scalar(self, f: impl Fn(Vec3) -> f32) -> R {
R::map3(self.x, self.y, self.z, f)
}
}
impl<R: Real> Add for Vec3R<R> {
type Output = Self;
#[inline(always)]
fn add(self, o: Self) -> Self {
Self::new(self.x + o.x, self.y + o.y, self.z + o.z)
}
}
impl<R: Real> Sub for Vec3R<R> {
type Output = Self;
#[inline(always)]
fn sub(self, o: Self) -> Self {
Self::new(self.x - o.x, self.y - o.y, self.z - o.z)
}
}
impl<R: Real> Mul<R> for Vec3R<R> {
type Output = Self;
#[inline(always)]
fn mul(self, s: R) -> Self {
Self::new(self.x * s, self.y * s, self.z * s)
}
}
impl<R: Real> Div<R> for Vec3R<R> {
type Output = Self;
#[inline(always)]
fn div(self, s: R) -> Self {
Self::new(self.x / s, self.y / s, self.z / s)
}
}
impl<R: Real> Neg for Vec3R<R> {
type Output = Self;
#[inline(always)]
fn neg(self) -> Self {
Self::new(-self.x, -self.y, -self.z)
}
}
impl From<Vec3> for Vec3R<f32> {
#[inline(always)]
fn from(v: Vec3) -> Self {
Self::new(v.x, v.y, v.z)
}
}
impl From<Vec3R<f32>> for Vec3 {
#[inline(always)]
fn from(v: Vec3R<f32>) -> Self {
Self::new(v.x, v.y, v.z)
}
}
impl From<super::simd::Vec3x8> for Vec3R<f32x8> {
#[inline(always)]
fn from(v: super::simd::Vec3x8) -> Self {
Self::new(v.x, v.y, v.z)
}
}
impl From<Vec3R<f32x8>> for super::simd::Vec3x8 {
#[inline(always)]
fn from(v: Vec3R<f32x8>) -> Self {
Self {
x: v.x,
y: v.y,
z: v.z,
}
}
}
#[inline(always)]
pub fn rotate_inverse<R: Real>(q: Quat, p: Vec3R<R>) -> Vec3R<R> {
R::rotate_inverse(q, p)
}
#[inline(always)]
pub fn twist<R: Real>(p: Vec3R<R>, strength: f32) -> Vec3R<R> {
R::twist(p, strength)
}
#[inline(always)]
pub fn bend<R: Real>(p: Vec3R<R>, curvature: f32) -> Vec3R<R> {
let (s, c) = (p.x * R::splat(curvature)).sin_cos();
Vec3R::new(p.x * c - p.y * s, p.x * s + p.y * c, p.z)
}
#[inline(always)]
pub fn repeat_infinite<R: Real>(p: Vec3R<R>, spacing: Vec3, recip: Vec3) -> Vec3R<R> {
let cell = p.mul_vec(Vec3R::splat(recip)).round_half_up();
p - cell.mul_vec(Vec3R::splat(spacing))
}
#[inline(always)]
pub fn repeat_finite<R: Real>(p: Vec3R<R>, count: Vec3, spacing: Vec3) -> Vec3R<R> {
let limit = Vec3R::splat(count * 0.5);
let inv = Vec3R::splat(Vec3::new(1.0 / spacing.x, 1.0 / spacing.y, 1.0 / spacing.z));
let cell = p.mul_vec(inv).round_half_up().clamp(-limit, limit);
p - cell.mul_vec(Vec3R::splat(spacing))
}
#[inline(always)]
pub fn elongate<R: Real>(p: Vec3R<R>, amount: Vec3) -> Vec3R<R> {
let a = Vec3R::splat(amount);
p - p.clamp(-a, a)
}
#[inline(always)]
pub fn mirror<R: Real>(p: Vec3R<R>, axes: Vec3) -> Vec3R<R> {
Vec3R::new(
if axes.x != 0.0 { p.x.abs() } else { p.x },
if axes.y != 0.0 { p.y.abs() } else { p.y },
if axes.z != 0.0 { p.z.abs() } else { p.z },
)
}
#[inline(always)]
pub fn octant_mirror<R: Real>(p: Vec3R<R>) -> Vec3R<R> {
let (x, y, z) = (p.x.abs(), p.y.abs(), p.z.abs());
let (x, y) = (x.max(y), x.min(y));
let (y, z) = (y.max(z), y.min(z));
let (x, y) = (x.max(y), x.min(y));
Vec3R::new(x, y, z)
}
#[inline(always)]
pub fn revolution<R: Real>(p: Vec3R<R>, offset: f32) -> Vec3R<R> {
let r = (p.x * p.x + p.z * p.z).sqrt() - R::splat(offset);
Vec3R::new(r, p.y, R::zero())
}
#[inline(always)]
pub fn extrude_point<R: Real>(p: Vec3R<R>) -> Vec3R<R> {
Vec3R::new(p.x, p.y, R::zero())
}
#[inline(always)]
pub fn extrude_distance<R: Real>(d: R, original_z: R, half_height: f32) -> R {
let dz = original_z.abs() - R::splat(half_height);
let wx = d.max(R::zero());
let wy = dz.max(R::zero());
d.max(dz).min(R::zero()) + (wx * wx + wy * wy).sqrt()
}
#[inline(always)]
pub fn taper<R: Real>(p: Vec3R<R>, factor: f32) -> Vec3R<R> {
let den = R::one() - p.y * R::splat(factor);
let eps = R::splat(1e-6);
let mag = den.abs().max(eps);
let den = R::select(den.lt(R::zero()), -mag, mag);
let s = R::one() / den;
Vec3R::new(p.x * s, p.y, p.z * s)
}
#[inline(always)]
pub fn polar_repeat<R: Real>(p: Vec3R<R>, sector: f32, recip_sector: f32) -> Vec3R<R> {
let a = p.z.atan2(p.x);
let r = (p.x * p.x + p.z * p.z).sqrt();
let sector_angle = a - (a * R::splat(recip_sector)).round_half_up() * R::splat(sector);
let (s, c) = sector_angle.sin_cos();
Vec3R::new(r * c, p.y, r * s)
}
#[inline(always)]
pub fn shear<R: Real>(p: Vec3R<R>, sh: Vec3) -> Vec3R<R> {
Vec3R::new(
p.x,
p.y - R::splat(sh.x) * p.x,
p.z - R::splat(sh.y) * p.x - R::splat(sh.z) * p.y,
)
}
#[inline]
pub fn sweep_bezier<R: Real>(p: Vec3R<R>, p0: Vec2, p1: Vec2, p2: Vec2) -> Vec3R<R> {
let d = p.x.map2(p.z, |x, z| {
crate::modifiers::bezier_distance_2d(Vec2::new(x, z), p0, p1, p2)
});
Vec3R::new(d, p.y, R::zero())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::modifiers;
fn lanes(v: Vec3) -> Vec3R<f32x8> {
Vec3R::splat(v)
}
fn lane0(v: Vec3R<f32x8>) -> Vec3 {
Vec3::new(
v.x.as_array_ref()[0],
v.y.as_array_ref()[0],
v.z.as_array_ref()[0],
)
}
fn close(a: Vec3, b: Vec3) -> bool {
(a - b).length() < 1e-4
}
const PTS: [Vec3; 5] = [
Vec3::new(0.3, -0.7, 1.1),
Vec3::new(1.5, 1.5, 1.5),
Vec3::new(-2.0, 0.4, -0.9),
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(0.25, 0.0, 0.0),
];
#[test]
fn generic_laws_match_scalar_modifiers_on_both_instantiations() {
for p in PTS {
let checks: Vec<(&str, Vec3, Vec3, Vec3)> = vec![
(
"twist",
modifiers::modifier_twist(p, 1.5),
twist::<f32>(p.into(), 1.5).into(),
lane0(twist(lanes(p), 1.5)),
),
(
"bend",
modifiers::modifier_bend(p, 0.8),
bend::<f32>(p.into(), 0.8).into(),
lane0(bend(lanes(p), 0.8)),
),
(
"repeat_finite",
modifiers::modifier_repeat_finite(p, [2, 1, 2], Vec3::splat(1.5)),
repeat_finite::<f32>(p.into(), Vec3::new(2.0, 1.0, 2.0), Vec3::splat(1.5))
.into(),
lane0(repeat_finite(
lanes(p),
Vec3::new(2.0, 1.0, 2.0),
Vec3::splat(1.5),
)),
),
(
"octant_mirror",
modifiers::modifier_octant_mirror(p),
octant_mirror::<f32>(p.into()).into(),
lane0(octant_mirror(lanes(p))),
),
(
"polar_repeat",
modifiers::modifier_polar_repeat_rk(
p,
std::f32::consts::TAU / 6.0,
6.0 / std::f32::consts::TAU,
),
polar_repeat::<f32>(
p.into(),
std::f32::consts::TAU / 6.0,
6.0 / std::f32::consts::TAU,
)
.into(),
lane0(polar_repeat(
lanes(p),
std::f32::consts::TAU / 6.0,
6.0 / std::f32::consts::TAU,
)),
),
(
"sweep_bezier",
modifiers::modifier_sweep_bezier(
p,
Vec2::new(-1.0, 0.0),
Vec2::new(0.0, 1.0),
Vec2::new(1.0, 0.0),
),
sweep_bezier::<f32>(
p.into(),
Vec2::new(-1.0, 0.0),
Vec2::new(0.0, 1.0),
Vec2::new(1.0, 0.0),
)
.into(),
lane0(sweep_bezier(
lanes(p),
Vec2::new(-1.0, 0.0),
Vec2::new(0.0, 1.0),
Vec2::new(1.0, 0.0),
)),
),
(
"shear",
modifiers::modifier_shear(p, Vec3::new(1.0, 0.3, 0.0)),
shear::<f32>(p.into(), Vec3::new(1.0, 0.3, 0.0)).into(),
lane0(shear(lanes(p), Vec3::new(1.0, 0.3, 0.0))),
),
(
"taper",
modifiers::modifier_taper(p, 0.5),
taper::<f32>(p.into(), 0.5).into(),
lane0(taper(lanes(p), 0.5)),
),
(
"revolution",
modifiers::modifier_revolution(p, 0.6),
revolution::<f32>(p.into(), 0.6).into(),
lane0(revolution(lanes(p), 0.6)),
),
(
"rotate_inverse",
Quat::from_rotation_y(0.7).inverse() * p,
rotate_inverse::<f32>(Quat::from_rotation_y(0.7), p.into()).into(),
lane0(rotate_inverse(Quat::from_rotation_y(0.7), lanes(p))),
),
];
for (name, reference, scalar, simd) in checks {
assert!(
close(reference, scalar),
"{name} scalar @ {p:?}: {reference:?} vs {scalar:?}"
);
assert!(
close(reference, simd),
"{name} simd @ {p:?}: {reference:?} vs {simd:?}"
);
}
}
}
}