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