1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use core::{marker::PhantomData, ops::RangeInclusive};

use rust_gpu_bridge::prelude::{Vec2, Vec3};
use type_fields::field::Field;

use crate::{
    default,
    signed_distance_field::{
        adapters::normals::CentralDiffNormal, attributes::distance::Distance, SignedDistanceField,
    },
};

/// Asserts that the provided distance function is a field rather than a bound
#[derive(Debug, Clone, PartialEq)]
pub struct BoundChecker<Dim, Sdf> {
    pub sdf: Sdf,
    pub samples: RangeInclusive<isize>,
    pub step: f32,
    pub epsilon: f32,
    pub _phantom: PhantomData<Dim>,
}

impl<Dim, Sdf> Default for BoundChecker<Dim, Sdf>
where
    Sdf: Default,
{
    fn default() -> Self {
        BoundChecker {
            sdf: default(),
            samples: -10..=10,
            step: 2.0 / 20.0,
            epsilon: 0.5,
            _phantom: default(),
        }
    }
}

impl<Dim, Sdf> BoundChecker<Dim, Sdf> {
    const DERIV_EPSILON: f32 = 0.000005;
}

impl<Sdf> BoundChecker<Vec2, Sdf>
where
    Sdf: SignedDistanceField<Vec2, Distance> + Clone + 'static,
{
    pub fn is_field(self) -> bool {
        !self.is_bound()
    }

    pub fn is_bound(self) -> bool {
        // Iterate over a regular grid
        for x in self.samples.clone() {
            for y in self.samples.clone() {
                // Create sample coordinate
                let pos = Vec2::new(x as f32, y as f32) * self.step;

                // Calculate normal
                let normal = *CentralDiffNormal::<Sdf>::new(self.sdf.clone(), self.step)
                    .with(CentralDiffNormal::epsilon, self.epsilon)
                    .evaluate(pos);

                // Apply 1D central differencing along normal,
                // resulting in distance-space derivative
                let a = *self.sdf.evaluate(pos - normal * self.epsilon);
                let b = *self.sdf.evaluate(pos + normal * self.epsilon);
                let deriv = b - a;

                // Assert that derivative is 1 (w.r.t. floating-point error)
                if deriv.abs() - (self.epsilon * 2.0) > Self::DERIV_EPSILON {
                    panic!("{deriv:?} at position {pos:?} is non-unit, resulting in a bound.");
                    return true;
                }
            }
        }

        false
    }
}

impl<Sdf> BoundChecker<Vec3, Sdf>
where
    Sdf: SignedDistanceField<Vec3, Distance> + Clone + 'static,
{
    pub fn is_field(self) -> bool {
        !self.is_bound()
    }

    pub fn is_bound(self) -> bool {
        // Iterate over a regular grid
        for x in self.samples.clone() {
            for y in self.samples.clone() {
                for z in self.samples.clone() {
                    // Create sample coordinate
                    let pos = Vec3::new(x as f32, y as f32, z as f32) * self.step;

                    // Calculate normal
                    let normal = *CentralDiffNormal::<Sdf>::new(self.sdf.clone(), self.step)
                        .with(CentralDiffNormal::epsilon, self.epsilon)
                        .evaluate(pos);

                    // Apply 1D central differencing along normal,
                    // resulting in distance-space derivative
                    let a = *self.sdf.evaluate(pos - normal * self.epsilon);
                    let b = *self.sdf.evaluate(pos + normal * self.epsilon);
                    let deriv = b - a;

                    // Assert that derivative is 1 (w.r.t. floating-point error)
                    if deriv.abs() - (self.epsilon * 2.0) > Self::DERIV_EPSILON {
                        panic!("{deriv:?} at position {pos:?} is non-unit, resulting in a bound.");
                        return true;
                    }
                }
            }
        }

        false
    }
}