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