0%

Drawing Fractal (Mandelbrot Set) in Rust

September 23, 2025

Math

Rust

1. Result

2. Methametical Definition

For each , define , we say that is in a Mandelbrot set if the sequence

is bounded, which is the same as saying the seqeunce of complex numbers defined by is bounded with .

3. Coding

Since we have a sequential definition, in coding which is the same as saying

let mut z = Complex { re: 0.0, im: 0.0 };

for i in 0..n {
  z = z * z + c
}

is bounded for however large .

3.1. Preliminary Functions
3.1.1. Crates
use image::codecs::png::PngEncoder;
use image::{ExtendedColorType, ImageEncoder, ImageError};
use num::Complex;
use std::fs::File;
use std::time::Instant;
3.1.2. ecape_time
fn escape_time(c: Complex<f64>, limit: usize) -> Option<usize> {
    let mut z = Complex { re: 0.0, im: 0.0 };
    for i in 0..limit {
        if z.norm_sqr() > 4.0 {
            return Some(i);
        }
        z = z * z + c
    }
    None
}

A theorem in fractal analysis says that

Theorem. A point belongs to the Mandelbrot set if and only if for all .

To plot the graph of Mandelbrot set, it is enough to loop through a set of points in a complex plane and for each point we consider it as a point in Mandelbrot set if let mut z = c;, z = z * z + c; has norm smaller than 2 for each of 255 loops. If it is bigger than 2 for some iteration, we end the loop.

3.1.3. pixel_plane_to_complex_plane
fn pixel_plane_to_complex_plane(
    pixel_img_dim: (usize, usize),
    pixel_coordinate: (usize, usize),
    complex_plane_upper_left: Complex<f64>,
    complex_plane_lower_right: Complex<f64>,
) -> Complex<f64> {
    let complex_plane_width = complex_plane_lower_right.re - complex_plane_upper_left.re;
    let compelx_palne_height = complex_plane_upper_left.im - complex_plane_lower_right.im;
    let pixel_img_width = pixel_img_dim.0 as f64;
    let pixel_img_height = pixel_img_dim.1 as f64;
    let new_re = complex_plane_upper_left.re
        + (pixel_coordinate.0 as f64) * complex_plane_width / pixel_img_width;
    let new_im = complex_plane_lower_right.im
        + (pixel_img_height - pixel_coordinate.1 as f64) * compelx_palne_height / pixel_img_height;
    Complex {
        re: new_re,
        im: new_im,
    }
}
3.1.4. render
fn render(
    pixels: &mut [u8],
    image_dim: (usize, usize),
    complex_upper_left: Complex<f64>,
    complex_bottom_right: Complex<f64>,
) {
    assert!(pixels.len() == image_dim.0 * image_dim.1);
    for row in 0..image_dim.1 {
        for col in 0..image_dim.0 {
            let z = pixel_plane_to_complex_plane(
                image_dim,
                (col, row),
                complex_upper_left,
                complex_bottom_right,
            );
            pixels[row * image_dim.0 + col] = match escape_time(z, 255) {
                Some(count) => 255 - (count as u8),
                None => 0,
            }
        }
    }
}
3.1.5. write_png
fn write_png(file_name: &str, pixels: &[u8], dimension: (usize, usize)) -> Result<(), ImageError> {
    let output = File::create(file_name)?;
    let encoder = PngEncoder::new(output);
    encoder.write_image(
        pixels,
        dimension.0 as u32,
        dimension.1 as u32,
        ExtendedColorType::L8,
    )?;
    Ok(())
}
3.2. run_in_single_thread
fn run_in_single_thread() -> Result<(), ImageError> {
    let image_dim = (3000, 2000);
    let mut pixels = vec![0; image_dim.0 * image_dim.1];
    let z_upper_left = Complex {
        re: -1.20,
        im: 0.35,
    };
    let z_lower_right = Complex { re: -1.0, im: 0.20 };
    render(&mut pixels, image_dim, z_upper_left, z_lower_right);
    write_png("fractal.png", &pixels, image_dim)?;
    Ok(())
}