//|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| This file contains the implementation of the Graphic class. A Graphic is a //| collection of graphics primitives. //|________________________________________________________________________________ import java.lang.*; import java.util.*; import java.awt.*; class GraphicSlider extends Slider { NGraphic graphic; int dimension; int angle; GraphicSlider (NGraphic pgraphic, int pdimension, int pangle, int width, int height, int min, int max, int value) { // super(width, height, min, max, value); SetWidth(width); SetHeight(height); SetMinimum(min); SetMaximum(max); SetValue(value); SetBarColor(Color.black); dimension = pdimension; angle = pangle; graphic = pgraphic; } public void Motion () { // The slider has moved-- change the graphic appropriately graphic.ParameterChanged(dimension, angle, GetValue()); } public void Release () { // The slider has moved-- change the graphic appropriately // graphic.AngleChanged(dimension, angle, GetValue()); } } class Path extends Vector { Color color; //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| method Path of Path //| //| Purpose: Initialize a Path. //| //| Parameters: pcolor: the color list //|_________________________________________________________ Path(Color pcolor) { // Save the color color = pcolor; } //==== method Path of Path() ====\\ //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| method Draw of Path //| //| Purpose: Draw this path. //| //| Parameters: screenVertices: the screen coordinates of the vertices //|__________________________________________________________________________ void Draw(Graphics g, Vector screenVertices) { // Draw in the appropriate color g.setColor(color); // Move to the first point int vertexIndex = ((Integer) elementAt(0)).intValue(); Point lastScreenPoint = (Point) screenVertices.elementAt(vertexIndex); // Draw the other points short i; for (i = 1; i < size(); i++) { // Draw a line to the next point vertexIndex = ((Integer) elementAt(i)).intValue(); Point thisScreenPoint = (Point) screenVertices.elementAt(vertexIndex); g.drawLine(thisScreenPoint.x, thisScreenPoint.y, lastScreenPoint.x, lastScreenPoint.y); // Continue on to next line segment lastScreenPoint = thisScreenPoint; } } //==== method Draw of Path ====\\ } //==== class Path ====// class LiveScrollBar extends Scrollbar { java.applet.Applet applet; //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| method LiveScrollBar of LiveScrollBar //| //| Purpose: Initialize a LiveScrollBar. //| //| Parameters: applet: the applet which owns this scroll bar //|_________________________________________________________ LiveScrollBar(int a, int b, int c, int d, int e, java.applet.Applet papplet) { super(a, b, c, d, e); applet = papplet; } //==== method LiveScrollBar of LiveScrollBar() ====\\ public boolean mouseUp(Event ev, int x, int y) { applet.getAppletContext().showStatus("mouseDrag"); // return super(ev, x, y); return true; } } //==== class LiveScrollBar ====// class NPoint extends Vector { //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| method NPoint of NPoint //| //| Purpose: Initialize a NPoint. //| //| Parameters: n: the dimension of the point //|_________________________________________________________ NPoint(int n) { super(n); // Set the vector to [0, 0, ..., 0] int i; for (i = 1; i <= n; i++) addElement(new Double(0)); } //==== method NPoint of NPoint() ====// //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| method NPoint of NPoint //| //| Purpose: Initialize a NPoint. //| //| Parameters: x, y: the coordinates of the point //|_________________________________________________________ NPoint(double x, double y) { // This is a 2D point super(2); // Set the vector to [x, y] addElement(new Double(x)); addElement(new Double(y)); } //==== method NPoint of NPoint() ====// //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| method NPoint of NPoint //| //| Purpose: Initialize a NPoint. //| //| Parameters: x, y, z: the coordinates of the point //|_________________________________________________________ NPoint(double x, double y, double z) { // This is a 3D point super(3); // Set the vector to [x, y, z] addElement(new Double(x)); addElement(new Double(y)); addElement(new Double(z)); } //==== method NPoint of NPoint() ====// //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| method NPoint of NPoint //| //| Purpose: Initialize a NPoint. //| //| Parameters: x, y, z, w: the coordinates of the point //|_________________________________________________________ NPoint(double x, double y, double z, double w) { // This is a 4D point super(4); // Set the vector to [x, y, z, w] addElement(new Double(x)); addElement(new Double(y)); addElement(new Double(z)); addElement(new Double(w)); } //==== method NPoint of NPoint() ====// //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| method getCoordinate of NPoint //| //| Purpose: This returns the ith coordinate of this point. //| //| Parameters: i: the coordinate to return //|_________________________________________________________ double getCoordinate(int i) { // Return the ith coordinate return ((Double) elementAt(i)).doubleValue(); } //==== method getCoordinate of NPoint() ====// //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| method setCoordinate of NPoint //| //| Purpose: This sets the ith coordinate of this point. //| //| Parameters: i: the coordinate to set //| value: the new value for the ith coordinate //|_________________________________________________________ void setCoordinate(int i, double value) { // Set the ith coordinate setElementAt(new Double(value), i); } //==== method setCoordinate of NPoint() ====// } //==== NPoint() ====// class Matrix extends Object { // m is a Vector of Vectors of Doubles, // the elements of the n x n matrix. Vector rows; int n; //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| method Matrix of Matrix //| //| Purpose: This initializes this matrix. //| //| Parameters: size: the size of this (square) matrix //|_____________________________________________________________________________ Matrix(int size) { // Save the size n = size; // Create the Vector of rows rows = new Vector(); // Add all elements to this matrix int i; for (i = 0; i < n; i++) { Vector row = new Vector(); // Add a zero row int j; for (j = 0; j < n; j++) row.addElement(new Double(0)); rows.addElement(row); } } //==== method Matrix of Matrix ====\\ //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| method setElement of Matrix //| //| Purpose: This initializes this matrix. //| //| Parameters: r, c: the row, column of the element to change //| value: //|_____________________________________________________________________________ void setElement(int r, int c, double value) { // Change element r, c Vector row = (Vector) rows.elementAt(r); row.setElementAt(new Double(value), c); } //==== method setElement of Matrix ====\\ //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| method setElement of Matrix //| //| Purpose: This initializes this matrix. //| //| Parameters: r, c: the row, column of the element to change //| value: //|_____________________________________________________________________________ double getElement(int r, int c) { // Get element r, c Vector row = (Vector) rows.elementAt(r); return ((Double) row.elementAt(c)).doubleValue(); } //==== method getElement of Matrix ====\\ } //==== class Matrix ====// public class HyperCuber extends java.applet.Applet { NGraphic graphic; GraphicSlider perspectiveSlider3; GraphicSlider angleSlider3_1; GraphicSlider angleSlider3_2; GraphicSlider perspectiveSlider4; GraphicSlider angleSlider4_1; GraphicSlider angleSlider4_2; GraphicSlider angleSlider4_3; //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| method init of HyperCuber //| //| Purpose: Initialize a HyperCuber. //| //| Parameters: none //|_________________________________________________________ public void init() { // Set up to display a three-dimensional object // graphic = new Cube(); graphic = new HyperCube(); graphic.resize(150, 150); add(graphic); perspectiveSlider3 = new GraphicSlider(graphic, 3, 0, 100, 20, 0, 10, 7); add(perspectiveSlider3); angleSlider3_1 = new GraphicSlider(graphic, 3, 1, 100, 20, -180, 180, 30); add(angleSlider3_1); angleSlider3_2 = new GraphicSlider(graphic, 3, 2, 100, 20, 0, 360, 60); add(angleSlider3_2); perspectiveSlider4 = new GraphicSlider(graphic, 4, 0, 100, 20, 0, 10, 7); add(perspectiveSlider4); angleSlider4_1 = new GraphicSlider(graphic, 4, 1, 100, 20, 0, 360, 60); add(angleSlider4_1); angleSlider4_2 = new GraphicSlider(graphic, 4, 2, 100, 20, 0, 360, 60); add(angleSlider4_2); angleSlider4_3 = new GraphicSlider(graphic, 4, 3, 100, 20, 0, 360, 60); add(angleSlider4_3); } //==== method init() of HyperCuber ====\\ } //==== class HyperCuber ====// class Cube extends NGraphic { //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| method Cube of Cube //| //| Purpose: This creates a new Cube. //| //| Parameters: none //|_____________________________________________________________________________ Cube() { // Initialize this as a 3D NGraphic super(3); } //==== method Cube of Cube ====\\ //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| method build of Cube //| //| Purpose: This builds the graphic. It fills in the vertex list, and //| adds paths to the object. //| //| Parameters: none //|__________________________________________________________________________ public void build() { // Use the vertices of a cube as our vertices Vector threeDPoints = (Vector) vertexArrays.elementAt(3); threeDPoints.addElement(new NPoint(1, 1, 1)); threeDPoints.addElement(new NPoint(-1, -1, -1)); threeDPoints.addElement(new NPoint(-1, 1, -1)); threeDPoints.addElement(new NPoint(1, 1, -1)); threeDPoints.addElement(new NPoint(1, -1, -1)); threeDPoints.addElement(new NPoint(1, -1, 1)); threeDPoints.addElement(new NPoint(-1, -1, 1)); threeDPoints.addElement(new NPoint(-1, 1, 1)); Path path; path = new Path(Color.red); path.addElement(new Integer(1)); path.addElement(new Integer(2)); path.addElement(new Integer(3)); path.addElement(new Integer(4)); path.addElement(new Integer(1)); paths.addElement(path); path = new Path(Color.green); path.addElement(new Integer(5)); path.addElement(new Integer(6)); path.addElement(new Integer(7)); path.addElement(new Integer(0)); path.addElement(new Integer(5)); paths.addElement(path); path = new Path(Color.blue); path.addElement(new Integer(2)); path.addElement(new Integer(7)); paths.addElement(path); path = new Path(Color.blue); path.addElement(new Integer(3)); path.addElement(new Integer(0)); paths.addElement(path); path = new Path(Color.blue); path.addElement(new Integer(4)); path.addElement(new Integer(5)); paths.addElement(path); path = new Path(Color.blue); path.addElement(new Integer(1)); path.addElement(new Integer(6)); paths.addElement(path); } //==== method build of Cube ====\\ } //==== class Cube ====// class HyperCube extends NGraphic { //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| method HyperCube of HyperCube //| //| Purpose: This creates a new HyperCube. //| //| Parameters: none //|_____________________________________________________________________________ HyperCube() { // Initialize this as a 4D NGraphic super(4); } //==== method HyperCube of HyperCube ====\\ //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| method build of HyperCube //| //| Purpose: This builds the graphic. It fills in the vertex list, and //| adds paths to the object. //| //| Parameters: none //|__________________________________________________________________________ public void build() { // Use the vertices of a cube as our vertices Vector fourDPoints = (Vector) vertexArrays.elementAt(4); fourDPoints.addElement(new NPoint( 0, 0, 0, 0)); fourDPoints.addElement(new NPoint(-1, +1, -1, +1)); fourDPoints.addElement(new NPoint(-1, +1, +1, +1)); fourDPoints.addElement(new NPoint(+1, +1, +1, +1)); fourDPoints.addElement(new NPoint(+1, +1, -1, +1)); fourDPoints.addElement(new NPoint(-1, -1, -1, +1)); fourDPoints.addElement(new NPoint(-1, -1, +1, +1)); fourDPoints.addElement(new NPoint(+1, -1, +1, +1)); fourDPoints.addElement(new NPoint(+1, -1, -1, +1)); fourDPoints.addElement(new NPoint(-1, +1, -1, -1)); fourDPoints.addElement(new NPoint(-1, +1, +1, -1)); fourDPoints.addElement(new NPoint(+1, +1, +1, -1)); fourDPoints.addElement(new NPoint(+1, +1, -1, -1)); fourDPoints.addElement(new NPoint(-1, -1, -1, -1)); fourDPoints.addElement(new NPoint(-1, -1, +1, -1)); fourDPoints.addElement(new NPoint(+1, -1, +1, -1)); fourDPoints.addElement(new NPoint(+1, -1, -1, -1)); Path path; path = new Path(Color.green); path.addElement(new Integer(1)); path.addElement(new Integer(2)); path.addElement(new Integer(3)); path.addElement(new Integer(4)); path.addElement(new Integer(1)); path.addElement(new Integer(5)); path.addElement(new Integer(6)); path.addElement(new Integer(7)); path.addElement(new Integer(8)); path.addElement(new Integer(5)); paths.addElement(path); path = new Path(Color.green); path.addElement(new Integer(2)); path.addElement(new Integer(6)); paths.addElement(path); path = new Path(Color.green); path.addElement(new Integer(3)); path.addElement(new Integer(7)); paths.addElement(path); path = new Path(Color.green); path.addElement(new Integer(4)); path.addElement(new Integer(8)); paths.addElement(path); path = new Path(Color.red); path.addElement(new Integer(9)); path.addElement(new Integer(10)); path.addElement(new Integer(11)); path.addElement(new Integer(12)); path.addElement(new Integer(9)); path.addElement(new Integer(13)); path.addElement(new Integer(14)); path.addElement(new Integer(15)); path.addElement(new Integer(16)); path.addElement(new Integer(13)); paths.addElement(path); path = new Path(Color.red); path.addElement(new Integer(10)); path.addElement(new Integer(14)); paths.addElement(path); path = new Path(Color.red); path.addElement(new Integer(11)); path.addElement(new Integer(15)); paths.addElement(path); path = new Path(Color.red); path.addElement(new Integer(12)); path.addElement(new Integer(16)); paths.addElement(path); path = new Path(Color.blue); path.addElement(new Integer(1)); path.addElement(new Integer(9)); paths.addElement(path); path = new Path(Color.blue); path.addElement(new Integer(2)); path.addElement(new Integer(10)); paths.addElement(path); path = new Path(Color.blue); path.addElement(new Integer(3)); path.addElement(new Integer(11)); paths.addElement(path); path = new Path(Color.blue); path.addElement(new Integer(4)); path.addElement(new Integer(12)); paths.addElement(path); path = new Path(Color.blue); path.addElement(new Integer(5)); path.addElement(new Integer(13)); paths.addElement(path); path = new Path(Color.blue); path.addElement(new Integer(6)); path.addElement(new Integer(14)); paths.addElement(path); path = new Path(Color.blue); path.addElement(new Integer(7)); path.addElement(new Integer(15)); paths.addElement(path); path = new Path(Color.blue); path.addElement(new Integer(8)); path.addElement(new Integer(16)); paths.addElement(path); } //==== method build of HyperCube ====\\ } //==== class HyperCube ====// class NGraphic extends Canvas { // The primitives in this graphic Vector paths; // This is a list of lists. The nth list contains the vertex positions in n-space. Vector vertexArrays; Vector rotationMatrices; // The rotation matrices. The nth element in this // list is a handle to the rotation matrix used // to rotate n-space. Vector stereoMatrices; // This is the list of rotation matrices used for // the second image used in stereo. Note that all // the matrices except the 3D->2D matrix are aliases // of the matrices in the "rotationMatrices" list. Vector perspectiveParams; // The perspective paramaters. The nth element in // this array is the perspective parameter for // n-space (an int). Vector angleArrays; // The sines of the viewing angles. Vector screenPoints; //============================= Constants ==============================// double Pi = 3.14159265358979323846; int dimension = 3; //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| method NGraphic of NGraphic //| //| Purpose: This creates a new NGraphic. //| //| Parameters: n: the dimension of this graphic //|_____________________________________________________________________________ NGraphic(int n) { // Set up the graphic info paths = new Vector(); vertexArrays = new Vector(); rotationMatrices = new Vector(); perspectiveParams = new Vector(); angleArrays = new Vector(); // Remember the dimension dimension = n; // Fill in the first two fields (they are unused) rotationMatrices.addElement(new Matrix(1)); rotationMatrices.addElement(new Matrix(1)); rotationMatrices.addElement(new Matrix(1)); angleArrays.addElement(new Matrix(1)); angleArrays.addElement(new Matrix(1)); angleArrays.addElement(new Matrix(1)); int i; for (i = 3; i <= dimension; i++) { // Allocate space for angles (a Vector of Doubles) Vector angles_array = new Vector(i-1); // Fill in the sines and cosines array int j; for (j = 0; j <= i-2; j++) { if (i == 3) angles_array.addElement(new Double(5*Pi/6)); else angles_array.addElement(new Double(0)); } // Create this rotation matrix Matrix matrix = new Matrix(i); BuildRotMatrix(i, angles_array, matrix); // Add this angles array to the list angleArrays.addElement(angles_array); // Add this matrix to the list rotationMatrices.addElement(matrix); } perspectiveParams = new Vector(dimension); // Set all perspective parameters for (i = 0; i <= dimension; i++) { if ((i == 3) || (i == 4)) perspectiveParams.addElement(new Integer(7)); else perspectiveParams.addElement(new Integer(3)); } // Set up the screen points list screenPoints = new Vector(); // Set up all vertex arrays for (i = 0; i <= dimension; i++) vertexArrays.addElement(new Vector()); // Build the n-dimensional object build(); } //==== method NGraphic of NGraphic() ====\\ //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| method build of NGraphic //| //| Purpose: This builds the graphic. It fills in the vertex list, and //| adds paths to the object. //| //| Parameters: none //|__________________________________________________________________________ public void build() { // By default, the graphic is empty. Subclasses should override this } //==== method build of NGraphic ====// //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| method paint of NGraphic //| //| Purpose: Draw the graphic //| //| Parameters: none //|__________________________________________________________________________ public void paint(Graphics g) { // Project the object down to 2-space Project(dimension); // Draw a rectangle Dimension ourSize = size(); g.fillRect(0, 0, ourSize.width - 1, ourSize.height - 1); // Draw all paths int i; for (i = 0; i < paths.size(); i++) { // Get this path Path path = (Path) paths.elementAt(i); // Draw this primitive path.Draw(g, screenPoints); } } //==== method paint of NGraphic ====\\ //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| method OffsetAngle of NGraphic //| //| Purpose: Change one of the angles by some offset. //| //| Parameters: dimension: dimension this angle controls //| angle: the angle to change //| offset: the amount to change the angle by //|__________________________________________________________________________ void OffsetAngle(int dimension, int angle, double offset) { // Find the array of angles for this dimension Vector angles_array = (Vector) angleArrays.elementAt(dimension); // Change angle to old value + offset Double currentAngle = (Double) angles_array.elementAt(angle-1); ChangeAngle(dimension, angle, currentAngle.doubleValue() + offset); } //==== method OffsetAngle of NGraphic() ====\\ //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| method ChangeAngle of NGraphic //| //| Purpose: Change one of the angles. //| //| Parameters: dimension: dimension this angle controls //| angle: the angle to change //| value: new value of the angle //|__________________________________________________________________________ void ChangeAngle(int dimension, int angle, double value) { // Find the array of angles Vector angles_array = (Vector) angleArrays.elementAt(dimension); // Get the current value of the angle Double currentAngle = (Double) angles_array.elementAt(angle-1); double current_value = currentAngle.doubleValue(); // Change the value to the new value angles_array.setElementAt(new Double(value), angle-1); // Get the rotation matrix Matrix rotation_matrix = (Matrix) rotationMatrices.elementAt(dimension); // DEBUG: this is a "safer" way to build the matrix BuildRotMatrix(dimension, angles_array, rotation_matrix); // Build the matrix // UpdateRotMatrix(angle-1, current_value, value, // dimension, rotation_matrix); // Update the rotation matrix Project(dimension); // Project object with new angle } //==== method ChangeAngle of NGraphic() ====\\ //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| method ChangePerspective of NGraphic //| //| Purpose: Change the amount of perspective. //| //| Parameters: dimension: dimension to change perspective in //| perspective: new amount of perspective //|__________________________________________________________________________ void ChangePerspective(int dimension, int perspective) { // Change the parameter perspectiveParams.setElementAt(new Integer(perspective), dimension); // Project object with new perspective Project(dimension); } //==== method ChangePerspective of NGraphic() ====\\ //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| method ParameterChanged of NGraphic //| //| Purpose: This is called when a viewing parameter changes. //| //| Parameters: dimension: dimension to change the parameter in //| number: the parameter number (0=perspect., >0 = angle) //| value: the new parameter value //|__________________________________________________________________________ void ParameterChanged(int dimension, int number, int value) { if (number == 0) ChangePerspective(dimension, value); else ChangeAngle(dimension, number, ((double) value)*0.03490658504); repaint(); } //==== method ChangePerspective of NGraphic() ====\\ //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| method Project of NGraphic //| //| Purpose: Project the the vertices from the n-dimensional vertex list //| to the (n-1)-dimensional vertex list. This also propagates //| downward to (n-2), etc, down to 2D and the screen points. //| //| Parameters: n: dimension to project from //|__________________________________________________________________________ void Project(int n) { // Get the arrays of points for dimensions n and n-1 Vector points = (Vector) vertexArrays.elementAt(n); Vector projectedPoints = (Vector) vertexArrays.elementAt(n-1); // Clear the projected points list projectedPoints.removeAllElements(); // Get the rotation matrix Matrix rotmatrix = (Matrix) rotationMatrices.elementAt(n); // Loop through all points, rotating each one int vertex; for (vertex = 0; vertex < points.size(); vertex++) { // Get this source and projection point NPoint point = (NPoint) points.elementAt(vertex); // NPoint projectedPoint = (NPoint) projectedPoints.elementAt(vertex); // Rotate and project this point NPoint rotatedPoint = RotateVector(point, n, rotmatrix); NPoint projectedPoint = ProjectVector(rotatedPoint, n, ((Integer)perspectiveParams.elementAt(n)).intValue()); projectedPoints.addElement(projectedPoint); } if (n > 3) Project(n-1); // Recursively project downward to 3D else FitToPane(); // Map 2D points to screen points } //==== method Project() of NGraphic ====\\ //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| Procedure: ProjectVector //| //| Purpose: Project a vector from n-space to (n-1)-space, with perspective //| if desired. //| //| Parameters: point: the point to project //| dimension: the dimension of point //| persp_index: the perspective coefficient (0 for no perspective) //|_____________________________________________________________________________ NPoint ProjectVector(Vector point, int dimension, int persp_index) { double persp_table[] = new double[11]; persp_table[0] = 0.0; persp_table[1] = 50.0; persp_table[2] = 25.5; persp_table[3] = 15.5; persp_table[4] = 12.0; persp_table[5] = 9.0; persp_table[6] = 6.5; persp_table[7] = 4.5; persp_table[8] = 3.0; persp_table[9] = 2.5; persp_table[10] = 2.0; double perspective = persp_table[persp_index]; NPoint proj_point = new NPoint(dimension); if (perspective != 0) // Project point with perspective { double t = perspective / (perspective - ((Double) point.firstElement()).doubleValue()); if (t<0) t = -t; int i; for (i = 1; i < dimension; i++) proj_point.setElementAt(new Double(t * ((Double) point.elementAt(i)).doubleValue()), i-1); } else // No perspective-- just project it straight { // Just copy coordinates to project orthogonally int i; for (i=0; i < point.size(); i++) proj_point.setElementAt(point.elementAt(i+1), i); } return proj_point; } //==== ProjectVector() ====\\ //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| Procedure RotateVector //| //| Purpose: this procedure rotates a vector by multiplying it by //| a rotation matrix. //| //| Parameters: vector: the vector to rotate //| dim: dimension we're rotating in //| matrix: the rotation matrix //|____________________________________________________________________ NPoint RotateVector(Vector vector, int dim, Matrix matrix) { NPoint rotatedVector = new NPoint(dim); int row; // Multiply this vector by the rotation matrix for (row = 0; row < dim; row++) { // Initialize sum to 0 double sum = 0; int column; for (column = 0; column < dim; column++) { // Get the element at row, column double thisElement = matrix.getElement(row, column); // Add it into the sum sum += thisElement * ((Double) vector.elementAt(column)).doubleValue(); } // Set this coordinate rotatedVector.setElementAt(new Double(sum), row); } return rotatedVector; } //==== RotateVector() ====\\ //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| method FitToPane of NGraphic //| //| Purpose: Map the 2D vertices to their pane equivalents //| //| Parameters: fOtherView: none //|__________________________________________________________________________ void FitToPane() { double ymin = -3; double ymax = 3; double xmin = -3; double xmax = 3; // Get screen points list, and 2D points list Vector points2D = (Vector) vertexArrays.elementAt(2); // Clear out the screen points list screenPoints.removeAllElements(); // Find our boundaries Dimension ourSize = size(); int pane_left = 0; int pane_right = ourSize.height; int pane_top = 0; int pane_bottom = ourSize.width; // Fit all vertices to the pane int i; for (i = 0; i < points2D.size(); i++) { // Get this vertex Vector thisVertex = (Vector) points2D.elementAt(i); Point screenPoint = new Point(0,0); screenPoint.x = (int) (pane_left + (pane_right - pane_left) * (((Double) thisVertex.elementAt(0)).doubleValue() - xmin) / (xmax - xmin)); screenPoint.y = (int) (pane_bottom - (pane_bottom - pane_top) * (((Double) thisVertex.elementAt(1)).doubleValue() - ymin) / (ymax - ymin)); screenPoints.addElement(screenPoint); } } //==== method FitToPane of NGraphic ====\\ //|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //| Procedure BuildRotMatrix //| //| Purpose: this procedure fills in a dim-dimensional rotation matrix //| //| Parameters: dim: the dimension of this rotation //| angles: a matrix containing the angles //| matrix: matrix to change //|__________________________________________________________________ void BuildRotMatrix(int dim, Vector angles, Matrix matrix) { // // Here is the algorithm used to build the matrix from scratch. This algorith was // derived by examining correct rotation matrices up to size 7x7 (as derived by // Mathematica) and looking for patterns. The patterns were very obvious, but since // the analysis was not mathematical, this algorithm has not yet been formally PROVEN. // See Mathematica notebook RotMatrix Derivation for more information (available from Greg Ferrar). // // M[i,j] is the (i,j)th element of the rotation matrix, and rows and columns both // start at 0. s[i] and c[i] are the ith // element of the sines and cosines matrices, respectively. s[0] is defined to be 1.0 // (important for proper execution of step 5). // // 1. prod = 1; loop c from dim-1 to 1 step -1 { M[0,c] = -prod*s[c-1]; prod *= c[c-1]; } // 2. M[0, 0] = prod; // 3. loop r from 1 to dim-1 { M[r, r] = c[r-1] } // 4. loop r from 1 to dim-2 { loop c from r+1 to dim-1 { M[r, c] = 0 } } // 5. loop c from 0 to dim-2 { if (c==0) then prod = 1 else prod = -s[c-1]; // loop r from c+1 to dim-1 { M[r, c] = prod*s[r-1]; prod *= c[r-1] } } // // The matrix below is a sample 7x7 rotation matrix. The entries indicate which element // is generated by which step of the above algorithm. For instance, element (0,0) is 2, // indicating that the element in the 0th row and 0th column is generated by step 2 of // the algorithm. // // column = 0 column = dim-1 // | | // // [ 2 1 1 1 1 1 1 ] row = 0 // [ 5 3 4 4 4 4 4 ] // [ 5 5 3 4 4 4 4 ] // [ 5 5 5 3 4 4 4 ] // [ 5 5 5 5 3 4 4 ] // [ 5 5 5 5 5 3 4 ] // [ 5 5 5 5 5 0 3 ] row = dim-1 // double ANGLE_OFFSET = Pi/2000; // Matrix matrix = new Matrix(dim); double prod = 1.0; int r, c; for (c = dim-1; c >= 1; c--) // STEP 1 { matrix.setElement(0, c, -prod*Math.sin(((Double) angles.elementAt(c-1)).doubleValue() + ANGLE_OFFSET)); prod *= Math.cos(((Double) angles.elementAt(c-1)).doubleValue() + ANGLE_OFFSET); } matrix.setElement(0, 0, prod); // STEP 2 for (r = 1; r <= dim-1; r++) // STEP 3 matrix.setElement(r, r, Math.cos(((Double) angles.elementAt(r-1)).doubleValue() + ANGLE_OFFSET)); for (r = 1; r <= dim-2; r++) // STEP 4 for (c = r+1; c <= dim-1; c++) matrix.setElement(r, c, 0); for (c = 0; c <= dim-2; c++) // STEP 5 { if (c == 0) prod = 1.0; else prod = -Math.sin(((Double) angles.elementAt(c-1)).doubleValue() + ANGLE_OFFSET); for (r = c+1; r<= dim-1; r++) { matrix.setElement(r, c, prod * Math.sin(((Double) angles.elementAt(r-1)).doubleValue() + ANGLE_OFFSET)); prod *= Math.cos(((Double) angles.elementAt(r-1)).doubleValue() + ANGLE_OFFSET); } } } //==== BuildRotMatrix() ====\\ } //==== class NGraphic ====// /** * A Slider is a widget that varies between a minimum and a maximum * value. The user can drag a "thumb" to change the current value. As * the slider is dragged, Motion() is called. When the slider is * released, Release() is called. Override these two methods to give * the slider behavior. * * @version 0.90 15 Nov 1995 * @author Adam Doppelt */ class Slider extends Canvas { private final static int THUMB_SIZE = 14; private final static int BUFFER = 2; private final static int TEXT_HEIGHT = 18; private final static int TEXT_BUFFER = 3; private final static int DEFAULT_WIDTH = 100; private final static int DEFAULT_HEIGHT = 15; private final static int MIN_WIDTH = 2 * (THUMB_SIZE + BUFFER + 1); private final static int MIN_HEIGHT = 2 * (BUFFER + 1); private final static int DEFAULT_MIN = 1; private final static int DEFAULT_MAX = 100; int min_, max_, value_, pixel_; int pixelMin_, pixelMax_; Color backgroundColor_, thumbColor_, barColor_, slashColor_, textColor_; Font font_; /** * Constructs a slider. * @param container The container for this slider. */ public Slider () { min_ = DEFAULT_MIN; max_ = DEFAULT_MAX; resize(DEFAULT_WIDTH, DEFAULT_HEIGHT + TEXT_HEIGHT); font_ = new Font("TimesRoman", Font.PLAIN, 12); backgroundColor_ = Color.lightGray; thumbColor_ = Color.lightGray; barColor_ = Color.lightGray.darker(); slashColor_ = Color.black; textColor_ = Color.black; SetValue(1); } /** * This method is called when the "thumb" of the slider is dragged by * the user. Must be overridden to give the slider some behavior. * */ public void Motion () { ; } /** * This method is called when the "thumb" of the slider is released * after being dragged. Must be overridden to give the slider some * behavior. * */ public void Release () { ; } /** * Sets the maximum value for the slider. * @param num The new maximum. */ public void SetMaximum (int num) { max_ = num; if (max_ < min_) { int t = min_; min_ = max_; max_ = t; } SetValue(value_); } /** * Sets the minimum value for the slider. * @param num The new minimum. */ public void SetMinimum (int num) { min_ = num; if (max_ < min_) { int t = min_; min_ = max_; max_ = t; } SetValue(value_); } /** * Sets the current value for the slider. The thumb will move to * reflect the new setting. * @param num The new setting for the slider. * */ public void SetValue (int num) { value_ = num; if (value_ < min_) value_ = min_; else if (value_ > max_) value_ = max_; if (value_ != min_) pixel_ = (int)(Math.round(Math.abs((double)(value_ - min_) / (double)(max_ - min_)) * (double)(pixelMax_ - pixelMin_)) + pixelMin_); else pixel_ = pixelMin_; repaint(); } /** * Sets the height of the slider. This is the height of the entire * slider canvas, including space reserved for displaying the * current value. * @param num The new height. * */ public void SetHeight (int num) { if (num < MIN_HEIGHT + TEXT_HEIGHT) num = MIN_HEIGHT + TEXT_HEIGHT; resize(size().width, num); repaint(); } /** * Sets the width of the slider. This is the width of the actual * slider box. * @param num The new width. * */ public void SetWidth (int num) { if (num < MIN_WIDTH) num = MIN_WIDTH; resize(num, size().height); repaint(); } /** * Returns the current value for the slider. * @return The current value for the slider. */ public int GetValue () { return value_; } /** * Sets the background color for the slider. The "background" is the * area outside of the bar. * @param color The new background color. */ public void SetBackgroundColor(Color color) { backgroundColor_ = color; repaint(); } /** * Sets the color for the slider's thumb. The "thumb" is the box that * the user can slide back and forth. * @param color The new thumb color. */ public void SetThumbColor(Color color) { thumbColor_ = color; repaint(); } /** * Sets the color for the slider's bar. The "bar" is the rectangle * that the thumb slides around in. * @param color The new bar color. */ public void SetBarColor (Color color) { barColor_ = color; repaint(); } /** * Sets the slash color for the slider. The "slash" is the little * vertical line on the thumb. * @param color The new slash color. */ public void SetSlashColor(Color color) { slashColor_ = color; repaint(); } /** * Sets the color for the slider`s text. * @param color The new text color. */ public void SetTextColor(Color color) { textColor_ = color; repaint(); } /** * Sets the font for the slider`s text. * @param font The new font. */ public void SetFont(Font font) { font_ = font; repaint(); } /** * An internal method used to handle repaint events. */ public void paint(Graphics g) { int width = size().width; int height = size().height; g.setColor(backgroundColor_); g.fillRect(0, 0, width, TEXT_HEIGHT); g.setColor(barColor_); g.fill3DRect(0, TEXT_HEIGHT, width, height - TEXT_HEIGHT, false); g.setColor(thumbColor_); g.fill3DRect(pixel_ - THUMB_SIZE, TEXT_HEIGHT + BUFFER, THUMB_SIZE * 2 + 1, height - 2 * BUFFER - TEXT_HEIGHT, true); g.setColor(slashColor_); g.drawLine(pixel_, TEXT_HEIGHT + BUFFER + 1, pixel_, height - 2 * BUFFER); g.setColor(textColor_); g.setFont(font_); String str = String.valueOf(value_); g.drawString(str, pixel_ - (int)(getFontMetrics(font_).stringWidth(str) / 2), TEXT_HEIGHT - TEXT_BUFFER); } void HandleMouse(int x) { double percent; int width = size().width; pixel_ = Math.max(x, pixelMin_); pixel_ = Math.min(pixel_, pixelMax_); if (pixel_ != pixelMin_) percent = (((double)pixel_ - pixelMin_) / (pixelMax_ - pixelMin_)); else percent = 0; value_ = (int)(Math.round(percent * (double)(max_ - min_))) + min_; paint(getGraphics()); } /** * An internal method used to handle mouse down events. */ public boolean mouseDown (Event e, int x, int y) { HandleMouse(x); Motion(); return true; } /** * An internal method used to handle mouse drag events. */ public boolean mouseDrag (Event e, int x, int y) { HandleMouse(x); Motion(); return true; } /** * An internal method used to handle mouse up events. */ public boolean mouseUp (Event e, int x, int y) { HandleMouse(x); Release(); return true; } /** * An internal method used to handle resizing. */ public void reshape(int x, int y, int width, int height) { super.reshape(x, y, width, height); pixelMin_ = THUMB_SIZE + BUFFER; pixelMax_ = width - THUMB_SIZE - BUFFER - 1; if (value_ != min_) pixel_ = (int)(Math.round(Math.abs((double)(value_ - min_) / (double)(max_ - min_)) * (double)(pixelMax_ - pixelMin_)) + pixelMin_); else pixel_ = pixelMin_; } }