mirror of
https://github.com/Noratrieb/mandelbrot-rust.git
synced 2026-01-16 08:15:03 +01:00
initial commit
This commit is contained in:
parent
7577fc6041
commit
764d2ff644
3 changed files with 308 additions and 25 deletions
213
src/MandelbrotSet.java
Normal file
213
src/MandelbrotSet.java
Normal file
|
|
@ -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 ^
|
||||||
111
src/lib.rs
111
src/lib.rs
|
|
@ -1,16 +1,61 @@
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
use std::ops::{Add, Mul};
|
||||||
|
|
||||||
pub fn main(config: Config) -> Result<String, Box<dyn Error>> {
|
pub fn main(config: Config) -> Result<String, Box<dyn Error>> {
|
||||||
let start_time = SystemTime::now().elapsed()?;
|
let start_time = SystemTime::now().elapsed()?;
|
||||||
|
|
||||||
|
let coords = calculate_sample_points(&config);
|
||||||
|
|
||||||
let end_time = SystemTime::now().elapsed()?;
|
let end_time = SystemTime::now().elapsed()?;
|
||||||
println!("Time: {}", end_time.as_micros() - start_time.as_micros());
|
println!("Time: {}", end_time.as_micros() - start_time.as_micros());
|
||||||
Ok(String::from("hi"))
|
Ok(String::from("hi"))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
fn calculate_sample_points(config: &Config) -> Box<Vec<Vec<CNumber>>> {
|
||||||
|
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<Vec<Vec<CNumber>>> =
|
||||||
|
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<Vec<Vec<i32>>>) -> String {
|
||||||
|
let mut lines = vec![];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
struct CNumber {
|
struct CNumber {
|
||||||
real: f64,
|
real: f64,
|
||||||
imag: f64,
|
imag: f64,
|
||||||
|
|
@ -20,44 +65,43 @@ impl CNumber {
|
||||||
fn new(real: f64, imag: f64) -> CNumber {
|
fn new(real: f64, imag: f64) -> CNumber {
|
||||||
CNumber { real, imag }
|
CNumber { real, imag }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn add_mut(&mut self, other: CNumber) {
|
impl Add for &CNumber {
|
||||||
self.real += other.real;
|
type Output = CNumber;
|
||||||
self.imag += other.imag;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mul_mut(&mut self, other: CNumber) {
|
fn add(&self, b: CNumber) -> Self::Output {
|
||||||
self.real = self.real * self.real - other.imag * other.imag;
|
let real = self.real + b.real;
|
||||||
self.imag = self.real * other.imag + other.real * self.imag;
|
let imag = self.imag + b.imag;
|
||||||
|
|
||||||
|
CNumber { real, imag }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_c(a: &CNumber, b: &CNumber) -> CNumber {
|
impl Mul for &CNumber {
|
||||||
let real = a.real + b.real;
|
type Output = CNumber;
|
||||||
let imag = a.imag + b.imag;
|
|
||||||
|
|
||||||
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 {
|
pub struct Config {
|
||||||
quality: i32,
|
quality: i32,
|
||||||
width: i32,
|
width: f64,
|
||||||
threshold: f32,
|
threshold: f64,
|
||||||
//-- calculated
|
//-- calculated
|
||||||
height: i32,
|
height: f64,
|
||||||
center: CNumber,
|
center: CNumber,
|
||||||
iterations: i32,
|
iterations: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
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 height = width as f32 * 0.2;
|
||||||
|
|
||||||
let interesting_points = vec![CNumber::new(-0.75, 0.0), CNumber::new(-0.77568377, 0.13646737)];
|
let interesting_points = vec![CNumber::new(-0.75, 0.0), CNumber::new(-0.77568377, 0.13646737)];
|
||||||
|
|
@ -72,8 +116,27 @@ impl Config {
|
||||||
};
|
};
|
||||||
|
|
||||||
Config {
|
Config {
|
||||||
quality, width, height: height as i32,
|
quality,
|
||||||
center, iterations, threshold,
|
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];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,3 +1,10 @@
|
||||||
|
use mandelbrot_set::Config;
|
||||||
|
|
||||||
fn main() {
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue