use super::*;
use std::ops::*;
#[cfg(feature = "ultraviolet")]
use ultraviolet::f32x4;
pub type EstimateNormalDefault<T, V, S> =
EstimateNormal<T, V, S, CentralDifferenceEstimator<T, V, <V as Vec<T>>::Dimension>>;
pub type EstimateNormalFast<T, V, S> = EstimateNormal<T, V, S, TetrahedralEstimator<T, V>>;
pub struct EstimateNormal<T, V, S, E> {
pub sdf: S,
pub estimator: E,
_pd: std::marker::PhantomData<(T, V)>,
}
impl<T, V, S, E> EstimateNormal<T, V, S, E>
where
E: NormalEstimator<T, V>,
S: SDF<T, V>,
V: Vec<T>,
{
pub fn new(sdf: S, estimator: E) -> Self {
EstimateNormal {
sdf,
estimator,
_pd: std::marker::PhantomData,
}
}
#[inline]
pub fn normal_at(&self, p: V) -> V {
self.estimator.estimate_normal(self.sdf, p)
}
}
pub trait NormalEstimator<T, V: Vec<T>> {
fn estimate_normal<S: SDF<T, V>>(&self, sdf: S, p: V) -> V;
}
pub struct CentralDifferenceEstimator<T, V, D> {
pub eps: T,
_pdv: std::marker::PhantomData<V>,
_pdd: std::marker::PhantomData<D>,
}
impl<T, V: Vec<T>> CentralDifferenceEstimator<T, V, <V as Vec<T>>::Dimension> {
pub fn new(eps: T) -> Self {
CentralDifferenceEstimator {
eps,
_pdv: std::marker::PhantomData,
_pdd: std::marker::PhantomData,
}
}
}
impl<T, V> NormalEstimator<T, V> for CentralDifferenceEstimator<T, V, Dim3D>
where
T: Add<T, Output = T> + Sub<T, Output = T> + Copy,
V: Vec3<T>,
{
#[inline]
fn estimate_normal<S: SDF<T, V>>(&self, sdf: S, p: V) -> V {
let eps = self.eps;
V::new(
sdf.dist(V::new(p.x() + eps, p.y(), p.z()))
- sdf.dist(V::new(p.x() - eps, p.y(), p.z())),
sdf.dist(V::new(p.x(), p.y() + eps, p.z()))
- sdf.dist(V::new(p.x(), p.y() - eps, p.z())),
sdf.dist(V::new(p.x(), p.y(), p.z() + eps))
- sdf.dist(V::new(p.x(), p.y(), p.z() - eps)),
)
.normalized()
}
}
impl<T, V> NormalEstimator<T, V> for CentralDifferenceEstimator<T, V, Dim2D>
where
T: Add<T, Output = T> + Sub<T, Output = T> + Copy,
V: Vec2<T>,
{
#[inline]
fn estimate_normal<S: SDF<T, V>>(&self, sdf: S, p: V) -> V {
let eps = self.eps;
V::new(
sdf.dist(V::new(p.x() + eps, p.y())) - sdf.dist(V::new(p.x() - eps, p.y())),
sdf.dist(V::new(p.x(), p.y() + eps)) - sdf.dist(V::new(p.x(), p.y() - eps)),
)
.normalized()
}
}
#[cfg(feature = "ultraviolet")]
impl<V: Vec<f32x4>> Default for CentralDifferenceEstimator<f32x4, V, <V as Vec<f32x4>>::Dimension> {
fn default() -> Self {
Self::new(f32x4::from(0.000))
}
}
impl<V: Vec<f32>> Default for CentralDifferenceEstimator<f32, V, <V as Vec<f32>>::Dimension> {
fn default() -> Self {
Self::new(0.001)
}
}
impl<V: Vec<f64>> Default for CentralDifferenceEstimator<f64, V, <V as Vec<f64>>::Dimension> {
fn default() -> Self {
Self::new(0.001)
}
}
pub struct TetrahedralEstimator<T, V> {
pub eps: T,
_pdv: std::marker::PhantomData<V>,
}
impl<T, V: Vec<T>> TetrahedralEstimator<T, V> {
pub fn new(eps: T) -> Self {
TetrahedralEstimator {
eps,
_pdv: std::marker::PhantomData,
}
}
}
impl<T, V> NormalEstimator<T, V> for TetrahedralEstimator<T, V>
where
T: Add<T, Output = T> + Sub<T, Output = T> + Neg<Output = T> + One + Copy + std::fmt::Display,
V: Vec3<T>,
{
#[inline]
fn estimate_normal<S: SDF<T, V>>(&self, sdf: S, p: V) -> V {
let xyy = V::new(T::one(), -T::one(), -T::one());
let yyx = V::new(-T::one(), -T::one(), T::one());
let yxy = V::new(-T::one(), T::one(), -T::one());
let xxx = V::one();
let d1 = sdf.dist(p + xyy * self.eps);
let d2 = sdf.dist(p + yyx * self.eps);
let d3 = sdf.dist(p + yxy * self.eps);
let d4 = sdf.dist(p + xxx * self.eps);
(xyy * d1 + yyx * d2 + yxy * d3 + xxx * d4).normalized()
}
}
#[cfg(feature = "ultraviolet")]
impl<V: Vec<f32x4>> Default for TetrahedralEstimator<f32x4, V> {
fn default() -> Self {
Self::new(f32x4::from(0.001))
}
}
impl<V: Vec<f32>> Default for TetrahedralEstimator<f32, V> {
fn default() -> Self {
Self::new(0.001)
}
}
impl<V: Vec<f64>> Default for TetrahedralEstimator<f64, V> {
fn default() -> Self {
Self::new(0.001)
}
}