diff --git a/src/MandelbrotSet.java b/src/MandelbrotSet.java new file mode 100644 index 0000000..fb1069a --- /dev/null +++ b/src/MandelbrotSet.java @@ -0,0 +1,213 @@ +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 62707b9..57ebc52 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,16 +1,61 @@ use std::error::Error; use std::time::SystemTime; +use std::ops::{Add, Mul}; pub fn main(config: Config) -> Result> { let start_time = SystemTime::now().elapsed()?; + let coords = calculate_sample_points(&config); let end_time = SystemTime::now().elapsed()?; println!("Time: {}", end_time.as_micros() - start_time.as_micros()); Ok(String::from("hi")) } -#[derive(Copy, Clone)] +fn calculate_sample_points(config: &Config) -> Box>> { + let step_size_x = 3.0 / config.width; + let step_size_y = 2.0 / config.height; + + let offset_x = config.center.real - config.width / 2.0 * step_size_x; + let offset_y = -(config.center.imag - config.height / 2.0 * step_size_y); + + let mut coords: Box>> = + Box::from(vec![vec![CNumber::new(0.0, 0.0); config.width as usize]; config.height as usize]); + + for i in 0..config.width as usize { + for j in 0..config.height as usize { + coords[j][i].real = offset_x + step_size_x * i as f64; + coords[j][i].imag = offset_y + step_size_y * j as f64; + } + } + + println!("{:?}", coords[0][0]); + coords +} + +fn check_mandelbrot(number: &CNumber, iter: i32, threshold: f64) -> i32 { + let mut n = CNumber::new(0.0, 0.0); + let c = number; + + n = n + c; + + for _ in 0..iter { + n = n * n + c; + } + + if n.real < threshold && n.imag < threshold { + 1 + } else { + 0 + } +} + +fn draw(values: Box>>) -> String { + let mut lines = vec![]; + +} + +#[derive(Copy, Clone, Debug)] struct CNumber { real: f64, imag: f64, @@ -20,44 +65,43 @@ impl CNumber { fn new(real: f64, imag: f64) -> CNumber { CNumber { real, imag } } +} - fn add_mut(&mut self, other: CNumber) { - self.real += other.real; - self.imag += other.imag; - } +impl Add for &CNumber { + type Output = CNumber; - fn mul_mut(&mut self, other: CNumber) { - self.real = self.real * self.real - other.imag * other.imag; - self.imag = self.real * other.imag + other.real * self.imag; + fn add(&self, b: CNumber) -> Self::Output { + let real = self.real + b.real; + let imag = self.imag + b.imag; + + CNumber { real, imag } } } -fn add_c(a: &CNumber, b: &CNumber) -> CNumber { - let real = a.real + b.real; - let imag = a.imag + b.imag; +impl Mul for &CNumber { + type Output = CNumber; - CNumber { real, imag } + fn mul(self, b: Self) -> Self::Output { + let real = self.real * self.real - b.imag * b.imag; + let imag = self.real * b.imag + b.real * self.imag; + + CNumber { real, imag } + } } -fn mul_c(a: &CNumber, b: &CNumber) -> CNumber { - let real = a.real * a.real - b.imag * b.imag; - let imag = a.real * b.imag + b.real * a.imag; - - CNumber { real, imag } -} pub struct Config { quality: i32, - width: i32, - threshold: f32, + width: f64, + threshold: f64, //-- calculated - height: i32, + height: f64, center: CNumber, iterations: i32, } impl Config { - pub fn new(point_number: usize, quality: i32, width: i32, threshold: f32) -> Config{ + pub fn new(point_number: usize, quality: i32, width: i32, threshold: f32) -> Config { let height = width as f32 * 0.2; let interesting_points = vec![CNumber::new(-0.75, 0.0), CNumber::new(-0.77568377, 0.13646737)]; @@ -72,8 +116,27 @@ impl Config { }; Config { - quality, width, height: height as i32, - center, iterations, threshold, + quality, + width: width as f64, + height: height as f64, + center, + iterations, + threshold: threshold as f64, } } +} + +//#[cfg(tests)] +mod tests { + use crate::{Config, calculate_sample_points}; + + #[test] + fn correct_size_points() { + let config = Config::new(1, 0, 100, 0.0); + + let result = calculate_sample_points(&config); + + result[0][0]; + result[0][99]; + } } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index e7a11a9..1f9251e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,10 @@ +use mandelbrot_set::Config; + fn main() { - println!("Hello, world!"); + let config = Config::new(1, 3, 100, 100.0); + + match mandelbrot_set::main(config) { + Ok(s) => println!("{}", s), + Err(e) => println!("Error: {}", e) + } }