--- /dev/null 2015-04-01 20:20:30.714960462 +0300 +++ new/apps/samples/3DViewer/src/main/java/com/javafx/experiments/utils3d/geom/transform/GeneralTransform3D.java 2015-04-01 21:32:39.937401888 +0300 @@ -0,0 +1,952 @@ +/* + * Copyright (c) 2007, 2015, Oracle and/or its affiliates. + * All rights reserved. Use is subject to license terms. + * + * This file is available and licensed under the following license: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * - Neither the name of Oracle Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.javafx.experiments.utils3d.geom.transform; + +import com.javafx.experiments.utils3d.geom.BaseBounds; +import com.javafx.experiments.utils3d.geom.Point2D; +import com.javafx.experiments.utils3d.geom.Vec3d; +import com.javafx.experiments.utils3d.geom.Vec3f; + +/** + * A general-purpose 4x4 transform that may or may not be affine. The + * GeneralTransform is typically used only for projection transforms. + * + */ +public final class GeneralTransform3D implements CanTransformVec3d { + + //The 4x4 double-precision floating point matrix. The mathematical + //representation is row major, as in traditional matrix mathematics. + protected double[] mat = new double[16]; + + //flag if this is an identity transformation. + private boolean identity; + + /** + * Constructs and initializes a transform to the identity matrix. + */ + public GeneralTransform3D() { + setIdentity(); + } + + /** + * Returns true if the transform is affine. A transform is considered + * to be affine if the last row of its matrix is (0,0,0,1). Note that + * an instance of AffineTransform3D is always affine. + */ + public boolean isAffine() { + if (!isInfOrNaN() && + almostZero(mat[12]) && + almostZero(mat[13]) && + almostZero(mat[14]) && + almostOne(mat[15])) { + + return true; + } else { + return false; + } + } + + /** + * Sets the value of this transform to the specified transform. + * + * @param t1 the transform to copy into this transform. + * + * @return this transform + */ + public GeneralTransform3D set(GeneralTransform3D t1) { + System.arraycopy(t1.mat, 0, mat, 0, mat.length); + updateState(); + return this; + } + + /** + * Sets the matrix values of this transform to the values in the + * specified array. + * + * @param m an array of 16 values to copy into this transform in + * row major order. + * + * @return this transform + */ + public GeneralTransform3D set(double[] m) { + System.arraycopy(m, 0, mat, 0, mat.length); + updateState(); + return this; + } + + /** + * Returns a copy of an array of 16 values that contains the 4x4 matrix + * of this transform. The first four elements of the array will contain + * the top row of the transform matrix, etc. + * + * @param rv the return value, or null + * + * @return an array of 16 values + */ + public double[] get(double[] rv) { + if (rv == null) { + rv = new double[mat.length]; + } + System.arraycopy(mat, 0, rv, 0, mat.length); + + return rv; + } + + public double get(int index) { + assert ((index >= 0) && (index < mat.length)); + return mat[index]; + } + + private Vec3d tempV3d; + + public BaseBounds transform(BaseBounds src, BaseBounds dst) { + if (tempV3d == null) { + tempV3d = new Vec3d(); + } + return TransformHelper.general3dBoundsTransform(this, src, dst, tempV3d); + } + + /** + * Transform 2D point (with z == 0) + * @param point + * @param pointOut + * @return + */ + public Point2D transform(Point2D point, Point2D pointOut) { + if (pointOut == null) { + pointOut = new Point2D(); + } + + double w = (float) (mat[12] * point.x + mat[13] * point.y + + mat[15]); + + pointOut.x = (float) (mat[0] * point.x + mat[1] * point.y + + mat[3]); + pointOut.y = (float) (mat[4] * point.x + mat[5] * point.y + + mat[7]); + + pointOut.x /= w; + pointOut.y /= w; + + return pointOut; + } + + /** + * Transforms the point parameter with this transform and + * places the result into pointOut. The fourth element of the + * point input paramter is assumed to be one. + * + * @param point the input point to be transformed + * + * @param pointOut the transformed point + * + * @return the transformed point + */ + public Vec3d transform(Vec3d point, Vec3d pointOut) { + if (pointOut == null) { + pointOut = new Vec3d(); + } + + // compute here as point.x, point.y and point.z may change + // in case (point == pointOut), and mat[14] is usually != 0 + double w = (float) (mat[12] * point.x + mat[13] * point.y + + mat[14] * point.z + mat[15]); + + pointOut.x = (float) (mat[0] * point.x + mat[1] * point.y + + mat[2] * point.z + mat[3]); + pointOut.y = (float) (mat[4] * point.x + mat[5] * point.y + + mat[6] * point.z + mat[7]); + pointOut.z = (float) (mat[8] * point.x + mat[9] * point.y + + mat[10] * point.z + mat[11]); + + if (w != 0.0f) { + pointOut.x /= w; + pointOut.y /= w; + pointOut.z /= w; + } + + return pointOut; + } + + + /** + * Transforms the point parameter with this transform and + * places the result back into point. The fourth element of the + * point input paramter is assumed to be one. + * + * @param point the input point to be transformed + * + * @return the transformed point + */ + public Vec3d transform(Vec3d point) { + return transform(point, point); + } + + /** + * Transforms the normal parameter by this transform and places the value + * into normalOut. The fourth element of the normal is assumed to be zero. + * Note: For correct lighting results, if a transform has uneven scaling + * surface normals should transformed by the inverse transpose of + * the transform. This the responsibility of the application and is not + * done automatically by this method. + * + * @param normal the input normal to be transformed + * + * @param normalOut the transformed normal + * + * @return the transformed normal + */ + public Vec3f transformNormal(Vec3f normal, Vec3f normalOut) { + normal.x = (float) (mat[0]*normal.x + mat[1]*normal.y + + mat[2]*normal.z); + normal.y = (float) (mat[4]*normal.x + mat[5]*normal.y + + mat[6]*normal.z); + normal.z = (float) (mat[8]*normal.x + mat[9]*normal.y + + mat[10]*normal.z); + return normalOut; + } + + /** + * Transforms the normal parameter by this transform and places the value + * back into normal. The fourth element of the normal is assumed to be zero. + * Note: For correct lighting results, if a transform has uneven scaling + * surface normals should transformed by the inverse transpose of + * the transform. This the responsibility of the application and is not + * done automatically by this method. + * + * @param normal the input normal to be transformed + * + * @return the transformed normal + */ + public Vec3f transformNormal(Vec3f normal) { + return transformNormal(normal, normal); + } + + /** + * Sets the value of this transform to a perspective projection transform. + * This transform maps points from Eye Coordinates (EC) + * to Clipping Coordinates (CC). + * Note that the field of view is specified in radians. + * + * @param verticalFOV specifies whether the fov is vertical (Y direction). + * + * @param fov specifies the field of view in radians + * + * @param aspect specifies the aspect ratio. The aspect ratio is the ratio + * of width to height. + * + * @param zNear the distance to the frustum's near clipping plane. + * This value must be positive, (the value -zNear is the location of the + * near clip plane). + * + * @param zFar the distance to the frustum's far clipping plane + * + * @return this transform + */ + public GeneralTransform3D perspective(boolean verticalFOV, + double fov, double aspect, double zNear, double zFar) { + double sine; + double cotangent; + double deltaZ; + double half_fov = fov * 0.5; + + deltaZ = zFar - zNear; + sine = Math.sin(half_fov); + + cotangent = Math.cos(half_fov) / sine; + + mat[0] = verticalFOV ? cotangent / aspect : cotangent; + mat[5] = verticalFOV ? cotangent : cotangent * aspect; + mat[10] = -(zFar + zNear) / deltaZ; + mat[11] = -2.0 * zNear * zFar / deltaZ; + mat[14] = -1.0; + mat[1] = mat[2] = mat[3] = mat[4] = mat[6] = mat[7] = mat[8] = mat[9] = mat[12] = mat[13] = mat[15] = 0; + + updateState(); + return this; + } + + /** + * Sets the value of this transform to an orthographic (parallel) + * projection transform. + * This transform maps coordinates from Eye Coordinates (EC) + * to Clipping Coordinates (CC). Note that unlike the similar function + * in OpenGL, the clipping coordinates generated by the resulting + * transform are in a right-handed coordinate system. + * @param left the vertical line on the left edge of the near + * clipping plane mapped to the left edge of the graphics window + * @param right the vertical line on the right edge of the near + * clipping plane mapped to the right edge of the graphics window + * @param bottom the horizontal line on the bottom edge of the near + * clipping plane mapped to the bottom edge of the graphics window + * @param top the horizontal line on the top edge of the near + * clipping plane mapped to the top edge of the graphics window + * @param near the distance to the frustum's near clipping plane + * (the value -near is the location of the near clip plane) + * @param far the distance to the frustum's far clipping plane + * + * @return this transform + */ + public GeneralTransform3D ortho(double left, double right, double bottom, + double top, double near, double far) { + double deltax = 1 / (right - left); + double deltay = 1 / (top - bottom); + double deltaz = 1 / (far - near); + + mat[0] = 2.0 * deltax; + mat[3] = -(right + left) * deltax; + mat[5] = 2.0 * deltay; + mat[7] = -(top + bottom) * deltay; + mat[10] = 2.0 * deltaz; + mat[11] = (far + near) * deltaz; + mat[1] = mat[2] = mat[4] = mat[6] = mat[8] = + mat[9] = mat[12] = mat[13] = mat[14] = 0; + mat[15] = 1; + + updateState(); + return this; + } + + public double computeClipZCoord() { + double zEc = (1.0 - mat[15]) / mat[14]; + double zCc = mat[10] * zEc + mat[11]; + return zCc; + } + + /** + * Computes the determinant of this transform. + * + * @return the determinant + */ + public double determinant() { + // cofactor exapainsion along first row + return mat[0]*(mat[5]*(mat[10]*mat[15] - mat[11]*mat[14]) - + mat[6]*(mat[ 9]*mat[15] - mat[11]*mat[13]) + + mat[7]*(mat[ 9]*mat[14] - mat[10]*mat[13])) - + mat[1]*(mat[4]*(mat[10]*mat[15] - mat[11]*mat[14]) - + mat[6]*(mat[ 8]*mat[15] - mat[11]*mat[12]) + + mat[7]*(mat[ 8]*mat[14] - mat[10]*mat[12])) + + mat[2]*(mat[4]*(mat[ 9]*mat[15] - mat[11]*mat[13]) - + mat[5]*(mat[ 8]*mat[15] - mat[11]*mat[12]) + + mat[7]*(mat[ 8]*mat[13] - mat[ 9]*mat[12])) - + mat[3]*(mat[4]*(mat[ 9]*mat[14] - mat[10]*mat[13]) - + mat[5]*(mat[ 8]*mat[14] - mat[10]*mat[12]) + + mat[6]*(mat[ 8]*mat[13] - mat[ 9]*mat[12])); + } + + /** + * Inverts this transform in place. + * + * @return this transform + */ + public GeneralTransform3D invert() { + return invert(this); + } + + /** + * General invert routine. Inverts t1 and places the result in "this". + * Note that this routine handles both the "this" version and the + * non-"this" version. + * + * Also note that since this routine is slow anyway, we won't worry + * about allocating a little bit of garbage. + */ + private GeneralTransform3D invert(GeneralTransform3D t1) { + double[] tmp = new double[16]; + int[] row_perm = new int[4]; + + // Use LU decomposition and backsubstitution code specifically + // for floating-point 4x4 matrices. + // Copy source matrix to tmp + System.arraycopy(t1.mat, 0, tmp, 0, tmp.length); + + // Calculate LU decomposition: Is the matrix singular? + if (!luDecomposition(tmp, row_perm)) { + // Matrix has no inverse + throw new SingularMatrixException(); + } + + // Perform back substitution on the identity matrix + // luDecomposition will set rot[] & scales[] for use + // in luBacksubstituation + mat[0] = 1.0; mat[1] = 0.0; mat[2] = 0.0; mat[3] = 0.0; + mat[4] = 0.0; mat[5] = 1.0; mat[6] = 0.0; mat[7] = 0.0; + mat[8] = 0.0; mat[9] = 0.0; mat[10] = 1.0; mat[11] = 0.0; + mat[12] = 0.0; mat[13] = 0.0; mat[14] = 0.0; mat[15] = 1.0; + luBacksubstitution(tmp, row_perm, this.mat); + + updateState(); + return this; + } + + /** + * Given a 4x4 array "matrix0", this function replaces it with the + * LU decomposition of a row-wise permutation of itself. The input + * parameters are "matrix0" and "dimen". The array "matrix0" is also + * an output parameter. The vector "row_perm[4]" is an output + * parameter that contains the row permutations resulting from partial + * pivoting. The output parameter "even_row_xchg" is 1 when the + * number of row exchanges is even, or -1 otherwise. Assumes data + * type is always double. + * + * This function is similar to luDecomposition, except that it + * is tuned specifically for 4x4 matrices. + * + * @return true if the matrix is nonsingular, or false otherwise. + */ + private static boolean luDecomposition(double[] matrix0, + int[] row_perm) { + + // Reference: Press, Flannery, Teukolsky, Vetterling, + // _Numerical_Recipes_in_C_, Cambridge University Press, + // 1988, pp 40-45. + // + + // Can't re-use this temporary since the method is static. + double row_scale[] = new double[4]; + + // Determine implicit scaling information by looping over rows + { + int i, j; + int ptr, rs; + double big, temp; + + ptr = 0; + rs = 0; + + // For each row ... + i = 4; + while (i-- != 0) { + big = 0.0; + + // For each column, find the largest element in the row + j = 4; + while (j-- != 0) { + temp = matrix0[ptr++]; + temp = Math.abs(temp); + if (temp > big) { + big = temp; + } + } + + // Is the matrix singular? + if (big == 0.0) { + return false; + } + row_scale[rs++] = 1.0 / big; + } + } + + { + int j; + int mtx; + + mtx = 0; + + // For all columns, execute Crout's method + for (j = 0; j < 4; j++) { + int i, imax, k; + int target, p1, p2; + double sum, big, temp; + + // Determine elements of upper diagonal matrix U + for (i = 0; i < j; i++) { + target = mtx + (4*i) + j; + sum = matrix0[target]; + k = i; + p1 = mtx + (4*i); + p2 = mtx + j; + while (k-- != 0) { + sum -= matrix0[p1] * matrix0[p2]; + p1++; + p2 += 4; + } + matrix0[target] = sum; + } + + // Search for largest pivot element and calculate + // intermediate elements of lower diagonal matrix L. + big = 0.0; + imax = -1; + for (i = j; i < 4; i++) { + target = mtx + (4*i) + j; + sum = matrix0[target]; + k = j; + p1 = mtx + (4*i); + p2 = mtx + j; + while (k-- != 0) { + sum -= matrix0[p1] * matrix0[p2]; + p1++; + p2 += 4; + } + matrix0[target] = sum; + + // Is this the best pivot so far? + if ((temp = row_scale[i] * Math.abs(sum)) >= big) { + big = temp; + imax = i; + } + } + + if (imax < 0) { + return false; + } + + // Is a row exchange necessary? + if (j != imax) { + // Yes: exchange rows + k = 4; + p1 = mtx + (4*imax); + p2 = mtx + (4*j); + while (k-- != 0) { + temp = matrix0[p1]; + matrix0[p1++] = matrix0[p2]; + matrix0[p2++] = temp; + } + + // Record change in scale factor + row_scale[imax] = row_scale[j]; + } + + // Record row permutation + row_perm[j] = imax; + + // Is the matrix singular + if (matrix0[(mtx + (4*j) + j)] == 0.0) { + return false; + } + + // Divide elements of lower diagonal matrix L by pivot + if (j != (4-1)) { + temp = 1.0 / (matrix0[(mtx + (4*j) + j)]); + target = mtx + (4*(j+1)) + j; + i = 3 - j; + while (i-- != 0) { + matrix0[target] *= temp; + target += 4; + } + } + } + } + + return true; + } + + + /** + * Solves a set of linear equations. The input parameters "matrix1", + * and "row_perm" come from luDecompostionD4x4 and do not change + * here. The parameter "matrix2" is a set of column vectors assembled + * into a 4x4 matrix of floating-point values. The procedure takes each + * column of "matrix2" in turn and treats it as the right-hand side of the + * matrix equation Ax = LUx = b. The solution vector replaces the + * original column of the matrix. + * + * If "matrix2" is the identity matrix, the procedure replaces its contents + * with the inverse of the matrix from which "matrix1" was originally + * derived. + */ + // + // Reference: Press, Flannery, Teukolsky, Vetterling, + // _Numerical_Recipes_in_C_, Cambridge University Press, + // 1988, pp 44-45. + // + private static void luBacksubstitution(double[] matrix1, + int[] row_perm, + double[] matrix2) { + + int i, ii, ip, j, k; + int rp; + int cv, rv; + + // rp = row_perm; + rp = 0; + + // For each column vector of matrix2 ... + for (k = 0; k < 4; k++) { + // cv = &(matrix2[0][k]); + cv = k; + ii = -1; + + // Forward substitution + for (i = 0; i < 4; i++) { + double sum; + + ip = row_perm[rp+i]; + sum = matrix2[cv+4*ip]; + matrix2[cv+4*ip] = matrix2[cv+4*i]; + if (ii >= 0) { + // rv = &(matrix1[i][0]); + rv = i*4; + for (j = ii; j <= i-1; j++) { + sum -= matrix1[rv+j] * matrix2[cv+4*j]; + } + } + else if (sum != 0.0) { + ii = i; + } + matrix2[cv+4*i] = sum; + } + + // Backsubstitution + // rv = &(matrix1[3][0]); + rv = 3*4; + matrix2[cv+4*3] /= matrix1[rv+3]; + + rv -= 4; + matrix2[cv+4*2] = (matrix2[cv+4*2] - + matrix1[rv+3] * matrix2[cv+4*3]) / matrix1[rv+2]; + + rv -= 4; + matrix2[cv+4*1] = (matrix2[cv+4*1] - + matrix1[rv+2] * matrix2[cv+4*2] - + matrix1[rv+3] * matrix2[cv+4*3]) / matrix1[rv+1]; + + rv -= 4; + matrix2[cv+4*0] = (matrix2[cv+4*0] - + matrix1[rv+1] * matrix2[cv+4*1] - + matrix1[rv+2] * matrix2[cv+4*2] - + matrix1[rv+3] * matrix2[cv+4*3]) / matrix1[rv+0]; + } + } + + + /** + * Sets the value of this transform to the result of multiplying itself + * with transform t1 : this = this * t1. + * + * @param t1 the other transform + * + * @return this transform + */ + public GeneralTransform3D mul(BaseTransform t1) { + if (t1.isIdentity()) { + return this; + } + + double tmp0, tmp1, tmp2, tmp3; + double tmp4, tmp5, tmp6, tmp7; + double tmp8, tmp9, tmp10, tmp11; + double tmp12, tmp13, tmp14, tmp15; + + double mxx = t1.getMxx(); + double mxy = t1.getMxy(); + double mxz = t1.getMxz(); + double mxt = t1.getMxt(); + double myx = t1.getMyx(); + double myy = t1.getMyy(); + double myz = t1.getMyz(); + double myt = t1.getMyt(); + double mzx = t1.getMzx(); + double mzy = t1.getMzy(); + double mzz = t1.getMzz(); + double mzt = t1.getMzt(); + + tmp0 = mat[0] * mxx + mat[1] * myx + mat[2] * mzx; + tmp1 = mat[0] * mxy + mat[1] * myy + mat[2] * mzy; + tmp2 = mat[0] * mxz + mat[1] * myz + mat[2] * mzz; + tmp3 = mat[0] * mxt + mat[1] * myt + mat[2] * mzt + mat[3]; + tmp4 = mat[4] * mxx + mat[5] * myx + mat[6] * mzx; + tmp5 = mat[4] * mxy + mat[5] * myy + mat[6] * mzy; + tmp6 = mat[4] * mxz + mat[5] * myz + mat[6] * mzz; + tmp7 = mat[4] * mxt + mat[5] * myt + mat[6] * mzt + mat[7]; + tmp8 = mat[8] * mxx + mat[9] * myx + mat[10] * mzx; + tmp9 = mat[8] * mxy + mat[9] * myy + mat[10] * mzy; + tmp10 = mat[8] * mxz + mat[9] * myz + mat[10] * mzz; + tmp11 = mat[8] * mxt + mat[9] * myt + mat[10] * mzt + mat[11]; + if (isAffine()) { + tmp12 = tmp13 = tmp14 = 0; + tmp15 = 1; + } + else { + tmp12 = mat[12] * mxx + mat[13] * myx + mat[14] * mzx; + tmp13 = mat[12] * mxy + mat[13] * myy + mat[14] * mzy; + tmp14 = mat[12] * mxz + mat[13] * myz + mat[14] * mzz; + tmp15 = mat[12] * mxt + mat[13] * myt + mat[14] * mzt + mat[15]; + } + + mat[0] = tmp0; + mat[1] = tmp1; + mat[2] = tmp2; + mat[3] = tmp3; + mat[4] = tmp4; + mat[5] = tmp5; + mat[6] = tmp6; + mat[7] = tmp7; + mat[8] = tmp8; + mat[9] = tmp9; + mat[10] = tmp10; + mat[11] = tmp11; + mat[12] = tmp12; + mat[13] = tmp13; + mat[14] = tmp14; + mat[15] = tmp15; + + updateState(); + return this; + } + + /** + * Sets the value of this transform to the result of multiplying itself + * with a scale transform: + *
+     * scaletx =
+     *     [ sx  0  0  0 ]
+     *     [  0 sy  0  0 ]
+     *     [  0  0 sz  0 ]
+     *     [  0  0  0  1 ].
+     * this = this * scaletx.
+     * 
+ * + * @param sx the X coordinate scale factor + * @param sy the Y coordinate scale factor + * @param sz the Z coordinate scale factor + * + * @return this transform + */ + public GeneralTransform3D scale(double sx, double sy, double sz) { + boolean update = false; + + if (sx != 1.0) { + mat[0] *= sx; + mat[4] *= sx; + mat[8] *= sx; + mat[12] *= sx; + update = true; + } + if (sy != 1.0) { + mat[1] *= sy; + mat[5] *= sy; + mat[9] *= sy; + mat[13] *= sy; + update = true; + } + if (sz != 1.0) { + mat[2] *= sz; + mat[6] *= sz; + mat[10] *= sz; + mat[14] *= sz; + update = true; + } + + if (update) { + updateState(); + } + return this; + } + + /** + * Sets the value of this transform to the result of multiplying itself + * with transform t1 : this = this * t1. + * + * @param t1 the other transform + * + * @return this transform + */ + public GeneralTransform3D mul(GeneralTransform3D t1) { + if (t1.isIdentity()) { + return this; + } + + double tmp0, tmp1, tmp2, tmp3; + double tmp4, tmp5, tmp6, tmp7; + double tmp8, tmp9, tmp10, tmp11; + double tmp12, tmp13, tmp14, tmp15; + + if (t1.isAffine()) { + tmp0 = mat[0] * t1.mat[0] + mat[1] * t1.mat[4] + mat[2] * t1.mat[8]; + tmp1 = mat[0] * t1.mat[1] + mat[1] * t1.mat[5] + mat[2] * t1.mat[9]; + tmp2 = mat[0] * t1.mat[2] + mat[1] * t1.mat[6] + mat[2] * t1.mat[10]; + tmp3 = mat[0] * t1.mat[3] + mat[1] * t1.mat[7] + mat[2] * t1.mat[11] + mat[3]; + tmp4 = mat[4] * t1.mat[0] + mat[5] * t1.mat[4] + mat[6] * t1.mat[8]; + tmp5 = mat[4] * t1.mat[1] + mat[5] * t1.mat[5] + mat[6] * t1.mat[9]; + tmp6 = mat[4] * t1.mat[2] + mat[5] * t1.mat[6] + mat[6] * t1.mat[10]; + tmp7 = mat[4] * t1.mat[3] + mat[5] * t1.mat[7] + mat[6] * t1.mat[11] + mat[7]; + tmp8 = mat[8] * t1.mat[0] + mat[9] * t1.mat[4] + mat[10] * t1.mat[8]; + tmp9 = mat[8] * t1.mat[1] + mat[9] * t1.mat[5] + mat[10] * t1.mat[9]; + tmp10 = mat[8] * t1.mat[2] + mat[9] * t1.mat[6] + mat[10] * t1.mat[10]; + tmp11 = mat[8] * t1.mat[3] + mat[9] * t1.mat[7] + mat[10] * t1.mat[11] + mat[11]; + if (isAffine()) { + tmp12 = tmp13 = tmp14 = 0; + tmp15 = 1; + } + else { + tmp12 = mat[12] * t1.mat[0] + mat[13] * t1.mat[4] + + mat[14] * t1.mat[8]; + tmp13 = mat[12] * t1.mat[1] + mat[13] * t1.mat[5] + + mat[14] * t1.mat[9]; + tmp14 = mat[12] * t1.mat[2] + mat[13] * t1.mat[6] + + mat[14] * t1.mat[10]; + tmp15 = mat[12] * t1.mat[3] + mat[13] * t1.mat[7] + + mat[14] * t1.mat[11] + mat[15]; + } + } else { + tmp0 = mat[0] * t1.mat[0] + mat[1] * t1.mat[4] + mat[2] * t1.mat[8] + + mat[3] * t1.mat[12]; + tmp1 = mat[0] * t1.mat[1] + mat[1] * t1.mat[5] + mat[2] * t1.mat[9] + + mat[3] * t1.mat[13]; + tmp2 = mat[0] * t1.mat[2] + mat[1] * t1.mat[6] + mat[2] * t1.mat[10] + + mat[3] * t1.mat[14]; + tmp3 = mat[0] * t1.mat[3] + mat[1] * t1.mat[7] + mat[2] * t1.mat[11] + + mat[3] * t1.mat[15]; + tmp4 = mat[4] * t1.mat[0] + mat[5] * t1.mat[4] + mat[6] * t1.mat[8] + + mat[7] * t1.mat[12]; + tmp5 = mat[4] * t1.mat[1] + mat[5] * t1.mat[5] + mat[6] * t1.mat[9] + + mat[7] * t1.mat[13]; + tmp6 = mat[4] * t1.mat[2] + mat[5] * t1.mat[6] + mat[6] * t1.mat[10] + + mat[7] * t1.mat[14]; + tmp7 = mat[4] * t1.mat[3] + mat[5] * t1.mat[7] + mat[6] * t1.mat[11] + + mat[7] * t1.mat[15]; + tmp8 = mat[8] * t1.mat[0] + mat[9] * t1.mat[4] + mat[10] * t1.mat[8] + + mat[11] * t1.mat[12]; + tmp9 = mat[8] * t1.mat[1] + mat[9] * t1.mat[5] + mat[10] * t1.mat[9] + + mat[11] * t1.mat[13]; + tmp10 = mat[8] * t1.mat[2] + mat[9] * t1.mat[6] + + mat[10] * t1.mat[10] + mat[11] * t1.mat[14]; + + tmp11 = mat[8] * t1.mat[3] + mat[9] * t1.mat[7] + + mat[10] * t1.mat[11] + mat[11] * t1.mat[15]; + if (isAffine()) { + tmp12 = t1.mat[12]; + tmp13 = t1.mat[13]; + tmp14 = t1.mat[14]; + tmp15 = t1.mat[15]; + } else { + tmp12 = mat[12] * t1.mat[0] + mat[13] * t1.mat[4] + + mat[14] * t1.mat[8] + mat[15] * t1.mat[12]; + tmp13 = mat[12] * t1.mat[1] + mat[13] * t1.mat[5] + + mat[14] * t1.mat[9] + mat[15] * t1.mat[13]; + tmp14 = mat[12] * t1.mat[2] + mat[13] * t1.mat[6] + + mat[14] * t1.mat[10] + mat[15] * t1.mat[14]; + tmp15 = mat[12] * t1.mat[3] + mat[13] * t1.mat[7] + + mat[14] * t1.mat[11] + mat[15] * t1.mat[15]; + } + } + + mat[0] = tmp0; + mat[1] = tmp1; + mat[2] = tmp2; + mat[3] = tmp3; + mat[4] = tmp4; + mat[5] = tmp5; + mat[6] = tmp6; + mat[7] = tmp7; + mat[8] = tmp8; + mat[9] = tmp9; + mat[10] = tmp10; + mat[11] = tmp11; + mat[12] = tmp12; + mat[13] = tmp13; + mat[14] = tmp14; + mat[15] = tmp15; + + updateState(); + return this; + } + + /** + * Sets this transform to the identity matrix. + * + * @return this transform + */ + public GeneralTransform3D setIdentity() { + mat[0] = 1.0; mat[1] = 0.0; mat[2] = 0.0; mat[3] = 0.0; + mat[4] = 0.0; mat[5] = 1.0; mat[6] = 0.0; mat[7] = 0.0; + mat[8] = 0.0; mat[9] = 0.0; mat[10] = 1.0; mat[11] = 0.0; + mat[12] = 0.0; mat[13] = 0.0; mat[14] = 0.0; mat[15] = 1.0; + identity = true; + return this; + } + + /** + * Returns true if the transform is identity. A transform is considered + * to be identity if the diagonal elements of its matrix is all 1s + * otherwise 0s. + */ + public boolean isIdentity() { + return identity; + } + + private void updateState() { + //set the identity flag. + identity = + mat[0] == 1.0 && mat[5] == 1.0 && mat[10] == 1.0 && mat[15] == 1.0 && + mat[1] == 0.0 && mat[2] == 0.0 && mat[3] == 0.0 && + mat[4] == 0.0 && mat[6] == 0.0 && mat[7] == 0.0 && + mat[8] == 0.0 && mat[9] == 0.0 && mat[11] == 0.0 && + mat[12] == 0.0 && mat[13] == 0.0 && mat[14] == 0.0; + } + + // Check whether matrix has an Infinity or NaN value. If so, don't treat it + // as affine. + boolean isInfOrNaN() { + // The following is a faster version of the check. + // Instead of 3 tests per array element (Double.isInfinite is 2 tests), + // for a total of 48 tests, we will do 16 multiplies and 1 test. + double d = 0.0; + for (int i = 0; i < mat.length; i++) { + d *= mat[i]; + } + + return d != 0.0; + } + + private static final double EPSILON_ABSOLUTE = 1.0e-5; + + static boolean almostZero(double a) { + return ((a < EPSILON_ABSOLUTE) && (a > -EPSILON_ABSOLUTE)); + } + + static boolean almostOne(double a) { + return ((a < 1+EPSILON_ABSOLUTE) && (a > 1-EPSILON_ABSOLUTE)); + } + + public GeneralTransform3D copy() { + GeneralTransform3D newTransform = new GeneralTransform3D(); + newTransform.set(this); + return newTransform; + } + + /** + * Returns the matrix elements of this transform as a string. + * @return the matrix elements of this transform + */ + @Override + public String toString() { + return mat[0] + ", " + mat[1] + ", " + mat[2] + ", " + mat[3] + "\n" + + mat[4] + ", " + mat[5] + ", " + mat[6] + ", " + mat[7] + "\n" + + mat[8] + ", " + mat[9] + ", " + mat[10] + ", " + mat[11] + "\n" + + mat[12] + ", " + mat[13] + ", " + mat[14] + ", " + mat[15] + "\n"; + } + +}