terrain_forge/noise/
perlin.rs1use super::NoiseSource;
2
3pub 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}