mandelbrot/src/mandelbrot.rs

206 lines
6.1 KiB
Rust

use image::{ImageBuffer, Rgb, RgbImage};
use num_complex::Complex;
use rayon::prelude::*;
const MAX_ITERATIONS: u16 = 256;
const IMG_WIDTH: usize = 1280;
const IMG_HEIGHT: usize = 720;
#[derive(Debug, Clone)]
pub struct Mandelbrot {
iterations: Vec<u16>,
}
enum Colour {
White,
Black,
Gray,
}
impl Mandelbrot {
pub fn new() -> Self {
Mandelbrot {
iterations: vec![0; IMG_WIDTH * IMG_HEIGHT],
}
}
pub fn generate_image(&mut self) -> Vec<u8> {
// Create the image
// Pixel Format can be Rgba8UnormSrgb
self.escape_time();
let mut texture_buffer = vec![0; (IMG_WIDTH * IMG_HEIGHT) * 4];
for i in 0..texture_buffer.len() {
if i % 4 == 0 {
let iter_i = i / 4;
match Self::find_colour(self.iterations[iter_i]) {
Colour::White => {
texture_buffer[i] = 0x00;
texture_buffer[i + 1] = 0x00;
texture_buffer[i + 2] = 0x00;
texture_buffer[i + 3] = 0xFF;
}
Colour::Black => {
texture_buffer[i] = 0xFF;
texture_buffer[i + 1] = 0xFF;
texture_buffer[i + 2] = 0xFF;
texture_buffer[i + 3] = 0xFF;
}
Colour::Gray => {
texture_buffer[i] = 0xAA;
texture_buffer[i + 1] = 0xAA;
texture_buffer[i + 2] = 0xAA;
texture_buffer[i + 3] = 0xFF;
}
}
}
}
texture_buffer
}
pub fn generate_scaled_image(&mut self, x_bounds: (f64, f64), y_bounds: (f64, f64)) -> Vec<u8> {
let mut texture_buffer = vec![0; (IMG_WIDTH * IMG_HEIGHT) * 4];
self.iterations
.par_iter_mut()
.enumerate()
.for_each(|(i, value)| {
let px = i % IMG_WIDTH;
let py = i / IMG_WIDTH;
let c = Self::coords_to_complex(px, py, x_bounds, y_bounds);
*value = Self::calc_num_iterations(c);
});
for i in 0..texture_buffer.len() {
if i % 4 == 0 {
let iter_i = i / 4;
match Self::find_colour(self.iterations[iter_i]) {
Colour::White => {
texture_buffer[i] = 0x00;
texture_buffer[i + 1] = 0x00;
texture_buffer[i + 2] = 0x00;
texture_buffer[i + 3] = 0xFF;
}
Colour::Black => {
texture_buffer[i] = 0xFF;
texture_buffer[i + 1] = 0xFF;
texture_buffer[i + 2] = 0xFF;
texture_buffer[i + 3] = 0xFF;
}
Colour::Gray => {
texture_buffer[i] = 0xAA;
texture_buffer[i + 1] = 0xAA;
texture_buffer[i + 2] = 0xAA;
texture_buffer[i + 3] = 0xFF;
}
}
}
}
texture_buffer
}
fn escape_time(&mut self) {
self.iterations
.par_iter_mut()
.enumerate()
.for_each(|(i, value)| {
let px = i % IMG_WIDTH;
let py = i / IMG_WIDTH;
let c = Self::coords_to_complex(px, py, (-2.5, 1.0), (-1.0, 1.0));
*value = Self::calc_num_iterations(c);
});
}
pub fn escape_time_image() {
const IMG_WIDTH: usize = 1280;
const IMG_HEIGHT: usize = 720;
let mut img: RgbImage = ImageBuffer::new(IMG_WIDTH as u32, IMG_HEIGHT as u32);
let mut mandelbrot: Vec<u16> = vec![0; IMG_HEIGHT * IMG_WIDTH];
mandelbrot
.par_iter_mut()
.enumerate()
.for_each(|(i, value)| {
let px = i % IMG_WIDTH;
let py = i / IMG_WIDTH;
let c = Self::coords_to_complex(px, py, (-2.5, 1.0), (-1.0, 1.0));
*value = Self::calc_num_iterations(c);
});
for (px, py, pixel) in img.enumerate_pixels_mut() {
let i = px as usize + IMG_WIDTH * py as usize;
let num_iterations = mandelbrot[i];
match Self::find_colour(num_iterations) {
Colour::White => *pixel = Rgb([0x00, 0x00, 0x00]),
Colour::Black => *pixel = Rgb([0xFF, 0xFF, 0xFF]),
Colour::Gray => *pixel = Rgb([0xAA, 0xAA, 0xAA]),
}
}
img.save("assets/test.png").unwrap();
}
fn find_colour(iteration: u16) -> Colour {
match iteration {
MAX_ITERATIONS => Colour::Black,
other => match other % 2 {
0 => Colour::White,
1 => Colour::Gray,
_ => unreachable!(),
},
}
}
fn calc_num_iterations(c: Complex<f64>) -> u16 {
let mut z: Complex<f64> = Complex::new(0.0, 0.0);
let mut num_iterations: u16 = 0;
for i in 0..MAX_ITERATIONS {
if z.norm_sqr() > 4.0 {
num_iterations = i;
break;
}
z = (z * z) + c;
}
num_iterations
}
fn coords_to_complex(
px: usize,
py: usize,
x_bounds: (f64, f64),
y_bounds: (f64, f64),
) -> Complex<f64> {
Complex::new(
Self::scale_width(px, x_bounds),
Self::scale_height(py, y_bounds),
)
}
fn scale_width(px: usize, x_bounds: (f64, f64)) -> f64 {
// const X_MIN: f64 = -2.5;
// const X_MAX: f64 = 1.0;
x_bounds.0 + ((x_bounds.1 - x_bounds.0) * (px as f64 - 0.0)) / IMG_WIDTH as f64 - 0.0
}
fn scale_height(py: usize, y_bounds: (f64, f64)) -> f64 {
// const Y_MIN: f64 = -1.0;
// const Y_MAX: f64 = 1.0;
y_bounds.0 + ((y_bounds.1 - y_bounds.0) * (py as f64 - 0.0)) / IMG_HEIGHT as f64 - 0.0
}
}