As my go to first project for a language is a Mandelbrot, I had to implement one in Rust. I'm particularly annoyed by invalid_arg_count
, as I feel like there has to be a way to add the arg length checking to the match
. I also wish to find a way to multi-thread this in the future.
extern crate image;
extern crate num;
use std::env;
use num::Complex;
fn mandelbrot_at(c: Complex<f64>) -> bool {
const ITERATIONS: u32 = 1000;
let mut z: Complex<f64> = c.clone();
for _ in 0..ITERATIONS {
if z.norm_sqr() > 4.0 {
return false;
}
z = z * z + c;
}
true
}
fn mandelbrot_at_pixel(x: u32, y: u32, img_width: u32, img_height: u32) -> image::Rgb<u8> {
const MIN_X: f64 = -2.5;
const MAX_X: f64 = 1.0;
const MIN_Y: f64 = -1.0;
const MAX_Y: f64 = 1.0;
let x = (x as f64) / (img_width as f64) * (MAX_X - MIN_X) + MIN_X;
let y = (y as f64) / (img_height as f64) * (MAX_Y - MIN_Y) + MIN_Y;
if mandelbrot_at(Complex::new(x, y)) {
image::Rgb { data: [0xB7, 0x41, 0x0E] } // aka "rust"
} else {
image::Rgb { data: [0, 0, 0] }
}
}
fn mandelbrot(width: u32, height: u32) -> image::RgbImage {
image::RgbImage::from_fn(width,
height,
|x, y| mandelbrot_at_pixel(x, y, width, height))
}
fn invalid_arg_count() -> ! {
panic!("Must have exactly 3 arguments: width, height, destination (file)");
}
fn main() {
let args: Vec<String> = env::args().collect();
let args = match args.split_first() {
Some((_, slice)) => slice,
None => invalid_arg_count(),
};
if args.len() != 3 {
invalid_arg_count();
}
let width: u32 = args[0]
.parse()
.expect(&format!("Width must be an integer, was `{}`", args[0]));
let height: u32 = args[1]
.parse()
.expect(&format!("Height must be an integer, was `{}`", args[1]));
let ref dest = args[2];
println!("Generating mandelbrot of size {} by {}.", width, height);
let mandelbrot = mandelbrot(width, height);
println!("Writing mandelbrot to {}", dest);
mandelbrot.save(dest).unwrap();
}
This has dependencies on image
and num
.
Example usage:
mandelbrot 200 100 test.png
Output: