terrain_forge/algorithms/
noise_fill.rs1use crate::noise::{NoiseExt, Perlin, Simplex, Value, Worley};
2use crate::{Algorithm, Grid, Tile};
3
4#[derive(Debug, Clone, Copy, Default)]
5pub enum NoiseType {
6 #[default]
7 Perlin,
8 Simplex,
9 Value,
10 Worley,
11}
12
13#[derive(Debug, Clone)]
14pub struct NoiseFillConfig {
15 pub noise: NoiseType,
16 pub frequency: f64,
18 pub scale: f64,
20 pub output_range: (f64, f64),
22 pub threshold: f64,
24 pub fill_range: Option<(f64, f64)>,
26 pub octaves: u32,
28 pub lacunarity: f64,
30 pub persistence: f64,
32}
33
34impl Default for NoiseFillConfig {
35 fn default() -> Self {
36 Self {
37 noise: NoiseType::Perlin,
38 frequency: 0.08,
39 scale: 1.0,
40 output_range: (0.0, 1.0),
41 threshold: 0.0,
42 fill_range: None,
43 octaves: 1,
44 lacunarity: 2.0,
45 persistence: 0.5,
46 }
47 }
48}
49
50pub struct NoiseFill {
51 config: NoiseFillConfig,
52}
53
54impl NoiseFill {
55 pub fn new(config: NoiseFillConfig) -> Self {
56 Self { config }
57 }
58}
59
60impl Default for NoiseFill {
61 fn default() -> Self {
62 Self::new(NoiseFillConfig::default())
63 }
64}
65
66impl Algorithm<Tile> for NoiseFill {
67 fn generate(&self, grid: &mut Grid<Tile>, seed: u64) {
68 let (w, h) = (grid.width(), grid.height());
69 let scale = if self.config.scale > 0.0 {
70 self.config.scale
71 } else {
72 1.0
73 };
74 let frequency = self.config.frequency / scale;
75
76 match self.config.noise {
77 NoiseType::Perlin => {
78 let noise = Perlin::new(seed).with_frequency(frequency);
79 fill_with_config(grid, noise, &self.config);
80 }
81 NoiseType::Simplex => {
82 let noise = Simplex::new(seed).with_frequency(frequency);
83 fill_with_config(grid, noise, &self.config);
84 }
85 NoiseType::Value => {
86 let noise = Value::new(seed).with_frequency(frequency);
87 fill_with_config(grid, noise, &self.config);
88 }
89 NoiseType::Worley => {
90 let noise = Worley::new(seed).with_frequency(frequency);
91 fill_with_config(grid, noise, &self.config);
92 }
93 }
94
95 if w > 0 && h > 0 {
97 for x in 0..w {
98 grid.set(x as i32, 0, Tile::Wall);
99 grid.set(x as i32, (h - 1) as i32, Tile::Wall);
100 }
101 for y in 0..h {
102 grid.set(0, y as i32, Tile::Wall);
103 grid.set((w - 1) as i32, y as i32, Tile::Wall);
104 }
105 }
106 }
107
108 fn name(&self) -> &'static str {
109 "NoiseFill"
110 }
111}
112
113fn fill_with_config<N: crate::noise::NoiseSource>(
114 grid: &mut Grid<Tile>,
115 noise: N,
116 config: &NoiseFillConfig,
117) {
118 let (mut out_min, mut out_max) = config.output_range;
119 if out_min > out_max {
120 std::mem::swap(&mut out_min, &mut out_max);
121 }
122 let range_span = out_max - out_min;
123 let fill_range = config
124 .fill_range
125 .map(|(a, b)| if a <= b { (a, b) } else { (b, a) });
126
127 if config.octaves > 1 {
128 let fbm = noise.fbm(config.octaves, config.lacunarity, config.persistence);
129 fill_from_noise(
130 grid,
131 &fbm,
132 out_min,
133 range_span,
134 fill_range,
135 config.threshold,
136 );
137 } else {
138 fill_from_noise(
139 grid,
140 &noise,
141 out_min,
142 range_span,
143 fill_range,
144 config.threshold,
145 );
146 }
147}
148
149fn fill_from_noise<N: crate::noise::NoiseSource>(
150 grid: &mut Grid<Tile>,
151 noise: &N,
152 out_min: f64,
153 range_span: f64,
154 fill_range: Option<(f64, f64)>,
155 threshold: f64,
156) {
157 let (w, h) = (grid.width(), grid.height());
158 for y in 0..h {
159 for x in 0..w {
160 let raw = noise.sample(x as f64, y as f64);
161 let mut value = (raw + 1.0) * 0.5;
162 value = out_min + value * range_span;
163
164 let fill = match fill_range {
165 Some((min, max)) => value >= min && value <= max,
166 None => value >= threshold,
167 };
168
169 let tile = if fill { Tile::Floor } else { Tile::Wall };
170 grid.set(x as i32, y as i32, tile);
171 }
172 }
173}