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 }