1use super::functions::ImplicitSurface;
6use super::functions::*;
7
8pub struct SdfPlane {
10 pub normal: [f64; 3],
12 pub d: f64,
14}
15impl SdfPlane {
16 pub fn new(normal: [f64; 3], d: f64) -> Self {
18 Self {
19 normal: normalize(normal),
20 d,
21 }
22 }
23}
24pub struct CsgSmoothDifference {
28 pub a: Box<dyn ImplicitSurface>,
30 pub b: Box<dyn ImplicitSurface>,
32 pub k: f64,
34}
35impl CsgSmoothDifference {
36 pub fn new(a: Box<dyn ImplicitSurface>, b: Box<dyn ImplicitSurface>, k: f64) -> Self {
38 Self { a, b, k }
39 }
40}
41pub struct SdfCone {
45 pub apex: [f64; 3],
47 pub half_angle: f64,
49 pub height: f64,
51}
52impl SdfCone {
53 pub fn new(apex: [f64; 3], half_angle: f64, height: f64) -> Self {
55 Self {
56 apex,
57 half_angle,
58 height,
59 }
60 }
61}
62pub struct SdfSphere {
64 pub center: [f64; 3],
66 pub radius: f64,
68}
69impl SdfSphere {
70 pub fn new(center: [f64; 3], radius: f64) -> Self {
72 Self { center, radius }
73 }
74}
75pub struct CsgIntersection {
79 pub a: Box<dyn ImplicitSurface>,
81 pub b: Box<dyn ImplicitSurface>,
83}
84impl CsgIntersection {
85 pub fn new(a: Box<dyn ImplicitSurface>, b: Box<dyn ImplicitSurface>) -> Self {
87 Self { a, b }
88 }
89}
90pub enum CsgTree {
95 Leaf(Box<dyn ImplicitSurface>),
97 Node {
99 op: CsgOp,
101 left: Box<CsgTree>,
103 right: Box<CsgTree>,
105 },
106}
107impl CsgTree {
108 pub fn leaf(s: impl ImplicitSurface + 'static) -> Self {
110 CsgTree::Leaf(Box::new(s))
111 }
112 pub fn union(left: CsgTree, right: CsgTree) -> Self {
114 CsgTree::Node {
115 op: CsgOp::Union,
116 left: Box::new(left),
117 right: Box::new(right),
118 }
119 }
120 pub fn intersection(left: CsgTree, right: CsgTree) -> Self {
122 CsgTree::Node {
123 op: CsgOp::Intersection,
124 left: Box::new(left),
125 right: Box::new(right),
126 }
127 }
128 pub fn difference(left: CsgTree, right: CsgTree) -> Self {
130 CsgTree::Node {
131 op: CsgOp::Difference,
132 left: Box::new(left),
133 right: Box::new(right),
134 }
135 }
136}
137pub struct CsgUnion {
141 pub a: Box<dyn ImplicitSurface>,
143 pub b: Box<dyn ImplicitSurface>,
145}
146impl CsgUnion {
147 pub fn new(a: Box<dyn ImplicitSurface>, b: Box<dyn ImplicitSurface>) -> Self {
149 Self { a, b }
150 }
151}
152pub struct SdfTorus {
154 pub major_radius: f64,
156 pub minor_radius: f64,
158}
159impl SdfTorus {
160 pub fn new(major_radius: f64, minor_radius: f64) -> Self {
162 Self {
163 major_radius,
164 minor_radius,
165 }
166 }
167}
168#[derive(Debug, Clone, Copy, PartialEq, Eq)]
170pub enum CsgOp {
171 Union,
173 Intersection,
175 Difference,
177}
178pub struct SdfCapsule {
180 pub center: [f64; 3],
182 pub radius: f64,
184 pub half_height: f64,
186}
187impl SdfCapsule {
188 pub fn new(center: [f64; 3], radius: f64, half_height: f64) -> Self {
190 Self {
191 center,
192 radius,
193 half_height,
194 }
195 }
196}
197pub struct CsgOffsetSurface {
204 inner_sdf: Box<dyn Fn([f64; 3]) -> f64 + Send + Sync>,
206 pub offset: f64,
208}
209impl CsgOffsetSurface {
210 pub fn new(sdf: Box<dyn Fn([f64; 3]) -> f64 + Send + Sync>, offset: f64) -> Self {
212 Self {
213 inner_sdf: sdf,
214 offset,
215 }
216 }
217 pub fn eval(&self, p: [f64; 3]) -> f64 {
219 (self.inner_sdf)(p) - self.offset
220 }
221 pub fn finite_diff_gradient(&self, p: [f64; 3]) -> [f64; 3] {
223 const EPS: f64 = 1e-4;
224 [
225 (self.eval([p[0] + EPS, p[1], p[2]]) - self.eval([p[0] - EPS, p[1], p[2]]))
226 / (2.0 * EPS),
227 (self.eval([p[0], p[1] + EPS, p[2]]) - self.eval([p[0], p[1] - EPS, p[2]]))
228 / (2.0 * EPS),
229 (self.eval([p[0], p[1], p[2] + EPS]) - self.eval([p[0], p[1], p[2] - EPS]))
230 / (2.0 * EPS),
231 ]
232 }
233}
234impl ImplicitSurface for CsgOffsetSurface {
235 fn sdf(&self, p: [f64; 3]) -> f64 {
236 self.eval(p)
237 }
238 fn gradient(&self, p: [f64; 3]) -> [f64; 3] {
239 self.finite_diff_gradient(p)
240 }
241}
242#[derive(Debug, Clone)]
244pub struct MarchingCell {
245 pub min: [f64; 3],
247 pub step: f64,
249 pub corner_values: [f64; 8],
251}
252impl MarchingCell {
253 pub fn has_surface(&self) -> bool {
255 let has_inside = self.corner_values.iter().any(|&v| v < 0.0);
256 let has_outside = self.corner_values.iter().any(|&v| v >= 0.0);
257 has_inside && has_outside
258 }
259 fn edge_vertex(&self, i0: usize, i1: usize) -> [f64; 3] {
261 let offsets: [[f64; 3]; 8] = [
262 [0.0, 0.0, 0.0],
263 [self.step, 0.0, 0.0],
264 [self.step, self.step, 0.0],
265 [0.0, self.step, 0.0],
266 [0.0, 0.0, self.step],
267 [self.step, 0.0, self.step],
268 [self.step, self.step, self.step],
269 [0.0, self.step, self.step],
270 ];
271 let v0 = self.corner_values[i0];
272 let v1 = self.corner_values[i1];
273 let t = if (v1 - v0).abs() > 1e-14 {
274 v0 / (v0 - v1)
275 } else {
276 0.5
277 };
278 let p0 = add(self.min, offsets[i0]);
279 let p1 = add(self.min, offsets[i1]);
280 lerp3(p0, p1, t)
281 }
282 pub fn extract_vertices(&self) -> Vec<[f64; 3]> {
284 if !self.has_surface() {
285 return vec![];
286 }
287 let edges: [(usize, usize); 12] = [
288 (0, 1),
289 (1, 2),
290 (2, 3),
291 (3, 0),
292 (4, 5),
293 (5, 6),
294 (6, 7),
295 (7, 4),
296 (0, 4),
297 (1, 5),
298 (2, 6),
299 (3, 7),
300 ];
301 edges
302 .iter()
303 .filter_map(|&(i0, i1)| {
304 let v0 = self.corner_values[i0];
305 let v1 = self.corner_values[i1];
306 if v0.signum() != v1.signum() {
307 Some(self.edge_vertex(i0, i1))
308 } else {
309 None
310 }
311 })
312 .collect()
313 }
314}
315pub struct SdfBox {
317 pub center: [f64; 3],
319 pub half_extents: [f64; 3],
321}
322impl SdfBox {
323 pub fn new(center: [f64; 3], half_extents: [f64; 3]) -> Self {
325 Self {
326 center,
327 half_extents,
328 }
329 }
330}
331pub struct SdfCylinder {
333 pub center: [f64; 3],
335 pub radius: f64,
337}
338impl SdfCylinder {
339 pub fn new(center: [f64; 3], radius: f64) -> Self {
341 Self { center, radius }
342 }
343}
344pub struct CsgDifference {
348 pub a: Box<dyn ImplicitSurface>,
350 pub b: Box<dyn ImplicitSurface>,
352}
353impl CsgDifference {
354 pub fn new(a: Box<dyn ImplicitSurface>, b: Box<dyn ImplicitSurface>) -> Self {
356 Self { a, b }
357 }
358}
359pub struct CsgSmoothUnion {
363 pub a: Box<dyn ImplicitSurface>,
365 pub b: Box<dyn ImplicitSurface>,
367 pub k: f64,
369}
370impl CsgSmoothUnion {
371 pub fn new(a: Box<dyn ImplicitSurface>, b: Box<dyn ImplicitSurface>, k: f64) -> Self {
373 Self { a, b, k }
374 }
375}
376pub struct CsgSmoothIntersection {
380 pub a: Box<dyn ImplicitSurface>,
382 pub b: Box<dyn ImplicitSurface>,
384 pub k: f64,
386}
387impl CsgSmoothIntersection {
388 pub fn new(a: Box<dyn ImplicitSurface>, b: Box<dyn ImplicitSurface>, k: f64) -> Self {
390 Self { a, b, k }
391 }
392}
393#[derive(Debug, Clone, Copy, PartialEq)]
395pub enum PlaneSide {
396 Front,
398 Back,
400 OnPlane,
402}
403pub struct SdfCappedCylinder {
405 pub center: [f64; 3],
407 pub radius: f64,
409 pub half_height: f64,
411}
412impl SdfCappedCylinder {
413 pub fn new(center: [f64; 3], radius: f64, half_height: f64) -> Self {
415 Self {
416 center,
417 radius,
418 half_height,
419 }
420 }
421}
422pub struct SdfRoundedBox {
426 pub center: [f64; 3],
428 pub half_extents: [f64; 3],
430 pub radius: f64,
432}
433impl SdfRoundedBox {
434 pub fn new(center: [f64; 3], half_extents: [f64; 3], radius: f64) -> Self {
436 Self {
437 center,
438 half_extents,
439 radius,
440 }
441 }
442}
443
444#[cfg(test)]
445mod csg_offset_tests {
446 use super::super::functions::ImplicitSurface;
447 use super::CsgOffsetSurface;
448
449 fn unit_sphere_sdf(p: [f64; 3]) -> f64 {
450 (p[0] * p[0] + p[1] * p[1] + p[2] * p[2]).sqrt() - 1.0
451 }
452
453 #[test]
454 fn test_offset_sphere_eval_inside() {
455 let s = CsgOffsetSurface::new(Box::new(unit_sphere_sdf), 1.0);
456 assert!(s.eval([1.5, 0.0, 0.0]) < 0.0);
457 }
458
459 #[test]
460 fn test_offset_sphere_eval_outside() {
461 let s = CsgOffsetSurface::new(Box::new(unit_sphere_sdf), 1.0);
462 assert!(s.eval([3.5, 0.0, 0.0]) > 0.0);
463 }
464
465 #[test]
466 fn test_offset_sphere_on_surface() {
467 let s = CsgOffsetSurface::new(Box::new(unit_sphere_sdf), 1.0);
468 assert!(s.eval([2.0, 0.0, 0.0]).abs() < 1e-10);
469 }
470
471 #[test]
472 fn test_offset_sphere_gradient_outward() {
473 let s = CsgOffsetSurface::new(Box::new(unit_sphere_sdf), 1.0);
474 let g = s.finite_diff_gradient([2.0, 0.0, 0.0]);
475 assert!(g[0] > 0.5 && g[1].abs() < 0.01 && g[2].abs() < 0.01);
476 }
477
478 #[test]
479 fn test_implicit_surface_trait() {
480 let s = CsgOffsetSurface::new(Box::new(unit_sphere_sdf), 1.0);
481 let t: &dyn ImplicitSurface = &s;
482 assert!(t.sdf([2.5, 0.0, 0.0]) > 0.0);
483 assert!(t.sdf([1.5, 0.0, 0.0]) < 0.0);
484 }
485}