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 31 import com.sun.javafx.geom.transform.Affine3D; 32 import com.sun.javafx.geom.transform.BaseTransform; 33 import javafx.geometry.Point2D; 34 import javafx.geometry.Point3D; 35 36 37 /** 38 * This class represents an {@code Affine} object that scales coordinates 39 * by the specified factors. The matrix representing the scaling transformation 40 * is as follows: 41 * <pre> 42 * [ x 0 0 (1-x)*pivotX ] 43 * [ 0 y 0 (1-y)*pivotY ] 44 * [ 0 0 z (1-z)*pivotZ ] 45 * </pre> 46 * @since JavaFX 2.0 47 */ 48 public class Scale extends Transform { 49 /** 50 * Creates a default Scale (identity). 51 */ 52 public Scale() { 53 } 54 55 /** 56 * Creates a two-dimensional Scale. 57 * The pivot point is set to (0,0) 58 * @param x the factor by which coordinates are scaled along the X axis 59 * @param y the factor by which coordinates are scaled along the Y axis 60 */ 61 public Scale(double x, double y) { 62 setX(x); 63 setY(y); 64 } 65 66 /** 67 * Creates a two-dimensional Scale with pivot. 68 * @param x the factor by which coordinates are scaled along the X axis 69 * @param y the factor by which coordinates are scaled along the Y axis 70 * @param pivotX the X coordinate about which point the scale occurs 71 * @param pivotY the Y coordinate about which point the scale occurs 72 */ 73 public Scale(double x, double y, double pivotX, double pivotY) { 74 this(x, y); 75 setPivotX(pivotX); 76 setPivotY(pivotY); 77 } 78 79 /** 80 * Creates a three-dimensional Scale. 81 * The pivot point is set to (0,0,0) 82 * @param x the factor by which coordinates are scaled along the X axis 83 * @param y the factor by which coordinates are scaled along the Y axis 84 * @param z the factor by which coordinates are scaled along the Z axis 85 */ 86 public Scale(double x, double y, double z) { 87 this(x, y); 88 setZ(z); 89 } 90 91 /** 92 * Creates a three-dimensional Scale with pivot. 93 * @param x the factor by which coordinates are scaled along the X axis 94 * @param y the factor by which coordinates are scaled along the Y axis 95 * @param z the factor by which coordinates are scaled along the Z axis 96 * @param pivotX the X coordinate about which point the scale occurs 97 * @param pivotY the Y coordinate about which point the scale occurs 98 * @param pivotZ the Z coordinate about which point the scale occurs 99 */ 100 public Scale(double x, double y, double z, double pivotX, double pivotY, double pivotZ) { 101 this(x, y, pivotX, pivotY); 102 setZ(z); 103 setPivotZ(pivotZ); 104 } 105 106 /** 107 * Defines the factor by which coordinates are scaled 108 * along the X axis direction. The default value is {@code 1.0}. 109 */ 110 private DoubleProperty x; 111 112 113 public final void setX(double value) { 114 xProperty().set(value); 115 } 116 117 public final double getX() { 118 return x == null ? 1.0F : x.get(); 119 } 120 121 public final DoubleProperty xProperty() { 122 if (x == null) { 123 x = new DoublePropertyBase(1.0F) { 124 125 @Override 126 public void invalidated() { 127 transformChanged(); 128 } 129 130 @Override 131 public Object getBean() { 132 return Scale.this; 133 } 134 135 @Override 136 public String getName() { 137 return "x"; 138 } 139 }; 140 } 141 return x; 142 } 143 144 /** 145 * Defines the factor by which coordinates are scaled 146 * along the Y axis direction. The default value is {@code 1.0}. 147 */ 148 private DoubleProperty y; 149 150 151 public final void setY(double value) { 152 yProperty().set(value); 153 } 154 155 public final double getY() { 156 return y == null ? 1.0F : y.get(); 157 } 158 159 public final DoubleProperty yProperty() { 160 if (y == null) { 161 y = new DoublePropertyBase(1.0F) { 162 163 @Override 164 public void invalidated() { 165 transformChanged(); 166 } 167 168 @Override 169 public Object getBean() { 170 return Scale.this; 171 } 172 173 @Override 174 public String getName() { 175 return "y"; 176 } 177 }; 178 } 179 return y; 180 } 181 182 /** 183 * Defines the factor by which coordinates are scaled 184 * along the Z axis direction. The default value is {@code 1.0}. 185 */ 186 private DoubleProperty z; 187 188 189 public final void setZ(double value) { 190 zProperty().set(value); 191 } 192 193 public final double getZ() { 194 return z == null ? 1.0F : z.get(); 195 } 196 197 public final DoubleProperty zProperty() { 198 if (z == null) { 199 z = new DoublePropertyBase(1.0F) { 200 201 @Override 202 public void invalidated() { 203 transformChanged(); 204 } 205 206 @Override 207 public Object getBean() { 208 return Scale.this; 209 } 210 211 @Override 212 public String getName() { 213 return "z"; 214 } 215 }; 216 } 217 return z; 218 } 219 220 /** 221 * Defines the X coordinate about which point the scale occurs. 222 * 223 * @defaultValue 0.0 224 */ 225 private DoubleProperty pivotX; 226 227 228 public final void setPivotX(double value) { 229 pivotXProperty().set(value); 230 } 231 232 public final double getPivotX() { 233 return pivotX == null ? 0.0 : pivotX.get(); 234 } 235 236 public final DoubleProperty pivotXProperty() { 237 if (pivotX == null) { 238 pivotX = new DoublePropertyBase() { 239 240 @Override 241 public void invalidated() { 242 transformChanged(); 243 } 244 245 @Override 246 public Object getBean() { 247 return Scale.this; 248 } 249 250 @Override 251 public String getName() { 252 return "pivotX"; 253 } 254 }; 255 } 256 return pivotX; 257 } 258 259 /** 260 * Defines the Y coordinate about which point the scale occurs. 261 * 262 * @defaultValue 0.0 263 */ 264 private DoubleProperty pivotY; 265 266 267 public final void setPivotY(double value) { 268 pivotYProperty().set(value); 269 } 270 271 public final double getPivotY() { 272 return pivotY == null ? 0.0 : pivotY.get(); 273 } 274 275 public final DoubleProperty pivotYProperty() { 276 if (pivotY == null) { 277 pivotY = new DoublePropertyBase() { 278 279 @Override 280 public void invalidated() { 281 transformChanged(); 282 } 283 284 @Override 285 public Object getBean() { 286 return Scale.this; 287 } 288 289 @Override 290 public String getName() { 291 return "pivotY"; 292 } 293 }; 294 } 295 return pivotY; 296 } 297 298 /** 299 * Defines the Z coordinate about which point the scale occurs. 300 * 301 * @defaultValue 0.0 302 */ 303 private DoubleProperty pivotZ; 304 305 306 public final void setPivotZ(double value) { 307 pivotZProperty().set(value); 308 } 309 310 public final double getPivotZ() { 311 return pivotZ == null ? 0.0 : pivotZ.get(); 312 } 313 314 public final DoubleProperty pivotZProperty() { 315 if (pivotZ == null) { 316 pivotZ = new DoublePropertyBase() { 317 318 @Override 319 public void invalidated() { 320 transformChanged(); 321 } 322 323 @Override 324 public Object getBean() { 325 return Scale.this; 326 } 327 328 @Override 329 public String getName() { 330 return "pivotZ"; 331 } 332 }; 333 } 334 return pivotZ; 335 } 336 337 /* ************************************************************************* 338 * * 339 * Element getters * 340 * * 341 **************************************************************************/ 342 343 @Override 344 public double getMxx() { 345 return getX(); 346 } 347 348 @Override 349 public double getMyy() { 350 return getY(); 351 } 352 353 @Override 354 public double getMzz() { 355 return getZ(); 356 } 357 358 @Override 359 public double getTx() { 360 return (1-getX()) * getPivotX(); 361 } 362 363 @Override 364 public double getTy() { 365 return (1-getY()) * getPivotY(); 366 } 367 368 @Override 369 public double getTz() { 370 return (1-getZ()) * getPivotZ(); 371 } 372 373 /* ************************************************************************* 374 * * 375 * State getters * 376 * * 377 **************************************************************************/ 378 379 @Override 380 boolean computeIs2D() { 381 return getZ() == 1.0; 382 } 383 384 @Override 385 boolean computeIsIdentity() { 386 return getX() == 1.0 && getY() == 1.0 && getZ() == 1.0; 387 } 388 389 /* ************************************************************************* 390 * * 391 * Array getters * 392 * * 393 **************************************************************************/ 394 395 @Override 396 void fill2DArray(double[] array) { 397 final double sx = getX(); 398 final double sy = getY(); 399 400 array[0] = sx; 401 array[1] = 0.0; 402 array[2] = (1-sx) * getPivotX(); 403 array[3] = 0.0; 404 array[4] = sy; 405 array[5] = (1-sy) * getPivotY(); 406 } 407 408 @Override 409 void fill3DArray(double[] array) { 410 final double sx = getX(); 411 final double sy = getY(); 412 final double sz = getZ(); 413 414 array[0] = sx; 415 array[1] = 0.0; 416 array[2] = 0.0; 417 array[3] = (1-sx) * getPivotX(); 418 array[4] = 0.0; 419 array[5] = sy; 420 array[6] = 0.0; 421 array[7] = (1-sy) * getPivotY(); 422 array[8] = 0.0; 423 array[9] = 0.0; 424 array[10] = sz; 425 array[11] = (1-sz) * getPivotZ(); 426 } 427 428 /* ************************************************************************* 429 * * 430 * Transform creators * 431 * * 432 **************************************************************************/ 433 434 @Override 435 public Transform createConcatenation(Transform transform) { 436 final double sx = getX(); 437 final double sy = getY(); 438 final double sz = getZ(); 439 440 if (transform instanceof Scale) { 441 final Scale other = (Scale) transform; 442 if (other.getPivotX() == getPivotX() 443 && other.getPivotY() == getPivotY() 444 && other.getPivotZ() == getPivotZ()) { 445 return new Scale( 446 sx * other.getX(), 447 sy * other.getY(), 448 sz * other.getZ(), 449 getPivotX(), getPivotY(), getPivotZ()); 450 } 451 } 452 453 if (transform instanceof Translate) { 454 final Translate t = (Translate) transform; 455 456 final double tx = t.getX(); 457 final double ty = t.getY(); 458 final double tz = t.getZ(); 459 460 if ((tx == 0.0 || (sx != 1.0 && sx != 0.0)) && 461 (ty == 0.0 || (sy != 1.0 && sy != 0.0)) && 462 (tz == 0.0 || (sz != 1.0 && sz != 0.0))) { 463 return new Scale( 464 sx, sy, sz, 465 (sx != 1.0 ? sx * tx / (1 - sx) : 0) + getPivotX(), 466 (sy != 1.0 ? sy * ty / (1 - sy) : 0) + getPivotY(), 467 (sz != 1.0 ? sz * tz / (1 - sz) : 0) + getPivotZ()); 468 } 469 } 470 471 if (transform instanceof Affine) { 472 Affine a = (Affine) transform.clone(); 473 a.prepend(this); 474 return a; 475 } 476 477 final double txx = transform.getMxx(); 478 final double txy = transform.getMxy(); 479 final double txz = transform.getMxz(); 480 final double ttx = transform.getTx(); 481 final double tyx = transform.getMyx(); 482 final double tyy = transform.getMyy(); 483 final double tyz = transform.getMyz(); 484 final double tty = transform.getTy(); 485 final double tzx = transform.getMzx(); 486 final double tzy = transform.getMzy(); 487 final double tzz = transform.getMzz(); 488 final double ttz = transform.getTz(); 489 return new Affine( 490 sx * txx, sx * txy, sx * txz, sx * ttx + (1 - sx) * getPivotX(), 491 sy * tyx, sy * tyy, sy * tyz, sy * tty + (1 - sy) * getPivotY(), 492 sz * tzx, sz * tzy, sz * tzz, sz * ttz + (1 - sz) * getPivotZ()); 493 } 494 495 @Override 496 public Scale createInverse() throws NonInvertibleTransformException { 497 final double sx = getX(); 498 final double sy = getY(); 499 final double sz = getZ(); 500 501 if (sx == 0.0 || sy == 0.0 || sz == 0.0) { 502 throw new NonInvertibleTransformException( 503 "Zero scale is not invertible"); 504 } 505 506 return new Scale(1.0 / sx, 1.0 / sy, 1.0 / sz, 507 getPivotX(), getPivotY(), getPivotZ()); 508 } 509 510 @Override 511 public Scale clone() { 512 return new Scale(getX(), getY(), getZ(), 513 getPivotX(), getPivotY(), getPivotZ()); 514 } 515 516 /* ************************************************************************* 517 * * 518 * Transform, Inverse Transform * 519 * * 520 **************************************************************************/ 521 522 @Override 523 public Point2D transform(double x, double y) { 524 ensureCanTransform2DPoint(); 525 526 final double mxx = getX(); 527 final double myy = getY(); 528 529 return new Point2D( 530 mxx * x + (1 - mxx) * getPivotX(), 531 myy * y + (1 - myy) * getPivotY()); 532 } 533 534 @Override 535 public Point3D transform(double x, double y, double z) { 536 537 final double mxx = getX(); 538 final double myy = getY(); 539 final double mzz = getZ(); 540 541 return new Point3D( 542 mxx * x + (1 - mxx) * getPivotX(), 543 myy * y + (1 - myy) * getPivotY(), 544 mzz * z + (1 - mzz) * getPivotZ()); 545 } 546 547 @Override 548 void transform2DPointsImpl(double[] srcPts, int srcOff, 549 double[] dstPts, int dstOff, int numPts) { 550 final double xx = getX(); 551 final double yy = getY(); 552 final double px = getPivotX(); 553 final double py = getPivotY(); 554 555 while (--numPts >= 0) { 556 final double x = srcPts[srcOff++]; 557 final double y = srcPts[srcOff++]; 558 559 dstPts[dstOff++] = xx * x + (1 - xx) * px; 560 dstPts[dstOff++] = yy * y + (1 - yy) * py; 561 } 562 } 563 564 @Override 565 void transform3DPointsImpl(double[] srcPts, int srcOff, 566 double[] dstPts, int dstOff, int numPts) { 567 final double xx = getX(); 568 final double yy = getY(); 569 final double zz = getZ(); 570 final double px = getPivotX(); 571 final double py = getPivotY(); 572 final double pz = getPivotZ(); 573 574 while (--numPts >= 0) { 575 dstPts[dstOff++] = xx * srcPts[srcOff++] + (1 - xx) * px; 576 dstPts[dstOff++] = yy * srcPts[srcOff++] + (1 - yy) * py; 577 dstPts[dstOff++] = zz * srcPts[srcOff++] + (1 - zz) * pz; 578 } 579 } 580 581 @Override 582 public Point2D deltaTransform(double x, double y) { 583 ensureCanTransform2DPoint(); 584 585 return new Point2D( 586 getX() * x, 587 getY() * y); 588 } 589 590 @Override 591 public Point3D deltaTransform(double x, double y, double z) { 592 return new Point3D( 593 getX() * x, 594 getY() * y, 595 getZ() * z); 596 } 597 598 @Override 599 public Point2D inverseTransform(double x, double y) 600 throws NonInvertibleTransformException { 601 ensureCanTransform2DPoint(); 602 603 final double sx = getX(); 604 final double sy = getY(); 605 606 if (sx == 0.0 || sy == 0.0) { 607 throw new NonInvertibleTransformException( 608 "Zero scale is not invertible"); 609 } 610 611 final double mxx = 1.0 / sx; 612 final double myy = 1.0 / sy; 613 614 return new Point2D( 615 mxx * x + (1 - mxx) * getPivotX(), 616 myy * y + (1 - myy) * getPivotY()); 617 } 618 619 @Override 620 public Point3D inverseTransform(double x, double y, double z) 621 throws NonInvertibleTransformException { 622 final double sx = getX(); 623 final double sy = getY(); 624 final double sz = getZ(); 625 626 if (sx == 0.0 || sy == 0.0 || sz == 0.0) { 627 throw new NonInvertibleTransformException( 628 "Zero scale is not invertible"); 629 } 630 631 final double mxx = 1.0 / sx; 632 final double myy = 1.0 / sy; 633 final double mzz = 1.0 / sz; 634 635 return new Point3D( 636 mxx * x + (1 - mxx) * getPivotX(), 637 myy * y + (1 - myy) * getPivotY(), 638 mzz * z + (1 - mzz) * getPivotZ()); 639 } 640 641 @Override 642 void inverseTransform2DPointsImpl(double[] srcPts, int srcOff, 643 double[] dstPts, int dstOff, int numPts) 644 throws NonInvertibleTransformException { 645 final double sx = getX(); 646 final double sy = getY(); 647 648 if (sx == 0.0 || sy == 0.0) { 649 throw new NonInvertibleTransformException( 650 "Zero scale is not invertible"); 651 } 652 653 final double xx = 1.0 / sx; 654 final double yy = 1.0 / sy; 655 final double px = getPivotX(); 656 final double py = getPivotY(); 657 658 while (--numPts >= 0) { 659 dstPts[dstOff++] = xx * srcPts[srcOff++] + (1 - xx) * px; 660 dstPts[dstOff++] = yy * srcPts[srcOff++] + (1 - yy) * py; 661 } 662 } 663 664 @Override 665 void inverseTransform3DPointsImpl(double[] srcPts, int srcOff, 666 double[] dstPts, int dstOff, int numPts) 667 throws NonInvertibleTransformException { 668 669 final double sx = getX(); 670 final double sy = getY(); 671 final double sz = getZ(); 672 673 if (sx == 0.0 || sy == 0.0 || sz == 0.0) { 674 throw new NonInvertibleTransformException( 675 "Zero scale is not invertible"); 676 } 677 678 final double xx = 1.0 / sx; 679 final double yy = 1.0 / sy; 680 final double zz = 1.0 / sz; 681 final double px = getPivotX(); 682 final double py = getPivotY(); 683 final double pz = getPivotZ(); 684 685 while (--numPts >= 0) { 686 dstPts[dstOff++] = xx * srcPts[srcOff++] + (1 - xx) * px; 687 dstPts[dstOff++] = yy * srcPts[srcOff++] + (1 - yy) * py; 688 dstPts[dstOff++] = zz * srcPts[srcOff++] + (1 - zz) * pz; 689 } 690 } 691 692 @Override 693 public Point2D inverseDeltaTransform(double x, double y) 694 throws NonInvertibleTransformException { 695 ensureCanTransform2DPoint(); 696 697 final double sx = getX(); 698 final double sy = getY(); 699 700 if (sx == 0.0 || sy == 0.0) { 701 throw new NonInvertibleTransformException( 702 "Zero scale is not invertible"); 703 } 704 705 return new Point2D( 706 (1.0 / sx) * x, 707 (1.0 / sy) * y); 708 } 709 710 @Override 711 public Point3D inverseDeltaTransform(double x, double y, double z) 712 throws NonInvertibleTransformException { 713 714 final double sx = getX(); 715 final double sy = getY(); 716 final double sz = getZ(); 717 718 if (sx == 0.0 || sy == 0.0 || sz == 0.0) { 719 throw new NonInvertibleTransformException( 720 "Zero scale is not invertible"); 721 } 722 723 return new Point3D( 724 (1.0 / sx) * x, 725 (1.0 / sy) * y, 726 (1.0 / sz) * z); 727 } 728 729 /* ************************************************************************* 730 * * 731 * Other API * 732 * * 733 **************************************************************************/ 734 735 /** 736 * Returns a string representation of this {@code Scale} object. 737 * @return a string representation of this {@code Scale} object. 738 */ 739 @Override 740 public String toString() { 741 final StringBuilder sb = new StringBuilder("Scale ["); 742 743 sb.append("x=").append(getX()); 744 sb.append(", y=").append(getY()); 745 sb.append(", z=").append(getZ()); 746 sb.append(", pivotX=").append(getPivotX()); 747 sb.append(", pivotY=").append(getPivotY()); 748 sb.append(", pivotZ=").append(getPivotZ()); 749 750 return sb.append("]").toString(); 751 } 752 753 /* ************************************************************************* 754 * * 755 * Internal implementation stuff * 756 * * 757 **************************************************************************/ 758 759 @Override 760 void apply(final Affine3D trans) { 761 if (getPivotX() != 0 || getPivotY() != 0 || getPivotZ() != 0) { 762 trans.translate(getPivotX(), getPivotY(), getPivotZ()); 763 trans.scale(getX(), getY(), getZ()); 764 trans.translate(-getPivotX(), -getPivotY(), -getPivotZ()); 765 } else { 766 trans.scale(getX(), getY(), getZ()); 767 } 768 } 769 770 @Override 771 BaseTransform derive(BaseTransform trans) { 772 if (isIdentity()) { 773 return trans; 774 } 775 if (getPivotX() != 0 || getPivotY() != 0 || getPivotZ() != 0) { 776 trans = trans.deriveWithTranslation(getPivotX(), getPivotY(), getPivotZ()); 777 trans = trans.deriveWithScale(getX(), getY(), getZ()); 778 return trans.deriveWithTranslation(-getPivotX(), -getPivotY(), -getPivotZ()); 779 } else { 780 return trans.deriveWithScale(getX(), getY(), getZ()); 781 } 782 } 783 784 @Override 785 void validate() { 786 getX(); getPivotX(); 787 getY(); getPivotY(); 788 getZ(); getPivotZ(); 789 } 790 791 @Override 792 void appendTo(Affine a) { 793 a.appendScale(getX(), getY(), getZ(), 794 getPivotX(), getPivotY(), getPivotZ()); 795 } 796 797 @Override 798 void prependTo(Affine a) { 799 a.prependScale(getX(), getY(), getZ(), 800 getPivotX(), getPivotY(), getPivotZ()); 801 } 802 }