1 /* 2 * Copyright (c) 2011, 2016, 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 javafx.scene.transform; 27 28 import javafx.beans.property.DoubleProperty; 29 import javafx.beans.property.DoublePropertyBase; 30 import javafx.beans.property.ObjectProperty; 31 import javafx.beans.property.ObjectPropertyBase; 32 import javafx.geometry.Point3D; 33 34 import com.sun.javafx.geom.transform.Affine3D; 35 import com.sun.javafx.geom.transform.BaseTransform; 36 import javafx.geometry.Point2D; 37 38 39 /** 40 * This class represents an {@code Affine} object that rotates coordinates 41 * around an anchor point. This operation is equivalent to translating the 42 * coordinates so that the anchor point is at the origin (S1), then rotating them 43 * about the new origin (S2), and finally translating so that the 44 * intermediate origin is restored to the coordinates of the original 45 * anchor point (S3). 46 * <p/> 47 * For example, the matrix representing the returned transform of 48 * new Rotate (theta, x, y, z) around the Z-axis 49 * 50 * is : 51 * <pre> 52 * [ cos(theta) -sin(theta) 0 x-x*cos+y*sin ] 53 * [ sin(theta) cos(theta) 0 y-x*sin-y*cos ] 54 * [ 0 0 1 z ] 55 * </pre> 56 * <p> 57 * For example, to rotate a text 30 degrees around the Z-axis at 58 * anchor point of (50,30): 59 * <pre><code> 60 * Text text = new Text("This is a test"); 61 * text.setX(10); 62 * text.setY(50); 63 * text.setFont(new Font(20)); 64 * 65 * text.getTransforms().add(new Rotate(30, 50, 30)); 66 * </code></pre> 67 * </p> 68 * @since JavaFX 2.0 69 */ 70 71 public class Rotate extends Transform { 72 73 /** 74 * Specifies the X-axis as the axis of rotation. 75 */ 76 public static final Point3D X_AXIS = new Point3D(1,0,0); 77 78 /** 79 * Specifies the Y-axis as the axis of rotation. 80 */ 81 public static final Point3D Y_AXIS = new Point3D(0,1,0); 82 83 /** 84 * Specifies the Z-axis as the axis of rotation. 85 */ 86 public static final Point3D Z_AXIS = new Point3D(0,0,1); 87 88 /** 89 * Avoids lot of repeated computation. 90 * @see #MatrixCache 91 */ 92 private MatrixCache cache; 93 94 /** 95 * Avoids lot of repeated computation. 96 * @see #MatrixCache 97 */ 98 private MatrixCache inverseCache; 99 100 /** 101 * Creates a default Rotate transform (identity). 102 */ 103 public Rotate() { 104 } 105 106 /** 107 * Creates a two-dimensional Rotate transform. 108 * The pivot point is set to (0,0) 109 * @param angle the angle of rotation measured in degrees 110 */ 111 public Rotate(double angle) { 112 setAngle(angle); 113 } 114 115 /** 116 * Creates a three-dimensional Rotate transform. 117 * The pivot point is set to (0,0,0) 118 * @param angle the angle of rotation measured in degrees 119 * @param axis the axis of rotation 120 */ 121 public Rotate(double angle, Point3D axis) { 122 setAngle(angle); 123 setAxis(axis); 124 } 125 126 /** 127 * Creates a two-dimensional Rotate transform with pivot. 128 * @param angle the angle of rotation measured in degrees 129 * @param pivotX the X coordinate of the rotation pivot point 130 * @param pivotY the Y coordinate of the rotation pivot point 131 */ 132 public Rotate(double angle, double pivotX, double pivotY) { 133 setAngle(angle); 134 setPivotX(pivotX); 135 setPivotY(pivotY); 136 } 137 138 /** 139 * Creates a simple Rotate transform with three-dimensional pivot. 140 * @param angle the angle of rotation measured in degrees 141 * @param pivotX the X coordinate of the rotation pivot point 142 * @param pivotY the Y coordinate of the rotation pivot point 143 * @param pivotZ the Z coordinate of the rotation pivot point 144 */ 145 public Rotate(double angle, double pivotX, double pivotY, double pivotZ) { 146 this(angle, pivotX, pivotY); 147 setPivotZ(pivotZ); 148 } 149 150 /** 151 * Creates a three-dimensional Rotate transform with pivot. 152 * @param angle the angle of rotation measured in degrees 153 * @param pivotX the X coordinate of the rotation pivot point 154 * @param pivotY the Y coordinate of the rotation pivot point 155 * @param pivotZ the Z coordinate of the rotation pivot point 156 * @param axis the axis of rotation 157 */ 158 public Rotate(double angle, double pivotX, double pivotY, double pivotZ, Point3D axis) { 159 this(angle, pivotX, pivotY); 160 setPivotZ(pivotZ); 161 setAxis(axis); 162 } 163 164 /** 165 * Defines the angle of rotation measured in degrees. 166 */ 167 private DoubleProperty angle; 168 169 170 public final void setAngle(double value) { 171 angleProperty().set(value); 172 } 173 174 public final double getAngle() { 175 return angle == null ? 0.0 : angle.get(); 176 } 177 178 public final DoubleProperty angleProperty() { 179 if (angle == null) { 180 angle = new DoublePropertyBase() { 181 182 @Override 183 public void invalidated() { 184 transformChanged(); 185 } 186 187 @Override 188 public Object getBean() { 189 return Rotate.this; 190 } 191 192 @Override 193 public String getName() { 194 return "angle"; 195 } 196 }; 197 } 198 return angle; 199 } 200 201 /** 202 * Defines the X coordinate of the rotation pivot point. 203 * 204 * @defaultValue 0.0 205 */ 206 private DoubleProperty pivotX; 207 208 209 public final void setPivotX(double value) { 210 pivotXProperty().set(value); 211 } 212 213 public final double getPivotX() { 214 return pivotX == null ? 0.0 : pivotX.get(); 215 } 216 217 public final DoubleProperty pivotXProperty() { 218 if (pivotX == null) { 219 pivotX = new DoublePropertyBase() { 220 221 @Override 222 public void invalidated() { 223 transformChanged(); 224 } 225 226 @Override 227 public Object getBean() { 228 return Rotate.this; 229 } 230 231 @Override 232 public String getName() { 233 return "pivotX"; 234 } 235 }; 236 } 237 return pivotX; 238 } 239 240 /** 241 * Defines the Y coordinate of the rotation pivot point. 242 * 243 * @defaultValue 0.0 244 */ 245 private DoubleProperty pivotY; 246 247 248 public final void setPivotY(double value) { 249 pivotYProperty().set(value); 250 } 251 252 public final double getPivotY() { 253 return pivotY == null ? 0.0 : pivotY.get(); 254 } 255 256 public final DoubleProperty pivotYProperty() { 257 if (pivotY == null) { 258 pivotY = new DoublePropertyBase() { 259 260 @Override 261 public void invalidated() { 262 transformChanged(); 263 } 264 265 @Override 266 public Object getBean() { 267 return Rotate.this; 268 } 269 270 @Override 271 public String getName() { 272 return "pivotY"; 273 } 274 }; 275 } 276 return pivotY; 277 } 278 279 /** 280 * Defines the Z coordinate of the rotation pivot point. 281 * 282 * @defaultValue 0.0 283 */ 284 private DoubleProperty pivotZ; 285 286 287 public final void setPivotZ(double value) { 288 pivotZProperty().set(value); 289 } 290 291 public final double getPivotZ() { 292 return pivotZ == null ? 0.0 : pivotZ.get(); 293 } 294 295 public final DoubleProperty pivotZProperty() { 296 if (pivotZ == null) { 297 pivotZ = new DoublePropertyBase() { 298 299 @Override 300 public void invalidated() { 301 transformChanged(); 302 } 303 304 @Override 305 public Object getBean() { 306 return Rotate.this; 307 } 308 309 @Override 310 public String getName() { 311 return "pivotZ"; 312 } 313 }; 314 } 315 return pivotZ; 316 } 317 318 /** 319 * Defines the axis of rotation at the pivot point. 320 */ 321 private ObjectProperty<Point3D> axis; 322 323 324 public final void setAxis(Point3D value) { 325 axisProperty().set(value); 326 } 327 328 public final Point3D getAxis() { 329 return axis == null ? Z_AXIS : axis.get(); 330 } 331 332 public final ObjectProperty<Point3D> axisProperty() { 333 if (axis == null) { 334 axis = new ObjectPropertyBase<Point3D>(Z_AXIS) { 335 336 @Override 337 public void invalidated() { 338 transformChanged(); 339 } 340 341 @Override 342 public Object getBean() { 343 return Rotate.this; 344 } 345 346 @Override 347 public String getName() { 348 return "axis"; 349 } 350 }; 351 } 352 return axis; 353 } 354 355 /* ************************************************************************* 356 * * 357 * Element getters * 358 * * 359 **************************************************************************/ 360 361 @Override 362 public double getMxx() { 363 updateCache(); 364 return cache.mxx; 365 } 366 367 @Override 368 public double getMxy() { 369 updateCache(); 370 return cache.mxy; 371 } 372 373 @Override 374 public double getMxz() { 375 updateCache(); 376 return cache.mxz; 377 } 378 379 @Override 380 public double getTx() { 381 updateCache(); 382 return cache.tx; 383 } 384 385 @Override 386 public double getMyx() { 387 updateCache(); 388 return cache.myx; 389 } 390 391 @Override 392 public double getMyy() { 393 updateCache(); 394 return cache.myy; 395 } 396 397 @Override 398 public double getMyz() { 399 updateCache(); 400 return cache.myz; 401 } 402 403 @Override 404 public double getTy() { 405 updateCache(); 406 return cache.ty; 407 } 408 409 @Override 410 public double getMzx() { 411 updateCache(); 412 return cache.mzx; 413 } 414 415 @Override 416 public double getMzy() { 417 updateCache(); 418 return cache.mzy; 419 } 420 421 @Override 422 public double getMzz() { 423 updateCache(); 424 return cache.mzz; 425 } 426 427 @Override 428 public double getTz() { 429 updateCache(); 430 return cache.tz; 431 } 432 433 /* ************************************************************************* 434 * * 435 * State getters * 436 * * 437 **************************************************************************/ 438 439 @Override 440 boolean computeIs2D() { 441 final Point3D a = getAxis(); 442 return (a.getX() == 0.0 && a.getY() == 0.0) || getAngle() == 0; 443 } 444 445 @Override 446 boolean computeIsIdentity() { 447 if (getAngle() == 0.0) { 448 return true; 449 } 450 451 final Point3D a = getAxis(); 452 return a.getX() == 0 && a.getY() == 0 && a.getZ() == 0.0; 453 } 454 455 /* ************************************************************************* 456 * * 457 * Array getters * 458 * * 459 **************************************************************************/ 460 461 @Override 462 void fill2DArray(double[] array) { 463 updateCache(); 464 array[0] = cache.mxx; 465 array[1] = cache.mxy; 466 array[2] = cache.tx; 467 array[3] = cache.myx; 468 array[4] = cache.myy; 469 array[5] = cache.ty; 470 } 471 472 @Override 473 void fill3DArray(double[] array) { 474 updateCache(); 475 array[0] = cache.mxx; 476 array[1] = cache.mxy; 477 array[2] = cache.mxz; 478 array[3] = cache.tx; 479 array[4] = cache.myx; 480 array[5] = cache.myy; 481 array[6] = cache.myz; 482 array[7] = cache.ty; 483 array[8] = cache.mzx; 484 array[9] = cache.mzy; 485 array[10] = cache.mzz; 486 array[11] = cache.tz; 487 return; 488 } 489 490 /* ************************************************************************* 491 * * 492 * Transform creators * 493 * * 494 **************************************************************************/ 495 496 @Override 497 public Transform createConcatenation(Transform transform) { 498 if (transform instanceof Rotate) { 499 Rotate r = (Rotate) transform; 500 final double px = getPivotX(); 501 final double py = getPivotY(); 502 final double pz = getPivotZ(); 503 504 if ((r.getAxis() == getAxis() || 505 r.getAxis().normalize().equals(getAxis().normalize())) && 506 px == r.getPivotX() && 507 py == r.getPivotY() && 508 pz == r.getPivotZ()) { 509 return new Rotate(getAngle() + r.getAngle(), px, py, pz, getAxis()); 510 } 511 } 512 513 if (transform instanceof Affine) { 514 Affine a = (Affine) transform.clone(); 515 a.prepend(this); 516 return a; 517 } 518 519 return super.createConcatenation(transform); 520 } 521 522 @Override 523 public Transform createInverse() throws NonInvertibleTransformException { 524 return new Rotate(-getAngle(), getPivotX(), getPivotY(), getPivotZ(), 525 getAxis()); 526 } 527 528 @Override 529 public Rotate clone() { 530 return new Rotate(getAngle(), getPivotX(), getPivotY(), getPivotZ(), 531 getAxis()); 532 } 533 534 /* ************************************************************************* 535 * * 536 * Transform, Inverse Transform * 537 * * 538 **************************************************************************/ 539 540 @Override 541 public Point2D transform(double x, double y) { 542 ensureCanTransform2DPoint(); 543 544 updateCache(); 545 546 return new Point2D( 547 cache.mxx * x + cache.mxy * y + cache.tx, 548 cache.myx * x + cache.myy * y + cache.ty); 549 } 550 551 @Override 552 public Point3D transform(double x, double y, double z) { 553 updateCache(); 554 555 return new Point3D( 556 cache.mxx * x + cache.mxy * y + cache.mxz * z + cache.tx, 557 cache.myx * x + cache.myy * y + cache.myz * z + cache.ty, 558 cache.mzx * x + cache.mzy * y + cache.mzz * z + cache.tz); 559 } 560 561 @Override 562 void transform2DPointsImpl(double[] srcPts, int srcOff, 563 double[] dstPts, int dstOff, int numPts) { 564 updateCache(); 565 566 while (--numPts >= 0) { 567 final double x = srcPts[srcOff++]; 568 final double y = srcPts[srcOff++]; 569 570 dstPts[dstOff++] = cache.mxx * x + cache.mxy * y + cache.tx; 571 dstPts[dstOff++] = cache.myx * x + cache.myy * y + cache.ty; 572 } 573 } 574 575 @Override 576 void transform3DPointsImpl(double[] srcPts, int srcOff, 577 double[] dstPts, int dstOff, int numPts) { 578 579 updateCache(); 580 581 while (--numPts >= 0) { 582 final double x = srcPts[srcOff++]; 583 final double y = srcPts[srcOff++]; 584 final double z = srcPts[srcOff++]; 585 586 dstPts[dstOff++] = cache.mxx * x + cache.mxy * y + cache.mxz * z + cache.tx; 587 dstPts[dstOff++] = cache.myx * x + cache.myy * y + cache.myz * z + cache.ty; 588 dstPts[dstOff++] = cache.mzx * x + cache.mzy * y + cache.mzz * z + cache.tz; 589 } 590 } 591 592 @Override 593 public Point2D deltaTransform(double x, double y) { 594 ensureCanTransform2DPoint(); 595 596 updateCache(); 597 598 return new Point2D( 599 cache.mxx * x + cache.mxy * y, 600 cache.myx * x + cache.myy * y); 601 } 602 603 @Override 604 public Point3D deltaTransform(double x, double y, double z) { 605 updateCache(); 606 607 return new Point3D( 608 cache.mxx * x + cache.mxy * y + cache.mxz * z, 609 cache.myx * x + cache.myy * y + cache.myz * z, 610 cache.mzx * x + cache.mzy * y + cache.mzz * z); 611 } 612 613 @Override 614 public Point2D inverseTransform(double x, double y) { 615 ensureCanTransform2DPoint(); 616 617 updateInverseCache(); 618 619 return new Point2D( 620 inverseCache.mxx * x + inverseCache.mxy * y + inverseCache.tx, 621 inverseCache.myx * x + inverseCache.myy * y + inverseCache.ty); 622 } 623 624 @Override 625 public Point3D inverseTransform(double x, double y, double z) { 626 updateInverseCache(); 627 628 return new Point3D( 629 inverseCache.mxx * x + inverseCache.mxy * y + inverseCache.mxz * z 630 + inverseCache.tx, 631 inverseCache.myx * x + inverseCache.myy * y + inverseCache.myz * z 632 + inverseCache.ty, 633 inverseCache.mzx * x + inverseCache.mzy * y + inverseCache.mzz * z 634 + inverseCache.tz); 635 } 636 637 @Override 638 void inverseTransform2DPointsImpl(double[] srcPts, int srcOff, 639 double[] dstPts, int dstOff, int numPts) { 640 updateInverseCache(); 641 642 while (--numPts >= 0) { 643 final double x = srcPts[srcOff++]; 644 final double y = srcPts[srcOff++]; 645 646 dstPts[dstOff++] = inverseCache.mxx * x + inverseCache.mxy * y 647 + inverseCache.tx; 648 dstPts[dstOff++] = inverseCache.myx * x + inverseCache.myy * y 649 + inverseCache.ty; 650 } 651 } 652 653 @Override 654 void inverseTransform3DPointsImpl(double[] srcPts, int srcOff, 655 double[] dstPts, int dstOff, int numPts) { 656 657 updateInverseCache(); 658 659 while (--numPts >= 0) { 660 final double x = srcPts[srcOff++]; 661 final double y = srcPts[srcOff++]; 662 final double z = srcPts[srcOff++]; 663 664 dstPts[dstOff++] = inverseCache.mxx * x + inverseCache.mxy * y 665 + inverseCache.mxz * z + inverseCache.tx; 666 dstPts[dstOff++] = inverseCache.myx * x + inverseCache.myy * y 667 + inverseCache.myz * z + inverseCache.ty; 668 dstPts[dstOff++] = inverseCache.mzx * x + inverseCache.mzy * y 669 + inverseCache.mzz * z + inverseCache.tz; 670 } 671 } 672 673 @Override 674 public Point2D inverseDeltaTransform(double x, double y) { 675 ensureCanTransform2DPoint(); 676 677 updateInverseCache(); 678 679 return new Point2D( 680 inverseCache.mxx * x + inverseCache.mxy * y, 681 inverseCache.myx * x + inverseCache.myy * y); 682 } 683 684 @Override 685 public Point3D inverseDeltaTransform(double x, double y, double z) { 686 updateInverseCache(); 687 688 return new Point3D( 689 inverseCache.mxx * x + inverseCache.mxy * y + inverseCache.mxz * z, 690 inverseCache.myx * x + inverseCache.myy * y + inverseCache.myz * z, 691 inverseCache.mzx * x + inverseCache.mzy * y + inverseCache.mzz * z); 692 } 693 694 /* ************************************************************************* 695 * * 696 * Other API * 697 * * 698 **************************************************************************/ 699 700 /** 701 * Returns a string representation of this {@code Rotate} object. 702 * @return a string representation of this {@code Rotate} object. 703 */ 704 @Override 705 public String toString() { 706 final StringBuilder sb = new StringBuilder("Rotate ["); 707 708 sb.append("angle=").append(getAngle()); 709 sb.append(", pivotX=").append(getPivotX()); 710 sb.append(", pivotY=").append(getPivotY()); 711 sb.append(", pivotZ=").append(getPivotZ()); 712 sb.append(", axis=").append(getAxis()); 713 714 return sb.append("]").toString(); 715 } 716 717 /* ************************************************************************* 718 * * 719 * Internal implementation stuff * 720 * * 721 **************************************************************************/ 722 723 @Override 724 void apply(final Affine3D trans) { 725 double localPivotX = getPivotX(); 726 double localPivotY = getPivotY(); 727 double localPivotZ = getPivotZ(); 728 double localAngle = getAngle(); 729 730 if (localPivotX != 0 || localPivotY != 0 || localPivotZ != 0) { 731 trans.translate(localPivotX, localPivotY, localPivotZ); 732 trans.rotate(Math.toRadians(localAngle), 733 getAxis().getX(),getAxis().getY(), getAxis().getZ()); 734 trans.translate(-localPivotX, -localPivotY, -localPivotZ); 735 } else { 736 trans.rotate(Math.toRadians(localAngle), 737 getAxis().getX(), getAxis().getY(), getAxis().getZ()); 738 } 739 } 740 741 @Override 742 BaseTransform derive(BaseTransform trans) { 743 if (isIdentity()) { 744 return trans; 745 } 746 747 double localPivotX = getPivotX(); 748 double localPivotY = getPivotY(); 749 double localPivotZ = getPivotZ(); 750 double localAngle = getAngle(); 751 752 if (localPivotX != 0 || localPivotY != 0 || localPivotZ != 0) { 753 trans = trans.deriveWithTranslation(localPivotX, localPivotY, localPivotZ); 754 trans = trans.deriveWithRotation(Math.toRadians(localAngle), 755 getAxis().getX(),getAxis().getY(), getAxis().getZ()); 756 return trans.deriveWithTranslation(-localPivotX, -localPivotY, -localPivotZ); 757 } else { 758 return trans.deriveWithRotation(Math.toRadians(localAngle), 759 getAxis().getX(), getAxis().getY(), getAxis().getZ()); 760 } 761 } 762 763 @Override 764 void validate() { 765 getAxis(); 766 getAngle(); 767 getPivotX(); 768 getPivotY(); 769 getPivotZ(); 770 } 771 772 @Override 773 protected void transformChanged() { 774 if (cache != null) { 775 cache.invalidate(); 776 } 777 super.transformChanged(); 778 } 779 780 @Override 781 void appendTo(Affine a) { 782 a.appendRotation(getAngle(), getPivotX(), getPivotY(), getPivotZ(), 783 getAxis()); 784 } 785 786 @Override 787 void prependTo(Affine a) { 788 a.prependRotation(getAngle(), getPivotX(), getPivotY(), getPivotZ(), 789 getAxis()); 790 } 791 792 /** 793 * Updates the matrix cache 794 */ 795 private void updateCache() { 796 if (cache == null) { 797 cache = new MatrixCache(); 798 } 799 800 if (!cache.valid) { 801 cache.update(getAngle(), getAxis(), 802 getPivotX(), getPivotY(), getPivotZ()); 803 } 804 } 805 806 /** 807 * Updates the inverse matrix cache 808 */ 809 private void updateInverseCache() { 810 if (inverseCache == null) { 811 inverseCache = new MatrixCache(); 812 } 813 814 if (!inverseCache.valid) { 815 inverseCache.update(-getAngle(), getAxis(), 816 getPivotX(), getPivotY(), getPivotZ()); 817 } 818 } 819 820 /** 821 * Matrix cache. Computing single transformation matrix elements for 822 * a general rotation is quite expensive. Also each of those partial 823 * computations need some common operations to be made (compute sin 824 * and cos, normalize axis). Therefore with the direct element computations 825 * if all the getters for the elements are called to get the matrix, 826 * the result is slow. 827 * 828 * If a matrix element is asked for, we can reasonably anticipate that 829 * some other elements will be asked for as well. So when any element 830 * needs to be computed, we compute the entire matrix, cache it, 831 * and use the stored values until the transform changes. 832 */ 833 private static class MatrixCache { 834 boolean valid = false; 835 boolean is3D = false; 836 837 double mxx, mxy, mxz, tx, 838 myx, myy, myz, ty, 839 mzx, mzy, mzz, tz; 840 841 public MatrixCache() { 842 // to have the 3D part right when using 2D-only 843 mzz = 1.0; 844 } 845 846 public void update(double angle, Point3D axis, 847 double px, double py, double pz) { 848 849 final double rads = Math.toRadians(angle); 850 final double sin = Math.sin(rads); 851 final double cos = Math.cos(rads); 852 853 if (axis == Z_AXIS || 854 (axis.getX() == 0.0 && 855 axis.getY() == 0.0 && 856 axis.getZ() > 0.0)) { 857 // 2D case 858 mxx = cos; 859 mxy = -sin; 860 tx = px * (1 - cos) + py * sin; 861 myx = sin; 862 myy = cos; 863 ty = py * (1 - cos) - px * sin; 864 865 if (is3D) { 866 // Was 3D, needs to set the 3D values 867 mxz = 0.0; 868 myz = 0.0; 869 mzx = 0.0; 870 mzy = 0.0; 871 mzz = 1.0; 872 tz = 0.0; 873 is3D = false; 874 } 875 valid = true; 876 return; 877 } 878 // 3D case 879 is3D = true; 880 881 double axisX, axisY, axisZ; 882 883 if (axis == X_AXIS || axis == Y_AXIS || axis == Z_AXIS) { 884 axisX = axis.getX(); 885 axisY = axis.getY(); 886 axisZ = axis.getZ(); 887 } else { 888 // normalize 889 final double mag = Math.sqrt(axis.getX() * axis.getX() + 890 axis.getY() * axis.getY() + axis.getZ() * axis.getZ()); 891 892 if (mag == 0.0) { 893 mxx = 1; mxy = 0; mxz = 0; tx = 0; 894 myx = 0; myy = 1; myz = 0; ty = 0; 895 mzx = 0; mzy = 0; mzz = 1; tz = 0; 896 valid = true; 897 return; 898 } else { 899 axisX = axis.getX() / mag; 900 axisY = axis.getY() / mag; 901 axisZ = axis.getZ() / mag; 902 } 903 } 904 905 mxx = cos + axisX * axisX * (1 - cos); 906 mxy = axisX * axisY * (1 - cos) - axisZ * sin; 907 mxz = axisX * axisZ * (1 - cos) + axisY * sin; 908 tx = px * (1 - mxx) - py * mxy - pz * mxz; 909 910 myx = axisY * axisX * (1 - cos) + axisZ * sin; 911 myy = cos + axisY * axisY * (1 - cos); 912 myz = axisY * axisZ * (1 - cos) - axisX * sin; 913 ty = py * (1 - myy) - px * myx - pz * myz; 914 915 mzx = axisZ * axisX * (1 - cos) - axisY * sin; 916 mzy = axisZ * axisY * (1 - cos) + axisX * sin; 917 mzz = cos + axisZ * axisZ * (1 - cos); 918 tz = pz * (1 - mzz) - px * mzx - py * mzy; 919 920 valid = true; 921 } 922 923 public void invalidate() { 924 valid = false; 925 } 926 } 927 }