1pub use polydat::numeric::noise::{
26 PermTable, fbm_1d, fbm_2d, perlin_1d_algo, perlin_2d_algo, simplex_2d_algo,
27};
28
29fn perlin_1d_jit_constants(node: &Perlin1d) -> Vec<u64> {
34 vec![node.perm.perm.as_ptr() as u64, node.frequency.to_bits()]
35}
36fn perlin_2d_jit_constants(node: &Perlin2d) -> Vec<u64> {
37 vec![node.perm.perm.as_ptr() as u64, node.frequency.to_bits()]
38}
39fn simplex_2d_jit_constants(node: &Simplex2d) -> Vec<u64> {
40 vec![node.perm.perm.as_ptr() as u64, node.frequency.to_bits()]
41}
42
43#[polydat::polydat_node(category = Noise, jit_constants = perlin_1d_jit_constants)]
51fn perlin_1d(
52 input: u64,
53 seed: polydat::derive_support::Const<u64>,
54 frequency: polydat::derive_support::Const<f64>,
55 #[poly_const(PermTable::new, from = seed)] perm: &PermTable,
56) -> f64 {
57 perlin_1d_algo(perm, input as f64 * *frequency)
58}
59
60#[polydat::polydat_node(category = Noise, jit_constants = perlin_2d_jit_constants)]
61fn perlin_2d(
62 x: u64,
63 y: u64,
64 seed: polydat::derive_support::Const<u64>,
65 frequency: polydat::derive_support::Const<f64>,
66 #[poly_const(PermTable::new, from = seed)] perm: &PermTable,
67) -> f64 {
68 perlin_2d_algo(perm, x as f64 * *frequency, y as f64 * *frequency)
69}
70
71#[polydat::polydat_node(category = Noise, jit_constants = simplex_2d_jit_constants)]
72fn simplex_2d(
73 x: u64,
74 y: u64,
75 seed: polydat::derive_support::Const<u64>,
76 frequency: polydat::derive_support::Const<f64>,
77 #[poly_const(PermTable::new, from = seed)] perm: &PermTable,
78) -> f64 {
79 simplex_2d_algo(perm, x as f64 * *frequency, y as f64 * *frequency)
80}
81
82fn fractal_noise_1d_jit_constants(node: &FractalNoise1d) -> Vec<u64> {
87 vec![
88 node.perm.perm.as_ptr() as u64,
89 node.frequency.to_bits(),
90 node.octaves,
91 ]
92}
93fn fractal_noise_2d_jit_constants(node: &FractalNoise2d) -> Vec<u64> {
94 vec![
95 node.perm.perm.as_ptr() as u64,
96 node.frequency.to_bits(),
97 node.octaves,
98 ]
99}
100
101#[polydat::polydat_node(category = Noise, jit_constants = fractal_noise_1d_jit_constants)]
115fn fractal_noise_1d(
116 input: u64,
117 seed: polydat::derive_support::Const<u64>,
118 frequency: polydat::derive_support::Const<f64>,
119 #[poly_default(4u64)]
120 #[constraint(RangeU64 { min: 1, max: 64 })]
121 octaves: polydat::derive_support::Const<u64>,
122 #[poly_const(PermTable::new, from = seed)] perm: &PermTable,
123) -> f64 {
124 fbm_1d(perm, input as f64, *frequency, *octaves as u32)
125}
126
127#[polydat::polydat_node(category = Noise, jit_constants = fractal_noise_2d_jit_constants)]
132fn fractal_noise_2d(
133 x: u64,
134 y: u64,
135 seed: polydat::derive_support::Const<u64>,
136 frequency: polydat::derive_support::Const<f64>,
137 #[poly_default(4u64)]
138 #[constraint(RangeU64 { min: 1, max: 64 })]
139 octaves: polydat::derive_support::Const<u64>,
140 #[poly_const(PermTable::new, from = seed)] perm: &PermTable,
141) -> f64 {
142 fbm_2d(perm, x as f64, y as f64, *frequency, *octaves as u32)
143}
144#[cfg(test)]
145mod tests {
146 use super::*;
147 use polydat::ast::{PolydatNode, Value};
148
149 #[test]
150 fn perlin_1d_bounded() {
151 let node = Perlin1d::new(42, 0.01);
152 let mut out = [Value::None];
153 for i in 0..1000u64 {
154 node.eval(&[Value::U64(i)], &mut out);
155 let v = out[0].as_f64();
156 assert!((-1.0..=1.0).contains(&v), "out of range: {v} at i={i}");
157 }
158 }
159
160 #[test]
161 fn perlin_1d_smooth() {
162 let node = Perlin1d::new(42, 0.01);
164 let mut prev = [Value::None];
165 let mut curr = [Value::None];
166 node.eval(&[Value::U64(100)], &mut prev);
167 let mut large_jumps = 0;
168 for i in 101..200u64 {
169 node.eval(&[Value::U64(i)], &mut curr);
170 let diff = (curr[0].as_f64() - prev[0].as_f64()).abs();
171 if diff > 0.5 {
172 large_jumps += 1;
173 }
174 prev[0] = curr[0].clone();
175 }
176 assert!(large_jumps < 5, "too many large jumps: {large_jumps}");
178 }
179
180 #[test]
181 fn perlin_1d_deterministic() {
182 let node = Perlin1d::new(42, 0.1);
183 let mut out1 = [Value::None];
184 let mut out2 = [Value::None];
185 node.eval(&[Value::U64(123)], &mut out1);
186 node.eval(&[Value::U64(123)], &mut out2);
187 assert_eq!(out1[0].as_f64(), out2[0].as_f64());
188 }
189
190 #[test]
191 fn perlin_1d_different_seeds() {
192 let a = Perlin1d::new(1, 0.1);
193 let b = Perlin1d::new(2, 0.1);
194 let mut out_a = [Value::None];
195 let mut out_b = [Value::None];
196 let mut differ = false;
197 for i in 0..100u64 {
198 a.eval(&[Value::U64(i)], &mut out_a);
199 b.eval(&[Value::U64(i)], &mut out_b);
200 if (out_a[0].as_f64() - out_b[0].as_f64()).abs() > 0.01 {
201 differ = true;
202 break;
203 }
204 }
205 assert!(differ, "different seeds should produce different noise");
206 }
207
208 #[test]
209 fn perlin_2d_bounded() {
210 let node = Perlin2d::new(42, 0.01);
211 let mut out = [Value::None];
212 for x in 0..50u64 {
213 for y in 0..50u64 {
214 node.eval(&[Value::U64(x), Value::U64(y)], &mut out);
215 let v = out[0].as_f64();
216 assert!((-1.5..=1.5).contains(&v), "out of range: {v} at ({x},{y})");
217 }
218 }
219 }
220
221 #[test]
222 fn perlin_2d_smooth() {
223 let node = Perlin2d::new(42, 0.01);
224 let mut prev = [Value::None];
225 let mut curr = [Value::None];
226 node.eval(&[Value::U64(100), Value::U64(100)], &mut prev);
227 let mut large_jumps = 0;
228 for i in 101..150u64 {
229 node.eval(&[Value::U64(i), Value::U64(100)], &mut curr);
230 let diff = (curr[0].as_f64() - prev[0].as_f64()).abs();
231 if diff > 0.5 {
232 large_jumps += 1;
233 }
234 prev[0] = curr[0].clone();
235 }
236 assert!(large_jumps < 5, "too many large jumps: {large_jumps}");
237 }
238
239 #[test]
240 fn simplex_2d_bounded() {
241 let node = Simplex2d::new(42, 0.01);
242 let mut out = [Value::None];
243 for x in 0..50u64 {
244 for y in 0..50u64 {
245 node.eval(&[Value::U64(x), Value::U64(y)], &mut out);
246 let v = out[0].as_f64();
247 assert!((-1.5..=1.5).contains(&v), "out of range: {v}");
248 }
249 }
250 }
251
252 #[test]
253 fn fractal_1d_bounded() {
254 let node = FractalNoise1d::new(42, 0.01, 4);
255 let mut out = [Value::None];
256 for i in 0..500u64 {
257 node.eval(&[Value::U64(i)], &mut out);
258 let v = out[0].as_f64();
259 assert!((-1.5..=1.5).contains(&v), "out of range: {v}");
260 }
261 }
262
263 #[test]
264 fn fractal_1d_more_detail_than_single_octave() {
265 let single = Perlin1d::new(42, 0.01);
268 let fbm = FractalNoise1d::new(42, 0.01, 4);
269 let mut s_out = [Value::None];
270 let mut f_out = [Value::None];
271 let mut s_changes = 0.0;
272 let mut f_changes = 0.0;
273 let mut s_prev = 0.0;
274 let mut f_prev = 0.0;
275 for i in 0..500u64 {
276 single.eval(&[Value::U64(i)], &mut s_out);
277 fbm.eval(&[Value::U64(i)], &mut f_out);
278 if i > 0 {
279 s_changes += (s_out[0].as_f64() - s_prev).abs();
280 f_changes += (f_out[0].as_f64() - f_prev).abs();
281 }
282 s_prev = s_out[0].as_f64();
283 f_prev = f_out[0].as_f64();
284 }
285 assert!(
287 f_changes > s_changes * 0.8,
288 "FBM should have comparable or more detail: single={s_changes}, fbm={f_changes}"
289 );
290 }
291
292 #[test]
293 fn fractal_2d_bounded() {
294 let node = FractalNoise2d::new(42, 0.01, 3);
295 let mut out = [Value::None];
296 for x in 0..30u64 {
297 for y in 0..30u64 {
298 node.eval(&[Value::U64(x), Value::U64(y)], &mut out);
299 let v = out[0].as_f64();
300 assert!((-1.5..=1.5).contains(&v), "out of range: {v}");
301 }
302 }
303 }
304}