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