#include #include #include "Go.h" #include "GoText.h" #include "GoGlutInterface.h" class SierpinskiSponge : public GoGlutInterface { public: GoTriangles *data; int count; GoMatrix currentModelview; int xStart; int yStart; boolean createSponge; GoText *info; int depth; // recursion depth SierpinskiSponge(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(void) { 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. // void render(void) { 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. // 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. // 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(¤tModelview); } // // Interface mouseDragged method. // 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(¤tModelview); // // Redisplay. // rerender(); } }; void usage(void) { fprintf(stderr, "Usage: sierpinskiSponge \n"); exit(1); } int main(int argc, char *argv[]) { int depth; if(argc != 2) { usage(); } // Recursion depth. depth = atoi(argv[1]); GoGlutInterface::init(&argc, argv); GoGlutInterface::windowSize(250, 250); new SierpinskiSponge(depth); GoGlutInterface::mainLoop(); return(0); }