#[inline]
fn xorshift(mut x: u64) -> u64 {
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
x.wrapping_mul(0x2545_F491_4F6C_DD1D)
}
#[inline]
fn hash_u32(seed: u32) -> f32 {
let h = xorshift(seed as u64 | ((seed as u64) << 32));
((h >> 40) as f32) / ((1u32 << 24) as f32) * 2.0 - 1.0
}
#[inline]
pub fn value_noise(t: f32, freq: f32, seed: u32) -> f32 {
let idx = (t * freq).floor() as i32 as u32;
hash_u32(seed ^ idx)
}
pub fn perlin1d(t: f32, freq: f32, seed: u32) -> f32 {
let x = t * freq;
let i = x.floor();
let f = x - i;
let a = hash_u32(seed ^ (i as i32 as u32));
let b = hash_u32(seed ^ ((i as i32 + 1) as u32));
let s = f * f * (3.0 - 2.0 * f);
a + (b - a) * s
}
pub fn brown_walk(t: f32, step_hz: f32, scale: f32, seed: u32) -> f32 {
let mut sum = 0.0;
let mut amp = 1.0;
let mut freq = step_hz;
for octave in 0..4 {
sum += perlin1d(t, freq, seed.wrapping_add(octave * 131)) * amp;
amp *= 0.5;
freq *= 2.0;
}
sum * scale
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deterministic() {
assert_eq!(value_noise(1.3, 2.0, 42), value_noise(1.3, 2.0, 42));
assert_eq!(perlin1d(0.7, 1.5, 7), perlin1d(0.7, 1.5, 7));
}
#[test]
fn perlin_in_range() {
for i in 0..1000 {
let v = perlin1d(i as f32 * 0.01, 3.7, 99);
assert!(v.abs() <= 1.01, "perlin out of range: {v}");
}
}
}