/* * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javafx.scene.transform; import javafx.beans.property.DoubleProperty; import javafx.beans.property.DoublePropertyBase; import com.sun.javafx.geom.transform.Affine3D; import com.sun.javafx.geom.transform.BaseTransform; import javafx.geometry.Point2D; import javafx.geometry.Point3D; /** * This class represents an {@code Affine} object that scales coordinates * by the specified factors. The matrix representing the scaling transformation * around a pivot point {@code (pivotX, pivotY, pivotZ)} with scaling factors * {@code x}, {@code y} and {@code z} is as follows: *
 *              [   x   0   0   (1-x)*pivotX   ]
 *              [   0   y   0   (1-y)*pivotY   ]
 *              [   0   0   z   (1-z)*pivotZ   ]
 * 
* @since JavaFX 2.0 */ public class Scale extends Transform { /** * Creates a default Scale (identity). */ public Scale() { } /** * Creates a two-dimensional Scale. * The pivot point is set to (0,0) * @param x the factor by which coordinates are scaled along the X axis * @param y the factor by which coordinates are scaled along the Y axis */ public Scale(double x, double y) { setX(x); setY(y); } /** * Creates a two-dimensional Scale with pivot. * @param x the factor by which coordinates are scaled along the X axis * @param y the factor by which coordinates are scaled along the Y axis * @param pivotX the X coordinate about which point the scale occurs * @param pivotY the Y coordinate about which point the scale occurs */ public Scale(double x, double y, double pivotX, double pivotY) { this(x, y); setPivotX(pivotX); setPivotY(pivotY); } /** * Creates a three-dimensional Scale. * The pivot point is set to (0,0,0) * @param x the factor by which coordinates are scaled along the X axis * @param y the factor by which coordinates are scaled along the Y axis * @param z the factor by which coordinates are scaled along the Z axis */ public Scale(double x, double y, double z) { this(x, y); setZ(z); } /** * Creates a three-dimensional Scale with pivot. * @param x the factor by which coordinates are scaled along the X axis * @param y the factor by which coordinates are scaled along the Y axis * @param z the factor by which coordinates are scaled along the Z axis * @param pivotX the X coordinate about which point the scale occurs * @param pivotY the Y coordinate about which point the scale occurs * @param pivotZ the Z coordinate about which point the scale occurs */ public Scale(double x, double y, double z, double pivotX, double pivotY, double pivotZ) { this(x, y, pivotX, pivotY); setZ(z); setPivotZ(pivotZ); } /** * Defines the factor by which coordinates are scaled * along the X axis direction. The default value is {@code 1.0}. */ private DoubleProperty x; public final void setX(double value) { xProperty().set(value); } public final double getX() { return x == null ? 1.0F : x.get(); } public final DoubleProperty xProperty() { if (x == null) { x = new DoublePropertyBase(1.0F) { @Override public void invalidated() { transformChanged(); } @Override public Object getBean() { return Scale.this; } @Override public String getName() { return "x"; } }; } return x; } /** * Defines the factor by which coordinates are scaled * along the Y axis direction. The default value is {@code 1.0}. */ private DoubleProperty y; public final void setY(double value) { yProperty().set(value); } public final double getY() { return y == null ? 1.0F : y.get(); } public final DoubleProperty yProperty() { if (y == null) { y = new DoublePropertyBase(1.0F) { @Override public void invalidated() { transformChanged(); } @Override public Object getBean() { return Scale.this; } @Override public String getName() { return "y"; } }; } return y; } /** * Defines the factor by which coordinates are scaled * along the Z axis direction. The default value is {@code 1.0}. */ private DoubleProperty z; public final void setZ(double value) { zProperty().set(value); } public final double getZ() { return z == null ? 1.0F : z.get(); } public final DoubleProperty zProperty() { if (z == null) { z = new DoublePropertyBase(1.0F) { @Override public void invalidated() { transformChanged(); } @Override public Object getBean() { return Scale.this; } @Override public String getName() { return "z"; } }; } return z; } /** * Defines the X coordinate about which point the scale occurs. * * @defaultValue 0.0 */ private DoubleProperty pivotX; public final void setPivotX(double value) { pivotXProperty().set(value); } public final double getPivotX() { return pivotX == null ? 0.0 : pivotX.get(); } public final DoubleProperty pivotXProperty() { if (pivotX == null) { pivotX = new DoublePropertyBase() { @Override public void invalidated() { transformChanged(); } @Override public Object getBean() { return Scale.this; } @Override public String getName() { return "pivotX"; } }; } return pivotX; } /** * Defines the Y coordinate about which point the scale occurs. * * @defaultValue 0.0 */ private DoubleProperty pivotY; public final void setPivotY(double value) { pivotYProperty().set(value); } public final double getPivotY() { return pivotY == null ? 0.0 : pivotY.get(); } public final DoubleProperty pivotYProperty() { if (pivotY == null) { pivotY = new DoublePropertyBase() { @Override public void invalidated() { transformChanged(); } @Override public Object getBean() { return Scale.this; } @Override public String getName() { return "pivotY"; } }; } return pivotY; } /** * Defines the Z coordinate about which point the scale occurs. * * @defaultValue 0.0 */ private DoubleProperty pivotZ; public final void setPivotZ(double value) { pivotZProperty().set(value); } public final double getPivotZ() { return pivotZ == null ? 0.0 : pivotZ.get(); } public final DoubleProperty pivotZProperty() { if (pivotZ == null) { pivotZ = new DoublePropertyBase() { @Override public void invalidated() { transformChanged(); } @Override public Object getBean() { return Scale.this; } @Override public String getName() { return "pivotZ"; } }; } return pivotZ; } /* ************************************************************************* * * * Element getters * * * **************************************************************************/ @Override public double getMxx() { return getX(); } @Override public double getMyy() { return getY(); } @Override public double getMzz() { return getZ(); } @Override public double getTx() { return (1-getX()) * getPivotX(); } @Override public double getTy() { return (1-getY()) * getPivotY(); } @Override public double getTz() { return (1-getZ()) * getPivotZ(); } /* ************************************************************************* * * * State getters * * * **************************************************************************/ @Override boolean computeIs2D() { return getZ() == 1.0; } @Override boolean computeIsIdentity() { return getX() == 1.0 && getY() == 1.0 && getZ() == 1.0; } /* ************************************************************************* * * * Array getters * * * **************************************************************************/ @Override void fill2DArray(double[] array) { final double sx = getX(); final double sy = getY(); array[0] = sx; array[1] = 0.0; array[2] = (1-sx) * getPivotX(); array[3] = 0.0; array[4] = sy; array[5] = (1-sy) * getPivotY(); } @Override void fill3DArray(double[] array) { final double sx = getX(); final double sy = getY(); final double sz = getZ(); array[0] = sx; array[1] = 0.0; array[2] = 0.0; array[3] = (1-sx) * getPivotX(); array[4] = 0.0; array[5] = sy; array[6] = 0.0; array[7] = (1-sy) * getPivotY(); array[8] = 0.0; array[9] = 0.0; array[10] = sz; array[11] = (1-sz) * getPivotZ(); } /* ************************************************************************* * * * Transform creators * * * **************************************************************************/ @Override public Transform createConcatenation(Transform transform) { final double sx = getX(); final double sy = getY(); final double sz = getZ(); if (transform instanceof Scale) { final Scale other = (Scale) transform; if (other.getPivotX() == getPivotX() && other.getPivotY() == getPivotY() && other.getPivotZ() == getPivotZ()) { return new Scale( sx * other.getX(), sy * other.getY(), sz * other.getZ(), getPivotX(), getPivotY(), getPivotZ()); } } if (transform instanceof Translate) { final Translate t = (Translate) transform; final double tx = t.getX(); final double ty = t.getY(); final double tz = t.getZ(); if ((tx == 0.0 || (sx != 1.0 && sx != 0.0)) && (ty == 0.0 || (sy != 1.0 && sy != 0.0)) && (tz == 0.0 || (sz != 1.0 && sz != 0.0))) { return new Scale( sx, sy, sz, (sx != 1.0 ? sx * tx / (1 - sx) : 0) + getPivotX(), (sy != 1.0 ? sy * ty / (1 - sy) : 0) + getPivotY(), (sz != 1.0 ? sz * tz / (1 - sz) : 0) + getPivotZ()); } } if (transform instanceof Affine) { Affine a = (Affine) transform.clone(); a.prepend(this); return a; } final double txx = transform.getMxx(); final double txy = transform.getMxy(); final double txz = transform.getMxz(); final double ttx = transform.getTx(); final double tyx = transform.getMyx(); final double tyy = transform.getMyy(); final double tyz = transform.getMyz(); final double tty = transform.getTy(); final double tzx = transform.getMzx(); final double tzy = transform.getMzy(); final double tzz = transform.getMzz(); final double ttz = transform.getTz(); return new Affine( sx * txx, sx * txy, sx * txz, sx * ttx + (1 - sx) * getPivotX(), sy * tyx, sy * tyy, sy * tyz, sy * tty + (1 - sy) * getPivotY(), sz * tzx, sz * tzy, sz * tzz, sz * ttz + (1 - sz) * getPivotZ()); } @Override public Scale createInverse() throws NonInvertibleTransformException { final double sx = getX(); final double sy = getY(); final double sz = getZ(); if (sx == 0.0 || sy == 0.0 || sz == 0.0) { throw new NonInvertibleTransformException( "Zero scale is not invertible"); } return new Scale(1.0 / sx, 1.0 / sy, 1.0 / sz, getPivotX(), getPivotY(), getPivotZ()); } @Override public Scale clone() { return new Scale(getX(), getY(), getZ(), getPivotX(), getPivotY(), getPivotZ()); } /* ************************************************************************* * * * Transform, Inverse Transform * * * **************************************************************************/ @Override public Point2D transform(double x, double y) { ensureCanTransform2DPoint(); final double mxx = getX(); final double myy = getY(); return new Point2D( mxx * x + (1 - mxx) * getPivotX(), myy * y + (1 - myy) * getPivotY()); } @Override public Point3D transform(double x, double y, double z) { final double mxx = getX(); final double myy = getY(); final double mzz = getZ(); return new Point3D( mxx * x + (1 - mxx) * getPivotX(), myy * y + (1 - myy) * getPivotY(), mzz * z + (1 - mzz) * getPivotZ()); } @Override void transform2DPointsImpl(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts) { final double xx = getX(); final double yy = getY(); final double px = getPivotX(); final double py = getPivotY(); while (--numPts >= 0) { final double x = srcPts[srcOff++]; final double y = srcPts[srcOff++]; dstPts[dstOff++] = xx * x + (1 - xx) * px; dstPts[dstOff++] = yy * y + (1 - yy) * py; } } @Override void transform3DPointsImpl(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts) { final double xx = getX(); final double yy = getY(); final double zz = getZ(); final double px = getPivotX(); final double py = getPivotY(); final double pz = getPivotZ(); while (--numPts >= 0) { dstPts[dstOff++] = xx * srcPts[srcOff++] + (1 - xx) * px; dstPts[dstOff++] = yy * srcPts[srcOff++] + (1 - yy) * py; dstPts[dstOff++] = zz * srcPts[srcOff++] + (1 - zz) * pz; } } @Override public Point2D deltaTransform(double x, double y) { ensureCanTransform2DPoint(); return new Point2D( getX() * x, getY() * y); } @Override public Point3D deltaTransform(double x, double y, double z) { return new Point3D( getX() * x, getY() * y, getZ() * z); } @Override public Point2D inverseTransform(double x, double y) throws NonInvertibleTransformException { ensureCanTransform2DPoint(); final double sx = getX(); final double sy = getY(); if (sx == 0.0 || sy == 0.0) { throw new NonInvertibleTransformException( "Zero scale is not invertible"); } final double mxx = 1.0 / sx; final double myy = 1.0 / sy; return new Point2D( mxx * x + (1 - mxx) * getPivotX(), myy * y + (1 - myy) * getPivotY()); } @Override public Point3D inverseTransform(double x, double y, double z) throws NonInvertibleTransformException { final double sx = getX(); final double sy = getY(); final double sz = getZ(); if (sx == 0.0 || sy == 0.0 || sz == 0.0) { throw new NonInvertibleTransformException( "Zero scale is not invertible"); } final double mxx = 1.0 / sx; final double myy = 1.0 / sy; final double mzz = 1.0 / sz; return new Point3D( mxx * x + (1 - mxx) * getPivotX(), myy * y + (1 - myy) * getPivotY(), mzz * z + (1 - mzz) * getPivotZ()); } @Override void inverseTransform2DPointsImpl(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts) throws NonInvertibleTransformException { final double sx = getX(); final double sy = getY(); if (sx == 0.0 || sy == 0.0) { throw new NonInvertibleTransformException( "Zero scale is not invertible"); } final double xx = 1.0 / sx; final double yy = 1.0 / sy; final double px = getPivotX(); final double py = getPivotY(); while (--numPts >= 0) { dstPts[dstOff++] = xx * srcPts[srcOff++] + (1 - xx) * px; dstPts[dstOff++] = yy * srcPts[srcOff++] + (1 - yy) * py; } } @Override void inverseTransform3DPointsImpl(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts) throws NonInvertibleTransformException { final double sx = getX(); final double sy = getY(); final double sz = getZ(); if (sx == 0.0 || sy == 0.0 || sz == 0.0) { throw new NonInvertibleTransformException( "Zero scale is not invertible"); } final double xx = 1.0 / sx; final double yy = 1.0 / sy; final double zz = 1.0 / sz; final double px = getPivotX(); final double py = getPivotY(); final double pz = getPivotZ(); while (--numPts >= 0) { dstPts[dstOff++] = xx * srcPts[srcOff++] + (1 - xx) * px; dstPts[dstOff++] = yy * srcPts[srcOff++] + (1 - yy) * py; dstPts[dstOff++] = zz * srcPts[srcOff++] + (1 - zz) * pz; } } @Override public Point2D inverseDeltaTransform(double x, double y) throws NonInvertibleTransformException { ensureCanTransform2DPoint(); final double sx = getX(); final double sy = getY(); if (sx == 0.0 || sy == 0.0) { throw new NonInvertibleTransformException( "Zero scale is not invertible"); } return new Point2D( (1.0 / sx) * x, (1.0 / sy) * y); } @Override public Point3D inverseDeltaTransform(double x, double y, double z) throws NonInvertibleTransformException { final double sx = getX(); final double sy = getY(); final double sz = getZ(); if (sx == 0.0 || sy == 0.0 || sz == 0.0) { throw new NonInvertibleTransformException( "Zero scale is not invertible"); } return new Point3D( (1.0 / sx) * x, (1.0 / sy) * y, (1.0 / sz) * z); } /* ************************************************************************* * * * Other API * * * **************************************************************************/ /** * Returns a string representation of this {@code Scale} object. * @return a string representation of this {@code Scale} object. */ @Override public String toString() { final StringBuilder sb = new StringBuilder("Scale ["); sb.append("x=").append(getX()); sb.append(", y=").append(getY()); sb.append(", z=").append(getZ()); sb.append(", pivotX=").append(getPivotX()); sb.append(", pivotY=").append(getPivotY()); sb.append(", pivotZ=").append(getPivotZ()); return sb.append("]").toString(); } /* ************************************************************************* * * * Internal implementation stuff * * * **************************************************************************/ @Override void apply(final Affine3D trans) { if (getPivotX() != 0 || getPivotY() != 0 || getPivotZ() != 0) { trans.translate(getPivotX(), getPivotY(), getPivotZ()); trans.scale(getX(), getY(), getZ()); trans.translate(-getPivotX(), -getPivotY(), -getPivotZ()); } else { trans.scale(getX(), getY(), getZ()); } } @Override BaseTransform derive(BaseTransform trans) { if (isIdentity()) { return trans; } if (getPivotX() != 0 || getPivotY() != 0 || getPivotZ() != 0) { trans = trans.deriveWithTranslation(getPivotX(), getPivotY(), getPivotZ()); trans = trans.deriveWithScale(getX(), getY(), getZ()); return trans.deriveWithTranslation(-getPivotX(), -getPivotY(), -getPivotZ()); } else { return trans.deriveWithScale(getX(), getY(), getZ()); } } @Override void validate() { getX(); getPivotX(); getY(); getPivotY(); getZ(); getPivotZ(); } @Override void appendTo(Affine a) { a.appendScale(getX(), getY(), getZ(), getPivotX(), getPivotY(), getPivotZ()); } @Override void prependTo(Affine a) { a.prependScale(getX(), getY(), getZ(), getPivotX(), getPivotY(), getPivotZ()); } }