import java.applet.Applet; import java.awt.*; import go.*; import goText.*; class GoSierpinskiSponge extends GoInterface { GoTriangles data; int count; GoMatrix currentModelview = new GoMatrix(); int xStart; int yStart; boolean createSponge; GoText info; int depth; // recursion depth GoSierpinskiSponge(int depth) { this.depth = depth; // // Background and foreground colors. // go.background(0.5, 0.0, 0.5); go.color(0, 1, 0); // // Enable a light. (Use the default values) // go.light(Go.LIGHT_0, true); // // Initial view. // go.rotate(20, 1, 1, 1); // // Don't want to see back facing triangles. // go.enable(Go.CULL); // // Initialize the information feedback text used in render // method during the creation of the Sierpinski sponge. // info = new GoText(0.0, 0.0, 0.0, // position 1.0, 0.0, // path 0.3, 0.3, // scale GoText.LEFT_CENTER, // alignment new GoHershey("hersheyFonts/futura.l"), // font ""); // text (nothing for now) // // Inform the render method that the Sierpinski sponge // data needs to be computed. // createSponge = true; } void sponge(int depth, double x0, double y0, double z0, double d) { int i, j, k; double x, y, z; if(depth > 0) { depth--; d /= 3.0; for(i = 0, x = x0; i < 3; i++, x += d) { for(j = 0, y = y0; j < 3; j++, y += d) { for(k = 0, z = z0; k < 3; k++, z += d) { if(!(i == 1 && j == 1 && k == 1) && !(i == 0 && j == 1 && k == 1) && !(i == 2 && j == 1 && k == 1) && !(i == 1 && j == 0 && k == 1) && !(i == 1 && j == 2 && k == 1) && !(i == 1 && j == 1 && k == 0) && !(i == 1 && j == 1 && k == 2)) { sponge(depth, x, y, z, d); } } } } } else { data.append(108); data.xyz(count*3 + 0, x0 , y0 , z0); // 0 data.xyz(count*3 + 1, x0 , y0 , z0+d); // 1 data.xyz(count*3 + 2, x0 , y0+d, z0+d); // 3 data.xyz(count*3 + 3, x0 , y0 , z0); // 0 data.xyz(count*3 + 4, x0 , y0+d, z0+d); // 3 data.xyz(count*3 + 5, x0 , y0+d, z0); // 2 data.xyz(count*3 + 6, x0+d, y0 , z0); // 6 data.xyz(count*3 + 7, x0+d, y0+d, z0+d); // 5 data.xyz(count*3 + 8, x0+d, y0 , z0+d); // 7 data.xyz(count*3 + 9, x0+d, y0 , z0); // 6 data.xyz(count*3 + 10, x0+d, y0+d, z0); // 4 data.xyz(count*3 + 11, x0+d, y0+d, z0+d); // 5 data.xyz(count*3 + 12, x0 , y0+d, z0); // 2 data.xyz(count*3 + 13, x0 , y0+d, z0+d); // 3 data.xyz(count*3 + 14, x0+d, y0+d, z0+d); // 5 data.xyz(count*3 + 15, x0 , y0+d, z0); // 2 data.xyz(count*3 + 16, x0+d, y0+d, z0+d); // 5 data.xyz(count*3 + 17, x0+d, y0+d, z0); // 4 data.xyz(count*3 + 18, x0 , y0 , z0); // 0 data.xyz(count*3 + 19, x0+d, y0 , z0+d); // 7 data.xyz(count*3 + 20, x0 , y0 , z0+d); // 1 data.xyz(count*3 + 21, x0 , y0 , z0); // 0 data.xyz(count*3 + 22, x0+d, y0 , z0); // 6 data.xyz(count*3 + 23, x0+d, y0 , z0+d); // 7 data.xyz(count*3 + 24, x0 , y0 , z0+d); // 1 data.xyz(count*3 + 25, x0+d, y0 , z0+d); // 7 data.xyz(count*3 + 26, x0+d, y0+d, z0+d); // 5 data.xyz(count*3 + 27, x0 , y0 , z0+d); // 1 data.xyz(count*3 + 28, x0+d, y0+d, z0+d); // 5 data.xyz(count*3 + 29, x0 , y0+d, z0+d); // 3 data.xyz(count*3 + 30, x0 , y0 , z0); // 0 data.xyz(count*3 + 31, x0+d, y0+d, z0); // 4 data.xyz(count*3 + 32, x0+d, y0 , z0); // 6 data.xyz(count*3 + 33, x0 , y0 , z0); // 0 data.xyz(count*3 + 34, x0 , y0+d, z0); // 2 data.xyz(count*3 + 35, x0+d, y0+d, z0); // 4 count += 36; } } // // Helper function that determines if two numbers are // almost of the same value. // boolean same(double n1, double n2) { double fudge = 0.00001; if(n1 > n2 - fudge && n1 < n2 + fudge) { return true; } return false; } // // Remove any triangles that are completely enclosed. // void minimize() { int triNum = data.vertexNumber() / 3; int i, j; // Set duplicate triangles to zero for all coordinates. for(i = 0; i < triNum - 14; i += 2) { for(j = i + 14; j < triNum; j += 4) { if(same(data.x(i*3), data.x(j*3)) && same(data.x(i*3+1), data.x(j*3+2)) && same(data.x(i*3+2), data.x(j*3+1)) && same(data.y(i*3), data.y(j*3)) && same(data.y(i*3+1), data.y(j*3+2)) && same(data.y(i*3+2), data.y(j*3+1)) && same(data.z(i*3), data.z(j*3)) && same(data.z(i*3+1), data.z(j*3+2)) && same(data.z(i*3+2), data.z(j*3+1))) { data.xyz(i*3, 0.0, 0.0, 0.0); data.xyz(i*3+1, 0.0, 0.0, 0.0); data.xyz(i*3+2, 0.0, 0.0, 0.0); data.xyz(i*3+3, 0.0, 0.0, 0.0); data.xyz(i*3+4, 0.0, 0.0, 0.0); data.xyz(i*3+5, 0.0, 0.0, 0.0); data.xyz(j*3, 0.0, 0.0, 0.0); data.xyz(j*3+1, 0.0, 0.0, 0.0); data.xyz(j*3+2, 0.0, 0.0, 0.0); data.xyz(j*3+3, 0.0, 0.0, 0.0); data.xyz(j*3+4, 0.0, 0.0, 0.0); data.xyz(j*3+5, 0.0, 0.0, 0.0); break; } } } // Pack the triangles. int count = 0; for(i = 0; i < triNum; i++) { if(!(data.x(i*3) == 0.0 && data.x(i*3+1) == 0.0 && data.x(i*3+2) == 0.0)) { data.xyz(count*3, data.x(i*3), data.y(i*3), data.z(i*3)); data.xyz(count*3+1, data.x(i*3+1), data.y(i*3+1), data.z(i*3+1)); data.xyz(count*3+2, data.x(i*3+2), data.y(i*3+2), data.z(i*3+2)); count++; } } data.remove(count * 3, (triNum - count) * 3); } // // Interface render method. // // Function will first create then render the Sierpinski sponge. // public void render() { if(createSponge) { count = 0; data = new GoTriangles(0, Go.AUTO_NORMAL); go.push(Go.MODELVIEW); go.identity(); info.text("Generating Sponge...."); info.position(-1.8, 1.7, 0.0); go.clear(Go.IMAGE); info.render(go); swap(); sponge(depth, -1.0, -1.0, -1.0, 2.0); info.text("Minimizing...."); info.position(-1.8, 1.3, 0.0); info.render(go); swap(); minimize(); go.pop(Go.MODELVIEW); createSponge = false; rerender(); } else { go.clear(Go.IMAGE); go.render(data); swap(); } } // // Interface resized method. // public void resized(int width, int height) { // // Setup projection matrix. // go.push(Go.MATRIX_MODE); go.matrixMode(Go.PROJECTION); go.identity(); if(width > height) { go.ortho(-2.0 * width / height, 2.0 * width / height, -2.0, 2.0, -2.0, 2.0); } else { go.ortho(-2.0, 2.0, -2.0 * height / width, 2.0 * height / width, -2.0, 2.0); } go.pop(Go.MATRIX_MODE); } // // Interface mousePressed method. // public void mousePressed(int x, int y) { // // Save values that will be used to track the // mouse in the mouseDragged() method. // xStart = x; yStart = y; go.getModelview(currentModelview); } // // Interface mouseDragged method. // public void mouseDragged(int x, int y) { int xEnd = x; int yEnd = y; // // Setup modelview matrix based on mouse position. // go.identity(); double xTheta = (yEnd - yStart) / 2; double yTheta = (xEnd - xStart) / 2; go.rotate(xTheta, 1, 0, 0); go.rotate(yTheta, 0, 1, 0); // // Post multiply current modelview matrix. // go.multiply(currentModelview); // // Redisplay. // rerender(); } }; public class sierpinskiSponge extends Applet { public void init() { // Fonts will be found relative to the html document // that includes this applet. GoFont.path(getDocumentBase()); // Recursion depth. int depth = Integer.valueOf(getParameter("depth")).intValue(); setLayout(new GridLayout(1, 1)); add(new GoSierpinskiSponge(depth)); } }