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
//! ALICE-Cache bridge: SDF evaluation result caching
//!
//! Caches SDF distance field evaluations using ALICE-Cache to avoid
//! redundant computation during interactive editing and mesh generation.
use alice_cache::AliceCache;
use std::hash::{Hash, Hasher};
/// Quantised 3D point for cache keys.
///
/// Coordinates are quantised to a configurable grid resolution
/// so that nearby lookups share cache entries.
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct GridPoint {
/// X grid coordinate.
pub x: i32,
/// Y grid coordinate.
pub y: i32,
/// Z grid coordinate.
pub z: i32,
}
impl Hash for GridPoint {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_i32(self.x);
state.write_i32(self.y);
state.write_i32(self.z);
}
}
impl GridPoint {
/// Quantise a floating-point position to the grid.
#[inline]
pub fn from_f32(x: f32, y: f32, z: f32, inv_cell: f32) -> Self {
Self {
x: (x * inv_cell).round() as i32,
y: (y * inv_cell).round() as i32,
z: (z * inv_cell).round() as i32,
}
}
}
/// Cached SDF evaluator.
///
/// Wraps an `AliceCache` keyed by quantised grid positions, returning
/// previously computed distances when available.
pub struct SdfEvalCache {
cache: AliceCache<GridPoint, f32>,
inv_cell: f32,
}
impl SdfEvalCache {
/// Create a new SDF evaluation cache.
///
/// * `capacity` — maximum number of cached distance values
/// * `cell_size` — quantisation grid cell size (smaller = more precise but more entries)
pub fn new(capacity: usize, cell_size: f32) -> Self {
Self {
cache: AliceCache::new(capacity),
inv_cell: 1.0 / cell_size,
}
}
/// Look up a cached distance value.
#[inline]
pub fn get(&self, x: f32, y: f32, z: f32) -> Option<f32> {
let key = GridPoint::from_f32(x, y, z, self.inv_cell);
self.cache.get(&key)
}
/// Store a computed distance value.
#[inline]
pub fn put(&self, x: f32, y: f32, z: f32, distance: f32) {
let key = GridPoint::from_f32(x, y, z, self.inv_cell);
self.cache.put(key, distance);
}
/// Cache hit rate.
pub fn hit_rate(&self) -> f64 {
self.cache.hit_rate()
}
/// Number of cached entries.
pub fn len(&self) -> usize {
self.cache.len()
}
/// Whether the cache is empty.
pub fn is_empty(&self) -> bool {
self.cache.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cache_roundtrip() {
let cache = SdfEvalCache::new(1024, 0.01);
cache.put(1.0, 2.0, 3.0, 0.42);
assert_eq!(cache.get(1.0, 2.0, 3.0), Some(0.42));
}
#[test]
fn test_cache_miss() {
let cache = SdfEvalCache::new(1024, 0.01);
assert_eq!(cache.get(5.0, 6.0, 7.0), None);
}
#[test]
fn test_quantisation_groups_nearby() {
let cache = SdfEvalCache::new(1024, 0.1);
cache.put(1.0, 2.0, 3.0, 0.5);
// Slightly different point within same cell
assert_eq!(cache.get(1.04, 2.03, 3.02), Some(0.5));
}
}