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)]
106fn fractal_noise_1d(
107 input: u64,
108 seed: polydat::derive_support::Const<u64>,
109 frequency: polydat::derive_support::Const<f64>,
110 #[poly_default(4u64)] octaves: polydat::derive_support::Const<u64>,
111 #[poly_const(PermTable::new, from = seed)] perm: &PermTable,
112) -> f64 {
113 fbm_1d(perm, input as f64, *frequency, *octaves as u32)
114}
115
116#[polydat::polydat_node(category = Noise, jit_constants = fractal_noise_2d_jit_constants)]
120fn fractal_noise_2d(
121 x: u64,
122 y: u64,
123 seed: polydat::derive_support::Const<u64>,
124 frequency: polydat::derive_support::Const<f64>,
125 #[poly_default(4u64)] octaves: polydat::derive_support::Const<u64>,
126 #[poly_const(PermTable::new, from = seed)] perm: &PermTable,
127) -> f64 {
128 fbm_2d(perm, x as f64, y as f64, *frequency, *octaves as u32)
129}
130#[cfg(test)]
131mod tests {
132 use super::*;
133 use polydat::ast::{PolydatNode, Value};
134
135 #[test]
136 fn perlin_1d_bounded() {
137 let node = Perlin1d::new(42, 0.01);
138 let mut out = [Value::None];
139 for i in 0..1000u64 {
140 node.eval(&[Value::U64(i)], &mut out);
141 let v = out[0].as_f64();
142 assert!((-1.0..=1.0).contains(&v), "out of range: {v} at i={i}");
143 }
144 }
145
146 #[test]
147 fn perlin_1d_smooth() {
148 let node = Perlin1d::new(42, 0.01);
150 let mut prev = [Value::None];
151 let mut curr = [Value::None];
152 node.eval(&[Value::U64(100)], &mut prev);
153 let mut large_jumps = 0;
154 for i in 101..200u64 {
155 node.eval(&[Value::U64(i)], &mut curr);
156 let diff = (curr[0].as_f64() - prev[0].as_f64()).abs();
157 if diff > 0.5 {
158 large_jumps += 1;
159 }
160 prev[0] = curr[0].clone();
161 }
162 assert!(large_jumps < 5, "too many large jumps: {large_jumps}");
164 }
165
166 #[test]
167 fn perlin_1d_deterministic() {
168 let node = Perlin1d::new(42, 0.1);
169 let mut out1 = [Value::None];
170 let mut out2 = [Value::None];
171 node.eval(&[Value::U64(123)], &mut out1);
172 node.eval(&[Value::U64(123)], &mut out2);
173 assert_eq!(out1[0].as_f64(), out2[0].as_f64());
174 }
175
176 #[test]
177 fn perlin_1d_different_seeds() {
178 let a = Perlin1d::new(1, 0.1);
179 let b = Perlin1d::new(2, 0.1);
180 let mut out_a = [Value::None];
181 let mut out_b = [Value::None];
182 let mut differ = false;
183 for i in 0..100u64 {
184 a.eval(&[Value::U64(i)], &mut out_a);
185 b.eval(&[Value::U64(i)], &mut out_b);
186 if (out_a[0].as_f64() - out_b[0].as_f64()).abs() > 0.01 {
187 differ = true;
188 break;
189 }
190 }
191 assert!(differ, "different seeds should produce different noise");
192 }
193
194 #[test]
195 fn perlin_2d_bounded() {
196 let node = Perlin2d::new(42, 0.01);
197 let mut out = [Value::None];
198 for x in 0..50u64 {
199 for y in 0..50u64 {
200 node.eval(&[Value::U64(x), Value::U64(y)], &mut out);
201 let v = out[0].as_f64();
202 assert!((-1.5..=1.5).contains(&v), "out of range: {v} at ({x},{y})");
203 }
204 }
205 }
206
207 #[test]
208 fn perlin_2d_smooth() {
209 let node = Perlin2d::new(42, 0.01);
210 let mut prev = [Value::None];
211 let mut curr = [Value::None];
212 node.eval(&[Value::U64(100), Value::U64(100)], &mut prev);
213 let mut large_jumps = 0;
214 for i in 101..150u64 {
215 node.eval(&[Value::U64(i), Value::U64(100)], &mut curr);
216 let diff = (curr[0].as_f64() - prev[0].as_f64()).abs();
217 if diff > 0.5 {
218 large_jumps += 1;
219 }
220 prev[0] = curr[0].clone();
221 }
222 assert!(large_jumps < 5, "too many large jumps: {large_jumps}");
223 }
224
225 #[test]
226 fn simplex_2d_bounded() {
227 let node = Simplex2d::new(42, 0.01);
228 let mut out = [Value::None];
229 for x in 0..50u64 {
230 for y in 0..50u64 {
231 node.eval(&[Value::U64(x), Value::U64(y)], &mut out);
232 let v = out[0].as_f64();
233 assert!((-1.5..=1.5).contains(&v), "out of range: {v}");
234 }
235 }
236 }
237
238 #[test]
239 fn fractal_1d_bounded() {
240 let node = FractalNoise1d::new(42, 0.01, 4);
241 let mut out = [Value::None];
242 for i in 0..500u64 {
243 node.eval(&[Value::U64(i)], &mut out);
244 let v = out[0].as_f64();
245 assert!((-1.5..=1.5).contains(&v), "out of range: {v}");
246 }
247 }
248
249 #[test]
250 fn fractal_1d_more_detail_than_single_octave() {
251 let single = Perlin1d::new(42, 0.01);
254 let fbm = FractalNoise1d::new(42, 0.01, 4);
255 let mut s_out = [Value::None];
256 let mut f_out = [Value::None];
257 let mut s_changes = 0.0;
258 let mut f_changes = 0.0;
259 let mut s_prev = 0.0;
260 let mut f_prev = 0.0;
261 for i in 0..500u64 {
262 single.eval(&[Value::U64(i)], &mut s_out);
263 fbm.eval(&[Value::U64(i)], &mut f_out);
264 if i > 0 {
265 s_changes += (s_out[0].as_f64() - s_prev).abs();
266 f_changes += (f_out[0].as_f64() - f_prev).abs();
267 }
268 s_prev = s_out[0].as_f64();
269 f_prev = f_out[0].as_f64();
270 }
271 assert!(
273 f_changes > s_changes * 0.8,
274 "FBM should have comparable or more detail: single={s_changes}, fbm={f_changes}"
275 );
276 }
277
278 #[test]
279 fn fractal_2d_bounded() {
280 let node = FractalNoise2d::new(42, 0.01, 3);
281 let mut out = [Value::None];
282 for x in 0..30u64 {
283 for y in 0..30u64 {
284 node.eval(&[Value::U64(x), Value::U64(y)], &mut out);
285 let v = out[0].as_f64();
286 assert!((-1.5..=1.5).contains(&v), "out of range: {v}");
287 }
288 }
289 }
290}