package matrix; import java.lang.Math; // Matrix3Dh for making transformations // to homogenous 3D Vectors (Vector3Dh) // including translations and perspective // transformations which can only be done // with the homogenous version carrying // an extra dimension. // These transformation can be applied // on other transformation to make composite // transformations. public class Matrix3Dh extends Matrix { public Matrix3Dh() { super(4); //{{INIT_CONTROLS //}} } public Matrix3Dh(Matrix M) throws MatrixDimensionError { super(4); copy(M); } public Matrix3Dh(Matrix3D M) { super(4); subcopy(M); } // promote a 2Dh to a 3Dh Matrix by filling in the // z row and column with 0 0 1 0: // M M 0 M // M M 0 M // 0 0 1 0 // M M 0 M public Matrix3Dh(Matrix2Dh M) { super(4); for (int i = 0; i < 2; i++) { setCell(i, 0, M.cell(i, 0)); setCell(i, 1, M.cell(i, 1)); setCell(i, 3, M.cell(i, 2)); } setCell(3, 0, M.cell(2, 0)); setCell(3, 1, M.cell(2, 1)); setCell(3, 3, M.cell(2, 2)); } // creates a transformation matrix to transforms // from one 3d coordinate system to another // 3d coordinate system at the eye with z-axis // pointing toward look-at and y-axis on the plane // of up and lookat (where -y axis is toward the // up direction). public static Matrix3Dh view(Vector3D eye, Vector3D lookat, Vector3D up) { // rotate to make Z point to lookat // and translate to eye Matrix3Dh R_T = Matrix3D.rotateToZ(lookat.subtract(eye)).applyOn( Matrix3Dh.translate(Vector3D.mult(eye,-1))); // rotate so y-axis points toward updir Vector3D updir = R_T.transform(up); // transform to new orientation double yang = Math.atan2(-updir.x(), -updir.y()); return Matrix3D.rotateZ(yang).applyOn(R_T); } // create a transformation matrix which when applied // to a Vector3D(X, Y, Z) yeilds Vector3D(X+x, Y+y, Z=z). public static Matrix3Dh translate(double x, double y, double z) { Matrix3Dh trans = new Matrix3Dh(); trans.setCell(3, 0, x); trans.setCell(3, 1, y); trans.setCell(3, 2, z); return trans; } public static Matrix3Dh translate(Vector3D v) { return translate(v.x(), v.y(), v.z()); } // Create a transformation matrix for projecting a // Vector3D to a Vector2D using the project() function. // When projecting onto a screen, the screen // is effectively the plane: z=p, and the origin // is at the center of the screen. public static Matrix3Dh perspective(double p) { Matrix3Dh P = new Matrix3Dh(); P.setCell(2, 3, 1 / p); P.setCell(3, 3, 0); return P; } // Create a perspective transformation which is scaled // and centered on the screen. public static Matrix3Dh perspective(double p, Vector2D center, Vector2D scale) { return Matrix3Dh.translate(center.x(), center.y(), 0).applyOn( Matrix3D.scale(scale.x(), scale.y(), 1)).applyOn( Matrix3Dh.perspective(p)); } /* scalar multiplication */ public static Matrix3Dh mult(Matrix3Dh M, double a) { Matrix3Dh result = new Matrix3Dh(); result.copy(M); result.selfMult(a); return result; } public Matrix mult(double a) { return mult(this, a); } /* vector multiplication */ public Vector3Dh applyOn(Vector3Dh v2) { // equivalent to Vector3Dh(Vector3D(Matrix.mult(this, v2))) // but optimized for the 3Dh case and no conversion needed. return new Vector3Dh(cell(0, 0) * v2.x() + cell(1, 0) * v2.y() + cell(2, 0) * v2.z() + cell(3, 0) * v2.w, cell(0, 1) * v2.x() + cell(1, 1) * v2.y() + cell(2, 1) * v2.z() + cell(3, 1) * v2.w, cell(0, 2) * v2.x() + cell(1, 2) * v2.y() + cell(2, 2) * v2.z() + cell(3, 2) * v2.w, cell(0, 3) * v2.x() + cell(1, 3) * v2.y() + cell(2, 3) * v2.z() + cell(3, 3) * v2.w); } public Vector3Dh applyOn(Vector3D v2) { // equivalent to Vector3Dh(Vector3D(Matrix.mult(this, Vector3Dh(v2)))) // but optimized for the 3Dh case and no conversion needed. return new Vector3Dh(cell(0, 0) * v2.x() + cell(1, 0) * v2.y() + cell(2, 0) * v2.z() + cell(3, 0), cell(0, 1) * v2.x() + cell(1, 1) * v2.y() + cell(2, 1) * v2.z() + cell(3, 1), cell(0, 2) * v2.x() + cell(1, 2) * v2.y() + cell(2, 2) * v2.z() + cell(3, 2), cell(0, 3) * v2.x() + cell(1, 3) * v2.y() + cell(2, 3) * v2.z() + cell(3, 3)); } // Apply on a Vector2D which equivalently get promoted to // a Vector3D with z = 0. public Vector3Dh applyOn(Vector2D v2) { // equivalent to Vector3Dh(Vector3D(Matrix.mult(this, Vector2Dh(Vector2D(v2))))) // but optimized for the 3Dh case and no conversion needed. return new Vector3Dh(cell(0, 0) * v2.x() + cell(1, 0) * v2.y() + cell(3, 0), cell(0, 1) * v2.x() + cell(1, 1) * v2.y() + cell(3, 1), cell(0, 2) * v2.x() + cell(1, 2) * v2.y() + cell(3, 2), cell(0, 3) * v2.x() + cell(1, 3) * v2.y() + cell(3, 3)); } // Project by applying the Matrix, homogenizing the // result, and ignore the z-coordinate (which would // just be the distance from eye to screen). public Vector2D project(Vector3D V) throws PointAtInfinity { Vector3Dh result = this.applyOn(V); return result.homogenized2D(); } // Project by applying the Matrix, homogenizing the // result, and ignore the z-coordinate (which would // just be the distance from eye to screen). public Vector2D project(Vector2D V) throws PointAtInfinity { // promotes the 2D vector to a 3D vector with z = 0 Vector3Dh result = this.applyOn(V); return result.homogenized2D(); } // Apply the matrix but don't perform a projection // by ignoring the resultant homogeneous coordinate. public Vector3D transform(Vector3D V) { return new Vector3D(cell(0, 0) * V.x() + cell(1, 0) * V.y() + cell(2, 0) * V.z() + cell(3, 0), cell(0, 1) * V.x() + cell(1, 1) * V.y() + cell(2, 1) * V.z() + cell(3, 1), cell(0, 2) * V.x() + cell(1, 2) * V.y() + cell(2, 2) * V.z() + cell(3, 2)); } // Apply the 3x3 sub-matrix which re-orients the vector // without translating or projecting it. public Vector3D orient(Vector3D V) { return new Vector3D(cell(0, 0) * V.x() + cell(1, 0) * V.y() + cell(2, 0) * V.z(), cell(0, 1) * V.x() + cell(1, 1) * V.y() + cell(2, 1) * V.z(), cell(0, 2) * V.x() + cell(1, 2) * V.y() + cell(2, 2) * V.z()); } /* matrix multiplication */ public Matrix3Dh applyOn(Matrix3Dh M2) { // equivalent to Matrix3Dh(Matrix.mult(this, M2)) // but optimized for the 3Dh case and no conversion needed. Matrix3Dh result = new Matrix3Dh(); for (int j = 0; j < rows(); j++) for (int i = 0; i < cols(); i++) result.setCell(i, j, cell(0, j) * M2.cell(i, 0) + cell(1, j) * M2.cell(i, 1) + cell(2, j) * M2.cell(i, 2) + cell(3, j) * M2.cell(i, 3)); return result; } public Matrix3Dh applyOn(Matrix3D M2) { // equivalent to Matrix3Dh(Matrix.mult(this, Matrix3Dh(M2))) // but optimized for the 3Dh case and no conversion needed return this.applyOn(new Matrix3Dh(M2)); } public static Matrix3Dh apply(Matrix3D M1, Matrix3Dh M2) { // equivalent to Matrix3Dh(Matrix.mult(Matrix3Dh(M1), M2)) // but optimized for the 3Dh case and no conversion needed. return (new Matrix3Dh(M1)).applyOn(M2); } public static Matrix3Dh apply(Matrix2Dh M1, Matrix3Dh M2) { // equivalent to Matrix3Dh(Matrix.mult(Matrix3Dh(Matrix3D(M1)), M2)) // but optimized for the 3Dh case and no conversion needed. return (new Matrix3Dh(M1)).applyOn(M2); } //{{DECLARE_CONTROLS //}} }