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 {
11 Self {
12 roughness: 0.6,
13 threshold: 0.4,
14 }
15 }
16}
17
18pub struct DiamondSquare {
19 config: DiamondSquareConfig,
20}
21
22impl DiamondSquare {
23 pub fn new(config: DiamondSquareConfig) -> Self {
24 Self { config }
25 }
26}
27
28impl Default for DiamondSquare {
29 fn default() -> Self {
30 Self::new(DiamondSquareConfig::default())
31 }
32}
33
34impl Algorithm<Tile> for DiamondSquare {
35 fn generate(&self, grid: &mut Grid<Tile>, seed: u64) {
36 let mut rng = Rng::new(seed);
37 let (w, h) = (grid.width(), grid.height());
38
39 let mut heights = vec![vec![0.0f64; w]; h];
41
42 for row in heights.iter_mut() {
44 for cell in row.iter_mut() {
45 *cell = rng.random();
46 }
47 }
48
49 let mut step = w.max(h) / 2;
51 let mut scale = self.config.roughness;
52
53 while step > 0 {
54 let mut y = step;
56 while y < h {
57 let mut x = step;
58 while x < w {
59 let mut sum = 0.0;
60 let mut count = 0;
61
62 if y >= step && x >= step {
63 sum += heights[y - step][x - step];
64 count += 1;
65 }
66 if y >= step && x + step < w {
67 sum += heights[y - step][x + step];
68 count += 1;
69 }
70 if y + step < h && x >= step {
71 sum += heights[y + step][x - step];
72 count += 1;
73 }
74 if y + step < h && x + step < w {
75 sum += heights[y + step][x + step];
76 count += 1;
77 }
78
79 if count > 0 {
80 heights[y][x] =
81 (sum / count as f64 + (rng.random() - 0.5) * scale).clamp(0.0, 1.0);
82 }
83 x += step * 2;
84 }
85 y += step * 2;
86 }
87
88 for (y, _row) in heights.iter_mut().enumerate() {
90 let x_start = if (y / step) % 2 == 0 { step } else { 0 };
91 let mut x = x_start;
92 while x < w {
93 x += step * 2;
94 }
95 }
96
97 step /= 2;
98 scale *= 0.5;
99 }
100
101 for (y, row) in heights.iter().enumerate() {
103 for (x, &height) in row.iter().enumerate() {
104 if height > self.config.threshold {
105 grid.set(x as i32, y as i32, Tile::Floor);
106 }
107 }
108 }
109 }
110
111 fn name(&self) -> &'static str {
112 "DiamondSquare"
113 }
114}