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 }