Skip to main content

alice_sdf/types/
modifiers.rs

1//! Shape modifier methods for SdfNode
2//!
3//! Author: Moroya Sakamoto
4
5use glam::{Vec2, Vec3};
6use std::sync::Arc;
7
8use super::SdfNode;
9
10impl SdfNode {
11    // === Modifier methods ===
12
13    /// Twist around Y-axis
14    #[must_use]
15    #[inline]
16    pub fn twist(self, strength: f32) -> Self {
17        Self::Twist {
18            child: Arc::new(self),
19            strength,
20        }
21    }
22
23    /// Bend around Y-axis
24    #[must_use]
25    #[inline]
26    pub fn bend(self, curvature: f32) -> Self {
27        Self::Bend {
28            child: Arc::new(self),
29            curvature,
30        }
31    }
32
33    /// Infinite repetition
34    #[must_use]
35    #[inline]
36    pub fn repeat_infinite(self, spacing_x: f32, spacing_y: f32, spacing_z: f32) -> Self {
37        Self::RepeatInfinite {
38            child: Arc::new(self),
39            spacing: Vec3::new(spacing_x, spacing_y, spacing_z),
40        }
41    }
42
43    /// Finite repetition
44    #[must_use]
45    #[inline]
46    pub fn repeat_finite(self, count: [u32; 3], spacing: Vec3) -> Self {
47        Self::RepeatFinite {
48            child: Arc::new(self),
49            count,
50            spacing,
51        }
52    }
53
54    /// Perlin noise displacement
55    #[must_use]
56    #[inline]
57    pub fn noise(self, amplitude: f32, frequency: f32, seed: u32) -> Self {
58        Self::Noise {
59            child: Arc::new(self),
60            amplitude,
61            frequency,
62            seed,
63        }
64    }
65
66    /// Round edges
67    #[must_use]
68    #[inline]
69    pub fn round(self, radius: f32) -> Self {
70        Self::Round {
71            child: Arc::new(self),
72            radius,
73        }
74    }
75
76    /// Create a shell (onion)
77    #[must_use]
78    #[inline]
79    pub fn onion(self, thickness: f32) -> Self {
80        Self::Onion {
81            child: Arc::new(self),
82            thickness,
83        }
84    }
85
86    /// Elongate along an axis
87    #[must_use]
88    #[inline]
89    pub fn elongate(self, x: f32, y: f32, z: f32) -> Self {
90        Self::Elongate {
91            child: Arc::new(self),
92            amount: Vec3::new(x, y, z),
93        }
94    }
95
96    /// Mirror along specified axes
97    #[must_use]
98    #[inline]
99    pub fn mirror(self, x: bool, y: bool, z: bool) -> Self {
100        Self::Mirror {
101            child: Arc::new(self),
102            axes: Vec3::new(
103                if x { 1.0 } else { 0.0 },
104                if y { 1.0 } else { 0.0 },
105                if z { 1.0 } else { 0.0 },
106            ),
107        }
108    }
109
110    /// Revolution around Y-axis
111    #[must_use]
112    #[inline]
113    pub fn revolution(self, offset: f32) -> Self {
114        Self::Revolution {
115            child: Arc::new(self),
116            offset,
117        }
118    }
119
120    /// Extrude along Z-axis
121    #[must_use]
122    #[inline]
123    pub fn extrude(self, height: f32) -> Self {
124        Self::Extrude {
125            child: Arc::new(self),
126            half_height: height * 0.5,
127        }
128    }
129
130    /// Sweep along a quadratic Bezier curve in the XZ plane.
131    /// Control points are (x, z) coordinates.
132    #[must_use]
133    #[inline]
134    pub fn sweep_bezier(self, p0: Vec2, p1: Vec2, p2: Vec2) -> Self {
135        Self::SweepBezier {
136            child: Arc::new(self),
137            p0,
138            p1,
139            p2,
140        }
141    }
142
143    /// Taper along Y-axis: the child is evaluated at
144    /// `(x, y, z) / (1 − y·factor)`, narrower at +Y for `factor > 0`.
145    ///
146    /// The child's reach (`[r_xz, r_y]` from its AABB) is computed here and
147    /// stored in the node; it lets the evaluators bound the parent-space
148    /// shape by a cone so that the singular plane `y = 1/factor` is not a
149    /// phantom surface (see `real::taper_bound`). Rebuild the node with this
150    /// constructor after changing the child.
151    #[must_use]
152    #[inline]
153    pub fn taper(self, factor: f32) -> Self {
154        let reach = crate::interval::taper_reach(&self);
155        Self::Taper {
156            child: Arc::new(self),
157            factor,
158            reach,
159        }
160    }
161
162    /// Sin-based displacement
163    #[must_use]
164    #[inline]
165    pub fn displacement(self, strength: f32) -> Self {
166        Self::Displacement {
167            child: Arc::new(self),
168            strength,
169        }
170    }
171
172    /// Sin-based displacement with custom amplitude + isotropic frequency
173    /// (= e.g. frequency 15-30 for fine scale-like patterns、 Vec3::splat 化)
174    #[must_use]
175    #[inline]
176    pub fn sine_displacement(self, amplitude: f32, frequency: f32) -> Self {
177        Self::SineDisplacement {
178            child: Arc::new(self),
179            amplitude,
180            frequency: glam::Vec3::splat(frequency),
181        }
182    }
183
184    /// Sin-based displacement with anisotropic per-axis frequency
185    /// (= e.g. `Vec3::new(10.0, 40.0, 10.0)` で軸別周波数、 細長菱形鱗用)
186    #[must_use]
187    #[inline]
188    pub fn sine_displacement_aniso(self, amplitude: f32, frequency: glam::Vec3) -> Self {
189        Self::SineDisplacement {
190            child: Arc::new(self),
191            amplitude,
192            frequency,
193        }
194    }
195
196    /// Polar repetition around Y-axis
197    #[must_use]
198    #[inline]
199    pub fn polar_repeat(self, count: u32) -> Self {
200        Self::PolarRepeat {
201            child: Arc::new(self),
202            count,
203        }
204    }
205
206    /// Octant mirror (48-fold symmetry)
207    #[must_use]
208    #[inline]
209    pub fn octant_mirror(self) -> Self {
210        Self::OctantMirror {
211            child: Arc::new(self),
212        }
213    }
214
215    /// Apply shear deformation
216    #[must_use]
217    #[inline]
218    pub fn shear(self, xy: f32, xz: f32, yz: f32) -> Self {
219        Self::Shear {
220            child: Arc::new(self),
221            shear: Vec3::new(xy, xz, yz),
222        }
223    }
224
225    /// Apply time-based animation
226    #[must_use]
227    #[inline]
228    pub fn animated(self, speed: f32, amplitude: f32) -> Self {
229        Self::Animated {
230            child: Arc::new(self),
231            speed,
232            amplitude,
233        }
234    }
235
236    /// Assign a material ID to this subtree
237    #[must_use]
238    #[inline]
239    pub fn with_material(self, material_id: u32) -> Self {
240        Self::WithMaterial {
241            child: Arc::new(self),
242            material_id,
243        }
244    }
245
246    /// Icosahedral symmetry (120-fold)
247    #[must_use]
248    #[inline]
249    pub fn icosahedral_symmetry(self) -> Self {
250        Self::IcosahedralSymmetry {
251            child: Arc::new(self),
252        }
253    }
254
255    /// Iterated Function System
256    #[must_use]
257    #[inline]
258    pub fn ifs(self, transforms: Vec<[f32; 16]>, iterations: u32) -> Self {
259        Self::IFS {
260            child: Arc::new(self),
261            transforms,
262            iterations,
263        }
264    }
265
266    /// Heightmap displacement
267    #[must_use]
268    #[inline]
269    pub fn heightmap_displacement(
270        self,
271        heightmap: Vec<f32>,
272        width: u32,
273        height: u32,
274        amplitude: f32,
275        scale: f32,
276    ) -> Self {
277        Self::HeightmapDisplacement {
278            child: Arc::new(self),
279            heightmap,
280            width,
281            height,
282            amplitude,
283            scale,
284        }
285    }
286
287    /// Surface roughness (FBM noise)
288    #[must_use]
289    #[inline]
290    pub fn surface_roughness(self, frequency: f32, amplitude: f32, octaves: u32) -> Self {
291        Self::SurfaceRoughness {
292            child: Arc::new(self),
293            frequency,
294            amplitude,
295            octaves,
296        }
297    }
298}