Skip to main content

terrain_forge/noise/
simplex.rs

1use super::NoiseSource;
2
3/// Simplex noise - faster than Perlin with fewer directional artifacts
4pub struct Simplex {
5    seed: u64,
6    frequency: f64,
7}
8
9impl Simplex {
10    const F2: f64 = 0.3660254037844386; // (sqrt(3) - 1) / 2
11    const G2: f64 = 0.21132486540518713; // (3 - sqrt(3)) / 6
12
13    /// Creates a new noise generator from the given seed.
14    pub fn new(seed: u64) -> Self {
15        Self {
16            seed,
17            frequency: 1.0,
18        }
19    }
20
21    /// Sets the base frequency.
22    pub fn with_frequency(mut self, frequency: f64) -> Self {
23        self.frequency = frequency;
24        self
25    }
26
27    fn hash(&self, x: i32, y: i32) -> usize {
28        let h = (x as u64)
29            .wrapping_mul(374761393)
30            .wrapping_add((y as u64).wrapping_mul(668265263))
31            .wrapping_add(self.seed);
32        (h ^ (h >> 13)).wrapping_mul(1274126177) as usize % 12
33    }
34
35    fn grad(hash: usize, x: f64, y: f64) -> f64 {
36        const GRAD: [(f64, f64); 12] = [
37            (1.0, 1.0),
38            (-1.0, 1.0),
39            (1.0, -1.0),
40            (-1.0, -1.0),
41            (1.0, 0.0),
42            (-1.0, 0.0),
43            (0.0, 1.0),
44            (0.0, -1.0),
45            (1.0, 1.0),
46            (-1.0, 1.0),
47            (1.0, -1.0),
48            (-1.0, -1.0),
49        ];
50        let (gx, gy) = GRAD[hash];
51        gx * x + gy * y
52    }
53}
54
55impl NoiseSource for Simplex {
56    fn sample(&self, x: f64, y: f64) -> f64 {
57        let x = x * self.frequency;
58        let y = y * self.frequency;
59
60        let s = (x + y) * Self::F2;
61        let i = (x + s).floor() as i32;
62        let j = (y + s).floor() as i32;
63
64        let t = (i + j) as f64 * Self::G2;
65        let x0 = x - (i as f64 - t);
66        let y0 = y - (j as f64 - t);
67
68        let (i1, j1) = if x0 > y0 { (1, 0) } else { (0, 1) };
69
70        let x1 = x0 - i1 as f64 + Self::G2;
71        let y1 = y0 - j1 as f64 + Self::G2;
72        let x2 = x0 - 1.0 + 2.0 * Self::G2;
73        let y2 = y0 - 1.0 + 2.0 * Self::G2;
74
75        let mut n = 0.0;
76        for &(dx, dy, di, dj) in &[(x0, y0, 0, 0), (x1, y1, i1, j1), (x2, y2, 1, 1)] {
77            let t = 0.5 - dx * dx - dy * dy;
78            if t > 0.0 {
79                let t2 = t * t;
80                n += t2 * t2 * Self::grad(self.hash(i + di, j + dj), dx, dy);
81            }
82        }
83        70.0 * n
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn simplex_deterministic() {
93        let noise = Simplex::new(12345);
94        assert_eq!(noise.sample(1.5, 2.5), noise.sample(1.5, 2.5));
95    }
96
97    #[test]
98    fn simplex_range() {
99        let noise = Simplex::new(42);
100        for i in 0..50 {
101            for j in 0..50 {
102                let v = noise.sample(i as f64 * 0.1, j as f64 * 0.1);
103                assert!((-1.0..=1.0).contains(&v), "Value {} out of range", v);
104            }
105        }
106    }
107}