View Javadoc
1   /*
2    * Copyright (c) 2002-2025 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit.javascript.host.dom;
16  
17  import org.htmlunit.corejs.javascript.Context;
18  import org.htmlunit.corejs.javascript.Function;
19  import org.htmlunit.corejs.javascript.FunctionObject;
20  import org.htmlunit.corejs.javascript.NativeArray;
21  import org.htmlunit.corejs.javascript.Scriptable;
22  import org.htmlunit.corejs.javascript.typedarrays.NativeFloat32Array;
23  import org.htmlunit.corejs.javascript.typedarrays.NativeFloat64Array;
24  import org.htmlunit.javascript.HtmlUnitScriptable;
25  import org.htmlunit.javascript.JavaScriptEngine;
26  import org.htmlunit.javascript.configuration.JsxClass;
27  import org.htmlunit.javascript.configuration.JsxConstructor;
28  import org.htmlunit.javascript.configuration.JsxFunction;
29  import org.htmlunit.javascript.configuration.JsxGetter;
30  import org.htmlunit.javascript.host.Window;
31  
32  /**
33   * A JavaScript object for {@code DOMMatrixReadOnly}.
34   *
35   * @author Ahmed Ashour
36   * @author Ronald Brill
37   */
38  @JsxClass
39  public class DOMMatrixReadOnly extends HtmlUnitScriptable {
40  
41      private double m11_;
42      private double m12_;
43      private double m13_;
44      private double m14_;
45  
46      private double m21_;
47      private double m22_;
48      private double m23_;
49      private double m24_;
50  
51      private double m31_;
52      private double m32_;
53      private double m33_;
54      private double m34_;
55  
56      private double m41_;
57      private double m42_;
58      private double m43_;
59      private double m44_;
60  
61      private boolean is2D_;
62  
63      /**
64       * Ctor.
65       */
66      public DOMMatrixReadOnly() {
67          m11_ = 1;
68          m12_ = 0;
69          m13_ = 0;
70          m14_ = 0;
71  
72          m21_ = 0;
73          m22_ = 1;
74          m23_ = 0;
75          m24_ = 0;
76  
77          m31_ = 0;
78          m32_ = 0;
79          m33_ = 1;
80          m34_ = 0;
81  
82          m41_ = 0;
83          m42_ = 0;
84          m43_ = 0;
85          m44_ = 1;
86  
87          is2D_ = true;
88      }
89  
90      /**
91       * JavaScript constructor.
92       * @param cx the current context
93       * @param scope the scope
94       * @param args the arguments to the WebSocket constructor
95       * @param ctorObj the function object
96       * @param inNewExpr Is new or not
97       * @return the java object to allow JavaScript to access
98       */
99      @JsxConstructor
100     public static DOMMatrixReadOnly jsConstructor(final Context cx, final Scriptable scope,
101             final Object[] args, final Function ctorObj, final boolean inNewExpr) {
102 
103         final DOMMatrixReadOnly matrix = new DOMMatrixReadOnly();
104         matrix.init(args, ctorObj);
105         return matrix;
106     }
107 
108     protected void init(final Object[] args, final Function ctorObj) {
109         final Window window = getWindow(ctorObj);
110         setParentScope(window);
111         setPrototype(((FunctionObject) ctorObj).getClassPrototype());
112 
113         if (args.length == 0 || JavaScriptEngine.isUndefined(args[0])) {
114             return;
115         }
116 
117         if (args[0] instanceof NativeArray) {
118             final NativeArray arrayArgs = (NativeArray) args[0];
119             if (arrayArgs.getLength() == 6) {
120                 m11_ = JavaScriptEngine.toNumber(arrayArgs.get(0));
121                 m12_ = JavaScriptEngine.toNumber(arrayArgs.get(1));
122 
123                 m21_ = JavaScriptEngine.toNumber(arrayArgs.get(2));
124                 m22_ = JavaScriptEngine.toNumber(arrayArgs.get(3));
125 
126                 m41_ = JavaScriptEngine.toNumber(arrayArgs.get(4));
127                 m42_ = JavaScriptEngine.toNumber(arrayArgs.get(5));
128 
129                 is2D_ = true;
130                 return;
131             }
132 
133             if (arrayArgs.getLength() == 16) {
134                 m11_ = JavaScriptEngine.toNumber(arrayArgs.get(0));
135                 m12_ = JavaScriptEngine.toNumber(arrayArgs.get(1));
136                 m13_ = JavaScriptEngine.toNumber(arrayArgs.get(2));
137                 m14_ = JavaScriptEngine.toNumber(arrayArgs.get(3));
138 
139                 m21_ = JavaScriptEngine.toNumber(arrayArgs.get(4));
140                 m22_ = JavaScriptEngine.toNumber(arrayArgs.get(5));
141                 m23_ = JavaScriptEngine.toNumber(arrayArgs.get(6));
142                 m24_ = JavaScriptEngine.toNumber(arrayArgs.get(7));
143 
144                 m31_ = JavaScriptEngine.toNumber(arrayArgs.get(8));
145                 m32_ = JavaScriptEngine.toNumber(arrayArgs.get(9));
146                 m33_ = JavaScriptEngine.toNumber(arrayArgs.get(10));
147                 m34_ = JavaScriptEngine.toNumber(arrayArgs.get(11));
148 
149                 m41_ = JavaScriptEngine.toNumber(arrayArgs.get(12));
150                 m42_ = JavaScriptEngine.toNumber(arrayArgs.get(13));
151                 m43_ = JavaScriptEngine.toNumber(arrayArgs.get(14));
152                 m44_ = JavaScriptEngine.toNumber(arrayArgs.get(15));
153 
154                 is2D_ = false;
155                 return;
156             }
157 
158             throw JavaScriptEngine.typeError("DOMMatrixReadOnly constructor: Matrix init sequence must have "
159                     + "a length of 6 or 16 (actual value: " + arrayArgs.getLength() + ")");
160         }
161 
162         throw JavaScriptEngine.asJavaScriptException(
163                 window,
164                 "An invalid or illegal string was specified",
165                 DOMException.SYNTAX_ERR);
166     }
167 
168     /**
169      * @return m11
170      */
171     @JsxGetter
172     public double getM11() {
173         return m11_;
174     }
175 
176     /**
177      * @param m11 the new value
178      */
179     public void setM11(final double m11) {
180         m11_ = m11;
181     }
182 
183     /**
184      * @return a
185      */
186     @JsxGetter
187     public double getA() {
188         return m11_;
189     }
190 
191     /**
192      * @return m12
193      */
194     @JsxGetter
195     public double getM12() {
196         return m12_;
197     }
198 
199     /**
200      * @param m12 the new value
201      */
202     public void setM12(final double m12) {
203         m12_ = m12;
204     }
205 
206     /**
207      * @return b
208      */
209     @JsxGetter
210     public double getB() {
211         return m12_;
212     }
213 
214     /**
215      * @return m13
216      */
217     @JsxGetter
218     public double getM13() {
219         return m13_;
220     }
221 
222     /**
223      * @param m13 the new value
224      */
225     public void setM13(final double m13) {
226         m13_ = m13;
227     }
228 
229     /**
230      * @return m14
231      */
232     @JsxGetter
233     public double getM14() {
234         return m14_;
235     }
236 
237     /**
238      * @param m14 the new value
239      */
240     public void setM14(final double m14) {
241         m14_ = m14;
242     }
243 
244     /**
245      * @return m21
246      */
247     @JsxGetter
248     public double getM21() {
249         return m21_;
250     }
251 
252     /**
253      * @param m21 the new value
254      */
255     public void setM21(final double m21) {
256         m21_ = m21;
257     }
258 
259     /**
260      * @return c
261      */
262     @JsxGetter
263     public double getC() {
264         return m21_;
265     }
266 
267     /**
268      * @return m22
269      */
270     @JsxGetter
271     public double getM22() {
272         return m22_;
273     }
274 
275     /**
276      * @param m22 the new value
277      */
278     public void setM22(final double m22) {
279         m22_ = m22;
280     }
281 
282     /**
283      * @return d
284      */
285     @JsxGetter
286     public double getD() {
287         return m22_;
288     }
289 
290     /**
291      * @return m23
292      */
293     @JsxGetter
294     public double getM23() {
295         return m23_;
296     }
297 
298     /**
299      * @param m23 the new value
300      */
301     public void setM23(final double m23) {
302         m23_ = m23;
303     }
304 
305     /**
306      * @return m24
307      */
308     @JsxGetter
309     public double getM24() {
310         return m24_;
311     }
312 
313     /**
314      * @param m24 the new value
315      */
316     public void setM24(final double m24) {
317         m24_ = m24;
318     }
319 
320     /**
321      * @return m31
322      */
323     @JsxGetter
324     public double getM31() {
325         return m31_;
326     }
327 
328     /**
329      * @param m31 the new value
330      */
331     public void setM31(final double m31) {
332         m31_ = m31;
333     }
334 
335     /**
336      * @return m32
337      */
338     @JsxGetter
339     public double getM32() {
340         return m32_;
341     }
342 
343     /**
344      * @param m32 the new value
345      */
346     public void setM32(final double m32) {
347         m32_ = m32;
348     }
349 
350     /**
351      * @return m33
352      */
353     @JsxGetter
354     public double getM33() {
355         return m33_;
356     }
357 
358     /**
359      * @param m33 the new value
360      */
361     public void setM33(final double m33) {
362         m33_ = m33;
363     }
364 
365     /**
366      * @return m34
367      */
368     @JsxGetter
369     public double getM34() {
370         return m34_;
371     }
372 
373     /**
374      * @param m34 the new value
375      */
376     public void setM34(final double m34) {
377         m34_ = m34;
378     }
379 
380     /**
381      * @return m41
382      */
383     @JsxGetter
384     public double getM41() {
385         return m41_;
386     }
387 
388     /**
389      * @param m41 the new value
390      */
391     public void setM41(final double m41) {
392         m41_ = m41;
393     }
394 
395     /**
396      * @return e
397      */
398     @JsxGetter
399     public double getE() {
400         return m41_;
401     }
402 
403     /**
404      * @return m42
405      */
406     @JsxGetter
407     public double getM42() {
408         return m42_;
409     }
410 
411     /**
412      * @param m42 the new value
413      */
414     public void setM42(final double m42) {
415         m42_ = m42;
416     }
417 
418     /**
419      * @return f
420      */
421     @JsxGetter
422     public double getF() {
423         return m42_;
424     }
425 
426     /**
427      * @return m43
428      */
429     @JsxGetter
430     public double getM43() {
431         return m43_;
432     }
433 
434     /**
435      * @param m43 the new value
436      */
437     public void setM43(final double m43) {
438         m43_ = m43;
439     }
440 
441     /**
442      * @return m44
443      */
444     @JsxGetter
445     public double getM44() {
446         return m44_;
447     }
448 
449     /**
450      * @param m44 the new value
451      */
452     public void setM44(final double m44) {
453         m44_ = m44;
454     }
455 
456     /**
457      * @return is2d
458      */
459     @JsxGetter
460     public boolean isIs2D() {
461         return is2D_;
462     }
463 
464     /**
465      * @param is2D the new value
466      */
467     public void setIs2D(final boolean is2D) {
468         is2D_ = is2D;
469     }
470 
471     /**
472      * @return a new matrix being the result of the original matrix flipped about the x-axis.
473      *     This is equivalent to multiplying the matrix by DOMMatrix(-1, 0, 0, 1, 0, 0).
474      *     The original matrix is not modified.
475      */
476     @JsxFunction
477     public DOMMatrix flipX() {
478         final DOMMatrix matrix = new DOMMatrix();
479         final Window window = getWindow();
480         matrix.setParentScope(window);
481         matrix.setPrototype(window.getPrototype(DOMMatrix.class));
482 
483         matrix.setM11(-m11_);
484         matrix.setM12(-m12_);
485         matrix.setM13(-m13_);
486         matrix.setM14(-m14_);
487 
488         matrix.setM21(m21_);
489         matrix.setM22(m22_);
490         matrix.setM23(m23_);
491         matrix.setM24(m24_);
492 
493         matrix.setM31(m31_);
494         matrix.setM32(m32_);
495         matrix.setM33(m33_);
496         matrix.setM34(m34_);
497 
498         matrix.setM41(m41_);
499         matrix.setM42(m42_);
500         matrix.setM43(m43_);
501         matrix.setM44(m44_);
502 
503         matrix.setIs2D(is2D_);
504         return matrix;
505     }
506 
507     /**
508      * @return a new matrix being the result of the original matrix flipped about the x-axis.
509      *     This is equivalent to multiplying the matrix by DOMMatrix(1, 0, 0, -1, 0, 0).
510      *     The original matrix is not modified.
511      */
512     @JsxFunction
513     public DOMMatrix flipY() {
514         final DOMMatrix matrix = new DOMMatrix();
515         final Window window = getWindow();
516         matrix.setParentScope(window);
517         matrix.setPrototype(window.getPrototype(DOMMatrix.class));
518 
519         matrix.setM11(m11_);
520         matrix.setM12(m12_);
521         matrix.setM13(m13_);
522         matrix.setM14(m14_);
523 
524         matrix.setM21(-m21_);
525         matrix.setM22(-m22_);
526         matrix.setM23(-m23_);
527         matrix.setM24(-m24_);
528 
529         matrix.setM31(m31_);
530         matrix.setM32(m32_);
531         matrix.setM33(m33_);
532         matrix.setM34(m34_);
533 
534         matrix.setM41(m41_);
535         matrix.setM42(m42_);
536         matrix.setM43(m43_);
537         matrix.setM44(m44_);
538 
539         matrix.setIs2D(is2D_);
540         return matrix;
541     }
542 
543     /**
544      * @return new matrix which is the inverse of the original matrix.
545      *     If the matrix cannot be inverted, the new matrix's components are all set to NaN
546      *     and its is2D property is set to false. The original matrix is not changed.
547      */
548     @JsxFunction
549     public DOMMatrix inverse() {
550         final DOMMatrix matrix = new DOMMatrix();
551         final Window window = getWindow();
552         matrix.setParentScope(window);
553         matrix.setPrototype(window.getPrototype(DOMMatrix.class));
554 
555         matrix.setM11(m11_);
556         matrix.setM12(m12_);
557         matrix.setM13(m13_);
558         matrix.setM14(m14_);
559 
560         matrix.setM21(m21_);
561         matrix.setM22(m22_);
562         matrix.setM23(m23_);
563         matrix.setM24(m24_);
564 
565         matrix.setM31(m31_);
566         matrix.setM32(m32_);
567         matrix.setM33(m33_);
568         matrix.setM34(m34_);
569 
570         matrix.setM41(m41_);
571         matrix.setM42(m42_);
572         matrix.setM43(m43_);
573         matrix.setM44(m44_);
574 
575         matrix.setIs2D(is2D_);
576         return matrix.invertSelf();
577     }
578 
579     /**
580      * @param other the matrix to multiply with this matrix
581      * @return a new matrix which is the dot product of the matrix and the otherMatrix parameter.
582      *     If otherMatrix is omitted, the matrix is multiplied by a matrix in which every element
583      *     is 0 except the bottom-right corner and the element immediately above
584      *     and to its left: m33 and m34. These have the default value of 1.
585      *     The original matrix is not modified.
586      */
587     @JsxFunction
588     public DOMMatrix multiply(final Object other) {
589         final DOMMatrix result = new DOMMatrix();
590         final Window window = getWindow();
591         result.setParentScope(window);
592         result.setPrototype(window.getPrototype(DOMMatrix.class));
593 
594         // Handle null/undefined by treating as identity matrix
595         if (other == null || JavaScriptEngine.isUndefined(other)) {
596             return result;
597         }
598 
599         if (!(other instanceof DOMMatrixReadOnly)) {
600             throw JavaScriptEngine.typeError("Failed to execute 'multiply' on 'DOMMatrixReadOnly': "
601                     + "parameter 1 is not of type 'DOMMatrixReadOnly'.");
602         }
603 
604         final DOMMatrixReadOnly otherMatrix = (DOMMatrixReadOnly) other;
605 
606         // Matrix multiplication: result = this * otherMatrix
607         // Standard matrix multiplication formula: C[i][j] = sum(A[i][k] * B[k][j])
608         result.setIs2D(is2D_ && otherMatrix.is2D_);
609 
610         result.setM11(m11_ * otherMatrix.m11_
611                 + m21_ * otherMatrix.m12_
612                 + m31_ * otherMatrix.m13_
613                 + m41_ * otherMatrix.m14_);
614         result.setM12(m12_ * otherMatrix.m11_
615                 + m22_ * otherMatrix.m12_
616                 + m32_ * otherMatrix.m13_
617                 + m42_ * otherMatrix.m14_);
618         if (!result.isIs2D()) {
619             result.setM13(m13_ * otherMatrix.m11_
620                     + m23_ * otherMatrix.m12_
621                     + m33_ * otherMatrix.m13_
622                     + m43_ * otherMatrix.m14_);
623             result.setM14(m14_ * otherMatrix.m11_
624                     + m24_ * otherMatrix.m12_
625                     + m34_ * otherMatrix.m13_
626                     + m44_ * otherMatrix.m14_);
627         }
628 
629         result.setM21(m11_ * otherMatrix.m21_
630                 + m21_ * otherMatrix.m22_
631                 + m31_ * otherMatrix.m23_
632                 + m41_ * otherMatrix.m24_);
633         result.setM22(m12_ * otherMatrix.m21_
634                 + m22_ * otherMatrix.m22_
635                 + m32_ * otherMatrix.m23_
636                 + m42_ * otherMatrix.m24_);
637         if (!result.isIs2D()) {
638             result.setM23(m13_ * otherMatrix.m21_
639                     + m23_ * otherMatrix.m22_
640                     + m33_ * otherMatrix.m23_
641                     + m43_ * otherMatrix.m24_);
642             result.setM24(m14_ * otherMatrix.m21_
643                     + m24_ * otherMatrix.m22_
644                     + m34_ * otherMatrix.m23_
645                     + m44_ * otherMatrix.m24_);
646         }
647 
648         if (!result.isIs2D()) {
649             result.setM31(m11_ * otherMatrix.m31_
650                     + m21_ * otherMatrix.m32_
651                     + m31_ * otherMatrix.m33_
652                     + m41_ * otherMatrix.m34_);
653             result.setM32(m12_ * otherMatrix.m31_
654                     + m22_ * otherMatrix.m32_
655                     + m32_ * otherMatrix.m33_
656                     + m42_ * otherMatrix.m34_);
657             result.setM33(m13_ * otherMatrix.m31_
658                     + m23_ * otherMatrix.m32_
659                     + m33_ * otherMatrix.m33_
660                     + m43_ * otherMatrix.m34_);
661             result.setM34(m14_ * otherMatrix.m31_
662                     + m24_ * otherMatrix.m32_
663                     + m34_ * otherMatrix.m33_
664                     + m44_ * otherMatrix.m34_);
665         }
666 
667         result.setM41(m11_ * otherMatrix.m41_
668                 + m21_ * otherMatrix.m42_
669                 + m31_ * otherMatrix.m43_
670                 + m41_ * otherMatrix.m44_);
671         result.setM42(m12_ * otherMatrix.m41_
672                 + m22_ * otherMatrix.m42_
673                 + m32_ * otherMatrix.m43_
674                 + m42_ * otherMatrix.m44_);
675         if (!result.isIs2D()) {
676             result.setM43(m13_ * otherMatrix.m41_
677                     + m23_ * otherMatrix.m42_
678                     + m33_ * otherMatrix.m43_
679                     + m43_ * otherMatrix.m44_);
680             result.setM44(m14_ * otherMatrix.m41_
681                     + m24_ * otherMatrix.m42_
682                     + m34_ * otherMatrix.m43_
683                     + m44_ * otherMatrix.m44_);
684         }
685 
686         return result;
687     }
688 
689     /**
690      * @param rotZ the rotation angle in degrees. If omitted, defaults to 0.
691      * @return a new matrix which is the result of the original matrix rotated by the specified angle.
692      *     The rotation is applied around the origin (0, 0) in the 2D plane.
693      *     The original matrix is not modified.
694      */
695     @JsxFunction
696     public DOMMatrixReadOnly rotate(final Object rotZ) {
697         final DOMMatrix result = new DOMMatrix();
698         final Window window = getWindow();
699         result.setParentScope(window);
700         result.setPrototype(window.getPrototype(DOMMatrix.class));
701 
702         // Handle undefined/null/missing parameter - default to 0
703         double angleInDegrees = 0;
704         if (rotZ != null && !JavaScriptEngine.isUndefined(rotZ)) {
705             angleInDegrees = JavaScriptEngine.toNumber(rotZ);
706         }
707 
708         // Convert degrees to radians
709         final double angleInRadians = Math.toRadians(angleInDegrees);
710         final double cos = Math.cos(angleInRadians);
711         final double sin = Math.sin(angleInRadians);
712 
713         // Create rotation matrix:
714         // [cos  -sin  0  0]
715         // [sin   cos  0  0]
716         // [ 0     0   1  0]
717         // [ 0     0   0  1]
718 
719         // For 2D matrices, only apply to the 2x2 part
720         if (is2D_) {
721             // Matrix multiplication: result = this * rotation
722             result.setM11(m11_ * cos + m21_ * sin);
723             result.setM12(m12_ * cos + m22_ * sin);
724 
725             result.setM21(m11_ * (-sin) + m21_ * cos);
726             result.setM22(m12_ * (-sin) + m22_ * cos);
727 
728             result.setM41(m41_);
729             result.setM42(m42_);
730 
731             result.setIs2D(true);
732         }
733         else {
734             // For 3D matrices, apply rotation to all relevant components
735             result.setM11(m11_ * cos + m21_ * sin);
736             result.setM12(m12_ * cos + m22_ * sin);
737             result.setM13(m13_ * cos + m23_ * sin);
738             result.setM14(m14_ * cos + m24_ * sin);
739 
740             result.setM21(m11_ * (-sin) + m21_ * cos);
741             result.setM22(m12_ * (-sin) + m22_ * cos);
742             result.setM23(m13_ * (-sin) + m23_ * cos);
743             result.setM24(m14_ * (-sin) + m24_ * cos);
744 
745             result.setM31(m31_);
746             result.setM32(m32_);
747             result.setM33(m33_);
748             result.setM34(m34_);
749 
750             result.setM41(m41_);
751             result.setM42(m42_);
752             result.setM43(m43_);
753             result.setM44(m44_);
754 
755             result.setIs2D(false);
756         }
757 
758         return result;
759     }
760 
761     /**
762      * Rotates the matrix by a given angle around the specified axis.
763      *
764      * @param xObj the x component of the axis
765      * @param yObj the y component of the axis
766      * @param zObj the z component of the axis
767      * @param alphaObj the rotation angle in degrees
768      * @return a new matrix which is the result of the original matrix rotated by the specified axis and angle.
769      */
770     @JsxFunction
771     public DOMMatrixReadOnly rotateAxisAngle(
772                 final Object xObj, final Object yObj, final Object zObj, final Object alphaObj) {
773         // Default values
774         double x = 0;
775         double y = 0;
776         double z = 1;
777         double alpha = 0;
778         if (xObj != null && !JavaScriptEngine.isUndefined(xObj)) {
779             x = JavaScriptEngine.toNumber(xObj);
780         }
781         if (yObj != null && !JavaScriptEngine.isUndefined(yObj)) {
782             y = JavaScriptEngine.toNumber(yObj);
783         }
784         if (zObj != null && !JavaScriptEngine.isUndefined(zObj)) {
785             z = JavaScriptEngine.toNumber(zObj);
786         }
787         if (alphaObj != null && !JavaScriptEngine.isUndefined(alphaObj)) {
788             alpha = JavaScriptEngine.toNumber(alphaObj);
789         }
790 
791         // If axis is (0,0,0), throw TypeError per spec
792         if (x == 0 && y == 0 && z == 0) {
793             final DOMMatrix result = new DOMMatrix();
794             final Window window = getWindow();
795             result.setParentScope(window);
796             result.setPrototype(window.getPrototype(DOMMatrix.class));
797             return result;
798         }
799 
800         // Normalize the axis
801         final double length = Math.sqrt(x * x + y * y + z * z);
802         x /= length;
803         y /= length;
804         z /= length;
805 
806         // Convert angle to radians
807         final double angle2 = Math.toRadians(alpha) / 2;
808 
809         // Compute rotation matrix
810         final double sc = Math.sin(angle2) * Math.cos(angle2);
811         final double sq = Math.pow(Math.sin(angle2), 2);
812 
813         final double x2 = x * x;
814         final double y2 = y * y;
815         final double z2 = z * z;
816 
817         final DOMMatrix rot = new DOMMatrix();
818 
819         rot.setM11(1 - 2 * (y2 + z2) * sq);
820         rot.setM12(2 * (x * y * sq + z * sc));
821         rot.setM13(2 * (x * z * sq - y * sc));
822         rot.setM14(0);
823 
824         rot.setM21(2 * (x * y * sq - z * sc));
825         rot.setM22(1 - 2 * (x2 + z2) * sq);
826         rot.setM23(2 * (y * z * sq + x * sc));
827         rot.setM24(0);
828 
829         rot.setM31(2 * (x * z * sq + y * sc));
830         rot.setM32(2 * (y * z * sq - x * sc));
831         rot.setM33(1 - 2 * (x2 + y2) * sq);
832         rot.setM34(0);
833 
834         rot.setM41(0);
835         rot.setM42(0);
836         rot.setM43(0);
837         rot.setM44(1);
838 
839         rot.setIs2D(false);
840 
841         // Multiply this * rot
842         final DOMMatrix multiplied = multiply(rot);
843         multiplied.setIs2D(is2D_ && x == 0 && y == 0);
844         return multiplied;
845     }
846 
847     /**
848      * @param alphaObj the angle, in degrees, by which to skew the matrix along the x-axis
849      * @return returns a new DOMMatrix created by applying the specified skew transformation
850      *     to the source matrix along its x-axis. The original matrix is not modified.
851      */
852     @JsxFunction
853     public DOMMatrixReadOnly skewX(final Object alphaObj) {
854         // Default values
855         double alpha = 0;
856         if (alphaObj != null && !JavaScriptEngine.isUndefined(alphaObj)) {
857             alpha = JavaScriptEngine.toNumber(alphaObj);
858         }
859 
860         // Convert angle to radians
861         final double angle = Math.toRadians(alpha);
862 
863         // Compute rotation matrix
864         final DOMMatrix rot = new DOMMatrix();
865 
866         rot.setM21(Math.tan(angle));
867 
868         // Multiply this * rot
869         final DOMMatrix multiplied = multiply(rot);
870         return multiplied;
871     }
872 
873     /**
874      * @param alphaObj the angle, in degrees, by which to skew the matrix along the y-axis
875      * @return returns a new DOMMatrix created by applying the specified skew transformation
876      *     to the source matrix along its x-axis. The original matrix is not modified.
877      */
878     @JsxFunction
879     public DOMMatrixReadOnly skewY(final Object alphaObj) {
880         // Default values
881         double alpha = 0;
882         if (alphaObj != null && !JavaScriptEngine.isUndefined(alphaObj)) {
883             alpha = JavaScriptEngine.toNumber(alphaObj);
884         }
885 
886         // Convert angle to radians
887         final double angle = Math.toRadians(alpha);
888 
889         // Compute rotation matrix
890         final DOMMatrixReadOnly rot = new DOMMatrixReadOnly();
891 
892         rot.m12_ = Math.tan(angle);
893 
894         // Multiply this * rot
895         final DOMMatrixReadOnly multiplied = multiply(rot);
896         return multiplied;
897     }
898 
899     /**
900      * Creates a new matrix being the result of the original matrix with a translation applied.
901      *
902      * @param xObj a number representing the abscissa (x-coordinate) of the translating vector
903      * @param yObj a number representing the ordinate (y-coordinate) of the translating vector
904      * @param zObj A number representing the z component of the translating vector. If not supplied,
905      *        this defaults to 0. If this is anything other than 0, the resulting matrix will be 3D
906      * @return a DOMMatrix containing a new matrix being the result of the matrix being translated
907      *         by the given vector. The original matrix is not modified.
908      *         If a translation is applied about the z-axis, the resulting matrix will be a 4x4 3D matrix.
909      */
910     @JsxFunction
911     public DOMMatrixReadOnly translate(final Object xObj, final Object yObj, final Object zObj) {
912         // Default values
913         double x = 0;
914         double y = 0;
915         double z = 0;
916         if (xObj != null && !JavaScriptEngine.isUndefined(xObj)) {
917             x = JavaScriptEngine.toNumber(xObj);
918         }
919         if (yObj != null && !JavaScriptEngine.isUndefined(yObj)) {
920             y = JavaScriptEngine.toNumber(yObj);
921         }
922         if (zObj != null && !JavaScriptEngine.isUndefined(zObj)) {
923             z = JavaScriptEngine.toNumber(zObj);
924         }
925 
926         final DOMMatrixReadOnly translate = new DOMMatrixReadOnly();
927 
928         translate.m41_ = x;
929         translate.m42_ = y;
930         translate.m43_ = z;
931 
932         translate.is2D_ = false;
933 
934         // Multiply this * rot
935         final DOMMatrix multiplied = multiply(translate);
936         multiplied.setIs2D(is2D_ && (zObj == null || JavaScriptEngine.isUndefined(zObj) || z == 0));
937         return multiplied;
938     }
939 
940     /**
941      * @return a new Float32Array containing all 16 elements
942      *     (m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44)
943      *     which comprise the matrix. The elements are stored into the array
944      *     as single-precision floating-point numbers in column-major (colexographical access, or "colex") order.
945      *     (In other words, down the first column from top to bottom, then the second column, and so forth.)
946      */
947     @JsxFunction
948     public NativeFloat32Array toFloat32Array() {
949         final NativeFloat32Array result =
950                 (NativeFloat32Array) JavaScriptEngine.newObject(getParentScope(), "Float32Array", new Object[] {16});
951 
952         result.setArrayElement(0, m11_);
953         result.setArrayElement(1, m12_);
954         result.setArrayElement(2, m13_);
955         result.setArrayElement(3, m14_);
956 
957         result.setArrayElement(4, m21_);
958         result.setArrayElement(5, m22_);
959         result.setArrayElement(6, m23_);
960         result.setArrayElement(7, m24_);
961 
962         result.setArrayElement(8, m31_);
963         result.setArrayElement(9, m32_);
964         result.setArrayElement(10, m33_);
965         result.setArrayElement(11, m34_);
966 
967         result.setArrayElement(12, m41_);
968         result.setArrayElement(13, m42_);
969         result.setArrayElement(14, m43_);
970         result.setArrayElement(15, m44_);
971 
972         return result;
973     }
974 
975     /**
976      * @return a new Float64Array containing all 16 elements
977      *     (m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44)
978      *     which comprise the matrix. The elements are stored into the array
979      *     as single-precision floating-point numbers in column-major (colexographical access, or "colex") order.
980      *     (In other words, down the first column from top to bottom, then the second column, and so forth.)
981      */
982     @JsxFunction
983     public NativeFloat64Array toFloat64Array() {
984         final NativeFloat64Array result =
985                 (NativeFloat64Array) JavaScriptEngine.newObject(getParentScope(), "Float64Array", new Object[] {16});
986 
987         result.setArrayElement(0, m11_);
988         result.setArrayElement(1, m12_);
989         result.setArrayElement(2, m13_);
990         result.setArrayElement(3, m14_);
991 
992         result.setArrayElement(4, m21_);
993         result.setArrayElement(5, m22_);
994         result.setArrayElement(6, m23_);
995         result.setArrayElement(7, m24_);
996 
997         result.setArrayElement(8, m31_);
998         result.setArrayElement(9, m32_);
999         result.setArrayElement(10, m33_);
1000         result.setArrayElement(11, m34_);
1001 
1002         result.setArrayElement(12, m41_);
1003         result.setArrayElement(13, m42_);
1004         result.setArrayElement(14, m43_);
1005         result.setArrayElement(15, m44_);
1006 
1007         return result;
1008     }
1009 }