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