1use crate::Grid;
6
7pub fn create_sphere_mask(
21 grid: &Grid,
22 center_x: f64, center_y: f64, center_z: f64,
23 radius: f64,
24) -> Vec<u8> {
25 let (nx, ny, nz) = grid.dims;
26 let mut mask = vec![0u8; nx * ny * nz];
27 let r2 = radius * radius;
28
29 for k in 0..nz {
30 for j in 0..ny {
31 for i in 0..nx {
32 let dx = i as f64 - center_x;
33 let dy = j as f64 - center_y;
34 let dz = k as f64 - center_z;
35 if dx * dx + dy * dy + dz * dz <= r2 {
36 mask[i + j * nx + k * nx * ny] = 1;
37 }
38 }
39 }
40 }
41
42 mask
43}
44
45#[inline]
47pub fn apply_mask_zero(data: &mut [f64], mask: &[u8]) {
48 for i in 0..data.len() {
49 if mask[i] == 0 {
50 data[i] = 0.0;
51 }
52 }
53}
54
55pub fn erode_mask(mask: &[u8], grid: &Grid, iterations: usize) -> Vec<u8> {
60 let (nx, ny, nz) = grid.dims;
61 let mut current = mask.to_vec();
62 for _ in 0..iterations {
63 let mut eroded = current.clone();
64 for z in 0..nz {
65 for y in 0..ny {
66 for x in 0..nx {
67 let idx = x + y * nx + z * nx * ny;
68 if current[idx] == 0 {
69 continue;
70 }
71 if x == 0
72 || x == nx - 1
73 || y == 0
74 || y == ny - 1
75 || z == 0
76 || z == nz - 1
77 || current[idx - 1] == 0
78 || current[idx + 1] == 0
79 || current[idx - nx] == 0
80 || current[idx + nx] == 0
81 || current[idx - nx * ny] == 0
82 || current[idx + nx * ny] == 0
83 {
84 eroded[idx] = 0;
85 }
86 }
87 }
88 }
89 current = eroded;
90 }
91 current
92}
93
94pub fn dilate_mask(mask: &[u8], grid: &Grid, iterations: usize) -> Vec<u8> {
98 let (nx, ny, nz) = grid.dims;
99 let mut current = mask.to_vec();
100 for _ in 0..iterations {
101 let mut dilated = current.clone();
102 for z in 0..nz {
103 for y in 0..ny {
104 for x in 0..nx {
105 let idx = x + y * nx + z * nx * ny;
106 if current[idx] == 1 {
107 continue;
108 }
109 let has_neighbor = (x > 0 && current[idx - 1] == 1)
110 || (x < nx - 1 && current[idx + 1] == 1)
111 || (y > 0 && current[idx - nx] == 1)
112 || (y < ny - 1 && current[idx + nx] == 1)
113 || (z > 0 && current[idx - nx * ny] == 1)
114 || (z < nz - 1 && current[idx + nx * ny] == 1);
115 if has_neighbor {
116 dilated[idx] = 1;
117 }
118 }
119 }
120 }
121 current = dilated;
122 }
123 current
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 fn grid(nx: usize, ny: usize, nz: usize) -> Grid {
131 Grid::new(nx, ny, nz, 1.0, 1.0, 1.0)
132 }
133
134 #[test]
135 fn test_sphere_mask_basic() {
136 let mask = create_sphere_mask(&grid(10, 10, 10), 5.0, 5.0, 5.0, 3.0);
137 assert_eq!(mask.len(), 1000);
138
139 assert_eq!(mask[5 + 5 * 10 + 5 * 100], 1);
141
142 assert_eq!(mask[0], 0);
144
145 let count: usize = mask.iter().map(|&m| m as usize).sum();
147 assert!(count > 50 && count < 200, "Sphere voxel count {} seems wrong", count);
148 }
149
150 #[test]
151 fn test_sphere_mask_non_cubic() {
152 let mask = create_sphere_mask(&grid(20, 10, 5), 10.0, 5.0, 2.5, 2.0);
153 assert_eq!(mask.len(), 1000);
154
155 assert_eq!(mask[10 + 5 * 20 + 2 * 20 * 10], 1);
157 }
158
159 #[test]
160 fn test_sphere_mask_zero_radius() {
161 let mask = create_sphere_mask(&grid(5, 5, 5), 2.0, 2.0, 2.0, 0.0);
162 let count: usize = mask.iter().map(|&m| m as usize).sum();
164 assert_eq!(count, 1);
165 }
166
167 #[test]
168 fn test_apply_mask_zero() {
169 let mask = vec![1, 0, 1, 0, 1];
170 let mut data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
171 apply_mask_zero(&mut data, &mask);
172 assert_eq!(data, vec![1.0, 0.0, 3.0, 0.0, 5.0]);
173 }
174
175 #[test]
176 fn test_erode_mask_cube() {
177 let mask = vec![1u8; 27];
179 let result = erode_mask(&mask, &grid(3, 3, 3), 1);
180 assert_eq!(result.iter().filter(|&&v| v == 1).count(), 1); }
182
183 #[test]
184 fn test_dilate_mask_single_voxel() {
185 let mut mask = vec![0u8; 125];
187 mask[2 + 2 * 5 + 2 * 25] = 1; let result = dilate_mask(&mask, &grid(5, 5, 5), 1);
189 assert_eq!(result.iter().filter(|&&v| v == 1).count(), 7); }
191}