diff --git a/img.png b/img.png index 2bae902..f6791b2 100644 Binary files a/img.png and b/img.png differ diff --git a/src/MandelbrotSet.java b/src/MandelbrotSet.java deleted file mode 100644 index 9d34cbb..0000000 --- a/src/MandelbrotSet.java +++ /dev/null @@ -1,213 +0,0 @@ -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; - -import javax.imageio.ImageIO; - -public class MandelbrotSet { - - public static void main(String[] args) { - - double progress = 0; - double amount; - - // I think there might be some stuff mixed up in the code but it works - // not sure though - - double[][] interestingPoints = { { -0.75, 0 }, { -0.77568377, 0.13646737 } }; - - // important settings------------------------------------------------------------- - int pointNumber = 1; - int quality = 2; // 0 = very low, 1 = low, 2 = medium, 3 = HIGH, 4 = ultra, any number bigger than 5 = custom - double zoomSpeed = 1.1; // >1 - int frames = 100; - int width = 1920; - //................................................................................ - - // less important settings - boolean advancedIndicators = true; - double forceCenterX = interestingPoints[pointNumber][0]; - double forceCenterY = interestingPoints[pointNumber][1]; - boolean imageMode = true; - int height = 1080; - boolean locked = true; - float ratio = 2 / 3f; // .2 for editor 2/3 for image - double zoom = 1; - int iterations; - double threshold = 100; - char low = ' '; - char HIGH = '#'; - - if (quality == 0) { - iterations = 20; - } else if (quality == 1) { - iterations = 50; - } else if (quality == 2) { - iterations = 100; - } else if (quality == 3) { - iterations = 500; - } else if (quality == 4) { - iterations = 1000; - } else { - iterations = quality; - } - - double offsetX; - double offsetY; - if (locked) { - height = (int) ((float) width * ratio); - } - - // TIME - long startTime = System.currentTimeMillis(); - - for (int frameCounter = 0; frameCounter < frames; frameCounter++) { - // progress = 0; - BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); - File f = null; - - // the printed image is 3 * 2 (from x = -3 to x = 1 and - double stepSizeX = (3 / (float) width) / zoom; - double stepSizeY = (2 / (float) height) / zoom; - - offsetX = forceCenterX - width / 2 * stepSizeX; - offsetY = -(forceCenterY - height / 2 * stepSizeY); - - // calculate coords using stepSize and hardcoded corner coords: - // create an array of complex numbers, where the position of each sample will be stored - CNumber[][] coords = new CNumber[width][height]; - - - for (int i = 0; i < width; i++) { - for (int j = 0; j < height; j++) { - coords[i][j] = new CNumber(); // fill the array - coords[i][j].real = offsetX + stepSizeX * i; // calculate the position on the real numberline - coords[i][j].imag = offsetY - stepSizeY * j; // calculate the position on the imaginary numberline - } - } - - // calculate values - double[][] values = new double[width][height]; // new array of booleans for the drawing - for (int i = 0; i < width; i++) { - for (int j = 0; j < height; j++) { - values[i][j] = checkMandelbrot(coords[i][j], iterations, threshold); // check if the number is inside of th set - } - - } - - if (imageMode) { - - createImage(image, frameCounter, f, width, height, values); - - } else { - // draw - draw(low, HIGH, width, height, values); - } - - System.out.println("------------------------Frame " + frameCounter + " finished------------------------"); - - zoom *= zoomSpeed; - } - - // TIME - long endtime = System.currentTimeMillis(); - long completionTimeLong = endtime - startTime; - double completionTimeSec = (double) completionTimeLong / 1000.0; - System.out.println("Calculated " + frames + " frame/s. Process took " + completionTimeSec + "s"); - - } - - static void draw(char low, char HIGH, int width, int height, double[][] values) { - // a method to draw a filled rectangle of size width * height - // each cell can be low or HIGH, and it will show the corresponding char in each cell - String line; - - for (int i = 0; i < height; i++) { - line = ""; - // for every line: - for (int j = 0; j < width; j++) { - // for every char: - double value = values[j][i]; - if (value >= 1) { - line += HIGH; - } else { - line += low; - } - } - - System.out.println(line); - } - } - - static double checkMandelbrot(CNumber number, int iterations, double threshold) { - - // start - CNumber n = new CNumber(); - CNumber c = number; - - // first - n = CNumber.add(n, c); - - for (int i = 0; i < iterations; i++) { - n = CNumber.add(CNumber.multiply(n, n), c); // CNumber.multiply(n, n) - } - - // System.out.println(n.real + " " + n.imag); - - if (n.real < threshold && n.imag < threshold) { - return 1; - } else { - return 0; - } - - - } - - static void createImage(BufferedImage image, int counter, File f, int width, int height, double[][] values) { - - - System.out.println("Frame: " + counter + " | Started creating image..."); - - - - int p0 = getColorAsInt(0, 0, 0, 0); - int p1 = getColorAsInt(0, 50, 50, 50); - int p2 = getColorAsInt(0, 100, 100, 100); - int p3 = getColorAsInt(0, 150, 150, 150); - int p4 = getColorAsInt(0, 200, 200, 200); - int pMax = getColorAsInt(0, 255, 255, 255); - - int threshold1 = 10; - int threshold2 = 20; - - for (int i = 0; i < width; i++) { - for (int j = 0; j < height; j++) { - if (values[i][j] >= 1) { - image.setRGB(i, j, p0); - } else { - image.setRGB(i, j, pMax); - } - } - } - - try { - f = new File("sequence\\Sequence" + counter + ".png"); - ImageIO.write(image, "png", f); - System.out.println(f.getAbsolutePath()); - } catch (IOException e) { - System.out.println(e); - } - - System.out.println("Frame: " + counter + " | Finished creating image."); - } - - public static int getColorAsInt(int a, int r, int g, int b) { - - int p1 = (a << 24) | (r << 16) | (g << 8) | b; - - return p1; - } - -} - -// ^ originaler italienischer spaghetti code ^ \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index e07544a..9582322 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,29 +1,26 @@ -use std::error::Error; -use std::time::{SystemTime, Duration}; -use std::ops::{Add, Mul}; use std::env::Args; +use std::error::Error; use std::fmt::{Display, Formatter}; -use image::{RgbImage, ImageBuffer, ImageResult}; use std::io::{self, Write}; +use std::ops::{Add, Mul}; +use std::time::{Duration, SystemTime}; + +use image::{ImageBuffer, Rgb, RgbImage}; pub fn run(config: Config) -> Result<(), Box> { let start_time = SystemTime::now(); let debug = config.debug; - let result = check_whole_mandelbrot(&config); - - if debug { println!("calculated after: {}", format_time(start_time.elapsed()?)); } - if config.is_image { - create_image(&result, config.iterations, "img.png")?; - if debug { println!("image created after: {}", format_time(start_time.elapsed()?)); } - } - if config.is_console { + check_whole_mandelbrot_img_single_pass(&config)?; + } else { + let result = check_whole_mandelbrot(&config); let draw = draw(&result, config.iterations); println!("{}", draw); - if debug { println!("drawn after: {}", format_time(start_time.elapsed()?)); } } + if debug { println!("calculated in: {}", format_time(start_time.elapsed()?)); } + if debug { println!("Total Time: {}", format_time(start_time.elapsed()?)); } Ok(()) } @@ -88,15 +85,57 @@ fn check_mandelbrot(x: usize, y: usize, config: &Config, offset: &CNumber, step_ config.iterations } -static HIGH: &str = "#"; -static LOW: &str = " "; +fn check_whole_mandelbrot_img_single_pass(config: &Config) -> Result<(), Box> { + let height = config.width * 2.0 / 3.0; + + let mut image: RgbImage = ImageBuffer::new(config.width as u32, height as u32); + + let step_size = CNumber { + real: 3.0 / config.width, + imag: 2.0 / height, + }; + let offset = CNumber { + real: config.center.real - config.width / 2.0 * step_size.real, + imag: -(config.center.imag - height / 2.0 * step_size.imag) - 2.0, + }; + + + for i in 0..height as usize { + for j in 0..config.width as usize { + let value = check_mandelbrot(j, i, config, &offset, &step_size); + *image.get_pixel_mut(j as u32, i as u32) = get_color_for_pixel(value, config.iterations) + } + + if config.debug { + let progress = i as f64 / height; + print!("\r{:.2}% {}", progress * 100.0, progress_bar(progress)); + let _ = io::stdout().flush(); + } + } + + if config.debug { + println!("\r100.00% {}", progress_bar(1.0)); + } + + image.save(&config.image_path)?; + + Ok(()) +} + +fn get_color_for_pixel(value: u32, iter: u32) -> Rgb { + if value < iter { + image::Rgb([255, 255, 255]) + } else { + image::Rgb([0, 0, 0]) + } +} fn draw(values: &Vec>, iterations: u32) -> String { let mut out = String::new(); for line in values { for char in line { - out += if char < &iterations { LOW } else { HIGH }; + out += if char < &iterations { " " } else { "#" }; } out += "\n"; } @@ -104,22 +143,6 @@ fn draw(values: &Vec>, iterations: u32) -> String { out } -fn create_image(values: &Vec>, iterations: u32, path: &str) -> ImageResult<()> { - let w = values[0].len() as u32; - let h = values.len() as u32; - - let mut image: RgbImage = ImageBuffer::new(w as u32, h as u32); - - for y in 0..h { - for x in 0..w { - let val = values[y as usize][x as usize]; - *image.get_pixel_mut(x, y) = if val < iterations { image::Rgb([255, 255, 255]) } else { image::Rgb([0, 0, 0]) }; - } - } - - image.save(path) -} - static BAR_SIZE: usize = 50; fn progress_bar(progress: f64) -> String { @@ -135,31 +158,34 @@ fn progress_bar(progress: f64) -> String { fn format_time(d: Duration) -> String { if d.as_micros() < 10 { - format!("{}ns", d.as_nanos()) - } else if d.as_millis() < 10 { - format!("{}μs", d.as_micros()) - } else if d.as_secs() < 10 { - format!("{}ms", d.as_millis()) - } else { - let secs = d.as_secs(); - - if secs < 60 { - format!("{}s", secs) - } else { - let mins = secs / 60; - let secs = secs % 60; - - if mins < 60 { - format!("{}m {}s", mins, secs) - } else { - let hours = mins / 60; - let mins = mins % 60; - format!("{}h {}m {}s", hours, mins, secs) - } - } + return format!("{}ns", d.as_nanos()); } + if d.as_millis() < 10 { + return format!("{}μs", d.as_micros()); + } + if d.as_secs() < 10 { + return format!("{}ms", d.as_millis()); + } + + let ms = d.as_millis() % 1000; + let secs = d.as_secs(); + + if secs < 60 { + return format!("{}s {}ms", secs, ms); + } + + let mins = secs / 60; + let secs = secs % 60; + + if mins < 60 { + return format!("{}m {}s {}ms", mins, secs, ms); + } + let hours = mins / 60; + let mins = mins % 60; + format!("{}h {}m {}s {}ms", hours, mins, secs, ms) } + #[derive(Copy, Clone, Debug, PartialEq)] struct CNumber { real: f64, @@ -202,7 +228,6 @@ pub struct Config { center: CNumber, iterations: u32, is_image: bool, - is_console: bool, image_path: String, debug: bool, } @@ -222,10 +247,6 @@ impl Config { }; } - if !config.is_image { - config.is_console = true; - } - Ok(config) } @@ -265,8 +286,6 @@ impl Config { match key { Some("img") | Some("image") => self.is_image = true, - Some("console") | Some("cli") => - self.is_console = true, Some("debug") | Some("dbg") => self.debug = true, _ => return Err(Box::new(PropertyError { msg: format!("Property not found: {}", key.unwrap_or_else(|| "")) })) @@ -277,10 +296,10 @@ impl Config { pub fn default() -> Config { - Config::new(1, 3, 100, 100.0, false, String::from("img.png"), false, false) + Config::new(1, 3, 100, 100.0, false, String::from("img.png"), false) } - pub fn new(point_number: usize, quality: i32, width: i32, threshold: f32, is_image: bool, image_path: String, is_console: bool, debug: bool) -> Config { + pub fn new(point_number: usize, quality: i32, width: i32, threshold: f32, is_image: bool, image_path: String, debug: bool) -> Config { let interesting_points = vec![CNumber::new(-0.75, 0.0), CNumber::new(-0.77568377, 0.13646737)]; let center = interesting_points[point_number]; let iterations = config_iter_from_quality(quality); @@ -291,7 +310,6 @@ impl Config { iterations, threshold: threshold as f64, is_image, - is_console, image_path, debug, } @@ -324,7 +342,7 @@ impl Error for PropertyError {} #[cfg(tests)] mod tests { - use crate::{CNumber, calculate_sample_points, check_mandelbrot, draw, HIGH, LOW, Config}; + use crate::{calculate_sample_points, check_mandelbrot, CNumber, Config, draw, HIGH, LOW}; #[test] fn cnumber_add_test() {