Created
December 22, 2017 16:10
-
-
Save eisterman/b7e96ea2361df8d46cddfba2d8385248 to your computer and use it in GitHub Desktop.
Mandelbrot Fractal Singlethread
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extern crate num; | |
use num::Complex; | |
/// Try to determine if `c` is in the Mandelbrot set, using at most `limit` | |
/// iterations to decide. | |
/// | |
/// If `c` is not a member, return `Some(i)` where `i` is the number of | |
/// iterations it took for `c` to leave the circle of radius two centered on the | |
/// origin. If `c` seems to be a member (more precisely, if we reached the | |
/// iteration limit without being able to prove that `c` is not a member), | |
/// return `None`. | |
fn escape_time(c: Complex<f64>, limit: u32) -> Option<u32> { | |
let mut z = Complex { re: 0.0, im: 0.0 }; | |
for i in 0..limit { | |
z = z*z + c; | |
if z.norm_sqr() > 4.0 { | |
return Some(i); | |
} | |
} | |
None | |
} | |
use std::str::FromStr; | |
/// Parse the string `s` as a coordinate pair, like `"400x600"` or `"1.0,0.5"`. | |
/// | |
/// Specifically, `s` should have the form <left><sep><right>, where <sep> is | |
/// the character given by the `separator` argument, and <left> and <right> are | |
/// both strings that can be parsed by `T::from_str`. | |
/// | |
/// If `s` has the proper form, return `Some<(x,y)>`. If it doesn't parse | |
/// correctly, return `None`. | |
fn parse_pair<T: FromStr>(s: &str, separator: char) -> Option<(T, T)> { | |
match s.find(separator) { | |
None => None, | |
Some(index) => { | |
match (T::from_str(&s[..index]), T::from_str(&s[index + 1..])) { | |
(Ok(l), Ok(r)) => Some((l,r)), | |
_ => None | |
} | |
} | |
} | |
} | |
#[test] | |
fn test_parse_pair() { | |
assert_eq!(parse_pair::<i32>("", ','), None); | |
assert_eq!(parse_pair::<i32>("10,", ','), None); | |
assert_eq!(parse_pair::<i32>(",10", ','), None); | |
assert_eq!(parse_pair::<i32>("10,20", ','), Some((10, 20))); | |
assert_eq!(parse_pair::<i32>("10,20xy", ','), None); | |
assert_eq!(parse_pair::<f64>("0.5x", 'x'), None); | |
assert_eq!(parse_pair::<f64>("0.5x1.5", 'x'), Some((0.5, 1.5))); | |
} | |
/// Parse a pair of floating-point numbers separated by a comma as a complex | |
/// number. | |
fn parse_complex(s: &str) -> Option<Complex<f64>> { | |
match parse_pair(s, ',') { | |
Some((re, im)) => Some(Complex { re, im }), | |
None => None | |
} | |
} | |
#[test] | |
fn test_parse_complex() { | |
assert_eq!(parse_complex("1.25,-0.0625"), | |
Some(Complex { re: 1.25, im: -0.0625 })); | |
assert_eq!(parse_complex(",-0.0625"), None); | |
} | |
/// Given the row and column of a pixel in the output image, return the | |
/// corresponding point on the complex plane. | |
/// | |
/// `bounds` is a pair giving the width and height of the image in pixels. | |
/// `pixel` is a (column, row) pair indicating a particular pixel in that image. | |
/// The `upper_left` and `lower_right` parameters are points on the complex | |
/// plane designating the area our image covers. | |
fn pixel_to_point(bounds: (usize, usize), | |
pixel: (usize, usize), | |
upper_left: Complex<f64>, | |
lower_right: Complex<f64>) -> Complex<f64> { | |
let (width, height) = (lower_right.re - upper_left.re, | |
upper_left.im - lower_right.im); | |
Complex { | |
re: upper_left.re + pixel.0 as f64 * width / bounds.0 as f64, | |
im: upper_left.im - pixel.1 as f64 * height / bounds.1 as f64 } | |
} | |
#[test] | |
fn test_pixel_to_point() { | |
assert_eq!(pixel_to_point((100,100), (25,75), | |
Complex{ re: -1.0, im: 1.0}, | |
Complex{ re: 1.0, im: -1.0}), | |
Complex{ re: -0.5, im:-0.5}); | |
} | |
/// Render a rectangle of the Mandelbrot set into a buffer of pixels. | |
/// | |
/// The `bounds` argument gives the width and height of the buffer `pixels`, | |
/// which holds one grayscale pixel per byte. The `upper_left` and | |
/// `lower_right` argument specify points on the complex plane corresponding | |
/// to the upper-left and lower-right corners of the pixel buffer. | |
fn render(pixels: &mut [u8], | |
bounds: (usize, usize), | |
upper_left: Complex<f64>, | |
lower_right: Complex<f64>) | |
{ | |
assert!(pixels.len() == bounds.0 * bounds.1); | |
for row in 0 .. bounds.1 { | |
for column in 0 .. bounds.0 { | |
let point = pixel_to_point(bounds, | |
(column, row), | |
upper_left, | |
lower_right); | |
pixels[row* bounds.0 + column] = | |
match escape_time(point, 255) { | |
None => 0, | |
Some(count) => 255 - count as u8 | |
}; | |
} | |
} | |
} | |
extern crate image; | |
use image::ColorType; | |
use image::png::PNGEncoder; | |
use std::fs::File; | |
/// Write the buffer `pixels`, whose dimensions are given by `bounds`m to | |
/// the file named `filename`. | |
fn write_image(filename: &str, pixels: &[u8], bounds: (usize, usize)) | |
-> std::io::Result<()> //Result<(), std::io::Error> | |
{ | |
let output = File::create(filename)?; | |
let encoder = PNGEncoder::new(output); | |
encoder.encode(&pixels, | |
bounds.0 as u32, | |
bounds.1 as u32, | |
ColorType::Gray(8))?; | |
Ok(()) | |
} | |
use std::io::Write; | |
fn main() { | |
let args: Vec<String> = std::env::args().collect(); | |
if args.len() != 5 { | |
writeln!(std::io::stderr(), | |
"Usare: mandelbrot FILE PIXELS UPPERLEFT LOWERRIGHT").unwrap(); | |
writeln!(std::io::stderr(), | |
"Example: {} mandel.png 1000x750 -1.20,0.35 -1,0.20", | |
args[0]).unwrap(); | |
std::process::exit(1); | |
} | |
let bounds = parse_pair(&args[2], 'x') | |
.expect("error parsing image dimensions"); | |
let upper_left = parse_complex(&args[3]) | |
.expect("error parsing upper left corner point"); | |
let lower_right = parse_complex(&args[4]) | |
.expect("error parsing lower right corner point"); | |
// Macro, create a Vector of `0` with dimension `bounds.0 * bounds.1`. | |
let mut pixels = vec![0; bounds.0 * bounds.1]; | |
render(&mut pixels, bounds, upper_left, lower_right); | |
write_image(&args[1], &pixels, bounds) | |
.expect("error writing PNG file"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment