terrain_forge/noise/
value.rs1use super::NoiseSource;
2
3pub struct Value {
5 seed: u64,
6 frequency: f64,
7}
8
9impl Value {
10 pub fn new(seed: u64) -> Self {
11 Self {
12 seed,
13 frequency: 1.0,
14 }
15 }
16
17 pub fn with_frequency(mut self, frequency: f64) -> Self {
18 self.frequency = frequency;
19 self
20 }
21
22 fn hash(&self, x: i32, y: i32) -> f64 {
24 let h = (x as u64)
25 .wrapping_mul(374761393)
26 .wrapping_add((y as u64).wrapping_mul(668265263))
27 .wrapping_add(self.seed);
28 let h = (h ^ (h >> 13)).wrapping_mul(1274126177);
29 (h as i64 as f64) / (i64::MAX as f64)
31 }
32
33 fn lerp(a: f64, b: f64, t: f64) -> f64 {
34 a + t * (b - a)
35 }
36
37 fn smoothstep(t: f64) -> f64 {
38 t * t * (3.0 - 2.0 * t)
39 }
40}
41
42impl NoiseSource for Value {
43 fn sample(&self, x: f64, y: f64) -> f64 {
44 let x = x * self.frequency;
45 let y = y * self.frequency;
46
47 let x0 = x.floor() as i32;
48 let y0 = y.floor() as i32;
49
50 let dx = Self::smoothstep(x - x0 as f64);
51 let dy = Self::smoothstep(y - y0 as f64);
52
53 let v00 = self.hash(x0, y0);
54 let v10 = self.hash(x0 + 1, y0);
55 let v01 = self.hash(x0, y0 + 1);
56 let v11 = self.hash(x0 + 1, y0 + 1);
57
58 let vx0 = Self::lerp(v00, v10, dx);
59 let vx1 = Self::lerp(v01, v11, dx);
60
61 Self::lerp(vx0, vx1, dy)
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn value_deterministic() {
71 let noise = Value::new(12345);
72 assert_eq!(noise.sample(1.5, 2.5), noise.sample(1.5, 2.5));
73 }
74
75 #[test]
76 fn value_range() {
77 let noise = Value::new(42);
78 for i in 0..100 {
79 for j in 0..100 {
80 let v = noise.sample(i as f64 * 0.1, j as f64 * 0.1);
81 assert!((-1.0..=1.0).contains(&v), "Value {} out of range", v);
82 }
83 }
84 }
85}