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 }