//
// Copyright 2001 Ken Perlin package render; //----- SIMPLE CLASS TO HANDLE BASIC 3D MATRIX OPERATIONS ----- /** Provides functionality for 4x4 3D matrix manipulations. It's thread-safe. @author Ken Perlin 2001 */ public class Matrix { private String notice = "Copyright 2001 Ken Perlin. All rights reserved."; private static double identity[] = new double[16]; static { for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) { identity[(i << 2) + j] = (i == j ? 1 : 0); } } private double mMatrix[] = new double[16]; private double tmp[] = new double[16]; private double tmp2[] = new double[16]; /** Default constructor. */ public Matrix() { } /** Constructor takes an array of 16 elements to populate the 4x4 matrix. @param a 4x4 quaternion values */ public Matrix(double a[]) { if (a.length == 4) { // quaternion double Nq = a[0] * a[0] + a[1] * a[1] + a[2] * a[2] + a[3] * a[3]; double s = (Nq > 0.0) ? (2.0 / Nq) : 0.0; double xs = a[0] * s, ys = a[1] * s, zs = a[2] * s; double wx = a[3] * xs, wy = a[3] * ys, wz = a[3] * zs; double xx = a[0] * xs, xy = a[0] * ys, xz = a[0] * zs; double yy = a[1] * ys, yz = a[1] * zs, zz = a[2] * zs; mMatrix[(0 << 2) + 0] = 1.0 - (yy + zz); mMatrix[(1 << 2) + 0] = xy + wz; mMatrix[(2 << 2) + 0] = xz - wy; mMatrix[(0 << 2) + 1] = xy - wz; mMatrix[(1 << 2) + 1] = 1.0 - (xx + zz); mMatrix[(2 << 2) + 1] = yz + wx; mMatrix[(0 << 2) + 2] = xz + wy; mMatrix[(1 << 2) + 2] = yz - wx; mMatrix[(2 << 2) + 2] = 1.0 - (xx + yy); mMatrix[(0 << 2) + 3] = mMatrix[(1 << 2) + 3] = mMatrix[(2 << 2) + 3] = mMatrix[(3 << 2) + 0] = mMatrix[(3 << 2) + 1] = mMatrix[(3 << 2) + 2] = 0.0; mMatrix[(3 << 2) + 3] = 1.0; } else { System.arraycopy(a, 0, mMatrix, 0, 16); } } /** Returns matrix value at m[i, j]. @param i row index @param j column index @return value at specified location */ public final double get(int i, int j) { return mMatrix[(i << 2) + j]; } /** Sets matrix value at m[i,j] to d. @param i row index @param j column index @param d the new value */ public final void set(int i, int j, double d) { mMatrix[(i << 2) + j] = d; } /** Returns the actual array containing the matrix (not thread-safe). @return the actual matrix array of 16 elements @see #get */ public final double[] getUnsafe() { return mMatrix; } /** Returns a copy of matrix (thread-safe)/ @return a copy of the matrix array (16 elements). @see #getUnsafe */ public final double[] get() { double m[] = new double[16]; System.arraycopy(mMatrix, 0, m, 0, 16); return m; } /** Sets the desired matrix to the identity matrix. @param m the matrix to be modified */ public static final void identity(Matrix m) { System.arraycopy(identity, 0, m.getUnsafe(), 0, 16); } /** Sets the object matrix to the identity matrix. */ public final void identity() { System.arraycopy(identity, 0, mMatrix, 0, 16); } /** Sets the desired matrix array to the identity matrix. @param m matrix array */ private static void identity(double[] m) { System.arraycopy(identity, 0, m, 0, 16); } /** Copies contents from matrix src to the object matrix. @param src original matrix to be copied */ public final void copy(Matrix src) { System.arraycopy(src.getUnsafe(), 0, mMatrix, 0, 16); } private void preMultiply(double b[]) { double dst[] = getUnsafe(); System.arraycopy(mMatrix, 0, tmp, 0, 16); for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) { dst[(i << 2) + j] = 0.0; dst[(i << 2) + j] += tmp[(i << 2) + 0] * b[(0 << 2) + j]; dst[(i << 2) + j] += tmp[(i << 2) + 1] * b[(1 << 2) + j]; dst[(i << 2) + j] += tmp[(i << 2) + 2] * b[(2 << 2) + j]; dst[(i << 2) + j] += tmp[(i << 2) + 3] * b[(3 << 2) + j]; } } /** Premultiplies the object matrix by mb and stores the result in the object; As a result, the translation, scaling and rotation operations contained in mb are effectively performed before those in the object . @param mb the multiplier matrix */ public final void preMultiply(Matrix mb) { preMultiply(mb.getUnsafe()); } private void postMultiply(double b[]) { double dst[] = getUnsafe(); System.arraycopy(mMatrix, 0, tmp, 0, 16); for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) { dst[(i << 2) + j] = 0; dst[(i << 2) + j] += b[(i << 2) + 0] * tmp[(0 << 2) + j]; dst[(i << 2) + j] += b[(i << 2) + 1] * tmp[(1 << 2) + j]; dst[(i << 2) + j] += b[(i << 2) + 2] * tmp[(2 << 2) + j]; dst[(i << 2) + j] += b[(i << 2) + 3] * tmp[(3 << 2) + j]; } } /** Postmultiplies the object matrix by mb and stores the result in the object matrix; As a result, the translation, scaling and rotation operations contained in mb are effectively performed after those in the object matrix . @param mb the multiplier matrix */ public final void postMultiply(Matrix mb) { postMultiply(mb.getUnsafe()); } //----- ROUTINES TO ROTATE AND TRANSLATE MATRICES ----- /** Applies a translation by x, y, z to the obeject matrix. The shape or orientation of the object are not affected. @param x amount of translation along the x axis @param y amount of translation along the y axis @param z amount of translation along the z axis */ public final void translate(double x, double y, double z) { makeTranslationMatrix(tmp2, x, y, z); preMultiply(tmp2); } /** Modifies the object matrix to rotate about the X axis by angle theta. @param theta angle of rotation in radians */ public final void rotateX(double theta) { makeRotationMatrix(tmp2, 1, 2, theta); preMultiply(tmp2); } /** Modifies the object matrix to rotate about the Y axis by angle theta. @param theta angle of rotation in radians */ public final void rotateY(double theta) { makeRotationMatrix(tmp2, 2, 0, theta); preMultiply(tmp2); } /** Modifies the object matrix to rotate about the Z axis by angle theta. @param theta angle of rotation in radians */ public final void rotateZ(double theta) { makeRotationMatrix(tmp2, 0, 1, theta); preMultiply(tmp2); } /** Modifies the object matrix to rotate by angle theta about axis x,y,z. @param theta angle of rotation in radians @param x 1st coord of rotation axis @param y 2nd coord of rotation axis @param z 3rd coord of rotation axis */ public final void rotate(double theta, double x, double y, double z) { double unY = Math.atan2(y, x); double unX = Math.atan2(Math.sqrt(x * x + y * y), z); rotateZ(unY); rotateY(unX); rotateZ(theta); rotateY(-unX); rotateZ(-unY); } /** Scales the transformation matrix by x,y,z in the respective directions. @param x scale factor along the x axis @param y scale factor along the y axis @param z scale factor along the z axis */ public final void scale(double x, double y, double z) { makeScaleMatrix(tmp2, x, y, z); preMultiply(tmp2); } //----- INVERTING A 4x4 THAT WAS CREATED BY TRANSLATIONS+ROTATIONS+SCALES /** Inverts the 4x4 matrix and stores the result in the object matrix. @param msrc original matrix to be inverted */ public final void invert(Matrix msrc) { double src[] = msrc.getUnsafe(); double dst[] = mMatrix; // COMPUTE ADJOINT COFACTOR MATRIX FOR THE ROTATION+SCALE 3x3 for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) { int i0 = (i + 1) % 3; int i1 = (i + 2) % 3; int j0 = (j + 1) % 3; int j1 = (j + 2) % 3; dst[(j << 2) + i] = src[(i0 << 2) + j0] * src[(i1 << 2) + j1] - src[(i0 << 2) + j1] * src[(i1 << 2) + j0]; } // RENORMALIZE BY DETERMINANT TO GET ROTATION+SCALE 3x3 INVERSE double determinant = src[(0 << 2) + 0] * dst[(0 << 2) + 0] + src[(1 << 2) + 0] * dst[(0 << 2) + 1] + src[(2 << 2) + 0] * dst[(0 << 2) + 2]; double invd = 1.0 / determinant; for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) dst[(i << 2) + j] *= invd; // COMPUTE INVERSE TRANSLATION for (int i = 0; i < 3; i++) dst[(i << 2) + 3] = -dst[(i << 2) + 0] * src[(0 << 2) + 3] - dst[(i << 2) + 1] * src[(1 << 2) + 3] - dst[(i << 2) + 2] * src[(2 << 2) + 3]; } //----- FOR DEBUGGING ----- /** Converts the transformation matrix to a String. @param m matrix to be translated to text @return a textual representation of the matrix */ public final String toString(Matrix mm) { double m[] = mm.getUnsafe(); String s = "{"; for (int i = 0; i < 4; i++) { s += "{"; for (int j = 0; j < 4; j++) { int n = (int) (100 * m[(i << 2) + j]); s += (n / 100.) + (j == 3 ? "" : ","); } s += "}" + (i == 3 ? "" : ","); } return s + "}"; } //----- ROUTINES TO GENERATE TRANSFORMATION MATRICES ----- private static void makeTranslationMatrix(double m[], double x, double y, double z) { identity(m); m[(0 << 2) + 3] = x; m[(1 << 2) + 3] = y; m[(2 << 2) + 3] = z; } private static void makeRotationMatrix(double m[], int i, int j, double theta) { identity(m); m[(i << 2) + i] = m[(j << 2) + j] = Math.cos(theta); m[(i << 2) + j] = -Math.sin(theta); m[(j << 2) + i] = -m[(i << 2) + j]; } private static void makeScaleMatrix(double m[], double x, double y, double z) { identity(m); m[(0 << 2) + 0] *= x; m[(1 << 2) + 1] *= y; m[(2 << 2) + 2] *= z; } public String toString() { int k = 0; String s = new String("[ "); for (int i = 0; i < 4; i++) { s += "\t[ "; for (int j = 0; j < 4; j++) s += String.valueOf(mMatrix[k++]) + " "; s += "]\n"; } s += "]"; return s; } }