001/* 002 * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025 026package javafx.scene.transform; 027 028import javafx.beans.property.DoubleProperty; 029import javafx.beans.property.DoublePropertyBase; 030 031import com.sun.javafx.geom.transform.Affine3D; 032import javafx.geometry.Point2D; 033import javafx.geometry.Point3D; 034 035 036/** 037 * This class represents an {@code Affine} object that scales coordinates 038 * by the specified factors. The matrix representing the scaling transformation 039 * is as follows: 040 * <pre> 041 * [ x 0 0 (1-x)*pivotX ] 042 * [ 0 y 0 (1-y)*pivotY ] 043 * [ 0 0 z (1-z)*pivotZ ] 044 * </pre> 045 */ 046public class Scale extends Transform { 047 /** 048 * Creates a default Scale (identity). 049 */ 050 public Scale() { 051 } 052 053 /** 054 * Creates a two-dimensional Scale. 055 * @param x the factor by which coordinates are scaled along the X axis 056 * @param y the factor by which coordinates are scaled along the Y axis 057 */ 058 public Scale(double x, double y) { 059 setX(x); 060 setY(y); 061 } 062 063 /** 064 * Creates a two-dimensional Scale with pivot. 065 * @param x the factor by which coordinates are scaled along the X axis 066 * @param y the factor by which coordinates are scaled along the Y axis 067 * @param pivotX the X coordinate about which point the scale occurs 068 * @param pivotY the Y coordinate about which point the scale occurs 069 */ 070 public Scale(double x, double y, double pivotX, double pivotY) { 071 this(x, y); 072 setPivotX(pivotX); 073 setPivotY(pivotY); 074 } 075 076 /** 077 * Creates a three-dimensional Scale. 078 * @param x the factor by which coordinates are scaled along the X axis 079 * @param y the factor by which coordinates are scaled along the Y axis 080 * @param z the factor by which coordinates are scaled along the Z axis 081 */ 082 public Scale(double x, double y, double z) { 083 this(x, y); 084 setZ(z); 085 } 086 087 /** 088 * Creates a three-dimensional Scale with pivot. 089 * @param x the factor by which coordinates are scaled along the X axis 090 * @param y the factor by which coordinates are scaled along the Y axis 091 * @param z the factor by which coordinates are scaled along the Z axis 092 * @param pivotX the X coordinate about which point the scale occurs 093 * @param pivotY the Y coordinate about which point the scale occurs 094 * @param pivotZ the Z coordinate about which point the scale occurs 095 */ 096 public Scale(double x, double y, double z, double pivotX, double pivotY, double pivotZ) { 097 this(x, y, pivotX, pivotY); 098 setZ(z); 099 setPivotZ(pivotZ); 100 } 101 102 /** 103 * Defines the factor by which coordinates are scaled 104 * along the X axis direction. The default value is {@code 1.0}. 105 */ 106 private DoubleProperty x; 107 108 109 public final void setX(double value) { 110 xProperty().set(value); 111 } 112 113 public final double getX() { 114 return x == null ? 1.0F : x.get(); 115 } 116 117 public final DoubleProperty xProperty() { 118 if (x == null) { 119 x = new DoublePropertyBase(1.0F) { 120 121 @Override 122 public void invalidated() { 123 transformChanged(); 124 } 125 126 @Override 127 public Object getBean() { 128 return Scale.this; 129 } 130 131 @Override 132 public String getName() { 133 return "x"; 134 } 135 }; 136 } 137 return x; 138 } 139 140 /** 141 * Defines the factor by which coordinates are scaled 142 * along the Y axis direction. The default value is {@code 1.0}. 143 */ 144 private DoubleProperty y; 145 146 147 public final void setY(double value) { 148 yProperty().set(value); 149 } 150 151 public final double getY() { 152 return y == null ? 1.0F : y.get(); 153 } 154 155 public final DoubleProperty yProperty() { 156 if (y == null) { 157 y = new DoublePropertyBase(1.0F) { 158 159 @Override 160 public void invalidated() { 161 transformChanged(); 162 } 163 164 @Override 165 public Object getBean() { 166 return Scale.this; 167 } 168 169 @Override 170 public String getName() { 171 return "y"; 172 } 173 }; 174 } 175 return y; 176 } 177 178 /** 179 * Defines the factor by which coordinates are scaled 180 * along the Z axis direction. The default value is {@code 1.0}. 181 */ 182 private DoubleProperty z; 183 184 185 public final void setZ(double value) { 186 zProperty().set(value); 187 } 188 189 public final double getZ() { 190 return z == null ? 1.0F : z.get(); 191 } 192 193 public final DoubleProperty zProperty() { 194 if (z == null) { 195 z = new DoublePropertyBase(1.0F) { 196 197 @Override 198 public void invalidated() { 199 transformChanged(); 200 } 201 202 @Override 203 public Object getBean() { 204 return Scale.this; 205 } 206 207 @Override 208 public String getName() { 209 return "z"; 210 } 211 }; 212 } 213 return z; 214 } 215 216 /** 217 * Defines the X coordinate about which point the scale occurs. 218 */ 219 private DoubleProperty pivotX; 220 221 222 public final void setPivotX(double value) { 223 pivotXProperty().set(value); 224 } 225 226 public final double getPivotX() { 227 return pivotX == null ? 0.0 : pivotX.get(); 228 } 229 230 public final DoubleProperty pivotXProperty() { 231 if (pivotX == null) { 232 pivotX = new DoublePropertyBase() { 233 234 @Override 235 public void invalidated() { 236 transformChanged(); 237 } 238 239 @Override 240 public Object getBean() { 241 return Scale.this; 242 } 243 244 @Override 245 public String getName() { 246 return "pivotX"; 247 } 248 }; 249 } 250 return pivotX; 251 } 252 253 /** 254 * Defines the Y coordinate about which point the scale occurs. 255 */ 256 private DoubleProperty pivotY; 257 258 259 public final void setPivotY(double value) { 260 pivotYProperty().set(value); 261 } 262 263 public final double getPivotY() { 264 return pivotY == null ? 0.0 : pivotY.get(); 265 } 266 267 public final DoubleProperty pivotYProperty() { 268 if (pivotY == null) { 269 pivotY = new DoublePropertyBase() { 270 271 @Override 272 public void invalidated() { 273 transformChanged(); 274 } 275 276 @Override 277 public Object getBean() { 278 return Scale.this; 279 } 280 281 @Override 282 public String getName() { 283 return "pivotY"; 284 } 285 }; 286 } 287 return pivotY; 288 } 289 290 /** 291 * Defines the Z coordinate about which point the scale occurs. 292 */ 293 private DoubleProperty pivotZ; 294 295 296 public final void setPivotZ(double value) { 297 pivotZProperty().set(value); 298 } 299 300 public final double getPivotZ() { 301 return pivotZ == null ? 0.0 : pivotZ.get(); 302 } 303 304 public final DoubleProperty pivotZProperty() { 305 if (pivotZ == null) { 306 pivotZ = new DoublePropertyBase() { 307 308 @Override 309 public void invalidated() { 310 transformChanged(); 311 } 312 313 @Override 314 public Object getBean() { 315 return Scale.this; 316 } 317 318 @Override 319 public String getName() { 320 return "pivotZ"; 321 } 322 }; 323 } 324 return pivotZ; 325 } 326 327 /* ************************************************************************* 328 * * 329 * Element getters * 330 * * 331 **************************************************************************/ 332 333 @Override 334 public double getMxx() { 335 return getX(); 336 } 337 338 @Override 339 public double getMyy() { 340 return getY(); 341 } 342 343 @Override 344 public double getMzz() { 345 return getZ(); 346 } 347 348 @Override 349 public double getTx() { 350 return (1-getX()) * getPivotX(); 351 } 352 353 @Override 354 public double getTy() { 355 return (1-getY()) * getPivotY(); 356 } 357 358 @Override 359 public double getTz() { 360 return (1-getZ()) * getPivotZ(); 361 } 362 363 /* ************************************************************************* 364 * * 365 * State getters * 366 * * 367 **************************************************************************/ 368 369 @Override 370 boolean computeIs2D() { 371 return getZ() == 1.0; 372 } 373 374 @Override 375 boolean computeIsIdentity() { 376 return getX() == 1.0 && getY() == 1.0 && getZ() == 1.0; 377 } 378 379 /* ************************************************************************* 380 * * 381 * Array getters * 382 * * 383 **************************************************************************/ 384 385 @Override 386 void fill2DArray(double[] array) { 387 final double sx = getX(); 388 final double sy = getY(); 389 390 array[0] = sx; 391 array[1] = 0.0; 392 array[2] = (1-sx) * getPivotX(); 393 array[3] = 0.0; 394 array[4] = sy; 395 array[5] = (1-sy) * getPivotY(); 396 } 397 398 @Override 399 void fill3DArray(double[] array) { 400 final double sx = getX(); 401 final double sy = getY(); 402 final double sz = getZ(); 403 404 array[0] = sx; 405 array[1] = 0.0; 406 array[2] = 0.0; 407 array[3] = (1-sx) * getPivotX(); 408 array[4] = 0.0; 409 array[5] = sy; 410 array[6] = 0.0; 411 array[7] = (1-sy) * getPivotY(); 412 array[8] = 0.0; 413 array[9] = 0.0; 414 array[10] = sz; 415 array[11] = (1-sz) * getPivotZ(); 416 } 417 418 /* ************************************************************************* 419 * * 420 * Transform creators * 421 * * 422 **************************************************************************/ 423 424 @Override 425 public Transform createConcatenation(Transform transform) { 426 final double sx = getX(); 427 final double sy = getY(); 428 final double sz = getZ(); 429 430 if (transform instanceof Scale) { 431 final Scale other = (Scale) transform; 432 if (other.getPivotX() == getPivotX() 433 && other.getPivotY() == getPivotY() 434 && other.getPivotZ() == getPivotZ()) { 435 return new Scale( 436 sx * other.getX(), 437 sy * other.getY(), 438 sz * other.getZ(), 439 getPivotX(), getPivotY(), getPivotZ()); 440 } 441 } 442 443 if (transform instanceof Translate) { 444 final Translate t = (Translate) transform; 445 446 final double tx = t.getX(); 447 final double ty = t.getY(); 448 final double tz = t.getZ(); 449 450 if ((tx == 0.0 || (sx != 1.0 && sx != 0.0)) && 451 (ty == 0.0 || (sy != 1.0 && sy != 0.0)) && 452 (tz == 0.0 || (sz != 1.0 && sz != 0.0))) { 453 return new Scale( 454 sx, sy, sz, 455 (sx != 1.0 ? sx * tx / (1 - sx) : 0) + getPivotX(), 456 (sy != 1.0 ? sy * ty / (1 - sy) : 0) + getPivotY(), 457 (sz != 1.0 ? sz * tz / (1 - sz) : 0) + getPivotZ()); 458 } 459 } 460 461 if (transform instanceof Affine) { 462 Affine a = (Affine) transform.clone(); 463 a.prepend(this); 464 return a; 465 } 466 467 final double txx = transform.getMxx(); 468 final double txy = transform.getMxy(); 469 final double txz = transform.getMxz(); 470 final double ttx = transform.getTx(); 471 final double tyx = transform.getMyx(); 472 final double tyy = transform.getMyy(); 473 final double tyz = transform.getMyz(); 474 final double tty = transform.getTy(); 475 final double tzx = transform.getMzx(); 476 final double tzy = transform.getMzy(); 477 final double tzz = transform.getMzz(); 478 final double ttz = transform.getTz(); 479 return new Affine( 480 sx * txx, sx * txy, sx * txz, sx * ttx + (1 - sx) * getPivotX(), 481 sy * tyx, sy * tyy, sy * tyz, sy * tty + (1 - sy) * getPivotY(), 482 sz * tzx, sz * tzy, sz * tzz, sz * ttz + (1 - sz) * getPivotZ()); 483 } 484 485 @Override 486 public Scale createInverse() throws NonInvertibleTransformException { 487 final double sx = getX(); 488 final double sy = getY(); 489 final double sz = getZ(); 490 491 if (sx == 0.0 || sy == 0.0 || sz == 0.0) { 492 throw new NonInvertibleTransformException( 493 "Zero scale is not invertible"); 494 } 495 496 return new Scale(1.0 / sx, 1.0 / sy, 1.0 / sz, 497 getPivotX(), getPivotY(), getPivotZ()); 498 } 499 500 @Override 501 public Scale clone() { 502 return new Scale(getX(), getY(), getZ(), 503 getPivotX(), getPivotY(), getPivotZ()); 504 } 505 506 /* ************************************************************************* 507 * * 508 * Transform, Inverse Transform * 509 * * 510 **************************************************************************/ 511 512 @Override 513 public Point2D transform(double x, double y) { 514 ensureCanTransform2DPoint(); 515 516 final double mxx = getX(); 517 final double myy = getY(); 518 519 return new Point2D( 520 mxx * x + (1 - mxx) * getPivotX(), 521 myy * y + (1 - myy) * getPivotY()); 522 } 523 524 @Override 525 public Point3D transform(double x, double y, double z) { 526 527 final double mxx = getX(); 528 final double myy = getY(); 529 final double mzz = getZ(); 530 531 return new Point3D( 532 mxx * x + (1 - mxx) * getPivotX(), 533 myy * y + (1 - myy) * getPivotY(), 534 mzz * z + (1 - mzz) * getPivotZ()); 535 } 536 537 @Override 538 void transform2DPointsImpl(double[] srcPts, int srcOff, 539 double[] dstPts, int dstOff, int numPts) { 540 final double xx = getX(); 541 final double yy = getY(); 542 final double px = getPivotX(); 543 final double py = getPivotY(); 544 545 while (--numPts >= 0) { 546 final double x = srcPts[srcOff++]; 547 final double y = srcPts[srcOff++]; 548 549 dstPts[dstOff++] = xx * x + (1 - xx) * px; 550 dstPts[dstOff++] = yy * y + (1 - yy) * py; 551 } 552 } 553 554 @Override 555 void transform3DPointsImpl(double[] srcPts, int srcOff, 556 double[] dstPts, int dstOff, int numPts) { 557 final double xx = getX(); 558 final double yy = getY(); 559 final double zz = getZ(); 560 final double px = getPivotX(); 561 final double py = getPivotY(); 562 final double pz = getPivotZ(); 563 564 while (--numPts >= 0) { 565 dstPts[dstOff++] = xx * srcPts[srcOff++] + (1 - xx) * px; 566 dstPts[dstOff++] = yy * srcPts[srcOff++] + (1 - yy) * py; 567 dstPts[dstOff++] = zz * srcPts[srcOff++] + (1 - zz) * pz; 568 } 569 } 570 571 @Override 572 public Point2D deltaTransform(double x, double y) { 573 ensureCanTransform2DPoint(); 574 575 return new Point2D( 576 getX() * x, 577 getY() * y); 578 } 579 580 @Override 581 public Point3D deltaTransform(double x, double y, double z) { 582 return new Point3D( 583 getX() * x, 584 getY() * y, 585 getZ() * z); 586 } 587 588 @Override 589 public Point2D inverseTransform(double x, double y) 590 throws NonInvertibleTransformException { 591 ensureCanTransform2DPoint(); 592 593 final double sx = getX(); 594 final double sy = getY(); 595 596 if (sx == 0.0 || sy == 0.0) { 597 throw new NonInvertibleTransformException( 598 "Zero scale is not invertible"); 599 } 600 601 final double mxx = 1.0 / sx; 602 final double myy = 1.0 / sy; 603 604 return new Point2D( 605 mxx * x + (1 - mxx) * getPivotX(), 606 myy * y + (1 - myy) * getPivotY()); 607 } 608 609 @Override 610 public Point3D inverseTransform(double x, double y, double z) 611 throws NonInvertibleTransformException { 612 final double sx = getX(); 613 final double sy = getY(); 614 final double sz = getZ(); 615 616 if (sx == 0.0 || sy == 0.0 || sz == 0.0) { 617 throw new NonInvertibleTransformException( 618 "Zero scale is not invertible"); 619 } 620 621 final double mxx = 1.0 / sx; 622 final double myy = 1.0 / sy; 623 final double mzz = 1.0 / sz; 624 625 return new Point3D( 626 mxx * x + (1 - mxx) * getPivotX(), 627 myy * y + (1 - myy) * getPivotY(), 628 mzz * z + (1 - mzz) * getPivotZ()); 629 } 630 631 @Override 632 void inverseTransform2DPointsImpl(double[] srcPts, int srcOff, 633 double[] dstPts, int dstOff, int numPts) 634 throws NonInvertibleTransformException { 635 final double sx = getX(); 636 final double sy = getY(); 637 638 if (sx == 0.0 || sy == 0.0) { 639 throw new NonInvertibleTransformException( 640 "Zero scale is not invertible"); 641 } 642 643 final double xx = 1.0 / sx; 644 final double yy = 1.0 / sy; 645 final double px = getPivotX(); 646 final double py = getPivotY(); 647 648 while (--numPts >= 0) { 649 dstPts[dstOff++] = xx * srcPts[srcOff++] + (1 - xx) * px; 650 dstPts[dstOff++] = yy * srcPts[srcOff++] + (1 - yy) * py; 651 } 652 } 653 654 @Override 655 void inverseTransform3DPointsImpl(double[] srcPts, int srcOff, 656 double[] dstPts, int dstOff, int numPts) 657 throws NonInvertibleTransformException { 658 659 final double sx = getX(); 660 final double sy = getY(); 661 final double sz = getZ(); 662 663 if (sx == 0.0 || sy == 0.0 || sz == 0.0) { 664 throw new NonInvertibleTransformException( 665 "Zero scale is not invertible"); 666 } 667 668 final double xx = 1.0 / sx; 669 final double yy = 1.0 / sy; 670 final double zz = 1.0 / sz; 671 final double px = getPivotX(); 672 final double py = getPivotY(); 673 final double pz = getPivotZ(); 674 675 while (--numPts >= 0) { 676 dstPts[dstOff++] = xx * srcPts[srcOff++] + (1 - xx) * px; 677 dstPts[dstOff++] = yy * srcPts[srcOff++] + (1 - yy) * py; 678 dstPts[dstOff++] = zz * srcPts[srcOff++] + (1 - zz) * pz; 679 } 680 } 681 682 @Override 683 public Point2D inverseDeltaTransform(double x, double y) 684 throws NonInvertibleTransformException { 685 ensureCanTransform2DPoint(); 686 687 final double sx = getX(); 688 final double sy = getY(); 689 690 if (sx == 0.0 || sy == 0.0) { 691 throw new NonInvertibleTransformException( 692 "Zero scale is not invertible"); 693 } 694 695 return new Point2D( 696 (1.0 / sx) * x, 697 (1.0 / sy) * y); 698 } 699 700 @Override 701 public Point3D inverseDeltaTransform(double x, double y, double z) 702 throws NonInvertibleTransformException { 703 704 final double sx = getX(); 705 final double sy = getY(); 706 final double sz = getZ(); 707 708 if (sx == 0.0 || sy == 0.0 || sz == 0.0) { 709 throw new NonInvertibleTransformException( 710 "Zero scale is not invertible"); 711 } 712 713 return new Point3D( 714 (1.0 / sx) * x, 715 (1.0 / sy) * y, 716 (1.0 / sz) * z); 717 } 718 719 /* ************************************************************************* 720 * * 721 * Other API * 722 * * 723 **************************************************************************/ 724 725 /** 726 * Returns a string representation of this {@code Scale} object. 727 * @return a string representation of this {@code Scale} object. 728 */ 729 @Override 730 public String toString() { 731 final StringBuilder sb = new StringBuilder("Scale ["); 732 733 sb.append("x=").append(getX()); 734 sb.append(", y=").append(getY()); 735 sb.append(", z=").append(getZ()); 736 sb.append(", pivotX=").append(getPivotX()); 737 sb.append(", pivotY=").append(getPivotY()); 738 sb.append(", pivotZ=").append(getPivotZ()); 739 740 return sb.append("]").toString(); 741 } 742 743 /* ************************************************************************* 744 * * 745 * Internal implementation stuff * 746 * * 747 **************************************************************************/ 748 749 /** 750 * @treatAsPrivate implementation detail 751 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 752 */ 753 @Deprecated 754 @Override 755 public void impl_apply(final Affine3D trans) { 756 if (getPivotX() != 0 || getPivotY() != 0 || getPivotZ() != 0) { 757 trans.translate(getPivotX(), getPivotY(), getPivotZ()); 758 trans.scale(getX(), getY(), getZ()); 759 trans.translate(-getPivotX(), -getPivotY(), -getPivotZ()); 760 } else { 761 trans.scale(getX(), getY(), getZ()); 762 } 763 } 764 765 @Override 766 void validate() { 767 getX(); getPivotX(); 768 getY(); getPivotY(); 769 getZ(); getPivotZ(); 770 } 771 772 @Override 773 void appendTo(Affine a) { 774 a.appendScale(getX(), getY(), getZ(), 775 getPivotX(), getPivotY(), getPivotZ()); 776 } 777 778 @Override 779 void prependTo(Affine a) { 780 a.prependScale(getX(), getY(), getZ(), 781 getPivotX(), getPivotY(), getPivotZ()); 782 } 783}