@ -25,14 +25,16 @@ use image::ImageFormat;
use image ::Rgb ;
use image ::RgbImage ;
use num ::Complex ;
use std ::sync ::{ Arc , Mutex } ;
/// z(n) = z(n-1)^2 + c
/// Returns amount of iterations to decide that given 'c' will escape to infinity
/// Returns amount of iterations that were necessary to decide that z escapes to infinity.
/// Returns None if z doesn`t escape to infinity even after specified number of iterations
fn mandelbrot ( c : num ::Complex < f64 > , iterations : u32 ) -> Option < u32 > {
let mut z : Complex < f64 > = num ::Complex ::new ( 0.0 , 0.0 ) ;
for i in 0 .. iterations {
for n in 0 .. iterations {
if z . norm_sqr ( ) > 4.0 {
return Some ( i ) ;
return Some ( n ) ;
}
z = z * z + c ;
}
@ -40,11 +42,38 @@ fn mandelbrot(c: num::Complex<f64>, iterations: u32) -> Option<u32> {
return None ;
}
/// Converts a pixel in an image plane to the point in the imaginary plane
fn pixel_to_set_point ( x : u32 , y : u32 , width : u32 , height : u32 , r_s : f64 , r_e : f64 , i_s : f64 , i_e : f64 ) -> num ::Complex < f64 > {
return num ::Complex ::new (
r_s + ( x as f64 / width as f64 ) * ( r_e - r_s ) ,
i_s + ( y as f64 / height as f64 ) * ( i_e - i_s ) ,
)
}
/// Computes a gray color of the pixel that corresponds to the point in the imaginary plane based on
/// amount of iteraitons
fn compute_pixel_color ( c : num ::Complex < f64 > , max_iter : u32 , palette : u8 ) -> u8 {
let pixel_color : u8 ;
if let Some ( iterations ) = mandelbrot ( c , max_iter ) {
pixel_color = ( ( iterations as f64 / 255.0 ) . sin ( ) * 255.0 ) as u8 ;
} else {
if palette = = 0 {
// light
pixel_color = 255 ;
} else {
// dark
pixel_color = 0 ;
}
}
return pixel_color ;
}
fn main ( ) {
const RE_START : f64 = - 2.0 ;
const RE_END : f64 = 1.0 ;
const IM_START : f64 = - 2.0 ;
const IM_END : f64 = 2.0 ;
let mut re_start : f64 = - 2.5 ;
let mut re_end : f64 = 1.5 ;
let mut im_start : f64 = - 2.0 ;
let mut im_end : f64 = 2.0 ;
let mut width : u32 = 7680 ;
let mut height : u32 = 4320 ;
@ -53,7 +82,7 @@ fn main() {
let mut palette : u8 = 0 ;
let matches = clap ::App ::new ( "mandelplot" )
. version ( "0.1 .0" )
. version ( "0.3 .0" )
. author ( "Kasyanov Nikolay Alexeyevich (Unbewohnte)" )
. arg (
Arg ::new ( "max_iter" )
@ -91,8 +120,18 @@ fn main() {
. long ( "palette" )
. short ( 'p' )
)
. arg (
Arg ::new ( "m_set_dimensions" )
. help ( "Set real and imaginary constraints (real_start,imaginary_startxreal_end,imaginary_end)" )
. takes_value ( true )
. required ( false )
. default_value ( "-2.5,-2.0x1.5,2.0" )
. long ( "m_set_dimensions" )
. short ( 'z' )
)
. get_matches ( ) ;
// process given options
if let Some ( arg_max_iter ) = matches . value_of ( "max_iter" ) {
max_iter = arg_max_iter . parse ::< u32 > ( ) . unwrap ( ) ;
}
@ -123,38 +162,70 @@ fn main() {
}
}
let mut img = RgbImage ::new ( width , height ) ;
for y in 0 .. height {
for x in 0 .. width {
let c = num ::Complex ::new (
RE_START + ( x as f64 / width as f64 ) * ( RE_END - RE_START ) ,
IM_START + ( y as f64 / height as f64 ) * ( IM_END - IM_START ) ,
) ;
let pixel_color : u8 ;
if let Some ( iterations ) = mandelbrot ( c , max_iter ) {
pixel_color = ( ( iterations as f64 / 255.0 ) . sin ( ) * 255.0 ) as u8 ;
} else {
if palette = = 0 {
// light
pixel_color = 255 ;
} else {
// dark
pixel_color = 0 ;
if let Some ( arg_m_set_dimensions ) = matches . value_of ( "m_set_dimensions" ) {
match arg_m_set_dimensions . split_once ( "x" ) {
Some ( ( start , end ) ) = > {
match start . split_once ( "," ) {
Some ( ( real_start , imaginary_start ) ) = > {
re_start = real_start . parse ::< f64 > ( ) . unwrap ( ) ;
im_start = imaginary_start . parse ::< f64 > ( ) . unwrap ( ) ;
}
None = > { }
}
match end . split_once ( "," ) {
Some ( ( real_end , imaginary_end ) ) = > {
re_end = real_end . parse ::< f64 > ( ) . unwrap ( ) ;
im_end = imaginary_end . parse ::< f64 > ( ) . unwrap ( ) ;
}
None = > { }
}
}
None = > { }
}
}
let img = Arc ::new ( Mutex ::new ( RgbImage ::new ( width , height ) ) ) ;
// run the algorithm in a naive multi-threaded way
const AMOUNT_OF_THREADS : usize = 24 ;
let thread_work = ( height as f32 / AMOUNT_OF_THREADS as f32 ) as u32 ;
let mut threads = Vec ::with_capacity ( AMOUNT_OF_THREADS ) ;
let mut from : u32 = 0 ;
for _ in 0 .. AMOUNT_OF_THREADS {
let img_copy = img . clone ( ) ;
threads . push ( std ::thread ::spawn ( move | | {
for y in from .. from + thread_work {
for x in 0 .. width {
let c = pixel_to_set_point ( x , y , width , height , re_start , re_end , im_start , im_end ) ;
let pixel_color = compute_pixel_color ( c , max_iter , palette ) ;
img_copy . lock ( ) . unwrap ( ) . put_pixel ( x , y , Rgb ( [ pixel_color , pixel_color , pixel_color ] ) ) ;
}
}
} ) ) ;
img . put_pixel ( x , y , Rgb ( [ pixel_color , pixel_color , pixel_color ] ) ) ;
from + = thread_work ;
}
// wait for everyone to finish
for thread in threads {
match thread . join ( ) {
Ok ( _ ) = > { }
Err ( e ) = > { eprintln! ( "Error waiting for thread to finish: {:?}" , e ) ; }
}
}
match img . save_with_format ( image_name + ".png" , ImageFormat ::Png ) {
// save image
match img . lock ( ) . unwrap ( ) . save_with_format ( image_name + ".png" , ImageFormat ::Png ) {
Ok ( _ ) = > {
println! ( "Saved" )
}
Err ( e ) = > {
eprintln! ( "Could not save image: {}" , e )
}
}
} ;
}