Just Some Julia Sets
So one of the neat side things I learned at GHP was how the Mandelbrot Set works. Somehow I had the impression that the Mandelbrot Set was really complicated so I had never looked up the details. But it is really crazy-simple, assuming you know what a complex number is. Anyways, once I learned this, I just had to cook up my own mini-fractal generator and explore. I didn’t actually draw the Mandelbrot (yeah, now that I understand the Mandelbrot just doesn’t excite me anymore): I’m drawing some julia sets which are a very close relative.
And I did it in Processing which made it extra easy and fun.
class Cnum //complex number { float r; //real float i; //imaginary Cnum(float r_, float i_) { r = r_; i = i_; } String irep() { return "[" + r + " + " + i + "i]"; } String prep() { float theta = arg(); float r2 = sqrt((i*i) + (r*r)); return "[r=" + r2 + " theta=" + theta + "]"; } String toString() { return irep(); } boolean isInfinity() { return r == Float.POSITIVE_INFINITY; } boolean isZero() { return r == 0 && i == 0; } Cnum conj() { return new Cnum(r, i*-1); } Cnum neg() { return new Cnum(-r,-i); } float arg() { return atan2(i,r); } float abs() { return sqrt(r*r + i*i); } } Cnum add(Cnum a, Cnum b) { return new Cnum(a.r + b.r, a.i + b.i); } Cnum sub(Cnum a, Cnum b) { return new Cnum(a.r - b.r, a.i - b.i); } Cnum mul(Cnum a, Cnum b) { return new Cnum(a.r*b.r - a.i*b.i, a.i*b.r + a.r*b.i); } int WIDTH = 500; int HEIGHT = 500; boolean changeOnMove = true; void plot(Cnum a) { float xPercentage = (a.r - upperLeft.r)/(lowerRight.r - upperLeft.r); float yPercentage = (a.i - lowerRight.i)/(upperLeft.i - lowerRight.i); int x = round(xPercentage*WIDTH); int y = round((1 - yPercentage)*HEIGHT); point(x, y); } Cnum convert(int x, int y) { float xPercentage = x/float(WIDTH); float yPercentage = y/float(HEIGHT); return new Cnum((lowerRight.r - upperLeft.r)*xPercentage + upperLeft.r, (upperLeft.i - lowerRight.i)*(1 - yPercentage) + lowerRight.i); } Cnum upperLeft, lowerRight; PFont fontA; void setup() { upperLeft = new Cnum(-2, 2); lowerRight = new Cnum(2, -2); size(WIDTH, HEIGHT); stroke(226); fill(226); fontA = loadFont("SansSerif.plain-12.vlw"); // Set the font and its size (in units of pixels) textFont(fontA, 12); oldMouse = new Cnum(0,0); } void keyPressed() { if (key == 'q') exit(); if (key == 's') save("output.png"); if (key == ' ') { changeOnMove = !(changeOnMove); draggedX = 0; draggedY = 0; } } Cnum oldMouse; int maxIter = 20; void draw() { Cnum mouse; background(0); if(changeOnMove) { mouse = convert(mouseX, mouseY); if(mouse.r == oldMouse.r && mouse.i == oldMouse.i) { if(maxIter < 1000) maxIter = maxIter + 20; else return; } else { maxIter = 5; oldMouse = mouse; } } else { mouse = oldMouse; } for(int q = 0; q < WIDTH; q++) for(int j = 0; j < HEIGHT; j++) { Cnum orig = convert(q,j); //Cnum acc = add(mul(orig,orig), mouse) ; Cnum acc = orig ; //Cnum acc = add(orig, mouse); for(int i = 0; i < maxIter; i++) { acc = add(mul(mul(acc,acc),acc), mouse) ; if(acc.abs() > 2){ int strokeColor = 255 - i*5; if (strokeColor < 0 ) strokeColor = 0; stroke(strokeColor); plot(orig); break; } } } fill(0,0,222); text("z^2 + cl c=" + mouse.r + " + " + mouse.i + "i", 30, 60); if(draggedX != 0 || draggedY != 0) { stroke(0,0,222); noFill(); int max = max(mouseX - draggedX, mouseY - draggedY); rect(draggedX, draggedY, max, max); } } int draggedX = 0; int draggedY = 0; void mousePressed() { if(!changeOnMove) { draggedX = mouseX; draggedY = mouseY; } } void mouseReleased() { if(!changeOnMove && draggedX != 0 && draggedY != 0) { int temp; if(mouseX < draggedX) { temp = mouseX; mouseX = draggedX; draggedX = temp; } if(mouseY < draggedY) { temp = mouseY; mouseY = draggedY ; draggedY = temp; } int max = max (mouseX - draggedX, mouseY - draggedY); Cnum lowerRightNew = convert(draggedX + max, draggedY + max); Cnum upperLeftNew = convert(draggedX, draggedY); upperLeft = upperLeftNew; lowerRight = lowerRightNew; draggedX = 0; draggedY = 0; } }