Skip to main content

terrain_forge/algorithms/
voronoi.rs

1use crate::{Algorithm, Grid, Rng, Tile};
2
3#[derive(Debug, Clone)]
4pub struct VoronoiConfig {
5    pub num_points: usize,
6    pub floor_chance: f64,
7}
8
9impl Default for VoronoiConfig {
10    fn default() -> Self {
11        Self {
12            num_points: 15,
13            floor_chance: 0.5,
14        }
15    }
16}
17
18pub struct Voronoi {
19    config: VoronoiConfig,
20}
21
22impl Voronoi {
23    pub fn new(config: VoronoiConfig) -> Self {
24        Self { config }
25    }
26}
27
28impl Default for Voronoi {
29    fn default() -> Self {
30        Self::new(VoronoiConfig::default())
31    }
32}
33
34impl Algorithm<Tile> for Voronoi {
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 points: Vec<(usize, usize)> = (0..self.config.num_points)
40            .map(|_| (rng.range_usize(1, w - 1), rng.range_usize(1, h - 1)))
41            .collect();
42
43        let is_floor: Vec<bool> = (0..self.config.num_points)
44            .map(|_| rng.chance(self.config.floor_chance))
45            .collect();
46
47        for y in 1..h - 1 {
48            for x in 1..w - 1 {
49                let mut min_dist = usize::MAX;
50                let mut closest = 0;
51                for (i, &(px, py)) in points.iter().enumerate() {
52                    let dist = (x as i32 - px as i32).unsigned_abs() as usize
53                        + (y as i32 - py as i32).unsigned_abs() as usize;
54                    if dist < min_dist {
55                        min_dist = dist;
56                        closest = i;
57                    }
58                }
59                if is_floor[closest] {
60                    grid.set(x as i32, y as i32, Tile::Floor);
61                }
62            }
63        }
64    }
65
66    fn name(&self) -> &'static str {
67        "Voronoi"
68    }
69}