1use rayon::prelude::*;
5
6#[derive(Debug, Clone)]
8pub enum SdfCombine {
9 Union(SdfShape, SdfShape),
11 Intersection(SdfShape, SdfShape),
13 Subtraction(SdfShape, SdfShape),
15 SmoothUnion(SdfShape, SdfShape, f64),
17}
18impl SdfCombine {
19 pub fn signed_distance(&self, p: [f64; 3]) -> f64 {
21 match self {
22 SdfCombine::Union(a, b) => a.signed_distance(p).min(b.signed_distance(p)),
23 SdfCombine::Intersection(a, b) => a.signed_distance(p).max(b.signed_distance(p)),
24 SdfCombine::Subtraction(a, b) => a.signed_distance(p).max(-b.signed_distance(p)),
25 SdfCombine::SmoothUnion(a, b, k) => {
26 let da = a.signed_distance(p);
27 let db = b.signed_distance(p);
28 let h = (0.5 + 0.5 * (db - da) / k).clamp(0.0, 1.0);
29 db * (1.0 - h) + da * h - k * h * (1.0 - h)
30 }
31 }
32 }
33}
34#[derive(Debug, Clone)]
36pub enum SdfShape {
37 Sphere {
39 center: [f64; 3],
41 r: f64,
43 },
44 Box3 {
46 center: [f64; 3],
48 half: [f64; 3],
50 },
51 Capsule {
53 a: [f64; 3],
55 b: [f64; 3],
57 r: f64,
59 },
60}
61impl SdfShape {
62 pub fn signed_distance(&self, p: [f64; 3]) -> f64 {
66 match self {
67 SdfShape::Sphere { center, r } => {
68 let dx = p[0] - center[0];
69 let dy = p[1] - center[1];
70 let dz = p[2] - center[2];
71 (dx * dx + dy * dy + dz * dz).sqrt() - r
72 }
73 SdfShape::Box3 { center, half } => {
74 let qx = (p[0] - center[0]).abs() - half[0];
75 let qy = (p[1] - center[1]).abs() - half[1];
76 let qz = (p[2] - center[2]).abs() - half[2];
77 let ext = (qx.max(0.0).powi(2) + qy.max(0.0).powi(2) + qz.max(0.0).powi(2)).sqrt();
78 let interior = qx.max(qy).max(qz).min(0.0);
79 ext + interior
80 }
81 SdfShape::Capsule { a, b, r } => {
82 let ab = [b[0] - a[0], b[1] - a[1], b[2] - a[2]];
83 let ap = [p[0] - a[0], p[1] - a[1], p[2] - a[2]];
84 let ab_len2 = ab[0] * ab[0] + ab[1] * ab[1] + ab[2] * ab[2];
85 let t = if ab_len2 < 1e-12 {
86 0.0
87 } else {
88 ((ap[0] * ab[0] + ap[1] * ab[1] + ap[2] * ab[2]) / ab_len2).clamp(0.0, 1.0)
89 };
90 let closest = [a[0] + t * ab[0], a[1] + t * ab[1], a[2] + t * ab[2]];
91 let dx = p[0] - closest[0];
92 let dy = p[1] - closest[1];
93 let dz = p[2] - closest[2];
94 (dx * dx + dy * dy + dz * dz).sqrt() - r
95 }
96 }
97 }
98}
99#[derive(Debug, Clone)]
103pub struct GpuSdfGrid {
104 pub data: Vec<f64>,
106 pub nx: usize,
108 pub ny: usize,
110 pub nz: usize,
112 pub origin: [f64; 3],
114 pub cell_size: f64,
116}
117impl GpuSdfGrid {
118 pub fn new(nx: usize, ny: usize, nz: usize, origin: [f64; 3], cell_size: f64) -> Self {
120 Self {
121 data: vec![0.0_f64; nx * ny * nz],
122 nx,
123 ny,
124 nz,
125 origin,
126 cell_size,
127 }
128 }
129 #[inline]
131 pub fn index(&self, ix: usize, iy: usize, iz: usize) -> usize {
132 ix * self.ny * self.nz + iy * self.nz + iz
133 }
134 #[inline]
136 pub fn get(&self, ix: usize, iy: usize, iz: usize) -> f64 {
137 self.data[self.index(ix, iy, iz)]
138 }
139 #[inline]
141 pub fn cell_center(&self, ix: usize, iy: usize, iz: usize) -> [f64; 3] {
142 [
143 self.origin[0] + (ix as f64 + 0.5) * self.cell_size,
144 self.origin[1] + (iy as f64 + 0.5) * self.cell_size,
145 self.origin[2] + (iz as f64 + 0.5) * self.cell_size,
146 ]
147 }
148 pub fn sample_trilinear(&self, p: [f64; 3]) -> f64 {
152 let fx = (p[0] - self.origin[0]) / self.cell_size - 0.5;
153 let fy = (p[1] - self.origin[1]) / self.cell_size - 0.5;
154 let fz = (p[2] - self.origin[2]) / self.cell_size - 0.5;
155 let ix = fx.floor().clamp(0.0, (self.nx - 1) as f64) as usize;
156 let iy = fy.floor().clamp(0.0, (self.ny - 1) as f64) as usize;
157 let iz = fz.floor().clamp(0.0, (self.nz - 1) as f64) as usize;
158 let tx = (fx - ix as f64).clamp(0.0, 1.0);
159 let ty = (fy - iy as f64).clamp(0.0, 1.0);
160 let tz = (fz - iz as f64).clamp(0.0, 1.0);
161 let nx1 = (ix + 1).min(self.nx - 1);
162 let ny1 = (iy + 1).min(self.ny - 1);
163 let nz1 = (iz + 1).min(self.nz - 1);
164 let c000 = self.get(ix, iy, iz);
165 let c100 = self.get(nx1, iy, iz);
166 let c010 = self.get(ix, ny1, iz);
167 let c110 = self.get(nx1, ny1, iz);
168 let c001 = self.get(ix, iy, nz1);
169 let c101 = self.get(nx1, iy, nz1);
170 let c011 = self.get(ix, ny1, nz1);
171 let c111 = self.get(nx1, ny1, nz1);
172 let c00 = c000 * (1.0 - tx) + c100 * tx;
173 let c10 = c010 * (1.0 - tx) + c110 * tx;
174 let c01 = c001 * (1.0 - tx) + c101 * tx;
175 let c11 = c011 * (1.0 - tx) + c111 * tx;
176 let c0 = c00 * (1.0 - ty) + c10 * ty;
177 let c1 = c01 * (1.0 - ty) + c11 * ty;
178 c0 * (1.0 - tz) + c1 * tz
179 }
180 pub fn gradient_at(&self, p: [f64; 3]) -> [f64; 3] {
184 let h = self.cell_size * 0.5;
185 let gx = (self.sample_trilinear([p[0] + h, p[1], p[2]])
186 - self.sample_trilinear([p[0] - h, p[1], p[2]]))
187 / (2.0 * h);
188 let gy = (self.sample_trilinear([p[0], p[1] + h, p[2]])
189 - self.sample_trilinear([p[0], p[1] - h, p[2]]))
190 / (2.0 * h);
191 let gz = (self.sample_trilinear([p[0], p[1], p[2] + h])
192 - self.sample_trilinear([p[0], p[1], p[2] - h]))
193 / (2.0 * h);
194 [gx, gy, gz]
195 }
196}
197pub struct SphereTraceResult {
199 pub hit: bool,
201 pub position: [f64; 3],
203 pub t: f64,
205 pub iterations: usize,
207}
208pub struct SdfGrid {
210 pub nx: usize,
212 pub ny: usize,
214 pub nz: usize,
216 pub dx: f64,
218 pub origin: [f64; 3],
220 pub values: Vec<f64>,
222}
223impl SdfGrid {
224 pub fn new(nx: usize, ny: usize, nz: usize, dx: f64, origin: [f64; 3]) -> Self {
226 let n = nx * ny * nz;
227 Self {
228 nx,
229 ny,
230 nz,
231 dx,
232 origin,
233 values: vec![f64::MAX; n],
234 }
235 }
236 #[inline]
238 pub fn index(&self, i: usize, j: usize, k: usize) -> usize {
239 i * self.ny * self.nz + j * self.nz + k
240 }
241 #[inline]
243 pub fn world_pos(&self, i: usize, j: usize, k: usize) -> [f64; 3] {
244 [
245 self.origin[0] + (i as f64 + 0.5) * self.dx,
246 self.origin[1] + (j as f64 + 0.5) * self.dx,
247 self.origin[2] + (k as f64 + 0.5) * self.dx,
248 ]
249 }
250 #[inline]
252 pub fn get(&self, i: usize, j: usize, k: usize) -> f64 {
253 self.values[self.index(i, j, k)]
254 }
255 #[inline]
257 pub fn set(&mut self, i: usize, j: usize, k: usize, v: f64) {
258 let idx = self.index(i, j, k);
259 self.values[idx] = v;
260 }
261 pub fn compute_sphere_sdf(&mut self, center: [f64; 3], radius: f64) {
263 let _nx = self.nx;
264 let ny = self.ny;
265 let nz = self.nz;
266 let dx = self.dx;
267 let origin = self.origin;
268 self.values.par_iter_mut().enumerate().for_each(|(idx, v)| {
269 let i = idx / (ny * nz);
270 let j = (idx / nz) % ny;
271 let k = idx % nz;
272 let px = origin[0] + (i as f64 + 0.5) * dx;
273 let py = origin[1] + (j as f64 + 0.5) * dx;
274 let pz = origin[2] + (k as f64 + 0.5) * dx;
275 let dist =
276 ((px - center[0]).powi(2) + (py - center[1]).powi(2) + (pz - center[2]).powi(2))
277 .sqrt();
278 *v = dist - radius;
279 });
280 }
281 pub fn compute_box_sdf(&mut self, box_center: [f64; 3], half_extents: [f64; 3]) {
283 let _nx = self.nx;
284 let ny = self.ny;
285 let nz = self.nz;
286 let dx = self.dx;
287 let origin = self.origin;
288 self.values.par_iter_mut().enumerate().for_each(|(idx, v)| {
289 let i = idx / (ny * nz);
290 let j = (idx / nz) % ny;
291 let k = idx % nz;
292 let px = origin[0] + (i as f64 + 0.5) * dx - box_center[0];
293 let py = origin[1] + (j as f64 + 0.5) * dx - box_center[1];
294 let pz = origin[2] + (k as f64 + 0.5) * dx - box_center[2];
295 let qx = px.abs() - half_extents[0];
296 let qy = py.abs() - half_extents[1];
297 let qz = pz.abs() - half_extents[2];
298 let ext = (qx.max(0.0).powi(2) + qy.max(0.0).powi(2) + qz.max(0.0).powi(2)).sqrt();
299 let interior = qx.max(qy).max(qz).min(0.0);
300 *v = ext + interior;
301 });
302 }
303 pub fn compute_cylinder_sdf(&mut self, center: [f64; 2], radius: f64, half_height: f64) {
306 let ny = self.ny;
307 let nz = self.nz;
308 let dx = self.dx;
309 let origin = self.origin;
310 self.values.par_iter_mut().enumerate().for_each(|(idx, v)| {
311 let i = idx / (ny * nz);
312 let j = (idx / nz) % ny;
313 let k = idx % nz;
314 let px = origin[0] + (i as f64 + 0.5) * dx - center[0];
315 let py = origin[1] + (j as f64 + 0.5) * dx - center[1];
316 let pz = origin[2] + (k as f64 + 0.5) * dx;
317 let r = (px * px + py * py).sqrt();
318 let d_radial = r - radius;
319 let d_axial = pz.abs() - half_height;
320 let ext = (d_radial.max(0.0).powi(2) + d_axial.max(0.0).powi(2)).sqrt();
321 let interior = d_radial.max(d_axial).min(0.0);
322 *v = ext + interior;
323 });
324 }
325 pub fn compute_torus_sdf(&mut self, center: [f64; 3], major_radius: f64, minor_radius: f64) {
328 let ny = self.ny;
329 let nz = self.nz;
330 let dx = self.dx;
331 let origin = self.origin;
332 self.values.par_iter_mut().enumerate().for_each(|(idx, v)| {
333 let i = idx / (ny * nz);
334 let j = (idx / nz) % ny;
335 let k = idx % nz;
336 let px = origin[0] + (i as f64 + 0.5) * dx - center[0];
337 let py = origin[1] + (j as f64 + 0.5) * dx - center[1];
338 let pz = origin[2] + (k as f64 + 0.5) * dx - center[2];
339 let q_x = (px * px + pz * pz).sqrt() - major_radius;
340 let dist = (q_x * q_x + py * py).sqrt() - minor_radius;
341 *v = dist;
342 });
343 }
344 pub fn gradient_at(&self, i: usize, j: usize, k: usize) -> [f64; 3] {
346 let two_dx = 2.0 * self.dx;
347 let gx = if i == 0 {
348 (self.get(i + 1, j, k) - self.get(i, j, k)) / self.dx
349 } else if i + 1 == self.nx {
350 (self.get(i, j, k) - self.get(i - 1, j, k)) / self.dx
351 } else {
352 (self.get(i + 1, j, k) - self.get(i - 1, j, k)) / two_dx
353 };
354 let gy = if j == 0 {
355 (self.get(i, j + 1, k) - self.get(i, j, k)) / self.dx
356 } else if j + 1 == self.ny {
357 (self.get(i, j, k) - self.get(i, j - 1, k)) / self.dx
358 } else {
359 (self.get(i, j + 1, k) - self.get(i, j - 1, k)) / two_dx
360 };
361 let gz = if k == 0 {
362 (self.get(i, j, k + 1) - self.get(i, j, k)) / self.dx
363 } else if k + 1 == self.nz {
364 (self.get(i, j, k) - self.get(i, j, k - 1)) / self.dx
365 } else {
366 (self.get(i, j, k + 1) - self.get(i, j, k - 1)) / two_dx
367 };
368 [gx, gy, gz]
369 }
370 #[inline]
372 pub fn total_cells(&self) -> usize {
373 self.nx * self.ny * self.nz
374 }
375 pub fn sample(&self, pos: [f64; 3]) -> Option<f64> {
379 let fx = (pos[0] - self.origin[0]) / self.dx - 0.5;
380 let fy = (pos[1] - self.origin[1]) / self.dx - 0.5;
381 let fz = (pos[2] - self.origin[2]) / self.dx - 0.5;
382 if fx < 0.0 || fy < 0.0 || fz < 0.0 {
383 return None;
384 }
385 let ix = fx as usize;
386 let iy = fy as usize;
387 let iz = fz as usize;
388 if ix + 1 >= self.nx || iy + 1 >= self.ny || iz + 1 >= self.nz {
389 return None;
390 }
391 let tx = fx - ix as f64;
392 let ty = fy - iy as f64;
393 let tz = fz - iz as f64;
394 let c000 = self.get(ix, iy, iz);
395 let c100 = self.get(ix + 1, iy, iz);
396 let c010 = self.get(ix, iy + 1, iz);
397 let c110 = self.get(ix + 1, iy + 1, iz);
398 let c001 = self.get(ix, iy, iz + 1);
399 let c101 = self.get(ix + 1, iy, iz + 1);
400 let c011 = self.get(ix, iy + 1, iz + 1);
401 let c111 = self.get(ix + 1, iy + 1, iz + 1);
402 let c00 = c000 * (1.0 - tx) + c100 * tx;
403 let c10 = c010 * (1.0 - tx) + c110 * tx;
404 let c01 = c001 * (1.0 - tx) + c101 * tx;
405 let c11 = c011 * (1.0 - tx) + c111 * tx;
406 let c0 = c00 * (1.0 - ty) + c10 * ty;
407 let c1 = c01 * (1.0 - ty) + c11 * ty;
408 Some(c0 * (1.0 - tz) + c1 * tz)
409 }
410 pub fn gradient_at_point(&self, pos: [f64; 3]) -> Option<[f64; 3]> {
413 let fx = (pos[0] - self.origin[0]) / self.dx - 0.5;
414 let fy = (pos[1] - self.origin[1]) / self.dx - 0.5;
415 let fz = (pos[2] - self.origin[2]) / self.dx - 0.5;
416 if fx < 0.0 || fy < 0.0 || fz < 0.0 {
417 return None;
418 }
419 let ix = fx as usize;
420 let iy = fy as usize;
421 let iz = fz as usize;
422 if ix + 1 >= self.nx || iy + 1 >= self.ny || iz + 1 >= self.nz {
423 return None;
424 }
425 let eps = self.dx * 0.5;
426 let gx = (self.sample([pos[0] + eps, pos[1], pos[2]]).unwrap_or(0.0)
427 - self.sample([pos[0] - eps, pos[1], pos[2]]).unwrap_or(0.0))
428 / (2.0 * eps);
429 let gy = (self.sample([pos[0], pos[1] + eps, pos[2]]).unwrap_or(0.0)
430 - self.sample([pos[0], pos[1] - eps, pos[2]]).unwrap_or(0.0))
431 / (2.0 * eps);
432 let gz = (self.sample([pos[0], pos[1], pos[2] + eps]).unwrap_or(0.0)
433 - self.sample([pos[0], pos[1], pos[2] - eps]).unwrap_or(0.0))
434 / (2.0 * eps);
435 Some([gx, gy, gz])
436 }
437}
438#[derive(Debug, Clone)]
440pub struct Triangle {
441 pub v: [[f64; 3]; 3],
443}
444#[derive(Debug, Clone)]
446pub struct DistanceQuery {
447 pub distance: f64,
449 pub normal: [f64; 3],
451 pub closest_point: [f64; 3],
453 pub is_inside: bool,
455}