// // Menger Sponge (see http://mathworld.wolfram.com/MengerSponge.html) // // 2nd September 2003 // // A shape which in the limit has zero volume and infinite surface area! // // (Here, it's more of an exercise in recursion, and matrix stacks...) // // Tom Carden web@tom-carden.co.uk // // P4 2GHz struggles with more than 3! // remember the number of cubes drawn is 20^DEPTH (ie 1, 20, 400, 8000, 1280000, etc!) int MAX_DEPTH = 6; // start sensibly, use '+'/'-' to change int depth = 2; // spin me! float angle = 0.0f; float angleStep = PI/180.0f; // precalculate to avoid repeating multiplications float[] boxPos; // average of width and height float size, halfSize, tenthSize; // token optimisation float aThird = 1.0f / 3.0f; // toggle for drawing mode (others - 'w' wireframe, 's' solid, 'p' points, 'f' flat) boolean drawPoints = false, flatShade = true; MouseOver selectedButton; void setup() { size(300,300); background(40); // start with drawing flat selectedButton = buttons[0]; // set up useful variables size = width; halfSize = size / 2.0f; tenthSize = size / 10.0f; boxPos = new float[3]; for (int i = 0; i < boxPos.length; i++) { boxPos[i] = i*size / 3.0f; } } void loop() { selectedButton.perform(); // center and shrink slightly push(); translate(tenthSize,tenthSize,-tenthSize); scale(0.80f); // spinning angle = (angle + angleStep) % TWO_PI; translate(halfSize,halfSize,-halfSize); rotateX(angle); rotateY(angle); rotateZ(angle); translate(-halfSize,-halfSize,halfSize); // the recursive bit... sponge(depth); pop(); for (int i = 0; i < buttons.length; i++) { buttons[i].draw(); } for (int i = 0; i < depthButtons.length; i++) { depthButtons[i].draw(); } } void sponge(int aDepth) { // if at end of fractal if (aDepth == 0) { // draw a box the size of the screen, centered on the screen mybox(size); } // else move around and recurse else { for (int x = 0; x < 3; x++) { push(); translate(boxPos[x],0.0f,0.0f); for (int y = 0; y < 3; y++) { if (y == 1 && x == 1) continue; push(); translate(0.0f,boxPos[y],0.0f); for (int z = 0; z < 3; z++) { if (z == 1 && (y == 1 || x == 1 )) continue; push(); translate(0.0f,0.0f,-boxPos[z]); scale(aThird); sponge(aDepth-1); pop(); } pop(); } pop(); } } } void mybox(float size) { if (drawPoints) { point(0.0f,0.0f,-size); point(size,0.0f,-size); point(size,size,-size); point(0.0f,size,-size); point(0.0f,0.0f,0.0f); point(size,0.0f,0.0f); point(size,size,0.0f); point(0.0f,size,0.0f); } else { push(); translate(halfSize,halfSize,-halfSize); if (flatShade) { scale(size); beginShape(QUADS); fill(#c0c0f0); // blue vertex(-0.5f, 0.5f, 0.5f); vertex( 0.5f, 0.5f, 0.5f); vertex( 0.5f, -0.5f, 0.5f); vertex(-0.5f, -0.5f, 0.5f); // still blue vertex( 0.5f, 0.5f, -0.5f); vertex(-0.5f, 0.5f, -0.5f); vertex(-0.5f, -0.5f, -0.5f); vertex( 0.5f, -0.5f, -0.5f); fill(#f0c0c0); // red vertex(-0.5f, 0.5f, -0.5f); vertex(-0.5f, 0.5f, 0.5f); vertex(-0.5f, -0.5f, 0.5f); vertex(-0.5f, -0.5f, -0.5f); // still red vertex( 0.5f, 0.5f, 0.5f); vertex( 0.5f, 0.5f, -0.5f); vertex( 0.5f, -0.5f, -0.5f); vertex( 0.5f, -0.5f, 0.5f); fill(#c0f0c0); // green vertex(-0.5f, -0.5f, -0.5f); vertex( 0.5f, -0.5f, -0.5f); vertex( 0.5f, -0.5f, 0.5f); vertex(-0.5f, -0.5f, 0.5f); // still green vertex(-0.5f, 0.5f, -0.5f); vertex( 0.5f, 0.5f, -0.5f); vertex( 0.5f, 0.5f, 0.5f); vertex(-0.5f, 0.5f, 0.5f); endShape(); } else { box(size); } pop(); } } // // interaction code begins... // void mouseMoved() { cursor(ARROW); for (int i = 0; i < buttons.length; i++) { if (buttons[i].inside(mouseX,mouseY)) { cursor(HAND); break; } } for (int i = 0; i < depthButtons.length; i++) { if (depthButtons[i].inside(mouseX,mouseY)){ cursor(HAND); break; } } } void mousePressed() { for (int i = 0; i < buttons.length; i++) { if (buttons[i].inside(mouseX,mouseY)) { selectedButton = buttons[i]; break; } } for (int i = 0; i < depthButtons.length; i++) { if (depthButtons[i].inside(mouseX,mouseY)){ depthButtons[i].perform(); break; } } } abstract class MouseOver { public float x,y,w,h; public boolean selected; public MouseOver(float x, float y, float w, float h) { this.x = x; this.y = y; this.w = w; this.h = h; } public boolean inside(float x, float y) { return ((x >= this.x && x < (this.x+this.w)) && (y >= this.y && y < (this.y+this.h))); } public abstract void perform(); public abstract void draw(); } // // drawing settings begin... // MouseOver[] buttons = { new MouseOver(10,10,10,10) { // flatShade public void perform() { noStroke(); lights(); drawPoints = false; flatShade = true; } public void draw() { noStroke(); fill(255); rect(x,y,w,h); } }, new MouseOver(25,10,10,10) { // wireFrame public void perform() { noFill(); stroke(255); noLights(); drawPoints = false; flatShade = false; } public void draw() { noFill(); stroke(255); rect(x+1,y+1,w-2,h-2); } }, new MouseOver(40,10,10,10) { // points public void perform() { noFill(); stroke(255); noLights(); drawPoints = true; flatShade = false; } public void draw() { perform(); point(x+1,y+1); point(x+w-2,y+1); point(x+w-2,y+h-2); point(x+1,y+h-2); } }, new MouseOver(55,10,10,10) { // gradShade public void perform() { noStroke(); fill(255); lights(); drawPoints = false; flatShade = false; } public void draw() { noStroke(); beginShape(QUADS); fill(#e0e0e0); vertex(x, y); fill(#909090); vertex(x+w, y); fill(#000000); vertex(x+w, y+h); fill(#909090); vertex(x, y+h); endShape(); } } }; MouseOver[] depthButtons = { new MouseOver(75,10,10,10) { public void perform() { if (depth < MAX_DEPTH) depth++; } public void draw() { noFill(); stroke(255); line(x+(w/2),y+2,x+(w/2),y+h-2); line(x+2,y+(h/2),x+w-2,y+(h/2)); rect(x,y,w,h); } }, new MouseOver(85,10,10,10) { public void perform() { if (depth > 0) depth--; } public void draw() { noFill(); stroke(255); line(x+2,y+(h/2),x+w-2,y+(h/2)); rect(x,y,w,h); } } }; // // ...drawing settings end // // // ...interaction code ends //