Skip to main content

terrain_forge/noise/
perlin.rs

1use super::NoiseSource;
2
3/// Perlin noise generator
4pub struct Perlin {
5    frequency: f64,
6    perm: [u8; 512],
7}
8
9impl Perlin {
10    pub fn new(seed: u64) -> Self {
11        let mut base = [0u8; 256];
12        for (i, v) in base.iter_mut().enumerate() {
13            *v = i as u8;
14        }
15        let mut rng = crate::Rng::new(seed);
16        rng.shuffle(&mut base);
17
18        let mut perm = [0u8; 512];
19        for i in 0..512 {
20            perm[i] = base[i & 255];
21        }
22        Self {
23            frequency: 1.0,
24            perm,
25        }
26    }
27
28    pub fn with_frequency(mut self, frequency: f64) -> Self {
29        self.frequency = frequency;
30        self
31    }
32
33    fn gradient(hash: u8, x: f64, y: f64) -> f64 {
34        let h = hash & 7;
35        let u = if h < 4 { x } else { y };
36        let v = if h < 4 { y } else { x };
37        let u = if (h & 1) == 0 { u } else { -u };
38        let v = if (h & 2) == 0 { v } else { -v };
39        u + v
40    }
41
42    fn fade(t: f64) -> f64 {
43        t * t * t * (t * (t * 6.0 - 15.0) + 10.0)
44    }
45
46    fn lerp(a: f64, b: f64, t: f64) -> f64 {
47        a + t * (b - a)
48    }
49}
50
51impl NoiseSource for Perlin {
52    fn sample(&self, x: f64, y: f64) -> f64 {
53        let x = x * self.frequency;
54        let y = y * self.frequency;
55
56        let xi = x.floor() as i32 & 255;
57        let yi = y.floor() as i32 & 255;
58        let xf = x - x.floor();
59        let yf = y - y.floor();
60
61        let u = Self::fade(xf);
62        let v = Self::fade(yf);
63
64        let xi = xi as usize;
65        let yi = yi as usize;
66        let xi1 = xi + 1;
67        let yi1 = yi + 1;
68
69        let aa = self.perm[xi + self.perm[yi] as usize];
70        let ab = self.perm[xi + self.perm[yi1] as usize];
71        let ba = self.perm[xi1 + self.perm[yi] as usize];
72        let bb = self.perm[xi1 + self.perm[yi1] as usize];
73
74        let x1 = xf - 1.0;
75        let y1 = yf - 1.0;
76
77        let n00 = Self::gradient(aa, xf, yf);
78        let n10 = Self::gradient(ba, x1, yf);
79        let n01 = Self::gradient(ab, xf, y1);
80        let n11 = Self::gradient(bb, x1, y1);
81
82        let nx0 = Self::lerp(n00, n10, u);
83        let nx1 = Self::lerp(n01, n11, u);
84
85        Self::lerp(nx0, nx1, v)
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn perlin_deterministic() {
95        let noise = Perlin::new(12345);
96        let v1 = noise.sample(1.5, 2.5);
97        let v2 = noise.sample(1.5, 2.5);
98        assert_eq!(v1, v2);
99    }
100
101    #[test]
102    fn perlin_different_seeds() {
103        let n1 = Perlin::new(12345);
104        let n2 = Perlin::new(54321);
105        assert_ne!(n1.sample(1.5, 2.5), n2.sample(1.5, 2.5));
106    }
107
108    #[test]
109    fn perlin_range() {
110        let noise = Perlin::new(42);
111        for i in 0..100 {
112            for j in 0..100 {
113                let v = noise.sample(i as f64 * 0.1, j as f64 * 0.1);
114                assert!(
115                    (-1.5..=1.5).contains(&v),
116                    "Value {} out of expected range",
117                    v
118                );
119            }
120        }
121    }
122}