terrain_forge/algorithms/
diamond_square.rs1use crate::{Algorithm, Grid, Rng, Tile};
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct DiamondSquareConfig {
7 pub roughness: f64,
9 pub threshold: f64,
11}
12
13impl Default for DiamondSquareConfig {
14 fn default() -> Self {
15 Self {
16 roughness: 0.6,
17 threshold: 0.4,
18 }
19 }
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct DiamondSquare {
25 config: DiamondSquareConfig,
26}
27
28impl DiamondSquare {
29 pub fn new(config: DiamondSquareConfig) -> Self {
31 Self { config }
32 }
33}
34
35impl Default for DiamondSquare {
36 fn default() -> Self {
37 Self::new(DiamondSquareConfig::default())
38 }
39}
40
41impl Algorithm<Tile> for DiamondSquare {
42 fn generate(&self, grid: &mut Grid<Tile>, seed: u64) {
43 let mut rng = Rng::new(seed);
44 let (w, h) = (grid.width(), grid.height());
45
46 let mut heights = vec![vec![0.0f64; w]; h];
48
49 for row in heights.iter_mut() {
51 for cell in row.iter_mut() {
52 *cell = rng.random();
53 }
54 }
55
56 let mut step = w.max(h) / 2;
58 let mut scale = self.config.roughness;
59
60 while step > 0 {
61 let mut y = step;
63 while y < h {
64 let mut x = step;
65 while x < w {
66 let mut sum = 0.0;
67 let mut count = 0;
68
69 if y >= step && x >= step {
70 sum += heights[y - step][x - step];
71 count += 1;
72 }
73 if y >= step && x + step < w {
74 sum += heights[y - step][x + step];
75 count += 1;
76 }
77 if y + step < h && x >= step {
78 sum += heights[y + step][x - step];
79 count += 1;
80 }
81 if y + step < h && x + step < w {
82 sum += heights[y + step][x + step];
83 count += 1;
84 }
85
86 if count > 0 {
87 heights[y][x] =
88 (sum / count as f64 + (rng.random() - 0.5) * scale).clamp(0.0, 1.0);
89 }
90 x += step * 2;
91 }
92 y += step * 2;
93 }
94
95 for y in 0..h {
97 let x_start = if (y / step) % 2 == 0 { step } else { 0 };
98 let mut x = x_start;
99 while x < w {
100 let mut sum = 0.0;
101 let mut count = 0;
102 if x >= step {
103 sum += heights[y][x - step];
104 count += 1;
105 }
106 if x + step < w {
107 sum += heights[y][x + step];
108 count += 1;
109 }
110 if y >= step {
111 sum += heights[y - step][x];
112 count += 1;
113 }
114 if y + step < h {
115 sum += heights[y + step][x];
116 count += 1;
117 }
118 if count > 0 {
119 heights[y][x] =
120 (sum / count as f64 + (rng.random() - 0.5) * scale).clamp(0.0, 1.0);
121 }
122 x += step * 2;
123 }
124 }
125
126 step /= 2;
127 scale *= 0.5;
128 }
129
130 for (y, row) in heights.iter().enumerate() {
132 for (x, &height) in row.iter().enumerate() {
133 if height > self.config.threshold {
134 grid.set(x as i32, y as i32, Tile::Floor);
135 }
136 }
137 }
138 }
139
140 fn name(&self) -> &'static str {
141 "DiamondSquare"
142 }
143}