terrain_forge/noise/
value.rs

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