class Complex {
constructor(real, imaginary){
this.real = real;
this.imaginary = imaginary;
}
copy(){
return new Complex(this.real, this.imaginary)
}
square(){
return new Complex(this.real*this.real - this.imaginary*this.imaginary, 2*this.real*this.imaginary);
}
add(o){
return new Complex(this.real+o.real, this.imaginary+o.imaginary)
}
sub(o){
return new Complex(this.real-o.real, this.imaginary-o.imaginary)
}
isFinite(){
return isFinite(this.real) && isFinite(this.imaginary);
}
static zero(){
return new Complex(0, 0);
}
}
class ComplexField {
constructor(start, end, resX, resY) {
this.start = start;
this.end = end;
this.resX = resX;
this.resY = resY;
this.field = new Array(resX);
for (let r = 0; r <= resX; r++){
this.field[r] = new Array(resY);
for (let i = 0; i <= resY; i++){
this.field[r][i] = new FieldPoint(this.indexToPoint(r, i));
}
}
console.log(this.field[0].length);
}
stepSize(){
let step = {};
let size = this.end.sub(this.start);
step.x = size.real/this.resX;
step.y = size.imaginary/this.resY;
return step;
}
indexToPoint(x, y){
let step = this.stepSize();
let unOff = new Complex(x*step.x, y*step.y);
return unOff.add(this.start);
}
}
class FieldPoint {
constructor(pos){
this.pos = pos;
this.data = {};
}
}
class MandelField extends ComplexField {
constructor(start, end, resX, resY, theme){
super(start, end, resX, resY);
this.theme = theme;
for (let r = 0; r <= resX; r++){
for (let i = 0; i <= resY; i++){
this.field[r][i].data.mandelTotal = Complex.zero();
this.field[r][i].data.finite = true;
set(r, i, color(this.theme.mandelShade));
}
}
updatePixels();
this.iters = 0;
}
mandelIteration() {
for (let r = 0; r <= this.resX; r++){
for (let i = 0; i <= this.resY; i++){
let fieldPoint = this.field[r][i];
if (fieldPoint.data.finite){
fieldPoint.data.mandelTotal = fieldPoint.data.mandelTotal.square().add(fieldPoint.pos);
fieldPoint.data.finite = fieldPoint.data.mandelTotal.isFinite();
if (!fieldPoint.data.finite)
set(r, i, this.itersToColour());
if (!fieldPoint.data.finite && this.iters == 0 && this.theme.relative)
this.iters = 1;
}
}
}
updatePixels();
if (!this.theme.relative || this.iters != 0)
this.iters ++;
}
itersToColour(){
let f = this.iters/(this.theme.decayRate+this.iters)
return 256*(1-this.theme.decayToWhite+(2*this.theme.decayToWhite-1)*f);
}
}
class MandelTheme{
constructor(mandelShade, decayToWhite, decayRate, relative = 0){
this.mandelShade = mandelShade;
this.decayToWhite = decayToWhite;
this.decayRate = decayRate;
this.relative = relative;
}
}
let field;
let theme;
let mouseStartX;
let mouseStartY;
let mouseDown = false;
function mandelSetup(start, end, w) {
let size = end.sub(start);
let h = floor(w * size.imaginary / size.real);
resizeCanvas(w, h);
for (let x = 0; x < width; x++)
for (let y = 0; y < height; y++)
set(x, y, color(256));
updatePixels();
field = new MandelField(start, end, w, h, theme);
}
function setup() {
sketchesCreateCanvas(600, 600);
rectMode(CORNERS);
noFill();
//theme = new MandelTheme(15, 0, 35); // White to black
//theme = new MandelTheme(0, 0, 700); // Classic
//theme = new MandelTheme(0, 0, 3); // Dark
//theme = new MandelTheme(256, 1, 70); // Black to white
//theme = new MandelTheme(256, 1, 700); // Inverse classic
//theme = new MandelTheme(0, 1, 20); // High Contrast
theme = new MandelTheme(256, 1, 500, 1); // Small Level
mandelSetup(new Complex(-2, -1.5), new Complex(1, 1.5), 600)
}
function draw() {
field.mandelIteration();
if (mouseDown) {
stroke(25, 200, 255);
rect(mouseStartX, mouseStartY, mouseX, mouseY);
}
}
function mousePressed(event) {
if (event.button != 0 || event.target.id != "defaultCanvas0") return;
mouseStartX = mouseX;
mouseStartY = mouseY;
mouseDown = true;
}
function mouseReleased(event) {
if (event.button != 0 || event.target.id != "defaultCanvas0" || !mouseDown) return;
let mouseEnd = field.indexToPoint(mouseX, mouseY);
let mouseStart = field.indexToPoint(mouseStartX, mouseStartY);
mouseDown = false;
mandelSetup(mouseStart, mouseEnd, 600)
}
Return to Sketch