1 /*
   2  * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.javafx.geom.transform;
  27 
  28 import com.sun.javafx.geom.BaseBounds;
  29 import com.sun.javafx.geom.Point2D;
  30 import com.sun.javafx.geom.Vec3d;
  31 import com.sun.javafx.geom.Vec3f;
  32 
  33 /**
  34  * A general-purpose 4x4 transform that may or may not be affine. The
  35  * GeneralTransform is typically used only for projection transforms.
  36  *
  37  */
  38 public final class GeneralTransform3D implements CanTransformVec3d {
  39 
  40     //The 4x4 double-precision floating point matrix.  The mathematical
  41     //representation is row major, as in traditional matrix mathematics.
  42     protected double[] mat = new double[16];
  43 
  44     //flag if this is an identity transformation.
  45     private boolean identity;
  46 
  47     /**
  48      * Constructs and initializes a transform to the identity matrix.
  49      */
  50     public GeneralTransform3D() {
  51         setIdentity();
  52     }
  53 
  54     /**
  55      * Returns true if the transform is affine. A transform is considered
  56      * to be affine if the last row of its matrix is (0,0,0,1). Note that
  57      * an instance of AffineTransform3D is always affine.
  58      */
  59     public boolean isAffine() {
  60         if (!isInfOrNaN() &&
  61                 almostZero(mat[12]) &&
  62                 almostZero(mat[13]) &&
  63                 almostZero(mat[14]) &&
  64                 almostOne(mat[15])) {
  65 
  66             return true;
  67         } else {
  68             return false;
  69         }
  70     }
  71 
  72     /**
  73      * Sets the value of this transform to the specified transform.
  74      *
  75      * @param t1 the transform to copy into this transform.
  76      *
  77      * @return this transform
  78      */
  79     public GeneralTransform3D set(GeneralTransform3D t1) {
  80         System.arraycopy(t1.mat, 0, mat, 0, mat.length);
  81         updateState();
  82         return this;
  83     }
  84 
  85     /**
  86      * Sets the matrix values of this transform to the values in the
  87      * specified array.
  88      *
  89      * @param m an array of 16 values to copy into this transform in
  90      * row major order.
  91      *
  92      * @return this transform
  93      */
  94     public GeneralTransform3D set(double[] m) {
  95         System.arraycopy(m, 0, mat, 0, mat.length);
  96         updateState();
  97         return this;
  98     }
  99 
 100     /**
 101      * Returns a copy of an array of 16 values that contains the 4x4 matrix
 102      * of this transform. The first four elements of the array will contain
 103      * the top row of the transform matrix, etc.
 104      *
 105      * @param rv the return value, or null
 106      *
 107      * @return an array of 16 values
 108      */
 109     public double[] get(double[] rv) {
 110         if (rv == null) {
 111             rv = new double[mat.length];
 112         }
 113         System.arraycopy(mat, 0, rv, 0, mat.length);
 114 
 115         return rv;
 116     }
 117 
 118     public double get(int index) {
 119         assert ((index >= 0) && (index < mat.length));
 120         return mat[index];
 121     }
 122 
 123     private Vec3d tempV3d;
 124 
 125     public BaseBounds transform(BaseBounds src, BaseBounds dst) {
 126         if (tempV3d == null) {
 127             tempV3d = new Vec3d();
 128         }
 129         return TransformHelper.general3dBoundsTransform(this, src, dst, tempV3d);
 130     }
 131 
 132     /**
 133      * Transform 2D point (with z == 0)
 134      * @param point
 135      * @param pointOut
 136      * @return
 137      */
 138     public Point2D transform(Point2D point, Point2D pointOut) {
 139         if (pointOut == null) {
 140             pointOut = new Point2D();
 141         }
 142 
 143         double w = (float) (mat[12] * point.x + mat[13] * point.y
 144                  + mat[15]);
 145 
 146         pointOut.x = (float) (mat[0] * point.x + mat[1] * point.y
 147                 + mat[3]);
 148         pointOut.y = (float) (mat[4] * point.x + mat[5] * point.y
 149                 + mat[7]);
 150 
 151         pointOut.x /= w;
 152         pointOut.y /= w;
 153 
 154         return pointOut;
 155     }
 156 
 157     /**
 158      * Transforms the point parameter with this transform and
 159      * places the result into pointOut.  The fourth element of the
 160      * point input paramter is assumed to be one.
 161      *
 162      * @param point the input point to be transformed
 163      *
 164      * @param pointOut the transformed point
 165      *
 166      * @return the transformed point
 167      */
 168     public Vec3d transform(Vec3d point, Vec3d pointOut)  {
 169         if (pointOut == null) {
 170             pointOut = new Vec3d();
 171         }
 172 
 173         // compute here as point.x, point.y and point.z may change
 174         // in case (point == pointOut), and mat[14] is usually != 0
 175         double w = (float) (mat[12] * point.x + mat[13] * point.y
 176                 + mat[14] * point.z + mat[15]);
 177 
 178         pointOut.x = (float) (mat[0] * point.x + mat[1] * point.y
 179                 + mat[2] * point.z + mat[3]);
 180         pointOut.y = (float) (mat[4] * point.x + mat[5] * point.y
 181                 + mat[6] * point.z + mat[7]);
 182         pointOut.z = (float) (mat[8] * point.x + mat[9] * point.y
 183                 + mat[10] * point.z + mat[11]);
 184 
 185         if (w != 0.0f) {
 186             pointOut.x /= w;
 187             pointOut.y /= w;
 188             pointOut.z /= w;
 189         }
 190 
 191         return pointOut;
 192     }
 193 
 194 
 195     /**
 196      * Transforms the point parameter with this transform and
 197      * places the result back into point.  The fourth element of the
 198      * point input paramter is assumed to be one.
 199      *
 200      * @param point the input point to be transformed
 201      *
 202      * @return the transformed point
 203      */
 204     public Vec3d transform(Vec3d point) {
 205         return transform(new Vec3d(point), point);
 206     }
 207 
 208     /**
 209      * Transforms the normal parameter by this transform and places the value
 210      * into normalOut.  The fourth element of the normal is assumed to be zero.
 211      * Note: For correct lighting results, if a transform has uneven scaling
 212      * surface normals should transformed by the inverse transpose of
 213      * the transform. This the responsibility of the application and is not
 214      * done automatically by this method.
 215      *
 216      * @param normal the input normal to be transformed
 217      *
 218      * @param normalOut the transformed normal
 219      *
 220      * @return the transformed normal
 221      */
 222     public Vec3f transformNormal(Vec3f normal, Vec3f normalOut) {
 223         if (normalOut == null) {
 224             normalOut = new Vec3f();
 225         }
 226         normalOut.x =  (float) (mat[0]*normal.x + mat[1]*normal.y +
 227                             mat[2]*normal.z);
 228         normalOut.y =  (float) (mat[4]*normal.x + mat[5]*normal.y +
 229                             mat[6]*normal.z);
 230         normalOut.z =  (float) (mat[8]*normal.x + mat[9]*normal.y +
 231                              mat[10]*normal.z);
 232         return normalOut;
 233     }
 234 
 235     /**
 236      * Transforms the normal parameter by this transform and places the value
 237      * back into normal.  The fourth element of the normal is assumed to be zero.
 238      * Note: For correct lighting results, if a transform has uneven scaling
 239      * surface normals should transformed by the inverse transpose of
 240      * the transform. This the responsibility of the application and is not
 241      * done automatically by this method.
 242      *
 243      * @param normal the input normal to be transformed
 244      *
 245      * @return the transformed normal
 246      */
 247     public Vec3f transformNormal(Vec3f normal) {
 248         return transformNormal(new Vec3f(normal), normal);
 249     }
 250 
 251     /**
 252      * Sets the value of this transform to a perspective projection transform.
 253      * This transform maps points from Eye Coordinates (EC)
 254      * to Clipping Coordinates (CC).
 255      * Note that the field of view is specified in radians.
 256      *
 257      * @param verticalFOV specifies whether the fov is vertical (Y direction).
 258      *
 259      * @param fov specifies the field of view in radians
 260      *
 261      * @param aspect specifies the aspect ratio. The aspect ratio is the ratio
 262      * of width to height.
 263      *
 264      * @param zNear the distance to the frustum's near clipping plane.
 265      * This value must be positive, (the value -zNear is the location of the
 266      * near clip plane).
 267      *
 268      * @param zFar the distance to the frustum's far clipping plane
 269      *
 270      * @return this transform
 271      */
 272     public GeneralTransform3D perspective(boolean verticalFOV,
 273             double fov, double aspect, double zNear, double zFar) {
 274         double sine;
 275         double cotangent;
 276         double deltaZ;
 277         double half_fov = fov * 0.5;
 278 
 279         deltaZ = zFar - zNear;
 280         sine = Math.sin(half_fov);
 281 
 282         cotangent = Math.cos(half_fov) / sine;
 283 
 284         mat[0] = verticalFOV ? cotangent / aspect : cotangent;
 285         mat[5] = verticalFOV ? cotangent : cotangent * aspect;
 286         mat[10] = -(zFar + zNear) / deltaZ;
 287         mat[11] = -2.0 * zNear * zFar / deltaZ;
 288         mat[14] = -1.0;
 289         mat[1] = mat[2] = mat[3] = mat[4] = mat[6] = mat[7] = mat[8] = mat[9] = mat[12] = mat[13] = mat[15] = 0;
 290 
 291         updateState();
 292         return this;
 293     }
 294 
 295     /**
 296      * Sets the value of this transform to an orthographic (parallel)
 297      * projection transform.
 298      * This transform maps coordinates from Eye Coordinates (EC)
 299      * to Clipping Coordinates (CC).  Note that unlike the similar function
 300      * in OpenGL, the clipping coordinates generated by the resulting
 301      * transform are in a right-handed coordinate system.
 302      * @param left the vertical line on the left edge of the near
 303      * clipping plane mapped to the left edge of the graphics window
 304      * @param right the vertical line on the right edge of the near
 305      * clipping plane mapped to the right edge of the graphics window
 306      * @param bottom the horizontal line on the bottom edge of the near
 307      * clipping plane mapped to the bottom edge of the graphics window
 308      * @param top the horizontal line on the top edge of the near
 309      * clipping plane mapped to the top edge of the graphics window
 310      * @param near the distance to the frustum's near clipping plane
 311      * (the value -near is the location of the near clip plane)
 312      * @param far the distance to the frustum's far clipping plane
 313      *
 314      * @return this transform
 315      */
 316     public GeneralTransform3D ortho(double left, double right, double bottom,
 317                                     double top, double near, double far) {
 318         double deltax = 1 / (right - left);
 319         double deltay = 1 / (top - bottom);
 320         double deltaz = 1 / (far - near);
 321 
 322         mat[0] = 2.0 * deltax;
 323         mat[3] = -(right + left) * deltax;
 324         mat[5] = 2.0 * deltay;
 325         mat[7] = -(top + bottom) * deltay;
 326         mat[10] = 2.0 * deltaz;
 327         mat[11] = (far + near) * deltaz;
 328         mat[1] = mat[2] = mat[4] = mat[6] = mat[8] =
 329                 mat[9] = mat[12] = mat[13] = mat[14] = 0;
 330         mat[15] = 1;
 331 
 332         updateState();
 333         return this;
 334     }
 335 
 336     public double computeClipZCoord() {
 337         double zEc = (1.0 - mat[15]) / mat[14];
 338         double zCc = mat[10] * zEc + mat[11];
 339         return zCc;
 340     }
 341 
 342     /**
 343      * Computes the determinant of this transform.
 344      *
 345      * @return the determinant
 346      */
 347     public double determinant() {
 348          // cofactor exapainsion along first row
 349          return mat[0]*(mat[5]*(mat[10]*mat[15] - mat[11]*mat[14]) -
 350                         mat[6]*(mat[ 9]*mat[15] - mat[11]*mat[13]) +
 351                         mat[7]*(mat[ 9]*mat[14] - mat[10]*mat[13])) -
 352                 mat[1]*(mat[4]*(mat[10]*mat[15] - mat[11]*mat[14]) -
 353                         mat[6]*(mat[ 8]*mat[15] - mat[11]*mat[12]) +
 354                         mat[7]*(mat[ 8]*mat[14] - mat[10]*mat[12])) +
 355                 mat[2]*(mat[4]*(mat[ 9]*mat[15] - mat[11]*mat[13]) -
 356                         mat[5]*(mat[ 8]*mat[15] - mat[11]*mat[12]) +
 357                         mat[7]*(mat[ 8]*mat[13] - mat[ 9]*mat[12])) -
 358                 mat[3]*(mat[4]*(mat[ 9]*mat[14] - mat[10]*mat[13]) -
 359                         mat[5]*(mat[ 8]*mat[14] - mat[10]*mat[12]) +
 360                         mat[6]*(mat[ 8]*mat[13] - mat[ 9]*mat[12]));
 361     }
 362 
 363     /**
 364      * Inverts this transform in place.
 365      *
 366      * @return this transform
 367      */
 368     public GeneralTransform3D invert() {
 369         return invert(this);
 370     }
 371 
 372     /**
 373      * General invert routine.  Inverts t1 and places the result in "this".
 374      * Note that this routine handles both the "this" version and the
 375      * non-"this" version.
 376      *
 377      * Also note that since this routine is slow anyway, we won't worry
 378      * about allocating a little bit of garbage.
 379      */
 380     private GeneralTransform3D invert(GeneralTransform3D t1) {
 381         double[] tmp = new double[16];
 382         int[] row_perm = new int[4];
 383 
 384         // Use LU decomposition and backsubstitution code specifically
 385         // for floating-point 4x4 matrices.
 386         // Copy source matrix to tmp
 387         System.arraycopy(t1.mat, 0, tmp, 0, tmp.length);
 388 
 389         // Calculate LU decomposition: Is the matrix singular?
 390         if (!luDecomposition(tmp, row_perm)) {
 391             // Matrix has no inverse
 392             throw new SingularMatrixException();
 393         }
 394 
 395         // Perform back substitution on the identity matrix
 396         // luDecomposition will set rot[] & scales[] for use
 397         // in luBacksubstituation
 398         mat[0] = 1.0;  mat[1] = 0.0;  mat[2] = 0.0;  mat[3] = 0.0;
 399         mat[4] = 0.0;  mat[5] = 1.0;  mat[6] = 0.0;  mat[7] = 0.0;
 400         mat[8] = 0.0;  mat[9] = 0.0;  mat[10] = 1.0; mat[11] = 0.0;
 401         mat[12] = 0.0; mat[13] = 0.0; mat[14] = 0.0; mat[15] = 1.0;
 402         luBacksubstitution(tmp, row_perm, this.mat);
 403 
 404         updateState();
 405         return this;
 406     }
 407 
 408     /**
 409      * Given a 4x4 array "matrix0", this function replaces it with the
 410      * LU decomposition of a row-wise permutation of itself.  The input
 411      * parameters are "matrix0" and "dimen".  The array "matrix0" is also
 412      * an output parameter.  The vector "row_perm[4]" is an output
 413      * parameter that contains the row permutations resulting from partial
 414      * pivoting.  The output parameter "even_row_xchg" is 1 when the
 415      * number of row exchanges is even, or -1 otherwise.  Assumes data
 416      * type is always double.
 417      *
 418      * This function is similar to luDecomposition, except that it
 419      * is tuned specifically for 4x4 matrices.
 420      *
 421      * @return true if the matrix is nonsingular, or false otherwise.
 422      */
 423     private static boolean luDecomposition(double[] matrix0,
 424             int[] row_perm) {
 425 
 426         // Reference: Press, Flannery, Teukolsky, Vetterling,
 427         //            _Numerical_Recipes_in_C_, Cambridge University Press,
 428         //            1988, pp 40-45.
 429         //
 430 
 431         // Can't re-use this temporary since the method is static.
 432         double row_scale[] = new double[4];
 433 
 434         // Determine implicit scaling information by looping over rows
 435         {
 436             int i, j;
 437             int ptr, rs;
 438             double big, temp;
 439 
 440             ptr = 0;
 441             rs = 0;
 442 
 443             // For each row ...
 444             i = 4;
 445             while (i-- != 0) {
 446                 big = 0.0;
 447 
 448                 // For each column, find the largest element in the row
 449                 j = 4;
 450                 while (j-- != 0) {
 451                     temp = matrix0[ptr++];
 452                     temp = Math.abs(temp);
 453                     if (temp > big) {
 454                         big = temp;
 455                     }
 456                 }
 457 
 458                 // Is the matrix singular?
 459                 if (big == 0.0) {
 460                     return false;
 461                 }
 462                 row_scale[rs++] = 1.0 / big;
 463             }
 464         }
 465 
 466         {
 467             int j;
 468             int mtx;
 469 
 470             mtx = 0;
 471 
 472             // For all columns, execute Crout's method
 473             for (j = 0; j < 4; j++) {
 474                 int i, imax, k;
 475                 int target, p1, p2;
 476                 double sum, big, temp;
 477 
 478                 // Determine elements of upper diagonal matrix U
 479                 for (i = 0; i < j; i++) {
 480                     target = mtx + (4*i) + j;
 481                     sum = matrix0[target];
 482                     k = i;
 483                     p1 = mtx + (4*i);
 484                     p2 = mtx + j;
 485                     while (k-- != 0) {
 486                         sum -= matrix0[p1] * matrix0[p2];
 487                         p1++;
 488                         p2 += 4;
 489                     }
 490                     matrix0[target] = sum;
 491                 }
 492 
 493                 // Search for largest pivot element and calculate
 494                 // intermediate elements of lower diagonal matrix L.
 495                 big = 0.0;
 496                 imax = -1;
 497                 for (i = j; i < 4; i++) {
 498                     target = mtx + (4*i) + j;
 499                     sum = matrix0[target];
 500                     k = j;
 501                     p1 = mtx + (4*i);
 502                     p2 = mtx + j;
 503                     while (k-- != 0) {
 504                         sum -= matrix0[p1] * matrix0[p2];
 505                         p1++;
 506                         p2 += 4;
 507                     }
 508                     matrix0[target] = sum;
 509 
 510                     // Is this the best pivot so far?
 511                     if ((temp = row_scale[i] * Math.abs(sum)) >= big) {
 512                         big = temp;
 513                         imax = i;
 514                     }
 515                 }
 516 
 517                 if (imax < 0) {
 518                     return false;
 519                 }
 520 
 521                 // Is a row exchange necessary?
 522                 if (j != imax) {
 523                     // Yes: exchange rows
 524                     k = 4;
 525                     p1 = mtx + (4*imax);
 526                     p2 = mtx + (4*j);
 527                     while (k-- != 0) {
 528                         temp = matrix0[p1];
 529                         matrix0[p1++] = matrix0[p2];
 530                         matrix0[p2++] = temp;
 531                     }
 532 
 533                     // Record change in scale factor
 534                     row_scale[imax] = row_scale[j];
 535                 }
 536 
 537                 // Record row permutation
 538                 row_perm[j] = imax;
 539 
 540                 // Is the matrix singular
 541                 if (matrix0[(mtx + (4*j) + j)] == 0.0) {
 542                     return false;
 543                 }
 544 
 545                 // Divide elements of lower diagonal matrix L by pivot
 546                 if (j != (4-1)) {
 547                     temp = 1.0 / (matrix0[(mtx + (4*j) + j)]);
 548                     target = mtx + (4*(j+1)) + j;
 549                     i = 3 - j;
 550                     while (i-- != 0) {
 551                         matrix0[target] *= temp;
 552                         target += 4;
 553                     }
 554                 }
 555             }
 556         }
 557 
 558         return true;
 559     }
 560 
 561 
 562     /**
 563      * Solves a set of linear equations.  The input parameters "matrix1",
 564      * and "row_perm" come from luDecompostionD4x4 and do not change
 565      * here.  The parameter "matrix2" is a set of column vectors assembled
 566      * into a 4x4 matrix of floating-point values.  The procedure takes each
 567      * column of "matrix2" in turn and treats it as the right-hand side of the
 568      * matrix equation Ax = LUx = b.  The solution vector replaces the
 569      * original column of the matrix.
 570      *
 571      * If "matrix2" is the identity matrix, the procedure replaces its contents
 572      * with the inverse of the matrix from which "matrix1" was originally
 573      * derived.
 574      */
 575     //
 576     // Reference: Press, Flannery, Teukolsky, Vetterling,
 577     //        _Numerical_Recipes_in_C_, Cambridge University Press,
 578     //        1988, pp 44-45.
 579     //
 580     private static void luBacksubstitution(double[] matrix1,
 581             int[] row_perm,
 582             double[] matrix2) {
 583 
 584         int i, ii, ip, j, k;
 585         int rp;
 586         int cv, rv;
 587 
 588         //      rp = row_perm;
 589         rp = 0;
 590 
 591         // For each column vector of matrix2 ...
 592         for (k = 0; k < 4; k++) {
 593             //      cv = &(matrix2[0][k]);
 594             cv = k;
 595             ii = -1;
 596 
 597             // Forward substitution
 598             for (i = 0; i < 4; i++) {
 599                 double sum;
 600 
 601                 ip = row_perm[rp+i];
 602                 sum = matrix2[cv+4*ip];
 603                 matrix2[cv+4*ip] = matrix2[cv+4*i];
 604                 if (ii >= 0) {
 605                     //              rv = &(matrix1[i][0]);
 606                     rv = i*4;
 607                     for (j = ii; j <= i-1; j++) {
 608                         sum -= matrix1[rv+j] * matrix2[cv+4*j];
 609                     }
 610                 }
 611                 else if (sum != 0.0) {
 612                     ii = i;
 613                 }
 614                 matrix2[cv+4*i] = sum;
 615             }
 616 
 617             // Backsubstitution
 618             //      rv = &(matrix1[3][0]);
 619             rv = 3*4;
 620             matrix2[cv+4*3] /= matrix1[rv+3];
 621 
 622             rv -= 4;
 623             matrix2[cv+4*2] = (matrix2[cv+4*2] -
 624                             matrix1[rv+3] * matrix2[cv+4*3]) / matrix1[rv+2];
 625 
 626             rv -= 4;
 627             matrix2[cv+4*1] = (matrix2[cv+4*1] -
 628                             matrix1[rv+2] * matrix2[cv+4*2] -
 629                             matrix1[rv+3] * matrix2[cv+4*3]) / matrix1[rv+1];
 630 
 631             rv -= 4;
 632             matrix2[cv+4*0] = (matrix2[cv+4*0] -
 633                             matrix1[rv+1] * matrix2[cv+4*1] -
 634                             matrix1[rv+2] * matrix2[cv+4*2] -
 635                             matrix1[rv+3] * matrix2[cv+4*3]) / matrix1[rv+0];
 636         }
 637     }
 638 
 639 
 640     /**
 641      * Sets the value of this transform to the result of multiplying itself
 642      * with transform t1 : this = this * t1.
 643       *
 644      * @param t1 the other transform
 645      *
 646      * @return this transform
 647      */
 648     public GeneralTransform3D mul(BaseTransform t1) {
 649         if (t1.isIdentity()) {
 650             return this;
 651         }
 652 
 653         double tmp0, tmp1, tmp2, tmp3;
 654         double tmp4, tmp5, tmp6, tmp7;
 655         double tmp8, tmp9, tmp10, tmp11;
 656         double tmp12, tmp13, tmp14, tmp15;
 657 
 658         double mxx = t1.getMxx();
 659         double mxy = t1.getMxy();
 660         double mxz = t1.getMxz();
 661         double mxt = t1.getMxt();
 662         double myx = t1.getMyx();
 663         double myy = t1.getMyy();
 664         double myz = t1.getMyz();
 665         double myt = t1.getMyt();
 666         double mzx = t1.getMzx();
 667         double mzy = t1.getMzy();
 668         double mzz = t1.getMzz();
 669         double mzt = t1.getMzt();
 670 
 671         tmp0 = mat[0] * mxx + mat[1] * myx + mat[2] * mzx;
 672         tmp1 = mat[0] * mxy + mat[1] * myy + mat[2] * mzy;
 673         tmp2 = mat[0] * mxz + mat[1] * myz + mat[2] * mzz;
 674         tmp3 = mat[0] * mxt + mat[1] * myt + mat[2] * mzt + mat[3];
 675         tmp4 = mat[4] * mxx + mat[5] * myx + mat[6] * mzx;
 676         tmp5 = mat[4] * mxy + mat[5] * myy + mat[6] * mzy;
 677         tmp6 = mat[4] * mxz + mat[5] * myz + mat[6] * mzz;
 678         tmp7 = mat[4] * mxt + mat[5] * myt + mat[6] * mzt + mat[7];
 679         tmp8 = mat[8] * mxx + mat[9] * myx + mat[10] * mzx;
 680         tmp9 = mat[8] * mxy + mat[9] * myy + mat[10] * mzy;
 681         tmp10 = mat[8] * mxz + mat[9] * myz + mat[10] * mzz;
 682         tmp11 = mat[8] * mxt + mat[9] * myt + mat[10] * mzt + mat[11];
 683         if (isAffine()) {
 684             tmp12 = tmp13 = tmp14 = 0;
 685             tmp15 = 1;
 686         }
 687         else {
 688             tmp12 = mat[12] * mxx + mat[13] * myx + mat[14] * mzx;
 689             tmp13 = mat[12] * mxy + mat[13] * myy + mat[14] * mzy;
 690             tmp14 = mat[12] * mxz + mat[13] * myz + mat[14] * mzz;
 691             tmp15 = mat[12] * mxt + mat[13] * myt + mat[14] * mzt + mat[15];
 692         }
 693 
 694         mat[0] = tmp0;
 695         mat[1] = tmp1;
 696         mat[2] = tmp2;
 697         mat[3] = tmp3;
 698         mat[4] = tmp4;
 699         mat[5] = tmp5;
 700         mat[6] = tmp6;
 701         mat[7] = tmp7;
 702         mat[8] = tmp8;
 703         mat[9] = tmp9;
 704         mat[10] = tmp10;
 705         mat[11] = tmp11;
 706         mat[12] = tmp12;
 707         mat[13] = tmp13;
 708         mat[14] = tmp14;
 709         mat[15] = tmp15;
 710 
 711         updateState();
 712         return this;
 713     }
 714 
 715     /**
 716      * Sets the value of this transform to the result of multiplying itself
 717      * with a scale transform:
 718      * <pre>
 719      * scaletx =
 720      *     [ sx  0  0  0 ]
 721      *     [  0 sy  0  0 ]
 722      *     [  0  0 sz  0 ]
 723      *     [  0  0  0  1 ].
 724      * this = this * scaletx.
 725      * </pre>
 726      *
 727      * @param sx the X coordinate scale factor
 728      * @param sy the Y coordinate scale factor
 729      * @param sz the Z coordinate scale factor
 730      *
 731      * @return this transform
 732      */
 733     public GeneralTransform3D scale(double sx, double sy, double sz) {
 734         boolean update = false;
 735 
 736         if (sx != 1.0) {
 737             mat[0]  *= sx;
 738             mat[4]  *= sx;
 739             mat[8]  *= sx;
 740             mat[12] *= sx;
 741             update = true;
 742         }
 743         if (sy != 1.0) {
 744             mat[1]  *= sy;
 745             mat[5]  *= sy;
 746             mat[9]  *= sy;
 747             mat[13] *= sy;
 748             update = true;
 749         }
 750         if (sz != 1.0) {
 751             mat[2]  *= sz;
 752             mat[6]  *= sz;
 753             mat[10] *= sz;
 754             mat[14] *= sz;
 755             update = true;
 756         }
 757 
 758         if (update) {
 759             updateState();
 760         }
 761         return this;
 762     }
 763 
 764     /**
 765      * Sets the value of this transform to the result of multiplying itself
 766      * with transform t1 : this = this * t1.
 767       *
 768      * @param t1 the other transform
 769      *
 770      * @return this transform
 771      */
 772     public GeneralTransform3D mul(GeneralTransform3D t1) {
 773         if (t1.isIdentity()) {
 774             return this;
 775         }
 776 
 777         double tmp0, tmp1, tmp2, tmp3;
 778         double tmp4, tmp5, tmp6, tmp7;
 779         double tmp8, tmp9, tmp10, tmp11;
 780         double tmp12, tmp13, tmp14, tmp15;
 781 
 782         if (t1.isAffine()) {
 783             tmp0 = mat[0] * t1.mat[0] + mat[1] * t1.mat[4] + mat[2] * t1.mat[8];
 784             tmp1 = mat[0] * t1.mat[1] + mat[1] * t1.mat[5] + mat[2] * t1.mat[9];
 785             tmp2 = mat[0] * t1.mat[2] + mat[1] * t1.mat[6] + mat[2] * t1.mat[10];
 786             tmp3 = mat[0] * t1.mat[3] + mat[1] * t1.mat[7] + mat[2] * t1.mat[11] + mat[3];
 787             tmp4 = mat[4] * t1.mat[0] + mat[5] * t1.mat[4] + mat[6] * t1.mat[8];
 788             tmp5 = mat[4] * t1.mat[1] + mat[5] * t1.mat[5] + mat[6] * t1.mat[9];
 789             tmp6 = mat[4] * t1.mat[2] + mat[5] * t1.mat[6] + mat[6] * t1.mat[10];
 790             tmp7 = mat[4] * t1.mat[3] + mat[5] * t1.mat[7] + mat[6] * t1.mat[11] + mat[7];
 791             tmp8 = mat[8] * t1.mat[0] + mat[9] * t1.mat[4] + mat[10] * t1.mat[8];
 792             tmp9 = mat[8] * t1.mat[1] + mat[9] * t1.mat[5] + mat[10] * t1.mat[9];
 793             tmp10 = mat[8] * t1.mat[2] + mat[9] * t1.mat[6] + mat[10] * t1.mat[10];
 794             tmp11 = mat[8] * t1.mat[3] + mat[9] * t1.mat[7] + mat[10] * t1.mat[11] + mat[11];
 795             if (isAffine()) {
 796                 tmp12 = tmp13 = tmp14 = 0;
 797                 tmp15 = 1;
 798             }
 799             else {
 800                 tmp12 = mat[12] * t1.mat[0] + mat[13] * t1.mat[4] +
 801                         mat[14] * t1.mat[8];
 802                 tmp13 = mat[12] * t1.mat[1] + mat[13] * t1.mat[5] +
 803                         mat[14] * t1.mat[9];
 804                 tmp14 = mat[12] * t1.mat[2] + mat[13] * t1.mat[6] +
 805                         mat[14] * t1.mat[10];
 806                 tmp15 = mat[12] * t1.mat[3] + mat[13] * t1.mat[7] +
 807                         mat[14] * t1.mat[11] + mat[15];
 808             }
 809         } else {
 810             tmp0 = mat[0] * t1.mat[0] + mat[1] * t1.mat[4] + mat[2] * t1.mat[8] +
 811                     mat[3] * t1.mat[12];
 812             tmp1 = mat[0] * t1.mat[1] + mat[1] * t1.mat[5] + mat[2] * t1.mat[9] +
 813                     mat[3] * t1.mat[13];
 814             tmp2 = mat[0] * t1.mat[2] + mat[1] * t1.mat[6] + mat[2] * t1.mat[10] +
 815                     mat[3] * t1.mat[14];
 816             tmp3 = mat[0] * t1.mat[3] + mat[1] * t1.mat[7] + mat[2] * t1.mat[11] +
 817                     mat[3] * t1.mat[15];
 818             tmp4 = mat[4] * t1.mat[0] + mat[5] * t1.mat[4] + mat[6] * t1.mat[8] +
 819                     mat[7] * t1.mat[12];
 820             tmp5 = mat[4] * t1.mat[1] + mat[5] * t1.mat[5] + mat[6] * t1.mat[9] +
 821                     mat[7] * t1.mat[13];
 822             tmp6 = mat[4] * t1.mat[2] + mat[5] * t1.mat[6] + mat[6] * t1.mat[10] +
 823                     mat[7] * t1.mat[14];
 824             tmp7 = mat[4] * t1.mat[3] + mat[5] * t1.mat[7] + mat[6] * t1.mat[11] +
 825                     mat[7] * t1.mat[15];
 826             tmp8 = mat[8] * t1.mat[0] + mat[9] * t1.mat[4] + mat[10] * t1.mat[8] +
 827                     mat[11] * t1.mat[12];
 828             tmp9 = mat[8] * t1.mat[1] + mat[9] * t1.mat[5] + mat[10] * t1.mat[9] +
 829                     mat[11] * t1.mat[13];
 830             tmp10 = mat[8] * t1.mat[2] + mat[9] * t1.mat[6] +
 831                     mat[10] * t1.mat[10] + mat[11] * t1.mat[14];
 832 
 833             tmp11 = mat[8] * t1.mat[3] + mat[9] * t1.mat[7] +
 834                     mat[10] * t1.mat[11] + mat[11] * t1.mat[15];
 835             if (isAffine()) {
 836                 tmp12 = t1.mat[12];
 837                 tmp13 = t1.mat[13];
 838                 tmp14 = t1.mat[14];
 839                 tmp15 = t1.mat[15];
 840             } else {
 841                 tmp12 = mat[12] * t1.mat[0] + mat[13] * t1.mat[4] +
 842                         mat[14] * t1.mat[8] + mat[15] * t1.mat[12];
 843                 tmp13 = mat[12] * t1.mat[1] + mat[13] * t1.mat[5] +
 844                         mat[14] * t1.mat[9] + mat[15] * t1.mat[13];
 845                 tmp14 = mat[12] * t1.mat[2] + mat[13] * t1.mat[6] +
 846                         mat[14] * t1.mat[10] + mat[15] * t1.mat[14];
 847                 tmp15 = mat[12] * t1.mat[3] + mat[13] * t1.mat[7] +
 848                         mat[14] * t1.mat[11] + mat[15] * t1.mat[15];
 849             }
 850         }
 851 
 852         mat[0] = tmp0;
 853         mat[1] = tmp1;
 854         mat[2] = tmp2;
 855         mat[3] = tmp3;
 856         mat[4] = tmp4;
 857         mat[5] = tmp5;
 858         mat[6] = tmp6;
 859         mat[7] = tmp7;
 860         mat[8] = tmp8;
 861         mat[9] = tmp9;
 862         mat[10] = tmp10;
 863         mat[11] = tmp11;
 864         mat[12] = tmp12;
 865         mat[13] = tmp13;
 866         mat[14] = tmp14;
 867         mat[15] = tmp15;
 868 
 869         updateState();
 870         return this;
 871     }
 872 
 873     /**
 874      * Sets this transform to the identity matrix.
 875      *
 876      * @return this transform
 877      */
 878     public GeneralTransform3D setIdentity() {
 879         mat[0] = 1.0;  mat[1] = 0.0;  mat[2] = 0.0;  mat[3] = 0.0;
 880         mat[4] = 0.0;  mat[5] = 1.0;  mat[6] = 0.0;  mat[7] = 0.0;
 881         mat[8] = 0.0;  mat[9] = 0.0;  mat[10] = 1.0; mat[11] = 0.0;
 882         mat[12] = 0.0; mat[13] = 0.0; mat[14] = 0.0; mat[15] = 1.0;
 883         identity = true;
 884         return this;
 885     }
 886 
 887     /**
 888      * Returns true if the transform is identity. A transform is considered
 889      * to be identity if the diagonal elements of its matrix is all 1s
 890      * otherwise 0s.
 891      */
 892     public boolean isIdentity() {
 893         return identity;
 894     }
 895 
 896     private void updateState() {
 897         //set the identity flag.
 898         identity =
 899             mat[0]  == 1.0 && mat[5]  == 1.0 && mat[10] == 1.0 && mat[15] == 1.0 &&
 900             mat[1]  == 0.0 && mat[2]  == 0.0 && mat[3]  == 0.0 &&
 901             mat[4]  == 0.0 && mat[6]  == 0.0 && mat[7]  == 0.0 &&
 902             mat[8]  == 0.0 && mat[9]  == 0.0 && mat[11] == 0.0 &&
 903             mat[12] == 0.0 && mat[13] == 0.0 && mat[14] == 0.0;
 904     }
 905 
 906     // Check whether matrix has an Infinity or NaN value. If so, don't treat it
 907     // as affine.
 908     boolean isInfOrNaN() {
 909         // The following is a faster version of the check.
 910         // Instead of 3 tests per array element (Double.isInfinite is 2 tests),
 911         // for a total of 48 tests, we will do 16 multiplies and 1 test.
 912         double d = 0.0;
 913         for (int i = 0; i < mat.length; i++) {
 914             d *= mat[i];
 915         }
 916 
 917         return d != 0.0;
 918     }
 919 
 920     private static final double EPSILON_ABSOLUTE = 1.0e-5;
 921 
 922     static boolean almostZero(double a) {
 923         return ((a < EPSILON_ABSOLUTE) && (a > -EPSILON_ABSOLUTE));
 924     }
 925 
 926     static boolean almostOne(double a) {
 927         return ((a < 1+EPSILON_ABSOLUTE) && (a > 1-EPSILON_ABSOLUTE));
 928     }
 929 
 930     public GeneralTransform3D copy() {
 931         GeneralTransform3D newTransform = new GeneralTransform3D();
 932         newTransform.set(this);
 933         return newTransform;
 934     }
 935 
 936     /**
 937      * Returns the matrix elements of this transform as a string.
 938      * @return  the matrix elements of this transform
 939      */
 940     @Override
 941     public String toString() {
 942         return mat[0] + ", " + mat[1] + ", " + mat[2] + ", " + mat[3] + "\n" +
 943                 mat[4] + ", " + mat[5] + ", " + mat[6] + ", " + mat[7] + "\n" +
 944                 mat[8] + ", " + mat[9] + ", " + mat[10] + ", " + mat[11] + "\n" +
 945                 mat[12] + ", " + mat[13] + ", " + mat[14] + ", " + mat[15] + "\n";
 946     }
 947 
 948 }