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