terrain_forge/algorithms/
diamond_square.rs1use 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 let mut heights = vec![vec![0.0f64; w]; h];
32
33 for row in heights.iter_mut() {
35 for cell in row.iter_mut() {
36 *cell = rng.random();
37 }
38 }
39
40 let mut step = w.max(h) / 2;
42 let mut scale = self.config.roughness;
43
44 while step > 0 {
45 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 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 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}