use rand::{Rng, SeedableRng};
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum NoiseType {
White,
Brown,
Pink,
Blue,
Green,
Perlin,
}
impl NoiseType {
pub fn generate(self, length: usize) -> Vec<f32> {
match self {
NoiseType::White => {
let mut gen = WhiteNoise::new();
gen.generate(length)
}
NoiseType::Brown => {
let mut gen = BrownNoise::new();
gen.generate(length)
}
NoiseType::Pink => {
let mut gen = PinkNoise::new();
gen.generate(length)
}
NoiseType::Blue => {
let mut gen = BlueNoise::new();
gen.generate(length)
}
NoiseType::Green => {
let mut gen = GreenNoise::new();
gen.generate(length)
}
NoiseType::Perlin => {
let mut gen = PerlinNoise::new();
gen.generate(length)
}
}
}
}
pub trait NoiseGenerator {
fn sample(&mut self) -> f32;
fn generate(&mut self, length: usize) -> Vec<f32> {
(0..length).map(|_| self.sample()).collect()
}
}
#[derive(Debug)]
pub struct WhiteNoise {
rng: rand::rngs::StdRng,
}
impl WhiteNoise {
pub fn new() -> Self {
Self {
rng: rand::rngs::StdRng::from_rng(&mut rand::rng()),
}
}
pub fn with_seed(seed: u64) -> Self {
Self {
rng: rand::rngs::StdRng::seed_from_u64(seed),
}
}
#[inline]
pub fn sample(&mut self) -> f32 {
self.rng.random_range(-1.0..1.0)
}
pub fn generate(&mut self, length: usize) -> Vec<f32> {
(0..length).map(|_| self.sample()).collect()
}
}
impl Default for WhiteNoise {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct BrownNoise {
current: f32,
step_size: f32,
rng: rand::rngs::StdRng,
}
impl BrownNoise {
pub fn new() -> Self {
Self {
current: 0.0,
step_size: 0.05,
rng: rand::rngs::StdRng::from_rng(&mut rand::rng()),
}
}
pub fn with_seed(seed: u64) -> Self {
Self {
current: 0.0,
step_size: 0.05,
rng: rand::rngs::StdRng::seed_from_u64(seed),
}
}
pub fn with_step_size(step_size: f32) -> Self {
Self {
current: 0.0,
step_size,
rng: rand::rngs::StdRng::from_rng(&mut rand::rng()),
}
}
#[inline]
pub fn sample(&mut self) -> f32 {
let delta = self.rng.random_range(-self.step_size..self.step_size);
self.current += delta;
if self.current > 1.0 {
self.current = 2.0 - self.current; } else if self.current < -1.0 {
self.current = -2.0 - self.current; }
self.current = self.current.clamp(-1.0, 1.0);
self.current
}
pub fn generate(&mut self, length: usize) -> Vec<f32> {
(0..length).map(|_| self.sample()).collect()
}
pub fn reset(&mut self) {
self.current = 0.0;
}
}
impl Default for BrownNoise {
fn default() -> Self {
Self::new()
}
}
impl NoiseGenerator for WhiteNoise {
fn sample(&mut self) -> f32 {
WhiteNoise::sample(self)
}
fn generate(&mut self, length: usize) -> Vec<f32> {
WhiteNoise::generate(self, length)
}
}
impl NoiseGenerator for BrownNoise {
fn sample(&mut self) -> f32 {
BrownNoise::sample(self)
}
fn generate(&mut self, length: usize) -> Vec<f32> {
BrownNoise::generate(self, length)
}
}
#[derive(Debug)]
pub struct PinkNoise {
white: WhiteNoise,
rows: [f32; 7],
running_sum: f32,
updates: usize,
}
impl PinkNoise {
pub fn new() -> Self {
Self {
white: WhiteNoise::new(),
rows: [0.0; 7],
running_sum: 0.0,
updates: 0,
}
}
pub fn with_seed(seed: u64) -> Self {
Self {
white: WhiteNoise::with_seed(seed),
rows: [0.0; 7],
running_sum: 0.0,
updates: 0,
}
}
#[inline]
pub fn sample(&mut self) -> f32 {
let white_value = self.white.sample();
for (i, row) in self.rows.iter_mut().enumerate() {
if self.updates & (1 << i) == 0 {
self.running_sum -= *row;
*row = self.white.sample();
self.running_sum += *row;
break;
}
}
self.updates += 1;
(white_value + self.running_sum) / 8.0
}
pub fn generate(&mut self, length: usize) -> Vec<f32> {
(0..length).map(|_| self.sample()).collect()
}
}
impl Default for PinkNoise {
fn default() -> Self {
Self::new()
}
}
impl NoiseGenerator for PinkNoise {
fn sample(&mut self) -> f32 {
PinkNoise::sample(self)
}
fn generate(&mut self, length: usize) -> Vec<f32> {
PinkNoise::generate(self, length)
}
}
#[derive(Debug)]
pub struct BlueNoise {
white: WhiteNoise,
prev: f32,
}
impl BlueNoise {
pub fn new() -> Self {
Self {
white: WhiteNoise::new(),
prev: 0.0,
}
}
pub fn with_seed(seed: u64) -> Self {
Self {
white: WhiteNoise::with_seed(seed),
prev: 0.0,
}
}
#[inline]
pub fn sample(&mut self) -> f32 {
let current = self.white.sample();
let output = current - self.prev;
self.prev = current;
output * 0.5
}
pub fn generate(&mut self, length: usize) -> Vec<f32> {
(0..length).map(|_| self.sample()).collect()
}
}
impl Default for BlueNoise {
fn default() -> Self {
Self::new()
}
}
impl NoiseGenerator for BlueNoise {
fn sample(&mut self) -> f32 {
BlueNoise::sample(self)
}
fn generate(&mut self, length: usize) -> Vec<f32> {
BlueNoise::generate(self, length)
}
}
#[derive(Debug)]
pub struct GreenNoise {
pink: PinkNoise,
prev: f32,
}
impl GreenNoise {
pub fn new() -> Self {
Self {
pink: PinkNoise::new(),
prev: 0.0,
}
}
pub fn with_seed(seed: u64) -> Self {
Self {
pink: PinkNoise::with_seed(seed),
prev: 0.0,
}
}
#[inline]
pub fn sample(&mut self) -> f32 {
let pink_sample = self.pink.sample();
let highpassed = pink_sample - 0.5 * self.prev;
self.prev = pink_sample;
0.7 * pink_sample + 0.3 * highpassed
}
pub fn generate(&mut self, length: usize) -> Vec<f32> {
(0..length).map(|_| self.sample()).collect()
}
}
impl Default for GreenNoise {
fn default() -> Self {
Self::new()
}
}
impl NoiseGenerator for GreenNoise {
fn sample(&mut self) -> f32 {
GreenNoise::sample(self)
}
fn generate(&mut self, length: usize) -> Vec<f32> {
GreenNoise::generate(self, length)
}
}
#[derive(Debug)]
pub struct PerlinNoise {
x: f32,
frequency: f32,
seed: u32,
}
impl PerlinNoise {
pub fn new() -> Self {
let mut rng = rand::rngs::StdRng::from_rng(&mut rand::rng());
Self {
x: 0.0,
frequency: 0.02, seed: rng.random_range(0..u32::MAX),
}
}
pub fn with_seed(seed: u32) -> Self {
Self {
x: 0.0,
frequency: 0.02,
seed,
}
}
pub fn with_frequency(frequency: f32) -> Self {
let mut rng = rand::rngs::StdRng::from_rng(&mut rand::rng());
Self {
x: 0.0,
frequency,
seed: rng.random_range(0..u32::MAX),
}
}
pub fn set_frequency(&mut self, frequency: f32) {
self.frequency = frequency;
}
pub fn reset(&mut self) {
self.x = 0.0;
}
#[inline]
fn gradient(&self, x: i32) -> f32 {
let mut hash = x.wrapping_mul(374761393) as u32;
hash = hash.wrapping_add(self.seed);
hash = hash.wrapping_mul(1103515245);
hash = hash.wrapping_add(12345);
hash = (hash >> 16) & 0x7fff;
(hash as f32 / 16383.5) - 1.0
}
#[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)
}
#[inline]
pub fn sample(&mut self) -> f32 {
let xi = self.x.floor() as i32;
let xf = self.x - xi as f32;
let sx = Self::fade(xf);
let g0 = self.gradient(xi);
let g1 = self.gradient(xi + 1);
let d0 = g0 * xf;
let d1 = g1 * (xf - 1.0);
let result = Self::lerp(d0, d1, sx);
self.x += self.frequency;
result
}
pub fn generate(&mut self, length: usize) -> Vec<f32> {
(0..length).map(|_| self.sample()).collect()
}
}
impl Default for PerlinNoise {
fn default() -> Self {
Self::new()
}
}
impl NoiseGenerator for PerlinNoise {
fn sample(&mut self) -> f32 {
PerlinNoise::sample(self)
}
fn generate(&mut self, length: usize) -> Vec<f32> {
PerlinNoise::generate(self, length)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_white_noise_range() {
let mut white = WhiteNoise::new();
for _ in 0..1000 {
let sample = white.sample();
assert!(
sample >= -1.0 && sample <= 1.0,
"White noise sample {} out of range",
sample
);
}
}
#[test]
fn test_white_noise_is_random() {
let mut white = WhiteNoise::new();
let samples: Vec<f32> = (0..100).map(|_| white.sample()).collect();
let first = samples[0];
let has_variation = samples.iter().any(|&s| (s - first).abs() > 0.1);
assert!(has_variation, "White noise should have variation");
}
#[test]
fn test_white_noise_seeded_deterministic() {
let mut noise1 = WhiteNoise::with_seed(12345);
let mut noise2 = WhiteNoise::with_seed(12345);
for _ in 0..100 {
assert_eq!(
noise1.sample(),
noise2.sample(),
"Seeded white noise should be deterministic"
);
}
}
#[test]
fn test_white_noise_generate() {
let mut white = WhiteNoise::new();
let samples = white.generate(500);
assert_eq!(samples.len(), 500);
for &sample in &samples {
assert!(sample >= -1.0 && sample <= 1.0);
}
}
#[test]
fn test_brown_noise_range() {
let mut brown = BrownNoise::new();
for i in 0..10000 {
let sample = brown.sample();
assert!(
sample >= -1.0 && sample <= 1.0,
"Brown noise sample {} out of range at iteration {}",
sample,
i
);
}
}
#[test]
fn test_brown_noise_continuity() {
let mut brown = BrownNoise::new();
let mut prev = brown.sample();
for _ in 0..100 {
let current = brown.sample();
let diff = (current - prev).abs();
assert!(
diff <= 0.1, "Brown noise discontinuity: {} to {} (diff: {})",
prev,
current,
diff
);
prev = current;
}
}
#[test]
fn test_brown_noise_seeded_deterministic() {
let mut noise1 = BrownNoise::with_seed(54321);
let mut noise2 = BrownNoise::with_seed(54321);
for _ in 0..100 {
assert_eq!(
noise1.sample(),
noise2.sample(),
"Seeded brown noise should be deterministic"
);
}
}
#[test]
fn test_brown_noise_reset() {
let mut brown = BrownNoise::new();
for _ in 0..100 {
brown.sample();
}
brown.reset();
assert_eq!(brown.current, 0.0, "Reset should set current to 0");
}
#[test]
fn test_brown_noise_step_size() {
let mut smooth = BrownNoise::with_step_size(0.01);
let mut rough = BrownNoise::with_step_size(0.1);
let smooth_samples = smooth.generate(100);
let rough_samples = rough.generate(100);
let smooth_variance = calculate_variance(&smooth_samples);
let rough_variance = calculate_variance(&rough_samples);
assert!(
rough_variance > smooth_variance * 0.5,
"Larger step size should create more variance"
);
}
#[test]
fn test_brown_noise_generate() {
let mut brown = BrownNoise::new();
let samples = brown.generate(500);
assert_eq!(samples.len(), 500);
for &sample in &samples {
assert!(sample >= -1.0 && sample <= 1.0);
}
}
#[test]
fn test_brown_noise_low_frequency_bias() {
let mut brown = BrownNoise::with_seed(42);
let samples = brown.generate(10000);
let mut correlation_sum = 0.0;
for i in 0..samples.len() - 1 {
correlation_sum += samples[i] * samples[i + 1];
}
let avg_correlation = correlation_sum / (samples.len() - 1) as f32;
assert!(
avg_correlation.abs() > 0.05,
"Brown noise should show adjacent sample correlation, got: {}",
avg_correlation
);
}
fn calculate_variance(samples: &[f32]) -> f32 {
let mean: f32 = samples.iter().sum::<f32>() / samples.len() as f32;
let variance: f32 = samples
.iter()
.map(|&x| {
let diff = x - mean;
diff * diff
})
.sum::<f32>()
/ samples.len() as f32;
variance
}
#[test]
fn test_pink_noise_range() {
let mut pink = PinkNoise::new();
for _ in 0..1000 {
let sample = pink.sample();
assert!(
sample >= -1.0 && sample <= 1.0,
"Pink noise sample {} out of range",
sample
);
}
}
#[test]
fn test_pink_noise_seeded_deterministic() {
let mut noise1 = PinkNoise::with_seed(12345);
let mut noise2 = PinkNoise::with_seed(12345);
for _ in 0..100 {
assert_eq!(
noise1.sample(),
noise2.sample(),
"Seeded pink noise should be deterministic"
);
}
}
#[test]
fn test_blue_noise_range() {
let mut blue = BlueNoise::new();
for _ in 0..1000 {
let sample = blue.sample();
assert!(
sample >= -1.0 && sample <= 1.0,
"Blue noise sample {} out of range",
sample
);
}
}
#[test]
fn test_blue_noise_seeded_deterministic() {
let mut noise1 = BlueNoise::with_seed(54321);
let mut noise2 = BlueNoise::with_seed(54321);
for _ in 0..100 {
assert_eq!(
noise1.sample(),
noise2.sample(),
"Seeded blue noise should be deterministic"
);
}
}
#[test]
fn test_green_noise_range() {
let mut green = GreenNoise::new();
for _ in 0..1000 {
let sample = green.sample();
assert!(
sample >= -1.0 && sample <= 1.0,
"Green noise sample {} out of range",
sample
);
}
}
#[test]
fn test_green_noise_seeded_deterministic() {
let mut noise1 = GreenNoise::with_seed(11111);
let mut noise2 = GreenNoise::with_seed(11111);
for _ in 0..100 {
assert_eq!(
noise1.sample(),
noise2.sample(),
"Seeded green noise should be deterministic"
);
}
}
#[test]
fn test_perlin_noise_range() {
let mut perlin = PerlinNoise::new();
for _ in 0..1000 {
let sample = perlin.sample();
assert!(
sample >= -1.0 && sample <= 1.0,
"Perlin noise sample {} out of range",
sample
);
}
}
#[test]
fn test_perlin_noise_smoothness() {
let mut perlin = PerlinNoise::with_seed(42);
let samples = perlin.generate(200);
for i in 1..samples.len() {
let diff = (samples[i] - samples[i - 1]).abs();
assert!(
diff < 0.1,
"Perlin noise jump too large: {} at index {}",
diff,
i
);
}
}
#[test]
fn test_perlin_noise_seeded_deterministic() {
let mut noise1 = PerlinNoise::with_seed(99999);
let mut noise2 = PerlinNoise::with_seed(99999);
for _ in 0..100 {
assert_eq!(
noise1.sample(),
noise2.sample(),
"Seeded Perlin noise should be deterministic"
);
}
}
#[test]
fn test_perlin_noise_frequency() {
let mut slow = PerlinNoise::with_seed(42);
slow.set_frequency(0.01);
let mut fast = PerlinNoise::with_seed(42);
fast.set_frequency(0.1);
let slow_samples = slow.generate(100);
let fast_samples = fast.generate(100);
let slow_var: f32 = slow_samples.windows(2).map(|w| (w[1] - w[0]).abs()).sum();
let fast_var: f32 = fast_samples.windows(2).map(|w| (w[1] - w[0]).abs()).sum();
assert!(
fast_var > slow_var,
"Higher frequency should create more variation: slow={}, fast={}",
slow_var,
fast_var
);
}
#[test]
fn test_perlin_noise_reset() {
let mut perlin = PerlinNoise::with_seed(777);
let first_sample = perlin.sample();
for _ in 0..50 {
perlin.sample();
}
perlin.reset();
let reset_sample = perlin.sample();
assert_eq!(
first_sample,
reset_sample,
"Reset should return to beginning"
);
}
#[test]
fn test_noise_type_enum() {
let white_samples = NoiseType::White.generate(10);
let brown_samples = NoiseType::Brown.generate(10);
let pink_samples = NoiseType::Pink.generate(10);
let blue_samples = NoiseType::Blue.generate(10);
let green_samples = NoiseType::Green.generate(10);
let perlin_samples = NoiseType::Perlin.generate(10);
assert_eq!(white_samples.len(), 10);
assert_eq!(brown_samples.len(), 10);
assert_eq!(pink_samples.len(), 10);
assert_eq!(blue_samples.len(), 10);
assert_eq!(green_samples.len(), 10);
assert_eq!(perlin_samples.len(), 10);
}
}