```   1 /*
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
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  *
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  *
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 }
```