single pass

This commit is contained in:
nora 2021-03-09 10:57:21 +01:00
parent bab97c20c6
commit 36d838280d
3 changed files with 83 additions and 278 deletions

View file

@ -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 ^

View file

@ -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<dyn Error>> {
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<dyn Error>> {
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<u8> {
if value < iter {
image::Rgb([255, 255, 255])
} else {
image::Rgb([0, 0, 0])
}
}
fn draw(values: &Vec<Vec<u32>>, 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<Vec<u32>>, iterations: u32) -> String {
out
}
fn create_image(values: &Vec<Vec<u32>>, 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() {