ln-fractal/src/main.rs
2024-11-27 03:20:50 -05:00

87 lines
2.3 KiB
Rust

use image::{ImageBuffer, Rgb};
use rayon::prelude::*;
// Image size.
const IMGW: u32 = 1024;
const IMGH: u32 = 1024;
// Fractal settings.
const MAXITER: u32 = IMGW;
const MAXITER_F64: f64 = MAXITER as f64;
const ESCRAD: f64 = 2.4;
fn main() {
let w = IMGW as f64;
let h = IMGH as f64;
// Fractal bounds.
let mut width: f64 = 2.0; // Initial frame width
let mut xmin: f64 = -1.0;
let mut ymin: f64 = -1.0;
let target_x = -0.4638686756;
let target_y = 0.0723647;
let zoom_factor = 1.2;
for i in 1..200 {
let pxs: Vec<u8> = (0..IMGH)
.into_par_iter()
.flat_map(|y| {
let fy = y as f64;
(0..IMGW)
.into_par_iter()
.flat_map(move |x| {
let cx = map_range(x as f64, 0.0, w, xmin, xmin + width);
let cy = map_range(fy, 0.0, h, ymin, ymin + width);
let c = num_complex::Complex::new(cx, cy);
// Compute color for each pixel.
col_map(esc_time(c)).to_vec()
})
.collect::<Vec<u8>>()
})
.collect();
// Create the image from the pixel buffer.
let img = ImageBuffer::<Rgb<u8>, _>::from_raw(IMGW, IMGH, pxs).unwrap();
img.save(format!("imgs/{i}.png")).unwrap();
println!("#{i} done.");
// Adjust bounds for the next zoom level
xmin = target_x - width / 2.0;
ymin = target_y - width / 2.0;
width /= zoom_factor; // Zoom in by reducing width
}
}
// Maps a value from one range to another.
fn map_range(val: f64, imin: f64, imax: f64, omin: f64, omax: f64) -> f64 {
omin + (val - imin) * (omax - omin) / (imax - imin)
}
// Computes the escape time for a given point in the complex plane.
fn esc_time(c: num_complex::Complex<f64>) -> u32 {
let mut z = c;
for i in 0..MAXITER {
if z.norm() > ESCRAD {
return i;
}
z = (-z).ln() / z.ln();
}
MAXITER
}
// Maps the iteration count to a color.
fn col_map(iter: u32) -> [u8; 3] {
if iter == MAXITER {
[0, 0, 0] // Black for points that never escape.
} else {
let t = iter as f64 / MAXITER_F64;
let c = (t * MAXITER_F64) as u8;
[c, c, c]
}
}