import java.applet.*; import java.awt.*; import go.*; class Function extends GoInterface { // // The Function. // double fxy(double x, double y) { return 1.0 - Math.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 = new double[3]; double zClampMin, zClampMax; int drawMode; // Box data GoVertex top, bottom, sides; // Rotate vector. double rX, rY, rZ; int rotateAbout; int rotateAs; // Values for rotateAbout final static int X_AXIS = 1; final static int Y_AXIS = 2; final static int Z_AXIS = 3; // Values for rotateAs final static int SOLID = 0; final static int WIRE = 1; final static int BOX = 2; // Class temporary variables GoMatrix modelview = new GoMatrix(); GoMatrix saveModelview = new GoMatrix(); 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. // public void render() { 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. // public void resized(int width, int height) { double dx = eyeX - centerX; double dy = eyeY - centerY; double dz = eyeZ - centerZ; double d = Math.sqrt(dx * dx + dy * dy + dz * dz); double fovy = 2.0 * 180.0/Math.PI * Math.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 radius2 = radius * yUnits / xUnits; double fovyModified = Math.atan(radius2 / d) * 180.0 / Math.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. // public 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. // public void mousePressed(int x, int y) { requestFocus(); int width = go.width(); int height = go.height(); // // Init mouse info. // xStart = x; yStart = y; angleStart = Math.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. // public void mouseReleased(int x, int y) { drawMode = SOLID; rerender(); } // // Interface mouseDragged method. // public 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 = Math.atan2(height / 2.0 - yEnd, xEnd - width / 2.0); delta = (angleEnd - angleStart) * 180.0 / Math.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 = Math.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 Math.sqrt(dx*dx + dy*dy + dz*dz); } } public class rotate extends Applet { public void init() { // // 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 setLayout(new GridLayout(1, 1)); add(function); } }