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 }