use crate::compiled::real::LawMinMax;
use crate::compiled::real::Real;
use std::f32::consts::{FRAC_1_SQRT_2, SQRT_2};
#[inline(always)]
pub fn stairs_min(a: f32, b: f32, r: f32, n: f32) -> f32 {
stairs_min_r::<f32>(a, b, r, n)
}
#[inline(always)]
pub fn stairs_max(a: f32, b: f32, r: f32, n: f32) -> f32 {
-stairs_min(-a, -b, r, n)
}
#[inline(always)]
pub fn sdf_stairs_union(d1: f32, d2: f32, r: f32, n: f32) -> f32 {
stairs_min(d1, d2, r, n)
}
#[inline(always)]
pub fn sdf_stairs_intersection(d1: f32, d2: f32, r: f32, n: f32) -> f32 {
stairs_max(d1, d2, r, n)
}
#[inline(always)]
pub fn sdf_stairs_subtraction(d1: f32, d2: f32, r: f32, n: f32) -> f32 {
-stairs_min(-d1, d2, r, n)
}
#[inline(always)]
fn glsl_mod_r<R: Real>(a: R, b: f32) -> R {
a - R::splat(b) * (a / R::splat(b)).floor()
}
#[inline(always)]
pub fn stairs_min_r<R: Real>(a: R, b: R, r: f32, n: f32) -> R {
let n = n.law_max(1.0);
let s = R::splat(FRAC_1_SQRT_2);
let d = a.min(b);
let py = (a + b) * s;
let px = (b - a) * s;
let rn = r / n;
let off = (r - rn) * 0.5 * SQRT_2;
let px = px - R::splat(off) + R::splat(0.5 * SQRT_2 * rn);
let py = py - R::splat(off);
let step = r * SQRT_2 / n;
let hs = step * 0.5;
let px = glsl_mod_r(px + R::splat(hs), step) - R::splat(hs);
let d = d.min(py);
let npx = (px + py) * s;
let npy = (py - px) * s;
let edge = R::splat(0.5 * rn);
d.min((npx - edge).max(npy - edge))
}
#[inline(always)]
pub fn stairs_max_r<R: Real>(a: R, b: R, r: f32, n: f32) -> R {
-stairs_min_r(-a, -b, r, n)
}
#[inline(always)]
pub fn sdf_stairs_union_r<R: Real>(d1: R, d2: R, r: f32, n: f32) -> R {
stairs_min_r(d1, d2, r, n)
}
#[inline(always)]
pub fn sdf_stairs_intersection_r<R: Real>(d1: R, d2: R, r: f32, n: f32) -> R {
stairs_max_r(d1, d2, r, n)
}
#[inline(always)]
pub fn sdf_stairs_subtraction_r<R: Real>(d1: R, d2: R, r: f32, n: f32) -> R {
-stairs_min_r(-d1, d2, r, n)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_stairs_union_basic() {
let a = 5.0;
let b = 10.0;
let r = 0.5;
let n = 4.0;
let result = sdf_stairs_union(a, b, r, n);
assert!(
result <= a + 0.01,
"stairs union should approximate min when far apart"
);
}
#[test]
fn test_stairs_union_creates_steps() {
let r = 1.0;
let n = 4.0;
let a = 0.1;
let b = 0.2;
let stairs = sdf_stairs_union(a, b, r, n);
let hard = a.min(b);
assert!(stairs <= hard + 0.01);
}
#[test]
fn test_stairs_intersection_subtraction_duality() {
let a = 0.5;
let b = 0.3;
let r = 0.4;
let n = 3.0;
let int_result = sdf_stairs_intersection(a, b, r, n);
let _sub_result = sdf_stairs_subtraction(a, b, r, n);
let int_alt = -sdf_stairs_union(-a, -b, r, n);
assert!(
(int_result - int_alt).abs() < 1e-6,
"intersection = -union(-a,-b)"
);
}
}