terrain_forge/algorithms/
diamond_square.rs

1use crate::{Algorithm, Grid, Rng, Tile};
2
3#[derive(Debug, Clone)]
4pub struct DiamondSquareConfig {
5    pub roughness: f64,
6    pub threshold: f64,
7}
8
9impl Default for DiamondSquareConfig {
10    fn default() -> Self { Self { roughness: 0.6, threshold: 0.4 } }
11}
12
13pub struct DiamondSquare {
14    config: DiamondSquareConfig,
15}
16
17impl DiamondSquare {
18    pub fn new(config: DiamondSquareConfig) -> Self { Self { config } }
19}
20
21impl Default for DiamondSquare {
22    fn default() -> Self { Self::new(DiamondSquareConfig::default()) }
23}
24
25impl Algorithm<Tile> for DiamondSquare {
26    fn generate(&self, grid: &mut Grid<Tile>, seed: u64) {
27        let mut rng = Rng::new(seed);
28        let (w, h) = (grid.width(), grid.height());
29        
30        // Create heightmap
31        let mut heights = vec![vec![0.0f64; w]; h];
32        
33        // Initialize with noise
34        for row in heights.iter_mut() {
35            for cell in row.iter_mut() {
36                *cell = rng.random();
37            }
38        }
39        
40        // Diamond-square iterations to smooth
41        let mut step = w.max(h) / 2;
42        let mut scale = self.config.roughness;
43        
44        while step > 0 {
45            // Diamond step - set center of each square
46            let mut y = step;
47            while y < h {
48                let mut x = step;
49                while x < w {
50                    let mut sum = 0.0;
51                    let mut count = 0;
52                    
53                    if y >= step && x >= step { sum += heights[y - step][x - step]; count += 1; }
54                    if y >= step && x + step < w { sum += heights[y - step][x + step]; count += 1; }
55                    if y + step < h && x >= step { sum += heights[y + step][x - step]; count += 1; }
56                    if y + step < h && x + step < w { sum += heights[y + step][x + step]; count += 1; }
57                    
58                    if count > 0 {
59                        heights[y][x] = (sum / count as f64 + (rng.random() - 0.5) * scale).clamp(0.0, 1.0);
60                    }
61                    x += step * 2;
62                }
63                y += step * 2;
64            }
65            
66            // Square step - set edge midpoints
67            for (y, _row) in heights.iter_mut().enumerate() {
68                let x_start = if (y / step) % 2 == 0 { step } else { 0 };
69                let mut x = x_start;
70                while x < w {
71                    x += step * 2;
72                }
73            }
74            
75            step /= 2;
76            scale *= 0.5;
77        }
78        
79        // Convert to tiles
80        for (y, row) in heights.iter().enumerate() {
81            for (x, &height) in row.iter().enumerate() {
82                if height > self.config.threshold {
83                    grid.set(x as i32, y as i32, Tile::Floor);
84                }
85            }
86        }
87    }
88
89    fn name(&self) -> &'static str { "DiamondSquare" }
90}