Skip to main content

proof_engine/fractal/
julia.rs

1//! Julia set rendering.
2
3use super::mandelbrot::FractalPixel;
4
5#[derive(Debug, Clone)]
6pub struct JuliaParams {
7    pub c_re: f64, pub c_im: f64,
8    pub center_re: f64, pub center_im: f64,
9    pub zoom: f64, pub max_iter: u32,
10    pub escape_radius: f64,
11    pub width: u32, pub height: u32,
12}
13impl Default for JuliaParams {
14    fn default() -> Self {
15        Self { c_re: -0.7, c_im: 0.27015, center_re: 0.0, center_im: 0.0, zoom: 1.0, max_iter: 256, escape_radius: 4.0, width: 800, height: 600 }
16    }
17}
18
19pub struct JuliaRenderer;
20impl JuliaRenderer {
21    pub fn compute_pixel(z_re: f64, z_im: f64, c_re: f64, c_im: f64, max_iter: u32, escape_r2: f64) -> FractalPixel {
22        let mut zr = z_re; let mut zi = z_im;
23        for i in 0..max_iter {
24            let r2 = zr * zr; let i2 = zi * zi;
25            if r2 + i2 > escape_r2 {
26                let smooth = i as f64 + 1.0 - ((r2 + i2).ln() * 0.5 / 2.0_f64.ln()).ln() / 2.0_f64.ln();
27                return FractalPixel { iterations: i, smooth_iter: smooth, escaped: true, final_z_re: zr, final_z_im: zi };
28            }
29            zi = 2.0 * zr * zi + c_im;
30            zr = r2 - i2 + c_re;
31        }
32        FractalPixel { iterations: max_iter, smooth_iter: max_iter as f64, escaped: false, final_z_re: zr, final_z_im: zi }
33    }
34
35    pub fn render(params: &JuliaParams) -> Vec<FractalPixel> {
36        let (w, h) = (params.width, params.height);
37        let aspect = w as f64 / h as f64;
38        let scale = 2.0 / params.zoom;
39        let escape_r2 = params.escape_radius * params.escape_radius;
40        let mut pixels = Vec::with_capacity((w * h) as usize);
41        for py in 0..h { for px in 0..w {
42            let z_re = params.center_re + (px as f64 / w as f64 - 0.5) * scale * aspect;
43            let z_im = params.center_im + (py as f64 / h as f64 - 0.5) * scale;
44            pixels.push(Self::compute_pixel(z_re, z_im, params.c_re, params.c_im, params.max_iter, escape_r2));
45        }}
46        pixels
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53    #[test]
54    fn julia_renders() {
55        let params = JuliaParams { width: 10, height: 10, max_iter: 50, ..Default::default() };
56        let pixels = JuliaRenderer::render(&params);
57        assert_eq!(pixels.len(), 100);
58    }
59}