use {
crate::{
geometry::{
Point, Vector, Real, Aabb, Rotation, Scale, Translation,
BoundingBox, VectorExt, P2, V2
},
},
nalgebra::Scalar,
num_traits::Float,
};
pub trait SDF<T: Scalar, const D: usize> {
fn sdf(&self, p: Point<T, D>) -> T;
}
pub trait Lipschitz<T> {
fn lipschitz(&self) -> T;
}
impl <S, T, const D: usize> SDF<T, D> for Translation<S, T, D>
where S: SDF<T, D>,
T: Real {
fn sdf(&self, pixel: Point<T, D>) -> T {
self.shape.sdf(pixel - self.offset)
}
}
impl <S, T, const D: usize> SDF<T, D> for Rotation<S, T, D>
where S: SDF<T, D> + BoundingBox<T, D>,
T: Real {
fn sdf(&self, pixel: Point<T, D>) -> T {
let pivot = self.shape.bounding_box().center();
let pixel = self.rotation.matrix() * (pixel - pivot) + pivot.coords;
self.shape.sdf(Point::from(pixel))
}
}
impl <S, T, const D: usize> SDF<T, D> for Scale<S, T>
where S: SDF<T, D> + BoundingBox<T, D>,
T: Real {
fn sdf(&self, pixel: Point<T, D>) -> T {
let c = self.shape.bounding_box().center();
let pixel = (pixel - c) / self.scale + c.coords;
self.shape.sdf(Point::from(pixel)) * self.scale
}
}
pub fn boundary_rect<T: Real, const D: usize>(pixel: Point<T, D>) -> T {
let p5 = T::one() / (T::one() + T::one());
let q = pixel.coords.map(|x| (x - p5).abs() - p5);
let outside = q.map(|x| x.max(T::zero())).length();
let inside = q.iter()
.fold(T::neg_infinity(), |a, &b| a.max(b))
.min(T::zero());
-(outside + inside)
}
#[derive(Clone, Copy, Debug)]
pub struct Union<S1, S2> {
pub s1: S1,
pub s2: S2,
}
impl<T, S1, S2, const D: usize> SDF<T, D> for Union<S1, S2>
where T: Real,
S1: SDF<T, D>,
S2: SDF<T, D> {
fn sdf(&self, pixel: Point<T, D>) -> T {
self.s1.sdf(pixel).min(self.s2.sdf(pixel))
}}
impl<T, S1, S2, const D: usize> BoundingBox<T, D> for Union<S1, S2>
where T: Real,
S1: BoundingBox<T, D>,
S2: BoundingBox<T, D> {
fn bounding_box(&self) -> Aabb<T, D> {
self.s1.bounding_box().merge(&self.s2.bounding_box())
}}
impl<T, S1, S2> Lipschitz<T> for Union<S1, S2>
where T: Float,
S1: Lipschitz<T>,
S2: Lipschitz<T> {
fn lipschitz(&self) -> T {
self.s1.lipschitz().max(self.s2.lipschitz())
}}
#[derive(Clone, Copy, Debug)]
pub struct Subtraction<S1, S2> {
pub s1: S1,
pub s2: S2,
}
impl<T, S1, S2, const D: usize> SDF<T, D> for Subtraction<S1, S2>
where T: Real,
S1: SDF<T, D>,
S2: SDF<T, D> {
fn sdf(&self, pixel: Point<T, D>) -> T {
(-self.s2.sdf(pixel)).max(self.s1.sdf(pixel))
}}
impl<T, S1, S2, const D: usize> BoundingBox<T, D> for Subtraction<S1, S2>
where T: Real,
S1: BoundingBox<T, D>,
S2: BoundingBox<T, D> {
fn bounding_box(&self) -> Aabb<T, D> {
self.s1.bounding_box().merge(&self.s2.bounding_box())
}}
impl<T, S1, S2> Lipschitz<T> for Subtraction<S1, S2>
where T: Float,
S1: Lipschitz<T>,
S2: Lipschitz<T> {
fn lipschitz(&self) -> T {
self.s1.lipschitz().max(self.s2.lipschitz())
}}
#[derive(Clone, Copy, Debug)]
pub struct Intersection<S1, S2> {
pub s1: S1,
pub s2: S2,
}
impl<T, S1, S2, const D: usize> SDF<T, D> for Intersection<S1, S2>
where T: Real,
S1: SDF<T, D>,
S2: SDF<T, D> {
fn sdf(&self, pixel: Point<T, D>) -> T {
self.s1.sdf(pixel).max(self.s2.sdf(pixel))
}}
impl<T, S1, S2, const D: usize> BoundingBox<T, D> for Intersection<S1, S2>
where T: Real,
S1: BoundingBox<T, D>,
S2: BoundingBox<T, D> {
fn bounding_box(&self) -> Aabb<T, D> {
self.s1.bounding_box()
.clip(&self.s2.bounding_box())
.unwrap_or(Aabb {
min: Point::from(Vector::repeat(-T::one())),
max: Point::from(Vector::repeat(-T::one()))
})
}}
impl<T, S1, S2> Lipschitz<T> for Intersection<S1, S2>
where T: Float,
S1: Lipschitz<T>,
S2: Lipschitz<T> {
fn lipschitz(&self) -> T {
self.s1.lipschitz().max(self.s2.lipschitz())
}}
#[derive(Clone, Copy, Debug)]
pub struct SmoothMin<T, S1, S2> {
pub s1: S1,
pub s2: S2,
pub k: T
}
impl<T, S1, S2, const D: usize> SDF<T, D> for SmoothMin<T, S1, S2>
where T: Real,
S1: SDF<T, D>,
S2: SDF<T, D> {
fn sdf(&self, pixel: Point<T, D>) -> T {
let (s1, s2) = (self.s1.sdf(pixel), self.s2.sdf(pixel));
let res = (-self.k * s1).exp2() + (-self.k * s2).exp2();
-res.log2() / self.k
}}
impl<T, S1, S2, const D: usize> BoundingBox<T, D> for SmoothMin<T, S1, S2>
where T: Real,
S1: BoundingBox<T, D>,
S2: BoundingBox<T, D> {
fn bounding_box(&self) -> Aabb<T, D> {
self.s1.bounding_box().merge(&self.s2.bounding_box())
}}
impl<T, S1, S2> Lipschitz<T> for SmoothMin<T, S1, S2>
where T: Float,
S1: Lipschitz<T>,
S2: Lipschitz<T> {
fn lipschitz(&self) -> T {
self.s1.lipschitz().max(self.s2.lipschitz())
}}
#[derive(Clone, Copy, Debug)]
pub struct Shell<S, T> {
pub shape: S,
pub half_width: T
}
impl<T, S, const D: usize> SDF<T, D> for Shell<S, T>
where T: Real,
S: SDF<T, D> {
fn sdf(&self, pixel: Point<T, D>) -> T {
self.shape.sdf(pixel).abs() - self.half_width
}}
impl<T, S, const D: usize> BoundingBox<T, D> for Shell<S, T>
where T: Real,
S: BoundingBox<T, D> {
fn bounding_box(&self) -> Aabb<T, D> {
self.shape.bounding_box().inflate(self.half_width)
}}
impl<T, S> Lipschitz<T> for Shell<S, T>
where T: Float,
S: Lipschitz<T> {
fn lipschitz(&self) -> T {
self.shape.lipschitz()
}}
#[derive(Clone, Copy, Debug)]
pub struct Offset<S, T> {
pub shape: S,
pub radius: T
}
impl<T, S, const D: usize> SDF<T, D> for Offset<S, T>
where T: Real,
S: SDF<T, D> {
fn sdf(&self, pixel: Point<T, D>) -> T {
self.shape.sdf(pixel) - self.radius
}}
impl<T, S, const D: usize> BoundingBox<T, D> for Offset<S, T>
where T: Real,
S: BoundingBox<T, D> {
fn bounding_box(&self) -> Aabb<T, D> {
self.shape.bounding_box().inflate(self.radius)
}}
impl<T, S> Lipschitz<T> for Offset<S, T>
where T: Float,
S: Lipschitz<T> {
fn lipschitz(&self) -> T {
self.shape.lipschitz()
}}
#[derive(Clone, Copy, Debug)]
pub struct Extrude<S, T> {
pub shape: S,
pub half_height: T
}
macro_rules! impl_extrude {($(($base:literal, $dim:literal))*) => {$(
impl<T, S> SDF<T, $dim> for Extrude<S, T>
where T: Real,
S: SDF<T, $base> {
fn sdf(&self, pixel: Point<T, $dim>) -> T {
let base: Point<T, $base> = Point::from(std::array::from_fn(|a| pixel[a]));
let w = V2::new(
self.shape.sdf(base),
pixel[$dim - 1].abs() - self.half_height);
let outside = V2::new(w.x.max(T::zero()), w.y.max(T::zero())).length();
let inside = w.x.max(w.y).min(T::zero());
outside + inside
}}
impl<T, S> BoundingBox<T, $dim> for Extrude<S, T>
where T: Real,
S: BoundingBox<T, $base> {
fn bounding_box(&self) -> Aabb<T, $dim> {
let b = self.shape.bounding_box();
let (mut min, mut max) = ([T::zero(); $dim], [T::zero(); $dim]);
for a in 0..$base {
min[a] = b.min[a];
max[a] = b.max[a];
}
min[$dim - 1] = -self.half_height;
max[$dim - 1] = self.half_height;
Aabb::new(Point::from(min), Point::from(max))
}}
)*}}
impl_extrude!((1, 2) (2, 3) (3, 4) (4, 5) (5, 6));
impl<T, S> Lipschitz<T> for Extrude<S, T>
where T: Float,
S: Lipschitz<T> {
fn lipschitz(&self) -> T {
self.shape.lipschitz().max(T::one())
}}
#[derive(Clone, Copy, Debug)]
pub struct Revolve<S, T> {
pub shape: S,
pub radius: T
}
impl<T, S, const D: usize> SDF<T, D> for Revolve<S, T>
where T: Real,
S: SDF<T, 2> {
fn sdf(&self, pixel: Point<T, D>) -> T {
let radial = (1..D)
.fold(T::zero(), |acc, a| acc + pixel[a] * pixel[a])
.sqrt();
self.shape.sdf(P2::new(pixel[0], radial - self.radius))
}}
impl<T, S, const D: usize> BoundingBox<T, D> for Revolve<S, T>
where T: Real,
S: BoundingBox<T, 2> {
fn bounding_box(&self) -> Aabb<T, D> {
let b = self.shape.bounding_box();
let reach = (b.min[1] + self.radius).abs()
.max((b.max[1] + self.radius).abs());
let (mut min, mut max) = ([-reach; D], [reach; D]);
min[0] = b.min[0];
max[0] = b.max[0];
Aabb::new(Point::from(min), Point::from(max))
}}
impl<T, S> Lipschitz<T> for Revolve<S, T>
where T: Float,
S: Lipschitz<T> {
fn lipschitz(&self) -> T {
self.shape.lipschitz()
}}