1#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
2pub enum FieldKind {
3 #[default]
4 ExactDistance,
5 ConservativeBound,
6 Implicit,
7}
8
9impl FieldKind {
10 fn rank(self) -> u8 {
11 match self {
12 FieldKind::ExactDistance => 0,
13 FieldKind::ConservativeBound => 1,
14 FieldKind::Implicit => 2,
15 }
16 }
17
18 pub fn weaker(self, other: Self) -> Self {
19 if other.rank() > self.rank() {
20 other
21 } else {
22 self
23 }
24 }
25}
26
27pub const GRADIENT_STEP: f32 = 1.0e-3;
28
29pub const MIN_GRADIENT_NORM: f32 = 1.0e-4;
31
32pub trait DistanceField: Send + Sync {
34 fn field_kind(&self) -> FieldKind;
35
36 fn dimension(&self) -> u32;
37
38 fn distance(&self, point: [f32; 4]) -> f32;
39
40 fn error_at(&self, point: [f32; 4]) -> f32;
41
42 fn gradient(&self, point: [f32; 4]) -> [f32; 4] {
43 let mut gradient = [0.0; 4];
44 for axis in 0..self.dimension().min(gradient.len() as u32) as usize {
45 let mut ahead = point;
46 let mut behind = point;
47 ahead[axis] += GRADIENT_STEP;
48 behind[axis] -= GRADIENT_STEP;
49 gradient[axis] = (self.distance(ahead) - self.distance(behind)) / (2.0 * GRADIENT_STEP);
50 }
51 gradient
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 struct Ramp;
60
61 impl DistanceField for Ramp {
62 fn field_kind(&self) -> FieldKind {
63 FieldKind::Implicit
64 }
65
66 fn dimension(&self) -> u32 {
67 4
68 }
69
70 fn distance(&self, p: [f32; 4]) -> f32 {
71 p[0] + 2.0 * p[1] + 3.0 * p[2] + 4.0 * p[3] - 5.0
72 }
73
74 fn error_at(&self, _point: [f32; 4]) -> f32 {
75 0.0
76 }
77 }
78
79 #[test]
80 fn the_default_gradient_divides_by_the_full_two_sided_step() {
81 let gradient = Ramp.gradient([1.0, -2.0, 0.5, 3.0]);
82 for (axis, expected) in [1.0f32, 2.0, 3.0, 4.0].into_iter().enumerate() {
83 assert!(
84 (gradient[axis] - expected).abs() < 1.0e-3,
85 "axis {axis}: {} against slope {expected}",
86 gradient[axis]
87 );
88 }
89 }
90}