Skip to main content

oxiphysics_geometry/csg/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use super::functions::ImplicitSurface;
6#[allow(unused_imports)]
7use super::functions::*;
8#[allow(unused_imports)]
9use super::functions_2::*;
10
11/// An infinite plane: `dot(p, normal) - d = 0`.
12pub struct SdfPlane {
13    /// Unit outward normal.
14    pub normal: [f64; 3],
15    /// Plane offset: points with `dot(p, normal) > d` are outside.
16    pub d: f64,
17}
18impl SdfPlane {
19    /// Create a new plane.
20    pub fn new(normal: [f64; 3], d: f64) -> Self {
21        Self {
22            normal: normalize(normal),
23            d,
24        }
25    }
26}
27/// Smooth CSG difference.
28///
29/// SDF = smooth_max(sdf_a, -sdf_b, k).
30pub struct CsgSmoothDifference {
31    /// The base shape.
32    pub a: Box<dyn ImplicitSurface>,
33    /// The shape to subtract.
34    pub b: Box<dyn ImplicitSurface>,
35    /// Smoothing factor.
36    pub k: f64,
37}
38impl CsgSmoothDifference {
39    /// Create a new smooth difference.
40    pub fn new(a: Box<dyn ImplicitSurface>, b: Box<dyn ImplicitSurface>, k: f64) -> Self {
41        Self { a, b, k }
42    }
43}
44/// A cone (infinite half-cone) opening upward from the origin.
45///
46/// Defined by its half-angle in radians. SDF is exact on the surface.
47pub struct SdfCone {
48    /// Apex position.
49    pub apex: [f64; 3],
50    /// Half-angle in radians.
51    pub half_angle: f64,
52    /// Height of the finite cone.
53    pub height: f64,
54}
55impl SdfCone {
56    /// Create a new cone.
57    pub fn new(apex: [f64; 3], half_angle: f64, height: f64) -> Self {
58        Self {
59            apex,
60            half_angle,
61            height,
62        }
63    }
64}
65/// A sphere defined by centre and radius.
66pub struct SdfSphere {
67    /// World-space centre.
68    pub center: [f64; 3],
69    /// Sphere radius (must be positive).
70    pub radius: f64,
71}
72impl SdfSphere {
73    /// Create a new sphere.
74    pub fn new(center: [f64; 3], radius: f64) -> Self {
75        Self { center, radius }
76    }
77}
78/// CSG intersection: the region inside both `a` and `b`.
79///
80/// SDF = max(sdf_a, sdf_b).
81pub struct CsgIntersection {
82    /// First operand.
83    pub a: Box<dyn ImplicitSurface>,
84    /// Second operand.
85    pub b: Box<dyn ImplicitSurface>,
86}
87impl CsgIntersection {
88    /// Create a new intersection.
89    pub fn new(a: Box<dyn ImplicitSurface>, b: Box<dyn ImplicitSurface>) -> Self {
90        Self { a, b }
91    }
92}
93/// A node in a CSG tree.
94///
95/// Each node is either a leaf (wrapping an `ImplicitSurface`) or an interior
96/// node combining two subtrees with a `CsgOp`.
97pub enum CsgTree {
98    /// A leaf primitive.
99    Leaf(Box<dyn ImplicitSurface>),
100    /// An interior combination node.
101    Node {
102        /// The boolean operation to apply.
103        op: CsgOp,
104        /// Left operand.
105        left: Box<CsgTree>,
106        /// Right operand.
107        right: Box<CsgTree>,
108    },
109}
110impl CsgTree {
111    /// Construct a leaf node from any `ImplicitSurface`.
112    pub fn leaf(s: impl ImplicitSurface + 'static) -> Self {
113        CsgTree::Leaf(Box::new(s))
114    }
115    /// Construct a union node.
116    pub fn union(left: CsgTree, right: CsgTree) -> Self {
117        CsgTree::Node {
118            op: CsgOp::Union,
119            left: Box::new(left),
120            right: Box::new(right),
121        }
122    }
123    /// Construct an intersection node.
124    pub fn intersection(left: CsgTree, right: CsgTree) -> Self {
125        CsgTree::Node {
126            op: CsgOp::Intersection,
127            left: Box::new(left),
128            right: Box::new(right),
129        }
130    }
131    /// Construct a difference node (left minus right).
132    pub fn difference(left: CsgTree, right: CsgTree) -> Self {
133        CsgTree::Node {
134            op: CsgOp::Difference,
135            left: Box::new(left),
136            right: Box::new(right),
137        }
138    }
139}
140/// CSG union: the region inside either `a` or `b`.
141///
142/// SDF = min(sdf_a, sdf_b).
143pub struct CsgUnion {
144    /// First operand.
145    pub a: Box<dyn ImplicitSurface>,
146    /// Second operand.
147    pub b: Box<dyn ImplicitSurface>,
148}
149impl CsgUnion {
150    /// Create a new union.
151    pub fn new(a: Box<dyn ImplicitSurface>, b: Box<dyn ImplicitSurface>) -> Self {
152        Self { a, b }
153    }
154}
155/// A torus centred at the origin lying in the XZ plane.
156pub struct SdfTorus {
157    /// Major radius (distance from centre to tube centre).
158    pub major_radius: f64,
159    /// Minor radius (tube radius).
160    pub minor_radius: f64,
161}
162impl SdfTorus {
163    /// Create a new torus.
164    pub fn new(major_radius: f64, minor_radius: f64) -> Self {
165        Self {
166            major_radius,
167            minor_radius,
168        }
169    }
170}
171/// CSG boolean operation type.
172#[derive(Debug, Clone, Copy, PartialEq, Eq)]
173pub enum CsgOp {
174    /// Union: region inside either child.
175    Union,
176    /// Intersection: region inside both children.
177    Intersection,
178    /// Difference: region inside the left child but outside the right child.
179    Difference,
180}
181/// A capsule aligned with the Y axis.
182pub struct SdfCapsule {
183    /// Center of the capsule.
184    pub center: [f64; 3],
185    /// Capsule radius.
186    pub radius: f64,
187    /// Half-height of the cylindrical section.
188    pub half_height: f64,
189}
190impl SdfCapsule {
191    /// Create a new capsule.
192    pub fn new(center: [f64; 3], radius: f64, half_height: f64) -> Self {
193        Self {
194            center,
195            radius,
196            half_height,
197        }
198    }
199}
200/// Offset a `CsgTree` surface inward (negative `offset`) or outward (positive).
201///
202/// Returns a wrapper `SDF f(p) = original_sdf(p) - offset` so that the
203/// zero-level set is shifted by `offset` in the normal direction.
204///
205/// The returned object implements `ImplicitSurface`.
206pub struct CsgOffsetSurface {
207    /// The original SDF tree (as an evaluated function table over a grid is
208    /// impractical, so we store the sampler function as a closure via callback).
209    pub(super) inner_sdf: f64,
210    /// Offset value (positive = expand outward, negative = shrink inward).
211    pub offset: f64,
212    /// Bounding box for the original shape (used for gradient estimation).
213    pub bbox_min: [f64; 3],
214    /// Maximum bounding extent for gradient computation.
215    pub bbox_max: [f64; 3],
216}
217impl CsgOffsetSurface {
218    /// Create an offset wrapper (placeholder; use `csg_offset_sdf` for actual evaluation).
219    pub fn new(offset: f64, bbox_min: [f64; 3], bbox_max: [f64; 3]) -> Self {
220        Self {
221            inner_sdf: 0.0,
222            offset,
223            bbox_min,
224            bbox_max,
225        }
226    }
227}
228/// A cell in a marching-cubes grid.
229#[derive(Debug, Clone)]
230pub struct MarchingCell {
231    /// Min corner of the cell.
232    pub min: [f64; 3],
233    /// Cell side length.
234    pub step: f64,
235    /// SDF values at the 8 corners (index = ix*4 + iy*2 + iz).
236    pub corner_values: [f64; 8],
237}
238impl MarchingCell {
239    /// Return `true` if any corner is inside (negative SDF) and any is outside.
240    pub fn has_surface(&self) -> bool {
241        let has_inside = self.corner_values.iter().any(|&v| v < 0.0);
242        let has_outside = self.corner_values.iter().any(|&v| v >= 0.0);
243        has_inside && has_outside
244    }
245    /// Linear interpolation of position along edge between corner `i0` and `i1`.
246    fn edge_vertex(&self, i0: usize, i1: usize) -> [f64; 3] {
247        let offsets: [[f64; 3]; 8] = [
248            [0.0, 0.0, 0.0],
249            [self.step, 0.0, 0.0],
250            [self.step, self.step, 0.0],
251            [0.0, self.step, 0.0],
252            [0.0, 0.0, self.step],
253            [self.step, 0.0, self.step],
254            [self.step, self.step, self.step],
255            [0.0, self.step, self.step],
256        ];
257        let v0 = self.corner_values[i0];
258        let v1 = self.corner_values[i1];
259        let t = if (v1 - v0).abs() > 1e-14 {
260            v0 / (v0 - v1)
261        } else {
262            0.5
263        };
264        let p0 = add(self.min, offsets[i0]);
265        let p1 = add(self.min, offsets[i1]);
266        lerp3(p0, p1, t)
267    }
268    /// Extract surface vertices from this cell (one per crossing edge).
269    pub fn extract_vertices(&self) -> Vec<[f64; 3]> {
270        if !self.has_surface() {
271            return vec![];
272        }
273        let edges: [(usize, usize); 12] = [
274            (0, 1),
275            (1, 2),
276            (2, 3),
277            (3, 0),
278            (4, 5),
279            (5, 6),
280            (6, 7),
281            (7, 4),
282            (0, 4),
283            (1, 5),
284            (2, 6),
285            (3, 7),
286        ];
287        edges
288            .iter()
289            .filter_map(|&(i0, i1)| {
290                let v0 = self.corner_values[i0];
291                let v1 = self.corner_values[i1];
292                if v0.signum() != v1.signum() {
293                    Some(self.edge_vertex(i0, i1))
294                } else {
295                    None
296                }
297            })
298            .collect()
299    }
300}
301/// An axis-aligned box defined by centre and half-extents.
302pub struct SdfBox {
303    /// World-space centre.
304    pub center: [f64; 3],
305    /// Half-extents along each axis.
306    pub half_extents: [f64; 3],
307}
308impl SdfBox {
309    /// Create a new box.
310    pub fn new(center: [f64; 3], half_extents: [f64; 3]) -> Self {
311        Self {
312            center,
313            half_extents,
314        }
315    }
316}
317/// An infinite cylinder along the Y axis.
318pub struct SdfCylinder {
319    /// World-space centre (on the axis).
320    pub center: [f64; 3],
321    /// Cylinder radius.
322    pub radius: f64,
323}
324impl SdfCylinder {
325    /// Create a new cylinder.
326    pub fn new(center: [f64; 3], radius: f64) -> Self {
327        Self { center, radius }
328    }
329}
330/// CSG difference: region inside `a` but outside `b`.
331///
332/// SDF = max(sdf_a, -sdf_b).
333pub struct CsgDifference {
334    /// The base shape.
335    pub a: Box<dyn ImplicitSurface>,
336    /// The shape to subtract.
337    pub b: Box<dyn ImplicitSurface>,
338}
339impl CsgDifference {
340    /// Create a new difference.
341    pub fn new(a: Box<dyn ImplicitSurface>, b: Box<dyn ImplicitSurface>) -> Self {
342        Self { a, b }
343    }
344}
345/// Smooth CSG union using the polynomial smooth-min kernel.
346///
347/// SDF = smooth_min(sdf_a, sdf_b, k).
348pub struct CsgSmoothUnion {
349    /// First operand.
350    pub a: Box<dyn ImplicitSurface>,
351    /// Second operand.
352    pub b: Box<dyn ImplicitSurface>,
353    /// Smoothing factor (larger -> smoother blend).
354    pub k: f64,
355}
356impl CsgSmoothUnion {
357    /// Create a new smooth union.
358    pub fn new(a: Box<dyn ImplicitSurface>, b: Box<dyn ImplicitSurface>, k: f64) -> Self {
359        Self { a, b, k }
360    }
361}
362/// Smooth CSG intersection.
363///
364/// SDF = smooth_max(sdf_a, sdf_b, k).
365pub struct CsgSmoothIntersection {
366    /// First operand.
367    pub a: Box<dyn ImplicitSurface>,
368    /// Second operand.
369    pub b: Box<dyn ImplicitSurface>,
370    /// Smoothing factor.
371    pub k: f64,
372}
373impl CsgSmoothIntersection {
374    /// Create a new smooth intersection.
375    pub fn new(a: Box<dyn ImplicitSurface>, b: Box<dyn ImplicitSurface>, k: f64) -> Self {
376        Self { a, b, k }
377    }
378}
379/// Classification of a point relative to a plane.
380#[derive(Debug, Clone, Copy, PartialEq)]
381pub enum PlaneSide {
382    /// Point is on the front (positive) side.
383    Front,
384    /// Point is on the back (negative) side.
385    Back,
386    /// Point is on the plane (within tolerance).
387    OnPlane,
388}
389/// A finite capped cylinder aligned with the Y axis.
390pub struct SdfCappedCylinder {
391    /// World-space center.
392    pub center: [f64; 3],
393    /// Cylinder radius.
394    pub radius: f64,
395    /// Half-height (distance from centre to cap).
396    pub half_height: f64,
397}
398impl SdfCappedCylinder {
399    /// Create a new capped cylinder.
400    pub fn new(center: [f64; 3], radius: f64, half_height: f64) -> Self {
401        Self {
402            center,
403            radius,
404            half_height,
405        }
406    }
407}
408/// Rounded box: axis-aligned box with rounded edges.
409///
410/// SDF = SdfBox.sdf(p) - rounding_radius
411pub struct SdfRoundedBox {
412    /// World-space centre.
413    pub center: [f64; 3],
414    /// Half-extents (before rounding).
415    pub half_extents: [f64; 3],
416    /// Corner rounding radius.
417    pub radius: f64,
418}
419impl SdfRoundedBox {
420    /// Create a new rounded box.
421    pub fn new(center: [f64; 3], half_extents: [f64; 3], radius: f64) -> Self {
422        Self {
423            center,
424            half_extents,
425            radius,
426        }
427    }
428}