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