pub fn generate(
seed: u32,
frequency: f32,
octaves: u32,
persistence: f32,
length: usize,
) -> Vec<f32> {
let mut result = Vec::with_capacity(length);
for i in 0..length {
let x = i as f32 * frequency;
let mut value = 0.0;
let mut amplitude = 1.0;
let mut max_value = 0.0;
for octave in 0..octaves {
let freq = 2.0_f32.powi(octave as i32);
let noise = perlin_1d(x * freq, seed.wrapping_add(octave));
value += noise * amplitude;
max_value += amplitude;
amplitude *= persistence;
}
let normalized = (value / max_value + 1.0) * 0.5;
result.push(normalized.clamp(0.0, 1.0));
}
result
}
pub fn perlin_noise_bipolar(
seed: u32,
frequency: f32,
octaves: u32,
persistence: f32,
length: usize,
) -> Vec<f32> {
let mut result = Vec::with_capacity(length);
for i in 0..length {
let x = i as f32 * frequency;
let mut value = 0.0;
let mut amplitude = 1.0;
let mut max_value = 0.0;
for octave in 0..octaves {
let freq = 2.0_f32.powi(octave as i32);
let noise = perlin_1d(x * freq, seed.wrapping_add(octave));
value += noise * amplitude;
max_value += amplitude;
amplitude *= persistence;
}
let normalized = value / max_value;
result.push(normalized.clamp(-1.0, 1.0));
}
result
}
fn perlin_1d(x: f32, seed: u32) -> f32 {
let x0 = x.floor() as i32;
let x1 = x0 + 1;
let fx = x - x0 as f32;
let sx = fade(fx);
let g0 = gradient(x0, seed);
let g1 = gradient(x1, seed);
let d0 = g0 * fx;
let d1 = g1 * (fx - 1.0);
lerp(d0, d1, sx)
}
#[inline]
fn fade(t: f32) -> f32 {
t * t * t * (t * (t * 6.0 - 15.0) + 10.0)
}
#[inline]
fn lerp(a: f32, b: f32, t: f32) -> f32 {
a + t * (b - a)
}
fn gradient(x: i32, seed: u32) -> f32 {
let mut hash = x.wrapping_mul(374761393) as u32;
hash = hash.wrapping_add(seed);
hash = hash.wrapping_mul(1103515245);
hash = hash.wrapping_add(12345);
hash = (hash >> 16) & 0x7fff;
(hash as f32 / 16383.5) - 1.0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_perlin_noise_length() {
let noise = generate(42, 0.1, 2, 0.5, 100);
assert_eq!(noise.len(), 100);
}
#[test]
fn test_perlin_noise_bounded() {
let noise = generate(123, 0.2, 4, 0.5, 200);
for &value in &noise {
assert!(value >= 0.0 && value <= 1.0, "Value {} out of range [0, 1]", value);
}
}
#[test]
fn test_perlin_noise_bipolar_bounded() {
let noise = perlin_noise_bipolar(7, 0.15, 3, 0.5, 150);
for &value in &noise {
assert!(value >= -1.0 && value <= 1.0, "Value {} out of range [-1, 1]", value);
}
}
#[test]
fn test_perlin_noise_smoothness() {
let noise = generate(99, 0.1, 2, 0.5, 100);
for i in 1..noise.len() {
let diff = (noise[i] - noise[i - 1]).abs();
assert!(diff < 0.3, "Jump too large: {} between indices {} and {}", diff, i - 1, i);
}
}
#[test]
fn test_perlin_noise_deterministic() {
let noise1 = generate(42, 0.1, 2, 0.5, 50);
let noise2 = generate(42, 0.1, 2, 0.5, 50);
for i in 0..noise1.len() {
assert_eq!(noise1[i], noise2[i], "Noise not deterministic at index {}", i);
}
}
#[test]
fn test_perlin_noise_different_seeds() {
let noise1 = generate(42, 0.2, 3, 0.5, 100);
let noise2 = generate(99, 0.2, 3, 0.5, 100);
let mut differences = 0;
for i in 0..noise1.len() {
if (noise1[i] - noise2[i]).abs() > 0.05 {
differences += 1;
}
}
assert!(differences > 15, "Different seeds should produce different sequences, found {} differences", differences);
}
#[test]
fn test_fade_function() {
assert_eq!(fade(0.0), 0.0);
assert_eq!(fade(1.0), 1.0);
assert!(fade(0.5) > 0.4 && fade(0.5) < 0.6);
}
#[test]
fn test_octaves_add_detail() {
let simple = generate(42, 0.1, 1, 0.5, 100);
let complex = generate(42, 0.1, 4, 0.5, 100);
let simple_var = simple.windows(2).map(|w| (w[1] - w[0]).abs()).sum::<f32>();
let complex_var = complex.windows(2).map(|w| (w[1] - w[0]).abs()).sum::<f32>();
assert!(complex_var > simple_var * 0.5, "More octaves should add detail");
}
}
pub fn smooth_drift() -> Vec<f32> {
generate(42, 0.05, 1, 0.5, 64)
}
pub fn classic() -> Vec<f32> {
generate(123, 0.1, 3, 0.5, 100)
}
pub fn rich_texture() -> Vec<f32> {
generate(99, 0.1, 4, 0.5, 128)
}
pub fn fast_modulation() -> Vec<f32> {
generate(7, 0.3, 2, 0.5, 200)
}
pub fn ambient_pad() -> Vec<f32> {
generate(555, 0.02, 2, 0.5, 256)
}