#include <stdio.h> #include <math.h> #include "Go.h" #include "GoGlutInterface.h" #define PI 3.14159265358979324 class Function : public GoGlutInterface { public: // // The Function. // double fxy(double x, double y) { return 1.0 - cos(x*x + y*y) / (x*x + y*y); } // View variables. double eyeX, eyeY, eyeZ; double centerX, centerY, centerZ; double upX, upY, upZ; double radius; boolean perspective; // Function data int triangleStripNum; int lineStripNum; GoVertex **triangleStripData; GoVertex **lineStripData; double normal_[3]; double zClampMin, zClampMax; int drawMode; // Box data GoVertex *top; GoVertex *bottom; GoVertex *sides; // Rotate vector double rX, rY, rZ; int rotateAbout; int rotateAs; // Values for rotateAbout enum { X_AXIS, Y_AXIS, Z_AXIS }; // Values for rotateAs enum { SOLID, WIRE, BOX }; // Class temporary variables GoMatrix modelview; GoMatrix saveModelview; int xStart, yStart; double angleStart; // // Constructor. // Function(double xMin, double xMax, double yMin, double yMax, double zMin, double zMax, int xDiv, int yDiv, double eyeX, double eyeY, double eyeZ, double upX, double upY, double upZ, boolean perspective) { // // Create the function and bounding box. // createFunction(xMin, xMax, yMin, yMax, zMin, zMax, xDiv, yDiv); createBox(xMin, yMin, zMin, xMax, yMax, zMax); // // Default drawing mode. // drawMode = SOLID; rotateAbout = Y_AXIS; rotateAs = WIRE; // // Setup a light. // go->light(Go::LIGHT_0, true); go->light(Go::LIGHT_0, Go::DIRECTIONAL, -1.3, 2.4, 1.0); // // Define the initial view. // NOTE: View variables are used in the resized() method. // this->eyeX = eyeX; this->eyeY = eyeY; this->eyeZ = eyeZ; centerX = (xMin + xMax) / 2.0; centerY = (yMin + yMax) / 2.0; centerZ = (zMin + zMax) / 2.0; this->upX = upX; this->upY = upY; this->upZ = upZ; radius = distance(centerX, centerY, centerZ, xMax, yMax, zMax); this->perspective = perspective; // // Initialize the modelview matrix. // go->lookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ); } // // Interface render method. // void render(void) { int i; go->clear(Go::IMAGE); // // Render function. // switch(drawMode) { case SOLID: go->color(1, 1, 1); // white for(i = 0; i < triangleStripNum; i++) { go->render(triangleStripData[i]); } go->color(1, 0, 0); // red for(i = 0; i < lineStripNum; i++) { go->render(lineStripData[i]); } break; case WIRE: go->color(1, 0, 0); // red for(i = 0; i < lineStripNum; i++) { go->render(lineStripData[i]); } break; } // // Render box. // go->color(0, 0, 0); // black go->render(top); go->render(bottom); go->render(sides); swap(); } // // Interface resized method. // void resized(int width, int height) { double dx = eyeX - centerX; double dy = eyeY - centerY; double dz = eyeZ - centerZ; double d = sqrt(dx * dx + dy * dy + dz * dz); double fovy = 2.0 * 180.0/PI * atan(radius / d); double near = d - radius; double far = d + radius; double xUnits, yUnits; if(width >= height) { xUnits = 2 * radius * width / height; yUnits = 2 * radius; } else { xUnits = 2 * radius; yUnits = 2 * radius * height / width; } // setup projection matrix go->matrixMode(Go::PROJECTION); go->identity(); if(perspective) { if(width >= height) { go->perspective(fovy, xUnits / yUnits, near, far); } else { double radiusModified = radius * yUnits / xUnits; double fovyModified = atan(radiusModified / d) * 180.0 / PI * 2.0; go->perspective(fovyModified, xUnits / yUnits, near, far); } } else { go->ortho(-xUnits / 2.0, xUnits / 2.0, -yUnits / 2.0, yUnits / 2.0, near, far); } go->matrixMode(Go::MODELVIEW); } // // Interface keyTyped method. // void keyTyped(char key) { if(key == 's') { rotateAs = SOLID; } else if(key == 'w') { rotateAs = WIRE; } else if(key == 'b') { rotateAs = BOX; } else if(key == '1') { rotateAbout = X_AXIS; } else if(key == '2') { rotateAbout = Y_AXIS; } else if(key == '3') { rotateAbout = Z_AXIS; } } // // Interface mousePressed method. // void mousePressed(int x, int y) { int width = go->width(); int height = go->height(); // // Init mouse info. // xStart = x; yStart = y; angleStart = atan2(height / 2.0 - yStart, xStart - width / 2.0); // // Save the current modelview matrix so that // it can be restored as the mouse is moved. // go->getModelview(&saveModelview); // // Redisplay in rotateAs mode. // switch(rotateAs) { case SOLID: drawMode = SOLID; break; case WIRE: drawMode = WIRE; break; case BOX: drawMode = BOX; break; } rerender(); // // Calc the rotation vector <rX, rY, rZ> // double x0 = 0.0; double y0 = 0.0; double z0 = 0.0; double x1; double y1; double z1; switch(rotateAbout) { case X_AXIS: x1 = 1.0; y1 = 0.0; z1 = 0.0; break; default: case Y_AXIS: x1 = 0.0; y1 = 1.0; z1 = 0.0; break; case Z_AXIS: x1 = 0.0; y1 = 0.0; z1 = 1.0; break; } go->push(Go::MODELVIEW); go->inverse(); go->getModelview(&modelview); go->pop(Go::MODELVIEW); double *m = modelview.m; double w = m[3] * x0 + m[7] * y0 + m[11] * z0 + m[15]; double x0N = (m[0] * x0 + m[4] * y0 + m[8] * z0 + m[12]) / w; double y0N = (m[1] * x0 + m[5] * y0 + m[9] * z0 + m[13]) / w; double z0N = (m[2] * x0 + m[6] * y0 + m[10] * z0 + m[14]) / w; w = m[3] * x1 + m[7] * y1 + m[11] * z1 + m[15]; double x1N = (m[0] * x1 + m[4] * y1 + m[8] * z1 + m[12]) / w; double y1N = (m[1] * x1 + m[5] * y1 + m[9] * z1 + m[13]) / w; double z1N = (m[2] * x1 + m[6] * y1 + m[10] * z1 + m[14]) / w; rX = x1N - x0N; rY = y1N - y0N; rZ = z1N - z0N; } // // Interface mouseReleased method. // void mouseReleased(int x, int y) { drawMode = SOLID; rerender(); } // // Interface mouseDragged method. // void mouseDragged(int x, int y) { int xEnd = x; int yEnd = y; int width = go->width(); int height = go->height(); go->load(&saveModelview); double delta; switch(rotateAbout) { case X_AXIS: delta = (yEnd - yStart) / 2.0; break; default: case Y_AXIS: delta = (xEnd - xStart) / 2.0; break; case Z_AXIS: double angleEnd = atan2(height / 2.0 - yEnd, xEnd - width / 2.0); delta = (angleEnd - angleStart) * 180.0 / PI; break; } go->translate(centerX, centerY, centerZ); go->rotate(delta, rX, rY, rZ); go->translate(-centerX, -centerY, -centerZ); rerender(); } // // Clamp function to Z range. // double fxyClamp(double x, double y) { double zVal = fxy(x, y); // // Chop off anything less than zMin or greater than zMax. // if(zVal < zClampMin) { return zClampMin; } else if(zVal > zClampMax) { return zClampMax; } else { return zVal; } } // // Derivative of function with respect to x. (used to calc normals) // double dfxy_dx(double x, double y) { double delta = 0.0000001; return 1.0/(2.0 * delta) * (fxy(x + delta, y) - fxy(x - delta, y)); } // // Derivative of function with respect to y. (used to calc normals) // double dfxy_dy(double x, double y) { double delta = 0.0000001; return 1.0/(2.0 * delta) * (fxy(x, y + delta) - fxy(x, y - delta)); } // // Normal vector at f(x, y) // void normal(double x, double y, double *n) { double dx = dfxy_dx(x, y); double dy = dfxy_dy(x, y); double den = sqrt(1.0 + dx * dx + dy * dy); n[0] = dx / den; n[1] = dy / den; n[2] = 1.0 / den; } // // Function creation. // void createFunction(double xMin, double xMax, double yMin, double yMax, double zMin, double zMax, int xDiv, int yDiv) { int i, j, count; double x, y; double xInc = (xMax - xMin) / xDiv; double yInc = (yMax - yMin) / yDiv; triangleStripNum = xDiv; lineStripNum = xDiv + yDiv + 2; triangleStripData = new GoVertex * [triangleStripNum]; lineStripData = new GoVertex * [lineStripNum]; // // Save Z clamp values for use in fxyClamp(). // zClampMin = zMin; zClampMax = zMax; // // Calc lineStripData. // count = 0; for(i = 0, x = xMin; i <= xDiv; i++, x += xInc, count++) { lineStripData[count] = new GoLineStrip(yDiv + 1); for(j = 0, y = yMin; j < yDiv + 1; j++, y += yInc) { lineStripData[count]->xyz(j, x, y, fxyClamp(x, y)); } } for(i = 0, y = yMin; i <= yDiv; i++, y += yInc, count++) { lineStripData[count] = new GoLineStrip(xDiv + 1); for(j = 0, x = xMin; j < xDiv + 1; j++, x += xInc) { lineStripData[count]->xyz(j, x, y, fxyClamp(x, y)); } } // // Calc triangleStripData. // count = 0; for(i = 0, x = xMin; i < xDiv; i++, x += xInc, count++) { triangleStripData[count] = new GoTriangleStrip(yDiv * 2 + 2, Go::NORMAL); triangleStripData[count]->xyz(0, x, yMin, fxyClamp(x, yMin)); normal(x, yMin, normal_); triangleStripData[count]->ijk(0, normal_[0], normal_[1], normal_[2]); triangleStripData[count]->xyz(1, x + xInc, yMin, fxyClamp(x + xInc, yMin)); normal(x + xInc, yMin, normal_); triangleStripData[count]->ijk(1, normal_[0], normal_[1], normal_[2]); for(j = 1, y = yMin + yInc; j <= yDiv; j++, y += yInc) { triangleStripData[count]->xyz(j * 2, x, y, fxyClamp(x, y)); normal(x, y, normal_); triangleStripData[count]->ijk(j * 2, normal_[0], normal_[1], normal_[2]); triangleStripData[count]->xyz(j * 2 + 1, x + xInc, y, fxyClamp(x + xInc, y)); normal(x + xInc, y, normal_); triangleStripData[count]->ijk(j * 2 + 1, normal_[0], normal_[1], normal_[2]); } } } // // Box creation. // void createBox(double xMin, double yMin, double zMin, double xMax, double yMax, double zMax) { top = new GoLineLoop(4); top->xyz(0, xMin, yMax, zMin); top->xyz(1, xMin, yMax, zMax); top->xyz(2, xMax, yMax, zMax); top->xyz(3, xMax, yMax, zMin); bottom = new GoLineLoop(4); bottom->xyz(0, xMin, yMin, zMin); bottom->xyz(1, xMin, yMin, zMax); bottom->xyz(2, xMax, yMin, zMax); bottom->xyz(3, xMax, yMin, zMin); sides = new GoLines(8); sides->xyz(0, xMin, yMin, zMin); sides->xyz(1, xMin, yMax, zMin); sides->xyz(2, xMax, yMin, zMin); sides->xyz(3, xMax, yMax, zMin); sides->xyz(4, xMin, yMin, zMax); sides->xyz(5, xMin, yMax, zMax); sides->xyz(6, xMax, yMin, zMax); sides->xyz(7, xMax, yMax, zMax); } // // Calculate distance between two points. // double distance(double x0, double y0, double z0, double x1, double y1, double z1) { double dx = x1 - x0; double dy = y1 - y0; double dz = z1 - z0; return sqrt(dx*dx + dy*dy + dz*dz); } }; void instructions() { printf("\n"); printf("Rotate about X - press \"1\"\n"); printf("Rotate about Y - press \"2\"\n"); printf("Rotate about Z - press \"3\"\n"); printf("\n"); printf("Rotate as Solid - press \"s\"\n"); printf("Rotate as Wire - press \"w\"\n"); printf("Rotate as Box - press \"b\"\n"); } int main(int argc, char *argv[]) { instructions(); // // Glut setup. // GoGlutInterface::init(&argc, argv); GoGlutInterface::windowName("Rotate"); GoGlutInterface::windowSize(350, 350); // // Create the function to be displayed. // Function *function = new Function(-3.0, 3.0, // X range -3.0, 3.0, // Y range -3.5, 1.5, // Z clamp range 18, 18, // X-Y fish-net resolution 3.9, -7.2, 6.0, // Eye location 0.0, 0.0, 1.0, // Up vector true); // Perspective GoGlutInterface::mainLoop(); return(0); }