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