terrain_forge/algorithms/
fractal.rs

1use crate::{Algorithm, Grid, Rng, Tile};
2
3#[derive(Debug, Clone)]
4pub struct FractalConfig {
5    // Which fractal to generate: "mandelbrot" or "julia"
6    pub fractal_type: String,
7    pub max_iterations: usize,
8}
9
10impl Default for FractalConfig {
11    fn default() -> Self {
12        Self {
13            fractal_type: "mandelbrot".to_string(),
14            max_iterations: 100,
15        }
16    }
17}
18
19pub struct Fractal {
20    config: FractalConfig,
21}
22
23impl Fractal {
24    pub fn new(config: FractalConfig) -> Self {
25        Self { config }
26    }
27}
28
29impl Default for Fractal {
30    fn default() -> Self {
31        Self::new(FractalConfig::default())
32    }
33}
34
35impl Algorithm<Tile> for Fractal {
36    fn generate(&self, grid: &mut Grid<Tile>, seed: u64) {
37        let mut rng = Rng::new(seed);
38        if self.config.fractal_type == "mandelbrot" {
39            generate_mandelbrot(grid, self.config.max_iterations);
40        } else if self.config.fractal_type == "julia" {
41            generate_julia(grid, &mut rng, self.config.max_iterations);
42        } else {
43            panic!("Unknown fractal type: {}", self.config.fractal_type);
44        }
45    }
46
47    fn name(&self) -> &'static str {
48        "Fractal"
49    }
50}
51
52fn generate_mandelbrot(grid: &mut Grid<Tile>, max_iter: usize) {
53    let (w, h) = (grid.width(), grid.height());
54
55    for y in 0..h {
56        for x in 0..w {
57            let cx = (x as f64 / w as f64 - 0.5) * 4.0 - 0.5;
58            let cy = (y as f64 / h as f64 - 0.5) * 4.0;
59
60            let mut zx = 0.0;
61            let mut zy = 0.0;
62            let mut iter = 0;
63
64            while zx * zx + zy * zy < 4.0 && iter < max_iter {
65                let temp = zx * zx - zy * zy + cx;
66                zy = 2.0 * zx * zy + cy;
67                zx = temp;
68                iter += 1;
69            }
70
71            if iter < max_iter / 3 {
72                grid.set(x as i32, y as i32, Tile::Floor);
73            }
74        }
75    }
76}
77
78fn generate_julia(grid: &mut Grid<Tile>, rng: &mut Rng, max_iter: usize) {
79    let (w, h) = (grid.width(), grid.height());
80    // Constrain Julia constants to a range that reliably yields structure.
81    let cx = rng.random() * 1.6 - 0.8;
82    let cy = rng.random() * 1.6 - 0.8;
83
84    for y in 0..h {
85        for x in 0..w {
86            let mut zx = (x as f64 / w as f64 - 0.5) * 3.0;
87            let mut zy = (y as f64 / h as f64 - 0.5) * 3.0;
88            let mut iter = 0;
89
90            while zx * zx + zy * zy < 4.0 && iter < max_iter {
91                let temp = zx * zx - zy * zy + cx;
92                zy = 2.0 * zx * zy + cy;
93                zx = temp;
94                iter += 1;
95            }
96
97            if iter < max_iter / 2 {
98                grid.set(x as i32, y as i32, Tile::Floor);
99            }
100        }
101    }
102}