1 module matrix_4d;
2 
3 import Math = math;
4 import MemUtil = mem_util;
5 
6 import matrix_3d;
7 import matrix_4x3d;
8 import matrix_3x2d;
9 
10 import vector_2d;
11 import vector_3d;
12 import vector_4d;
13 
14 import axis_angle_4d;
15 import quaternion_d;
16 
17 /*
18  * The MIT License
19  $!#@$@ Translated by jordan4ibanez
20  *
21  * Copyright (c) 2015-2021 Richard Greenlees
22  *
23  * Permission is hereby granted, free of charge, to any person obtaining a copy
24  * of this software and associated documentation files (the "Software"), to deal
25  * in the Software without restriction, including without limitation the rights
26  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
27  * copies of the Software, and to permit persons to whom the Software is
28  * furnished to do so, subject to the following conditions:
29  *
30  * The above copyright notice and this permission notice shall be included in
31  * all copies or substantial portions of the Software.
32  *
33  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
39  * THE SOFTWARE.
40  */
41 
42 /**
43  * Contains the definition of a 4x4 Matrix of doubles, and associated functions to transform
44  * it. The matrix is column-major to match OpenGL's interpretation, and it looks like this:
45  * <p>
46  *      m00  m10  m20  m30<br>
47  *      m01  m11  m21  m31<br>
48  *      m02  m12  m22  m32<br>
49  *      m03  m13  m23  m33<br>
50  * 
51  * @author Richard Greenlees
52  * @author Kai Burjack
53  */
54 
55 
56 
57 struct Matrix4d {
58 
59     double m00 = 1.0;
60     double m01 = 0.0;
61     double m02 = 0.0;
62     double m03 = 0.0;
63 
64     double m10 = 0.0;
65     double m11 = 1.0;
66     double m12 = 0.0;
67     double m13 = 0.0;
68 
69     double m20 = 0.0;
70     double m21 = 0.0;
71     double m22 = 1.0;
72     double m23 = 0.0;
73 
74     double m30 = 0.0;
75     double m31 = 0.0;
76     double m32 = 0.0;
77     double m33 = 1.0;
78 
79    /**
80 
81     * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)}
82     * identifying the plane with equation <code>x=-1</code> when using the identity matrix.  
83     */
84     static immutable int PLANE_NX = 0;
85     /**
86     * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)}
87     * identifying the plane with equation <code>x=1</code> when using the identity matrix.  
88     */
89     static immutable int PLANE_PX = 1;
90     /**
91     * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)}
92     * identifying the plane with equation <code>y=-1</code> when using the identity matrix.  
93     */
94     static immutable int PLANE_NY = 2;
95     /**
96     * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)}
97     * identifying the plane with equation <code>y=1</code> when using the identity matrix.  
98     */
99     static immutable int PLANE_PY = 3;
100     /**
101     * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)}
102     * identifying the plane with equation <code>z=-1</code> when using the identity matrix.  
103     */
104     static immutable int PLANE_NZ = 4;
105     /**
106     * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)}
107     * identifying the plane with equation <code>z=1</code> when using the identity matrix.  
108     */
109     static immutable int PLANE_PZ = 5;
110     /**
111     * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)}
112     * identifying the corner <code>(-1, -1, -1)</code> when using the identity matrix.
113     */
114     static immutable int CORNER_NXNYNZ = 0;
115     /**
116     * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)}
117     * identifying the corner <code>(1, -1, -1)</code> when using the identity matrix.
118     */
119     static immutable int CORNER_PXNYNZ = 1;
120     /**
121     * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)}
122     * identifying the corner <code>(1, 1, -1)</code> when using the identity matrix.
123     */
124     static immutable int CORNER_PXPYNZ = 2;
125     /**
126     * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)}
127     * identifying the corner <code>(-1, 1, -1)</code> when using the identity matrix.
128     */
129     static immutable int CORNER_NXPYNZ = 3;
130     /**
131     * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)}
132     * identifying the corner <code>(1, -1, 1)</code> when using the identity matrix.
133     */
134     static immutable int CORNER_PXNYPZ = 4;
135     /**
136     * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)}
137     * identifying the corner <code>(-1, -1, 1)</code> when using the identity matrix.
138     */
139     static immutable int CORNER_NXNYPZ = 5;
140     /**
141     * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)}
142     * identifying the corner <code>(-1, 1, 1)</code> when using the identity matrix.
143     */
144     static immutable int CORNER_NXPYPZ = 6;
145     /**
146     * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)}
147     * identifying the corner <code>(1, 1, 1)</code> when using the identity matrix.
148     */
149     static immutable int CORNER_PXPYPZ = 7;
150 
151     /**
152     * Bit returned by {@link #properties()} to indicate that the matrix represents a perspective transformation.
153     */
154     static immutable byte PROPERTY_PERSPECTIVE = 1<<0;
155     /**
156     * Bit returned by {@link #properties()} to indicate that the matrix represents an affine transformation.
157     */
158     static immutable byte PROPERTY_AFFINE = 1<<1;
159     /**
160     * Bit returned by {@link #properties()} to indicate that the matrix represents the identity transformation.
161     */
162     static immutable byte PROPERTY_IDENTITY = 1<<2;
163     /**
164     * Bit returned by {@link #properties()} to indicate that the matrix represents a pure translation transformation.
165     */
166     static immutable byte PROPERTY_TRANSLATION = 1<<3;
167     /**
168     * Bit returned by {@link #properties()} to indicate that the upper-left 3x3 submatrix represents an orthogonal
169     * matrix (i.e. orthonormal basis). For practical reasons, this property also always implies 
170     * {@link #PROPERTY_AFFINE} in this implementation.
171     */
172     static immutable byte PROPERTY_ORTHONORMAL = 1<<4;
173 
174 
175 
176     int properties = PROPERTY_IDENTITY | PROPERTY_AFFINE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL;
177 
178     /**
179      * Create a new {@link Matrix4d} and make it a copy of the given matrix.
180      * 
181      * @param mat
182      *          the {@link Matrix4d} to copy the values from
183      */
184     this(Matrix4d mat) {
185         set(mat);
186     }
187 
188     /**
189      * Create a new {@link Matrix4d} and set its upper 4x3 submatrix to the given matrix <code>mat</code>
190      * and all other elements to identity.
191      * 
192      * @param mat
193      *          the {@link Matrix4x3d} to copy the values from
194      */
195     this(Matrix4x3d mat) {
196         set(mat);
197     }
198 
199     /**
200      * Create a new {@link Matrix4d} by setting its uppper left 3x3 submatrix to the values of the given {@link Matrix3d}
201      * and the rest to identity.
202      * 
203      * @param mat
204      *          the {@link Matrix3d}
205      */
206     this(Matrix3d mat) {
207         set(mat);
208     }
209 
210     /**
211      * Create a new 4x4 matrix using the supplied double values.
212      * <p>
213      * The matrix layout will be:<br><br>
214      *   m00, m10, m20, m30<br>
215      *   m01, m11, m21, m31<br>
216      *   m02, m12, m22, m32<br>
217      *   m03, m13, m23, m33
218      * 
219      * @param m00
220      *          the value of m00
221      * @param m01
222      *          the value of m01
223      * @param m02
224      *          the value of m02
225      * @param m03
226      *          the value of m03
227      * @param m10
228      *          the value of m10
229      * @param m11
230      *          the value of m11
231      * @param m12
232      *          the value of m12
233      * @param m13
234      *          the value of m13
235      * @param m20
236      *          the value of m20
237      * @param m21
238      *          the value of m21
239      * @param m22
240      *          the value of m22
241      * @param m23
242      *          the value of m23
243      * @param m30
244      *          the value of m30
245      * @param m31
246      *          the value of m31
247      * @param m32
248      *          the value of m32
249      * @param m33
250      *          the value of m33
251      */
252     this(double m00, double m01, double m02, double m03,
253                     double m10, double m11, double m12, double m13, 
254                     double m20, double m21, double m22, double m23, 
255                     double m30, double m31, double m32, double m33) {
256         setm00(m00);
257         setm01(m01);
258         setm02(m02);
259         setm03(m03);
260         setm10(m10);
261         setm11(m11);
262         setm12(m12);
263         setm13(m13);
264         setm20(m20);
265         setm21(m21);
266         setm22(m22);
267         setm23(m23);
268         setm30(m30);
269         setm31(m31);
270         setm32(m32);
271         setm33(m33);
272         determineProperties();
273     }
274 
275     /**
276      * Create a new {@link Matrix4d} and initialize its four columns using the supplied vectors.
277      * 
278      * @param col0
279      *          the first column
280      * @param col1
281      *          the second column
282      * @param col2
283      *          the third column
284      * @param col3
285      *          the fourth column
286      */
287     this(ref Vector4d col0, Vector4d col1, Vector4d col2, Vector4d col3) {
288         set(col0, col1, col2, col3);
289     }
290 
291     /**
292      * Assume the given properties about this matrix.
293      * <p>
294      * Use one or multiple of 0, {@link Matrix4d#PROPERTY_IDENTITY},
295      * {@link Matrix4d#PROPERTY_TRANSLATION}, {@link Matrix4d#PROPERTY_AFFINE},
296      * {@link Matrix4d#PROPERTY_PERSPECTIVE}, {@link Matrix4fc#PROPERTY_ORTHONORMAL}.
297      * 
298      * @param properties
299      *          bitset of the properties to assume about this matrix
300      * @return this
301      */
302     ref public Matrix4d assume(int properties) return {
303         this.properties = cast(byte) properties;
304         return this;
305     }
306 
307     /**
308      * Compute and set the matrix properties returned by {@link #properties()} based
309      * on the current matrix element values.
310      * 
311      * @return this
312      */
313     ref public Matrix4d determineProperties() return {
314         int __properties = 0;
315         if (m03 == 0.0 && m13 == 0.0) {
316             if (m23 == 0.0 && m33 == 1.0) {
317                 __properties |= PROPERTY_AFFINE;
318                 if (m00 == 1.0 && m01 == 0.0 && m02 == 0.0 && m10 == 0.0 && m11 == 1.0 && m12 == 0.0 && m20 == 0.0
319                         && m21 == 0.0 && m22 == 1.0) {
320                     __properties |= PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL;
321                     if (m30 == 0.0 && m31 == 0.0 && m32 == 0.0)
322                         __properties |= PROPERTY_IDENTITY;
323                 }
324                 /* 
325                  * We do not determine orthogonality, since it would require arbitrary epsilons
326                  * and is rather expensive (6 dot products) in the worst case.
327                  */
328             } else if (m01 == 0.0 && m02 == 0.0 && m10 == 0.0 && m12 == 0.0 && m20 == 0.0 && m21 == 0.0 && m30 == 0.0
329                     && m31 == 0.0 && m33 == 0.0) {
330                 __properties |= PROPERTY_PERSPECTIVE;
331             }
332         }
333         this.properties = __properties;
334         return this;
335     }
336 
337     public int getProperties() {
338         return this.properties;
339     }
340 
341     /**
342      * Set the value of the matrix element at column 0 and row 0.
343      * 
344      * @param m00
345      *          the new value
346      * @return this
347      */
348     ref public Matrix4d setm00(double m00) return {
349         this.m00 = m00;
350         properties &= ~PROPERTY_ORTHONORMAL;
351         if (m00 != 1.0)
352             properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION);
353         return this;
354     }
355     /**
356      * Set the value of the matrix element at column 0 and row 1.
357      * 
358      * @param m01
359      *          the new value
360      * @return this
361      */
362     ref public Matrix4d setm01(double m01) return {
363         this.m01 = m01;
364         properties &= ~PROPERTY_ORTHONORMAL;
365         if (m01 != 0.0)
366             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION);
367         return this;
368     }
369     /**
370      * Set the value of the matrix element at column 0 and row 2.
371      * 
372      * @param m02
373      *          the new value
374      * @return this
375      */
376     ref public Matrix4d setm02(double m02) return {
377         this.m02 = m02;
378         properties &= ~PROPERTY_ORTHONORMAL;
379         if (m02 != 0.0)
380             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION);
381         return this;
382     }
383     /**
384      * Set the value of the matrix element at column 0 and row 3.
385      * 
386      * @param m03
387      *          the new value
388      * @return this
389      */
390     ref public Matrix4d setm03(double m03) return {
391         this.m03 = m03;
392         if (m03 != 0.0)
393             properties = 0;
394         return this;
395     }
396     /**
397      * Set the value of the matrix element at column 1 and row 0.
398      * 
399      * @param m10
400      *          the new value
401      * @return this
402      */
403     ref public Matrix4d setm10(double m10) return {
404         this.m10 = m10;
405         properties &= ~PROPERTY_ORTHONORMAL;
406         if (m10 != 0.0)
407             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION);
408         return this;
409     }
410     /**
411      * Set the value of the matrix element at column 1 and row 1.
412      * 
413      * @param m11
414      *          the new value
415      * @return this
416      */
417     ref public Matrix4d setm11(double m11) return {
418         this.m11 = m11;
419         properties &= ~PROPERTY_ORTHONORMAL;
420         if (m11 != 1.0)
421             properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION);
422         return this;
423     }
424     /**
425      * Set the value of the matrix element at column 1 and row 2.
426      * 
427      * @param m12
428      *          the new value
429      * @return this
430      */
431     ref public Matrix4d setm12(double m12) return {
432         this.m12 = m12;
433         properties &= ~PROPERTY_ORTHONORMAL;
434         if (m12 != 0.0)
435             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION);
436         return this;
437     }
438     /**
439      * Set the value of the matrix element at column 1 and row 3.
440      * 
441      * @param m13
442      *          the new value
443      * @return this
444      */
445     ref public Matrix4d setm13(double m13) return {
446         this.m13 = m13;
447         if (m03 != 0.0)
448             properties = 0;
449         return this;
450     }
451     /**
452      * Set the value of the matrix element at column 2 and row 0.
453      * 
454      * @param m20
455      *          the new value
456      * @return this
457      */
458     ref public Matrix4d setm20(double m20) return {
459         this.m20 = m20;
460         properties &= ~PROPERTY_ORTHONORMAL;
461         if (m20 != 0.0)
462             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION);
463         return this;
464     }
465     /**
466      * Set the value of the matrix element at column 2 and row 1.
467      * 
468      * @param m21
469      *          the new value
470      * @return this
471      */
472     ref public Matrix4d setm21(double m21) return {
473         this.m21 = m21;
474         properties &= ~PROPERTY_ORTHONORMAL;
475         if (m21 != 0.0)
476             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION);
477         return this;
478     }
479     /**
480      * Set the value of the matrix element at column 2 and row 2.
481      * 
482      * @param m22
483      *          the new value
484      * @return this
485      */
486     ref public Matrix4d setm22(double m22) return {
487         this.m22 = m22;
488         properties &= ~PROPERTY_ORTHONORMAL;
489         if (m22 != 1.0)
490             properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION);
491         return this;
492     }
493     /**
494      * Set the value of the matrix element at column 2 and row 3.
495      * 
496      * @param m23
497      *          the new value
498      * @return this
499      */
500     ref public Matrix4d setm23(double m23) return {
501         this.m23 = m23;
502         if (m23 != 0.0)
503             properties &= ~(PROPERTY_IDENTITY | PROPERTY_AFFINE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL);
504         return this;
505     }
506     /**
507      * Set the value of the matrix element at column 3 and row 0.
508      * 
509      * @param m30
510      *          the new value
511      * @return this
512      */
513     ref public Matrix4d setm30(double m30) return {
514         this.m30 = m30;
515         if (m30 != 0.0)
516             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE);
517         return this;
518     }
519     /**
520      * Set the value of the matrix element at column 3 and row 1.
521      * 
522      * @param m31
523      *          the new value
524      * @return this
525      */
526     ref public Matrix4d setm31(double m31) return {
527         this.m31 = m31;
528         if (m31 != 0.0)
529             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE);
530         return this;
531     }
532     /**
533      * Set the value of the matrix element at column 3 and row 2.
534      * 
535      * @param m32
536      *          the new value
537      * @return this
538      */
539     ref public Matrix4d setm32(double m32) return {
540         this.m32 = m32;
541         if (m32 != 0.0)
542             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE);
543         return this;
544     }
545     /**
546      * Set the value of the matrix element at column 3 and row 3.
547      * 
548      * @param m33
549      *          the new value
550      * @return this
551      */
552     ref public Matrix4d setm33(double m33) return {
553         this.m33 = m33;
554         if (m33 != 0.0)
555             properties &= ~(PROPERTY_PERSPECTIVE);
556         if (m33 != 1.0)
557             properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL | PROPERTY_AFFINE);
558         return this;
559     }
560 
561     ref Matrix4d _properties(int properties) return {
562         this.properties = properties;
563         return this;
564     }
565 
566     /**
567      * Set the value of the matrix element at column 0 and row 0 without updating the properties of the matrix.
568      * 
569      * @param m00
570      *          the new value
571      * @return this
572      */
573     ref Matrix4d _m00(double m00) return {
574         setm00(m00);
575         return this;
576     }
577     /**
578      * Set the value of the matrix element at column 0 and row 1 without updating the properties of the matrix.
579      * 
580      * @param m01
581      *          the new value
582      * @return this
583      */
584     ref Matrix4d _m01(double m01) return {
585         setm01(m01);
586         return this;
587     }
588     /**
589      * Set the value of the matrix element at column 0 and row 2 without updating the properties of the matrix.
590      * 
591      * @param m02
592      *          the new value
593      * @return this
594      */
595     ref Matrix4d _m02(double m02) return {
596         setm02(m02);
597         return this;
598     }
599     /**
600      * Set the value of the matrix element at column 0 and row 3 without updating the properties of the matrix.
601      * 
602      * @param m03
603      *          the new value
604      * @return this
605      */
606     ref Matrix4d _m03(double m03) return {
607         setm03(m03);
608         return this;
609     }
610     /**
611      * Set the value of the matrix element at column 1 and row 0 without updating the properties of the matrix.
612      * 
613      * @param m10
614      *          the new value
615      * @return this
616      */
617     ref Matrix4d _m10(double m10) return {
618         setm10(m10);
619         return this;
620     }
621     /**
622      * Set the value of the matrix element at column 1 and row 1 without updating the properties of the matrix.
623      * 
624      * @param m11
625      *          the new value
626      * @return this
627      */
628     ref Matrix4d _m11(double m11) return {
629         setm11(m11);
630         return this;
631     }
632     /**
633      * Set the value of the matrix element at column 1 and row 2 without updating the properties of the matrix.
634      * 
635      * @param m12
636      *          the new value
637      * @return this
638      */
639     ref Matrix4d _m12(double m12) return {
640         setm12(m12);
641         return this;
642     }
643     /**
644      * Set the value of the matrix element at column 1 and row 3 without updating the properties of the matrix.
645      * 
646      * @param m13
647      *          the new value
648      * @return this
649      */
650     ref Matrix4d _m13(double m13) return {
651         setm13(m13);
652         return this;
653     }
654     /**
655      * Set the value of the matrix element at column 2 and row 0 without updating the properties of the matrix.
656      * 
657      * @param m20
658      *          the new value
659      * @return this
660      */
661     ref Matrix4d _m20(double m20) return {
662         setm20(m20);
663         return this;
664     }
665     /**
666      * Set the value of the matrix element at column 2 and row 1 without updating the properties of the matrix.
667      * 
668      * @param m21
669      *          the new value
670      * @return this
671      */
672     ref Matrix4d _m21(double m21) return {
673         setm21(m21);
674         return this;
675     }
676     /**
677      * Set the value of the matrix element at column 2 and row 2 without updating the properties of the matrix.
678      * 
679      * @param m22
680      *          the new value
681      * @return this
682      */
683     ref Matrix4d _m22(double m22) return {
684         setm22(m22);
685         return this;
686     }
687     /**
688      * Set the value of the matrix element at column 2 and row 3 without updating the properties of the matrix.
689      * 
690      * @param m23
691      *          the new value
692      * @return this
693      */
694     ref Matrix4d _m23(double m23) return {
695         setm23(m23);
696         return this;
697     }
698     /**
699      * Set the value of the matrix element at column 3 and row 0 without updating the properties of the matrix.
700      * 
701      * @param m30
702      *          the new value
703      * @return this
704      */
705     ref Matrix4d _m30(double m30) return {
706         setm30(m30);
707         return this;
708     }
709     /**
710      * Set the value of the matrix element at column 3 and row 1 without updating the properties of the matrix.
711      * 
712      * @param m31
713      *          the new value
714      * @return this
715      */
716     ref Matrix4d _m31(double m31) return {
717         setm31(m31);
718         return this;
719     }
720     /**
721      * Set the value of the matrix element at column 3 and row 2 without updating the properties of the matrix.
722      * 
723      * @param m32
724      *          the new value
725      * @return this
726      */
727     ref Matrix4d _m32(double m32) return {
728         setm32(m32);
729         return this;
730     }
731     /**
732      * Set the value of the matrix element at column 3 and row 3 without updating the properties of the matrix.
733      * 
734      * @param m33
735      *          the new value
736      * @return this
737      */
738     ref Matrix4d _m33(double m33) return {
739         setm33(m33);
740         return this;
741     }
742 
743     /**
744      * Reset this matrix to the identity.
745      * <p>
746      * Please note that if a call to {@link #identity()} is immediately followed by a call to:
747      * {@link #translate(double, double, double) translate}, 
748      * {@link #rotate(double, double, double, double) rotate},
749      * {@link #scale(double, double, double) scale},
750      * {@link #perspective(double, double, double, double) perspective},
751      * {@link #frustum(double, double, double, double, double, double) frustum},
752      * {@link #ortho(double, double, double, double, double, double) ortho},
753      * {@link #ortho2D(double, double, double, double) ortho2D},
754      * {@link #lookAt(double, double, double, double, double, double, double, double, double) lookAt},
755      * {@link #lookAlong(double, double, double, double, double, double) lookAlong},
756      * or any of their overloads, then the call to {@link #identity()} can be omitted and the subsequent call replaced with:
757      * {@link #translation(double, double, double) translation},
758      * {@link #rotation(double, double, double, double) rotation},
759      * {@link #scaling(double, double, double) scaling},
760      * {@link #setPerspective(double, double, double, double) setPerspective},
761      * {@link #setFrustum(double, double, double, double, double, double) setFrustum},
762      * {@link #setOrtho(double, double, double, double, double, double) setOrtho},
763      * {@link #setOrtho2D(double, double, double, double) setOrtho2D},
764      * {@link #setLookAt(double, double, double, double, double, double, double, double, double) setLookAt},
765      * {@link #setLookAlong(double, double, double, double, double, double) setLookAlong},
766      * or any of their overloads.
767      * 
768      * @return this
769      */
770     ref public Matrix4d identity() return {
771         if ((properties & PROPERTY_IDENTITY) != 0)
772             return this;
773         _identity();
774         properties = PROPERTY_IDENTITY | PROPERTY_AFFINE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL;
775         return this;
776     }
777     private void _identity() {
778         _m00(1.0).
779         _m10(0.0).
780         _m20(0.0).
781         _m30(0.0).
782         _m01(0.0).
783         _m11(1.0).
784         _m21(0.0).
785         _m31(0.0).
786         _m02(0.0).
787         _m12(0.0).
788         _m22(1.0).
789         _m32(0.0).
790         _m03(0.0).
791         _m13(0.0).
792         _m23(0.0).
793         _m33(1.0);
794     }
795 
796     /**
797      * Store the values of the given matrix <code>m</code> into <code>this</code> matrix.
798      * 
799      * @see #Matrix4d(Matrix4d)
800      * @see #get(Matrix4d)
801      * 
802      * @param m
803      *          the matrix to copy the values from
804      * @return this
805      */
806     ref public Matrix4d set(Matrix4d m) return {
807         return
808         _m00(m.m00).
809         _m01(m.m01).
810         _m02(m.m02).
811         _m03(m.m03).
812         _m10(m.m10).
813         _m11(m.m11).
814         _m12(m.m12).
815         _m13(m.m13).
816         _m20(m.m20).
817         _m21(m.m21).
818         _m22(m.m22).
819         _m23(m.m23).
820         _m30(m.m30).
821         _m31(m.m31).
822         _m32(m.m32).
823         _m33(m.m33).
824         _properties(m.properties);
825     }
826 
827 
828     /**
829      * Store the values of the transpose of the given matrix <code>m</code> into <code>this</code> matrix.
830      * 
831      * @param m
832      *          the matrix to copy the transposed values from
833      * @return this
834      */
835     ref public Matrix4d setTransposed(Matrix4d m) return {
836         if ((m.properties & PROPERTY_IDENTITY) != 0)
837             return this.identity();
838         return setTransposedInternal(m);
839     }
840     ref private Matrix4d setTransposedInternal(Matrix4d m) return {
841         double nm10 = m.m01, nm12 = m.m21, nm13 = m.m31;
842         double nm20 = m.m02, nm21 = m.m12, nm30 = m.m03;
843         double nm31 = m.m13, nm32 = m.m23;
844         return this
845         ._m00(m.m00)._m01(m.m10)._m02(m.m20)._m03(m.m30)
846         ._m10(nm10)._m11(m.m11)._m12(nm12)._m13(nm13)
847         ._m20(nm20)._m21(nm21)._m22(m.m22)._m23(m.m32)
848         ._m30(nm30)._m31(nm31)._m32(nm32)._m33(m.m33)
849         ._properties(m.properties & PROPERTY_IDENTITY);
850     }
851 
852     /**
853      * Store the values of the given matrix <code>m</code> into <code>this</code> matrix
854      * and set the other matrix elements to identity.
855      * 
856      * @see #Matrix4d(Matrix4x3d)
857      * 
858      * @param m
859      *          the matrix to copy the values from
860      * @return this
861      */
862     ref public Matrix4d set(Matrix4x3d m) return {
863         return
864         _m00(m.m00).
865         _m01(m.m01).
866         _m02(m.m02).
867         _m03(0.0).
868         _m10(m.m10).
869         _m11(m.m11).
870         _m12(m.m12).
871         _m13(0.0).
872         _m20(m.m20).
873         _m21(m.m21).
874         _m22(m.m22).
875         _m23(0.0).
876         _m30(m.m30).
877         _m31(m.m31).
878         _m32(m.m32).
879         _m33(1.0).
880         _properties(m.properties | PROPERTY_AFFINE);
881     }
882 
883    
884     /**
885      * Set the upper left 3x3 submatrix of this {@link Matrix4d} to the given {@link Matrix3d} 
886      * and the rest to identity.
887      * 
888      * @see #Matrix4d(Matrix3d)
889      * 
890      * @param mat
891      *          the {@link Matrix3d}
892      * @return this
893      */
894     ref public Matrix4d set(Matrix3d mat) return {
895         return
896         _m00(mat.m00).
897         _m01(mat.m01).
898         _m02(mat.m02).
899         _m03(0.0).
900         _m10(mat.m10).
901         _m11(mat.m11).
902         _m12(mat.m12).
903         _m13(0.0).
904         _m20(mat.m20).
905         _m21(mat.m21).
906         _m22(mat.m22).
907         _m23(0.0).
908         _m30(0.0).
909         _m31(0.0).
910         _m32(0.0).
911         _m33(1.0).
912         _properties(PROPERTY_AFFINE);
913     }
914 
915     /**
916      * Set the upper left 3x3 submatrix of this {@link Matrix4d} to that of the given {@link Matrix4d} 
917      * and don't change the other elements.
918      * 
919      * @param mat
920      *          the {@link Matrix4d}
921      * @return this
922      */
923     ref public Matrix4d set3x3(Matrix4d mat) return {
924         return
925         _m00(mat.m00).
926         _m01(mat.m01).
927         _m02(mat.m02).
928         _m10(mat.m10).
929         _m11(mat.m11).
930         _m12(mat.m12).
931         _m20(mat.m20).
932         _m21(mat.m21).
933         _m22(mat.m22).
934         _properties(properties & mat.properties & ~(PROPERTY_PERSPECTIVE));
935     }
936 
937     /**
938      * Set the upper 4x3 submatrix of this {@link Matrix4d} to the given {@link Matrix4x3d} 
939      * and don't change the other elements.
940      * 
941      * @see Matrix4x3d#get(Matrix4d)
942      * 
943      * @param mat
944      *          the {@link Matrix4x3d}
945      * @return this
946      */
947     ref public Matrix4d set4x3(Matrix4x3d mat) return {
948         return
949         _m00(mat.m00).
950         _m01(mat.m01).
951         _m02(mat.m02).
952         _m10(mat.m10).
953         _m11(mat.m11).
954         _m12(mat.m12).
955         _m20(mat.m20).
956         _m21(mat.m21).
957         _m22(mat.m22).
958         _m30(mat.m30).
959         _m31(mat.m31).
960         _m32(mat.m32).
961         _properties(properties & mat.properties & ~(PROPERTY_PERSPECTIVE));
962     }
963 
964 
965     /**
966      * Set the upper 4x3 submatrix of this {@link Matrix4d} to the upper 4x3 submatrix of the given {@link Matrix4d} 
967      * and don't change the other elements.
968      * 
969      * @param mat
970      *          the {@link Matrix4d}
971      * @return this
972      */
973     ref public Matrix4d set4x3(Matrix4d mat) return {
974         return
975         _m00(mat.m00).
976         _m01(mat.m01).
977         _m02(mat.m02).
978         _m10(mat.m10).
979         _m11(mat.m11).
980         _m12(mat.m12).
981         _m20(mat.m20).
982         _m21(mat.m21).
983         _m22(mat.m22).
984         _m30(mat.m30).
985         _m31(mat.m31).
986         _m32(mat.m32).
987         _properties(properties & mat.properties & ~(PROPERTY_PERSPECTIVE));
988     }
989 
990     /**
991      * Set this matrix to be equivalent to the rotation specified by the given {@link AxisAngle4d}.
992      * 
993      * @param axisAngle
994      *          the {@link AxisAngle4d}
995      * @return this
996      */
997     ref public Matrix4d set(ref AxisAngle4d axisAngle) return {
998         double x = axisAngle.x;
999         double y = axisAngle.y;
1000         double z = axisAngle.z;
1001         double angle = axisAngle.angle;
1002         double invLength = Math.invsqrt(x*x + y*y + z*z);
1003         x *= invLength;
1004         y *= invLength;
1005         z *= invLength;
1006         double s = Math.sin(angle);
1007         double c = Math.cosFromSin(s, angle);
1008         double omc = 1.0 - c;
1009         _m00(c + x*x*omc).
1010         _m11(c + y*y*omc).
1011         _m22(c + z*z*omc);
1012         double tmp1 = x*y*omc;
1013         double tmp2 = z*s;
1014         _m10(tmp1 - tmp2).
1015         _m01(tmp1 + tmp2);
1016         tmp1 = x*z*omc;
1017         tmp2 = y*s;
1018         _m20(tmp1 + tmp2).
1019         _m02(tmp1 - tmp2);
1020         tmp1 = y*z*omc;
1021         tmp2 = x*s;
1022         _m21(tmp1 - tmp2).
1023         _m12(tmp1 + tmp2).
1024         _m03(0.0).
1025         _m13(0.0).
1026         _m23(0.0).
1027         _m30(0.0).
1028         _m31(0.0).
1029         _m32(0.0).
1030         _m33(1.0).
1031         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
1032         return this;
1033     }
1034 
1035 
1036     /**
1037      * Set this matrix to be equivalent to the rotation - and possibly scaling - specified by the given {@link Quaterniond}.
1038      * <p>
1039      * This method is equivalent to calling: <code>rotation(q)</code>
1040      * <p>
1041      * Reference: <a href="http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToMatrix/">http://www.euclideanspace.com/</a>
1042      * 
1043      * @see #rotation(ref Quaterniond)
1044      * 
1045      * @param q
1046      *          the {@link Quaterniond}
1047      * @return this
1048      */
1049     ref public Matrix4d set(ref Quaterniond q) return {
1050         return rotation(q);
1051     }
1052 
1053     /**
1054      * Multiply this matrix by the supplied <code>right</code> matrix.
1055      * <p>
1056      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the <code>right</code> matrix,
1057      * then the new matrix will be <code>M * R</code>. So when transforming a
1058      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
1059      * transformation of the right matrix will be applied first!
1060      * 
1061      * @param right
1062      *          the right operand of the multiplication
1063      * @return this
1064      */
1065     ref public Matrix4d mul(Matrix4d right) return {
1066         mul(right, this);
1067         return this;
1068     }
1069 
1070     public Matrix4d mul(Matrix4d right, ref Matrix4d dest) {
1071         if ((properties & PROPERTY_IDENTITY) != 0)
1072             return dest.set(right);
1073         else if ((right.properties & PROPERTY_IDENTITY) != 0)
1074             return dest.set(this);
1075         else if ((properties & PROPERTY_TRANSLATION) != 0 && (right.properties & PROPERTY_AFFINE) != 0)
1076             return mulTranslationAffine(right, dest);
1077         else if ((properties & PROPERTY_AFFINE) != 0 && (right.properties & PROPERTY_AFFINE) != 0)
1078             return mulAffine(right, dest);
1079         else if ((properties & PROPERTY_PERSPECTIVE) != 0 && (right.properties & PROPERTY_AFFINE) != 0)
1080             return mulPerspectiveAffine(right, dest);
1081         else if ((right.properties & PROPERTY_AFFINE) != 0)
1082             return mulAffineR(right, dest);
1083         return mul0(right, dest);
1084     }
1085 
1086     /**
1087      * Multiply this matrix by the supplied <code>right</code> matrix.
1088      * <p>
1089      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the <code>right</code> matrix,
1090      * then the new matrix will be <code>M * R</code>. So when transforming a
1091      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
1092      * transformation of the right matrix will be applied first!
1093      * <p>
1094      * This method neither assumes nor checks for any matrix properties of <code>this</code> or <code>right</code>
1095      * and will always perform a complete 4x4 matrix multiplication. This method should only be used whenever the
1096      * multiplied matrices do not have any properties for which there are optimized multiplication methods available.
1097      * 
1098      * @param right
1099      *          the right operand of the matrix multiplication
1100      * @return this
1101      */
1102     ref public Matrix4d mul0(Matrix4d right) return {
1103        mul0(right, this);
1104        return this;
1105     }
1106 
1107     public Matrix4d mul0(Matrix4d right, ref Matrix4d dest) {
1108         double nm00 = Math.fma(m00, right.m00, Math.fma(m10, right.m01, Math.fma(m20, right.m02, m30 * right.m03)));
1109         double nm01 = Math.fma(m01, right.m00, Math.fma(m11, right.m01, Math.fma(m21, right.m02, m31 * right.m03)));
1110         double nm02 = Math.fma(m02, right.m00, Math.fma(m12, right.m01, Math.fma(m22, right.m02, m32 * right.m03)));
1111         double nm03 = Math.fma(m03, right.m00, Math.fma(m13, right.m01, Math.fma(m23, right.m02, m33 * right.m03)));
1112         double nm10 = Math.fma(m00, right.m10, Math.fma(m10, right.m11, Math.fma(m20, right.m12, m30 * right.m13)));
1113         double nm11 = Math.fma(m01, right.m10, Math.fma(m11, right.m11, Math.fma(m21, right.m12, m31 * right.m13)));
1114         double nm12 = Math.fma(m02, right.m10, Math.fma(m12, right.m11, Math.fma(m22, right.m12, m32 * right.m13)));
1115         double nm13 = Math.fma(m03, right.m10, Math.fma(m13, right.m11, Math.fma(m23, right.m12, m33 * right.m13)));
1116         double nm20 = Math.fma(m00, right.m20, Math.fma(m10, right.m21, Math.fma(m20, right.m22, m30 * right.m23)));
1117         double nm21 = Math.fma(m01, right.m20, Math.fma(m11, right.m21, Math.fma(m21, right.m22, m31 * right.m23)));
1118         double nm22 = Math.fma(m02, right.m20, Math.fma(m12, right.m21, Math.fma(m22, right.m22, m32 * right.m23)));
1119         double nm23 = Math.fma(m03, right.m20, Math.fma(m13, right.m21, Math.fma(m23, right.m22, m33 * right.m23)));
1120         double nm30 = Math.fma(m00, right.m30, Math.fma(m10, right.m31, Math.fma(m20, right.m32, m30 * right.m33)));
1121         double nm31 = Math.fma(m01, right.m30, Math.fma(m11, right.m31, Math.fma(m21, right.m32, m31 * right.m33)));
1122         double nm32 = Math.fma(m02, right.m30, Math.fma(m12, right.m31, Math.fma(m22, right.m32, m32 * right.m33)));
1123         double nm33 = Math.fma(m03, right.m30, Math.fma(m13, right.m31, Math.fma(m23, right.m32, m33 * right.m33)));
1124         return dest
1125         ._m00(nm00)
1126         ._m01(nm01)
1127         ._m02(nm02)
1128         ._m03(nm03)
1129         ._m10(nm10)
1130         ._m11(nm11)
1131         ._m12(nm12)
1132         ._m13(nm13)
1133         ._m20(nm20)
1134         ._m21(nm21)
1135         ._m22(nm22)
1136         ._m23(nm23)
1137         ._m30(nm30)
1138         ._m31(nm31)
1139         ._m32(nm32)
1140         ._m33(nm33)
1141         ._properties(0);
1142     }
1143 
1144     /**
1145      * Multiply this matrix by the matrix with the supplied elements.
1146      * <p>
1147      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the <code>right</code> matrix whose 
1148      * elements are supplied via the parameters, then the new matrix will be <code>M * R</code>.
1149      * So when transforming a vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
1150      * transformation of the right matrix will be applied first!
1151      *
1152      * @param r00
1153      *          the m00 element of the right matrix
1154      * @param r01
1155      *          the m01 element of the right matrix
1156      * @param r02
1157      *          the m02 element of the right matrix
1158      * @param r03
1159      *          the m03 element of the right matrix
1160      * @param r10
1161      *          the m10 element of the right matrix
1162      * @param r11
1163      *          the m11 element of the right matrix
1164      * @param r12
1165      *          the m12 element of the right matrix
1166      * @param r13
1167      *          the m13 element of the right matrix
1168      * @param r20
1169      *          the m20 element of the right matrix
1170      * @param r21
1171      *          the m21 element of the right matrix
1172      * @param r22
1173      *          the m22 element of the right matrix
1174      * @param r23
1175      *          the m23 element of the right matrix
1176      * @param r30
1177      *          the m30 element of the right matrix
1178      * @param r31
1179      *          the m31 element of the right matrix
1180      * @param r32
1181      *          the m32 element of the right matrix
1182      * @param r33
1183      *          the m33 element of the right matrix
1184      * @return this
1185      */
1186     ref public Matrix4d mul(
1187             double r00, double r01, double r02, double r03,
1188             double r10, double r11, double r12, double r13,
1189             double r20, double r21, double r22, double r23,
1190             double r30, double r31, double r32, double r33) return {
1191         mul(r00, r01, r02, r03, r10, r11, r12, r13, r20, r21, r22, r23, r30, r31, r32, r33, this);
1192         return this;
1193     }
1194 
1195     public Matrix4d mul(
1196             double r00, double r01, double r02, double r03,
1197             double r10, double r11, double r12, double r13,
1198             double r20, double r21, double r22, double r23,
1199             double r30, double r31, double r32, double r33, ref Matrix4d dest) {
1200         if ((properties & PROPERTY_IDENTITY) != 0)
1201             return dest.set(r00, r01, r02, r03, r10, r11, r12, r13, r20, r21, r22, r23, r30, r31, r32, r33);
1202         else if ((properties & PROPERTY_AFFINE) != 0)
1203             return mulAffineL(r00, r01, r02, r03, r10, r11, r12, r13, r20, r21, r22, r23, r30, r31, r32, r33, dest);
1204         return mulGeneric(r00, r01, r02, r03, r10, r11, r12, r13, r20, r21, r22, r23, r30, r31, r32, r33, dest);
1205     }
1206     private Matrix4d mulAffineL(
1207             double r00, double r01, double r02, double r03,
1208             double r10, double r11, double r12, double r13,
1209             double r20, double r21, double r22, double r23,
1210             double r30, double r31, double r32, double r33, ref Matrix4d dest) {
1211         double nm00 = Math.fma(m00, r00, Math.fma(m10, r01, Math.fma(m20, r02, m30 * r03)));
1212         double nm01 = Math.fma(m01, r00, Math.fma(m11, r01, Math.fma(m21, r02, m31 * r03)));
1213         double nm02 = Math.fma(m02, r00, Math.fma(m12, r01, Math.fma(m22, r02, m32 * r03)));
1214         double nm03 = r03;
1215         double nm10 = Math.fma(m00, r10, Math.fma(m10, r11, Math.fma(m20, r12, m30 * r13)));
1216         double nm11 = Math.fma(m01, r10, Math.fma(m11, r11, Math.fma(m21, r12, m31 * r13)));
1217         double nm12 = Math.fma(m02, r10, Math.fma(m12, r11, Math.fma(m22, r12, m32 * r13)));
1218         double nm13 = r13;
1219         double nm20 = Math.fma(m00, r20, Math.fma(m10, r21, Math.fma(m20, r22, m30 * r23)));
1220         double nm21 = Math.fma(m01, r20, Math.fma(m11, r21, Math.fma(m21, r22, m31 * r23)));
1221         double nm22 = Math.fma(m02, r20, Math.fma(m12, r21, Math.fma(m22, r22, m32 * r23)));
1222         double nm23 = r23;
1223         double nm30 = Math.fma(m00, r30, Math.fma(m10, r31, Math.fma(m20, r32, m30 * r33)));
1224         double nm31 = Math.fma(m01, r30, Math.fma(m11, r31, Math.fma(m21, r32, m31 * r33)));
1225         double nm32 = Math.fma(m02, r30, Math.fma(m12, r31, Math.fma(m22, r32, m32 * r33)));
1226         double nm33 = r33;
1227         return dest
1228         ._m00(nm00)
1229         ._m01(nm01)
1230         ._m02(nm02)
1231         ._m03(nm03)
1232         ._m10(nm10)
1233         ._m11(nm11)
1234         ._m12(nm12)
1235         ._m13(nm13)
1236         ._m20(nm20)
1237         ._m21(nm21)
1238         ._m22(nm22)
1239         ._m23(nm23)
1240         ._m30(nm30)
1241         ._m31(nm31)
1242         ._m32(nm32)
1243         ._m33(nm33)
1244         ._properties(PROPERTY_AFFINE);
1245     }
1246     private Matrix4d mulGeneric(
1247             double r00, double r01, double r02, double r03,
1248             double r10, double r11, double r12, double r13,
1249             double r20, double r21, double r22, double r23,
1250             double r30, double r31, double r32, double r33, ref Matrix4d dest) {
1251         double nm00 = Math.fma(m00, r00, Math.fma(m10, r01, Math.fma(m20, r02, m30 * r03)));
1252         double nm01 = Math.fma(m01, r00, Math.fma(m11, r01, Math.fma(m21, r02, m31 * r03)));
1253         double nm02 = Math.fma(m02, r00, Math.fma(m12, r01, Math.fma(m22, r02, m32 * r03)));
1254         double nm03 = Math.fma(m03, r00, Math.fma(m13, r01, Math.fma(m23, r02, m33 * r03)));
1255         double nm10 = Math.fma(m00, r10, Math.fma(m10, r11, Math.fma(m20, r12, m30 * r13)));
1256         double nm11 = Math.fma(m01, r10, Math.fma(m11, r11, Math.fma(m21, r12, m31 * r13)));
1257         double nm12 = Math.fma(m02, r10, Math.fma(m12, r11, Math.fma(m22, r12, m32 * r13)));
1258         double nm13 = Math.fma(m03, r10, Math.fma(m13, r11, Math.fma(m23, r12, m33 * r13)));
1259         double nm20 = Math.fma(m00, r20, Math.fma(m10, r21, Math.fma(m20, r22, m30 * r23)));
1260         double nm21 = Math.fma(m01, r20, Math.fma(m11, r21, Math.fma(m21, r22, m31 * r23)));
1261         double nm22 = Math.fma(m02, r20, Math.fma(m12, r21, Math.fma(m22, r22, m32 * r23)));
1262         double nm23 = Math.fma(m03, r20, Math.fma(m13, r21, Math.fma(m23, r22, m33 * r23)));
1263         double nm30 = Math.fma(m00, r30, Math.fma(m10, r31, Math.fma(m20, r32, m30 * r33)));
1264         double nm31 = Math.fma(m01, r30, Math.fma(m11, r31, Math.fma(m21, r32, m31 * r33)));
1265         double nm32 = Math.fma(m02, r30, Math.fma(m12, r31, Math.fma(m22, r32, m32 * r33)));
1266         double nm33 = Math.fma(m03, r30, Math.fma(m13, r31, Math.fma(m23, r32, m33 * r33)));
1267         return dest
1268         ._m00(nm00)
1269         ._m01(nm01)
1270         ._m02(nm02)
1271         ._m03(nm03)
1272         ._m10(nm10)
1273         ._m11(nm11)
1274         ._m12(nm12)
1275         ._m13(nm13)
1276         ._m20(nm20)
1277         ._m21(nm21)
1278         ._m22(nm22)
1279         ._m23(nm23)
1280         ._m30(nm30)
1281         ._m31(nm31)
1282         ._m32(nm32)
1283         ._m33(nm33)
1284         ._properties(0);
1285     }
1286 
1287     /**
1288      * Multiply this matrix by the 3x3 matrix with the supplied elements expanded to a 4x4 matrix with 
1289      * all other matrix elements set to identity.
1290      * <p>
1291      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the <code>right</code> matrix whose 
1292      * elements are supplied via the parameters, then the new matrix will be <code>M * R</code>.
1293      * So when transforming a vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
1294      * transformation of the right matrix will be applied first!
1295      *
1296      * @param r00
1297      *          the m00 element of the right matrix
1298      * @param r01
1299      *          the m01 element of the right matrix
1300      * @param r02
1301      *          the m02 element of the right matrix
1302      * @param r10
1303      *          the m10 element of the right matrix
1304      * @param r11
1305      *          the m11 element of the right matrix
1306      * @param r12
1307      *          the m12 element of the right matrix
1308      * @param r20
1309      *          the m20 element of the right matrix
1310      * @param r21
1311      *          the m21 element of the right matrix
1312      * @param r22
1313      *          the m22 element of the right matrix
1314      * @return this
1315      */
1316     ref public Matrix4d mul3x3(
1317             double r00, double r01, double r02,
1318             double r10, double r11, double r12,
1319             double r20, double r21, double r22) return {
1320         mul3x3(r00, r01, r02, r10, r11, r12, r20, r21, r22, this);
1321         return this;
1322     }
1323     public Matrix4d mul3x3(
1324             double r00, double r01, double r02,
1325             double r10, double r11, double r12,
1326             double r20, double r21, double r22, ref Matrix4d dest) {
1327         if ((properties & PROPERTY_IDENTITY) != 0)
1328             return dest.set(r00, r01, r02, 0, r10, r11, r12, 0, r20, r21, r22, 0, 0, 0, 0, 1);
1329         return mulGeneric3x3(r00, r01, r02, r10, r11, r12, r20, r21, r22, dest);
1330     }
1331     private Matrix4d mulGeneric3x3(
1332             double r00, double r01, double r02,
1333             double r10, double r11, double r12,
1334             double r20, double r21, double r22, ref Matrix4d dest) {
1335         double nm00 = Math.fma(m00, r00, Math.fma(m10, r01, m20 * r02));
1336         double nm01 = Math.fma(m01, r00, Math.fma(m11, r01, m21 * r02));
1337         double nm02 = Math.fma(m02, r00, Math.fma(m12, r01, m22 * r02));
1338         double nm03 = Math.fma(m03, r00, Math.fma(m13, r01, m23 * r02));
1339         double nm10 = Math.fma(m00, r10, Math.fma(m10, r11, m20 * r12));
1340         double nm11 = Math.fma(m01, r10, Math.fma(m11, r11, m21 * r12));
1341         double nm12 = Math.fma(m02, r10, Math.fma(m12, r11, m22 * r12));
1342         double nm13 = Math.fma(m03, r10, Math.fma(m13, r11, m23 * r12));
1343         double nm20 = Math.fma(m00, r20, Math.fma(m10, r21, m20 * r22));
1344         double nm21 = Math.fma(m01, r20, Math.fma(m11, r21, m21 * r22));
1345         double nm22 = Math.fma(m02, r20, Math.fma(m12, r21, m22 * r22));
1346         double nm23 = Math.fma(m03, r20, Math.fma(m13, r21, m23 * r22));
1347         return dest
1348         ._m00(nm00)
1349         ._m01(nm01)
1350         ._m02(nm02)
1351         ._m03(nm03)
1352         ._m10(nm10)
1353         ._m11(nm11)
1354         ._m12(nm12)
1355         ._m13(nm13)
1356         ._m20(nm20)
1357         ._m21(nm21)
1358         ._m22(nm22)
1359         ._m23(nm23)
1360         ._m30(m30)
1361         ._m31(m31)
1362         ._m32(m32)
1363         ._m33(m33)
1364         ._properties(this.properties & PROPERTY_AFFINE);
1365     }
1366 
1367     /**
1368      * Pre-multiply this matrix by the supplied <code>left</code> matrix and store the result in <code>this</code>.
1369      * <p>
1370      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the <code>left</code> matrix,
1371      * then the new matrix will be <code>L * M</code>. So when transforming a
1372      * vector <code>v</code> with the new matrix by using <code>L * M * v</code>, the
1373      * transformation of <code>this</code> matrix will be applied first!
1374      *
1375      * @param left
1376      *          the left operand of the matrix multiplication
1377      * @return this
1378      */
1379     ref public Matrix4d mulLocal(Matrix4d left) return {
1380        mulLocal(left, this);
1381        return this;
1382     }
1383 
1384     public Matrix4d mulLocal(Matrix4d left, ref Matrix4d dest) {
1385         if ((properties & PROPERTY_IDENTITY) != 0)
1386             return dest.set(left);
1387         else if ((left.properties & PROPERTY_IDENTITY) != 0)
1388             return dest.set(this);
1389         else if ((properties & PROPERTY_AFFINE) != 0 && (left.properties & PROPERTY_AFFINE) != 0)
1390             return mulLocalAffine(left, dest);
1391         return mulLocalGeneric(left, dest);
1392     }
1393     private Matrix4d mulLocalGeneric(Matrix4d left, ref Matrix4d dest) {
1394         double nm00 = Math.fma(left.m00, m00, Math.fma(left.m10, m01, Math.fma(left.m20, m02, left.m30 * m03)));
1395         double nm01 = Math.fma(left.m01, m00, Math.fma(left.m11, m01, Math.fma(left.m21, m02, left.m31 * m03)));
1396         double nm02 = Math.fma(left.m02, m00, Math.fma(left.m12, m01, Math.fma(left.m22, m02, left.m32 * m03)));
1397         double nm03 = Math.fma(left.m03, m00, Math.fma(left.m13, m01, Math.fma(left.m23, m02, left.m33 * m03)));
1398         double nm10 = Math.fma(left.m00, m10, Math.fma(left.m10, m11, Math.fma(left.m20, m12, left.m30 * m13)));
1399         double nm11 = Math.fma(left.m01, m10, Math.fma(left.m11, m11, Math.fma(left.m21, m12, left.m31 * m13)));
1400         double nm12 = Math.fma(left.m02, m10, Math.fma(left.m12, m11, Math.fma(left.m22, m12, left.m32 * m13)));
1401         double nm13 = Math.fma(left.m03, m10, Math.fma(left.m13, m11, Math.fma(left.m23, m12, left.m33 * m13)));
1402         double nm20 = Math.fma(left.m00, m20, Math.fma(left.m10, m21, Math.fma(left.m20, m22, left.m30 * m23)));
1403         double nm21 = Math.fma(left.m01, m20, Math.fma(left.m11, m21, Math.fma(left.m21, m22, left.m31 * m23)));
1404         double nm22 = Math.fma(left.m02, m20, Math.fma(left.m12, m21, Math.fma(left.m22, m22, left.m32 * m23)));
1405         double nm23 = Math.fma(left.m03, m20, Math.fma(left.m13, m21, Math.fma(left.m23, m22, left.m33 * m23)));
1406         double nm30 = Math.fma(left.m00, m30, Math.fma(left.m10, m31, Math.fma(left.m20, m32, left.m30 * m33)));
1407         double nm31 = Math.fma(left.m01, m30, Math.fma(left.m11, m31, Math.fma(left.m21, m32, left.m31 * m33)));
1408         double nm32 = Math.fma(left.m02, m30, Math.fma(left.m12, m31, Math.fma(left.m22, m32, left.m32 * m33)));
1409         double nm33 = Math.fma(left.m03, m30, Math.fma(left.m13, m31, Math.fma(left.m23, m32, left.m33 * m33)));
1410         return dest
1411         ._m00(nm00)
1412         ._m01(nm01)
1413         ._m02(nm02)
1414         ._m03(nm03)
1415         ._m10(nm10)
1416         ._m11(nm11)
1417         ._m12(nm12)
1418         ._m13(nm13)
1419         ._m20(nm20)
1420         ._m21(nm21)
1421         ._m22(nm22)
1422         ._m23(nm23)
1423         ._m30(nm30)
1424         ._m31(nm31)
1425         ._m32(nm32)
1426         ._m33(nm33)
1427         ._properties(0);
1428     }
1429 
1430     /**
1431      * Pre-multiply this matrix by the supplied <code>left</code> matrix, both of which are assumed to be {@link #isAffine() affine}, and store the result in <code>this</code>.
1432      * <p>
1433      * This method assumes that <code>this</code> matrix and the given <code>left</code> matrix both represent an {@link #isAffine() affine} transformation
1434      * (i.e. their last rows are equal to <code>(0, 0, 0, 1)</code>)
1435      * and can be used to speed up matrix multiplication if the matrices only represent affine transformations, such as translation, rotation, scaling and shearing (in any combination).
1436      * <p>
1437      * This method will not modify either the last row of <code>this</code> or the last row of <code>left</code>.
1438      * <p>
1439      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the <code>left</code> matrix,
1440      * then the new matrix will be <code>L * M</code>. So when transforming a
1441      * vector <code>v</code> with the new matrix by using <code>L * M * v</code>, the
1442      * transformation of <code>this</code> matrix will be applied first!
1443      *
1444      * @param left
1445      *          the left operand of the matrix multiplication (the last row is assumed to be <code>(0, 0, 0, 1)</code>)
1446      * @return this
1447      */
1448     ref public Matrix4d mulLocalAffine(Matrix4d left) return {
1449        mulLocalAffine(left, this);
1450        return this;
1451     }
1452 
1453     public Matrix4d mulLocalAffine(Matrix4d left, ref Matrix4d dest) {
1454         double nm00 = left.m00 * m00 + left.m10 * m01 + left.m20 * m02;
1455         double nm01 = left.m01 * m00 + left.m11 * m01 + left.m21 * m02;
1456         double nm02 = left.m02 * m00 + left.m12 * m01 + left.m22 * m02;
1457         double nm03 = left.m03;
1458         double nm10 = left.m00 * m10 + left.m10 * m11 + left.m20 * m12;
1459         double nm11 = left.m01 * m10 + left.m11 * m11 + left.m21 * m12;
1460         double nm12 = left.m02 * m10 + left.m12 * m11 + left.m22 * m12;
1461         double nm13 = left.m13;
1462         double nm20 = left.m00 * m20 + left.m10 * m21 + left.m20 * m22;
1463         double nm21 = left.m01 * m20 + left.m11 * m21 + left.m21 * m22;
1464         double nm22 = left.m02 * m20 + left.m12 * m21 + left.m22 * m22;
1465         double nm23 = left.m23;
1466         double nm30 = left.m00 * m30 + left.m10 * m31 + left.m20 * m32 + left.m30;
1467         double nm31 = left.m01 * m30 + left.m11 * m31 + left.m21 * m32 + left.m31;
1468         double nm32 = left.m02 * m30 + left.m12 * m31 + left.m22 * m32 + left.m32;
1469         double nm33 = left.m33;
1470         dest._m00(nm00)
1471         ._m01(nm01)
1472         ._m02(nm02)
1473         ._m03(nm03)
1474         ._m10(nm10)
1475         ._m11(nm11)
1476         ._m12(nm12)
1477         ._m13(nm13)
1478         ._m20(nm20)
1479         ._m21(nm21)
1480         ._m22(nm22)
1481         ._m23(nm23)
1482         ._m30(nm30)
1483         ._m31(nm31)
1484         ._m32(nm32)
1485         ._m33(nm33)
1486         ._properties(PROPERTY_AFFINE);
1487         return dest;
1488     }
1489 
1490     /**
1491      * Multiply this matrix by the supplied <code>right</code> matrix.
1492      * <p>
1493      * The last row of the <code>right</code> matrix is assumed to be <code>(0, 0, 0, 1)</code>.
1494      * <p>
1495      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the <code>right</code> matrix,
1496      * then the new matrix will be <code>M * R</code>. So when transforming a
1497      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
1498      * transformation of the right matrix will be applied first!
1499      *
1500      * @param right
1501      *          the right operand of the matrix multiplication
1502      * @return this
1503      */
1504     ref public Matrix4d mul(Matrix4x3d right) return {
1505         mul(right, this);
1506         return this;
1507     }
1508 
1509     public Matrix4d mul(Matrix4x3d right, ref Matrix4d dest) {
1510         if ((properties & PROPERTY_IDENTITY) != 0)
1511             return dest.set(right);
1512         else if ((right.properties & PROPERTY_IDENTITY) != 0)
1513             return dest.set(this);
1514         else if ((properties & PROPERTY_TRANSLATION) != 0)
1515             return mulTranslation(right, dest);
1516         else if ((properties & PROPERTY_AFFINE) != 0)
1517             return mulAffine(right, dest);
1518         else if ((properties & PROPERTY_PERSPECTIVE) != 0)
1519             return mulPerspectiveAffine(right, dest);
1520         return mulGeneric(right, dest);
1521     }
1522     private Matrix4d mulTranslation(Matrix4x3d right, ref Matrix4d dest) {
1523         return dest
1524         ._m00(right.m00)
1525         ._m01(right.m01)
1526         ._m02(right.m02)
1527         ._m03(m03)
1528         ._m10(right.m10)
1529         ._m11(right.m11)
1530         ._m12(right.m12)
1531         ._m13(m13)
1532         ._m20(right.m20)
1533         ._m21(right.m21)
1534         ._m22(right.m22)
1535         ._m23(m23)
1536         ._m30(right.m30 + m30)
1537         ._m31(right.m31 + m31)
1538         ._m32(right.m32 + m32)
1539         ._m33(m33)
1540         ._properties(PROPERTY_AFFINE | (right.properties & PROPERTY_ORTHONORMAL));
1541     }
1542     private Matrix4d mulAffine(Matrix4x3d right, ref Matrix4d dest) {
1543         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
1544         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
1545         double m20 = this.m20, m21 = this.m21, m22 = this.m22;
1546         double rm00 = right.m00, rm01 = right.m01, rm02 = right.m02;
1547         double rm10 = right.m10, rm11 = right.m11, rm12 = right.m12;
1548         double rm20 = right.m20, rm21 = right.m21, rm22 = right.m22;
1549         double rm30 = right.m30, rm31 = right.m31, rm32 = right.m32;
1550         return dest
1551         ._m00(Math.fma(m00, rm00, Math.fma(m10, rm01, m20 * rm02)))
1552         ._m01(Math.fma(m01, rm00, Math.fma(m11, rm01, m21 * rm02)))
1553         ._m02(Math.fma(m02, rm00, Math.fma(m12, rm01, m22 * rm02)))
1554         ._m03(m03)
1555         ._m10(Math.fma(m00, rm10, Math.fma(m10, rm11, m20 * rm12)))
1556         ._m11(Math.fma(m01, rm10, Math.fma(m11, rm11, m21 * rm12)))
1557         ._m12(Math.fma(m02, rm10, Math.fma(m12, rm11, m22 * rm12)))
1558         ._m13(m13)
1559         ._m20(Math.fma(m00, rm20, Math.fma(m10, rm21, m20 * rm22)))
1560         ._m21(Math.fma(m01, rm20, Math.fma(m11, rm21, m21 * rm22)))
1561         ._m22(Math.fma(m02, rm20, Math.fma(m12, rm21, m22 * rm22)))
1562         ._m23(m23)
1563         ._m30(Math.fma(m00, rm30, Math.fma(m10, rm31, Math.fma(m20, rm32, m30))))
1564         ._m31(Math.fma(m01, rm30, Math.fma(m11, rm31, Math.fma(m21, rm32, m31))))
1565         ._m32(Math.fma(m02, rm30, Math.fma(m12, rm31, Math.fma(m22, rm32, m32))))
1566         ._m33(m33)
1567         ._properties(PROPERTY_AFFINE | (this.properties & right.properties & PROPERTY_ORTHONORMAL));
1568     }
1569     private Matrix4d mulGeneric(Matrix4x3d right, ref Matrix4d dest) {
1570         double nm00 = Math.fma(m00, right.m00, Math.fma(m10, right.m01, m20 * right.m02));
1571         double nm01 = Math.fma(m01, right.m00, Math.fma(m11, right.m01, m21 * right.m02));
1572         double nm02 = Math.fma(m02, right.m00, Math.fma(m12, right.m01, m22 * right.m02));
1573         double nm03 = Math.fma(m03, right.m00, Math.fma(m13, right.m01, m23 * right.m02));
1574         double nm10 = Math.fma(m00, right.m10, Math.fma(m10, right.m11, m20 * right.m12));
1575         double nm11 = Math.fma(m01, right.m10, Math.fma(m11, right.m11, m21 * right.m12));
1576         double nm12 = Math.fma(m02, right.m10, Math.fma(m12, right.m11, m22 * right.m12));
1577         double nm13 = Math.fma(m03, right.m10, Math.fma(m13, right.m11, m23 * right.m12));
1578         double nm20 = Math.fma(m00, right.m20, Math.fma(m10, right.m21, m20 * right.m22));
1579         double nm21 = Math.fma(m01, right.m20, Math.fma(m11, right.m21, m21 * right.m22));
1580         double nm22 = Math.fma(m02, right.m20, Math.fma(m12, right.m21, m22 * right.m22));
1581         double nm23 = Math.fma(m03, right.m20, Math.fma(m13, right.m21, m23 * right.m22));
1582         double nm30 = Math.fma(m00, right.m30, Math.fma(m10, right.m31, Math.fma(m20, right.m32, m30)));
1583         double nm31 = Math.fma(m01, right.m30, Math.fma(m11, right.m31, Math.fma(m21, right.m32, m31)));
1584         double nm32 = Math.fma(m02, right.m30, Math.fma(m12, right.m31, Math.fma(m22, right.m32, m32)));
1585         double nm33 = Math.fma(m03, right.m30, Math.fma(m13, right.m31, Math.fma(m23, right.m32, m33)));
1586         dest._m00(nm00)
1587         ._m01(nm01)
1588         ._m02(nm02)
1589         ._m03(nm03)
1590         ._m10(nm10)
1591         ._m11(nm11)
1592         ._m12(nm12)
1593         ._m13(nm13)
1594         ._m20(nm20)
1595         ._m21(nm21)
1596         ._m22(nm22)
1597         ._m23(nm23)
1598         ._m30(nm30)
1599         ._m31(nm31)
1600         ._m32(nm32)
1601         ._m33(nm33)
1602         ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
1603         return dest;
1604     }
1605     public Matrix4d mulPerspectiveAffine(Matrix4x3d view, ref Matrix4d dest) {
1606         double lm00 = m00, lm11 = m11, lm22 = m22, lm23 = m23;
1607         dest._m00(lm00 * view.m00)._m01(lm11 * view.m01)._m02(lm22 * view.m02)._m03(lm23 * view.m02).
1608         _m10(lm00 * view.m10)._m11(lm11 * view.m11)._m12(lm22 * view.m12)._m13(lm23 * view.m12).
1609         _m20(lm00 * view.m20)._m21(lm11 * view.m21)._m22(lm22 * view.m22)._m23(lm23 * view.m22).
1610         _m30(lm00 * view.m30)._m31(lm11 * view.m31)._m32(lm22 * view.m32 + m32)._m33(lm23 * view.m32)
1611         ._properties(0);
1612         return dest;
1613     }
1614 
1615 
1616     /**
1617      * Multiply this matrix by the supplied <code>right</code> matrix and store the result in <code>this</code>.
1618      * <p>
1619      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the <code>right</code> matrix,
1620      * then the new matrix will be <code>M * R</code>. So when transforming a
1621      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
1622      * transformation of the right matrix will be applied first!
1623      *
1624      * @param right
1625      *          the right operand of the matrix multiplication
1626      * @return this
1627      */
1628     ref public Matrix4d mul(Matrix3x2d right) return {
1629         mul(right, this);
1630         return this;
1631     }
1632 
1633     public Matrix4d mul(Matrix3x2d right, ref Matrix4d dest) {
1634         double nm00 = m00 * right.m00 + m10 * right.m01;
1635         double nm01 = m01 * right.m00 + m11 * right.m01;
1636         double nm02 = m02 * right.m00 + m12 * right.m01;
1637         double nm03 = m03 * right.m00 + m13 * right.m01;
1638         double nm10 = m00 * right.m10 + m10 * right.m11;
1639         double nm11 = m01 * right.m10 + m11 * right.m11;
1640         double nm12 = m02 * right.m10 + m12 * right.m11;
1641         double nm13 = m03 * right.m10 + m13 * right.m11;
1642         double nm30 = m00 * right.m20 + m10 * right.m21 + m30;
1643         double nm31 = m01 * right.m20 + m11 * right.m21 + m31;
1644         double nm32 = m02 * right.m20 + m12 * right.m21 + m32;
1645         double nm33 = m03 * right.m20 + m13 * right.m21 + m33;
1646         dest._m00(nm00)
1647         ._m01(nm01)
1648         ._m02(nm02)
1649         ._m03(nm03)
1650         ._m10(nm10)
1651         ._m11(nm11)
1652         ._m12(nm12)
1653         ._m13(nm13)
1654         ._m20(m20)
1655         ._m21(m21)
1656         ._m22(m22)
1657         ._m23(m23)
1658         ._m30(nm30)
1659         ._m31(nm31)
1660         ._m32(nm32)
1661         ._m33(nm33)
1662         ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
1663         return dest;
1664     }
1665 
1666 
1667 
1668 
1669     /**
1670      * Multiply <code>this</code> symmetric perspective projection matrix by the supplied {@link #isAffine() affine} <code>view</code> matrix.
1671      * <p>
1672      * If <code>P</code> is <code>this</code> matrix and <code>V</code> the <code>view</code> matrix,
1673      * then the new matrix will be <code>P * V</code>. So when transforming a
1674      * vector <code>v</code> with the new matrix by using <code>P * V * v</code>, the
1675      * transformation of the <code>view</code> matrix will be applied first!
1676      *
1677      * @param view
1678      *          the {@link #isAffine() affine} matrix to multiply <code>this</code> symmetric perspective projection matrix by
1679      * @return this
1680      */
1681     ref public Matrix4d mulPerspectiveAffine(Matrix4d view) return {
1682        mulPerspectiveAffine(view, this);
1683        return this;
1684     }
1685 
1686     public Matrix4d mulPerspectiveAffine(Matrix4d view, ref Matrix4d dest) {
1687         double nm00 = m00 * view.m00, nm01 = m11 * view.m01, nm02 = m22 * view.m02, nm03 = m23 * view.m02;
1688         double nm10 = m00 * view.m10, nm11 = m11 * view.m11, nm12 = m22 * view.m12, nm13 = m23 * view.m12;
1689         double nm20 = m00 * view.m20, nm21 = m11 * view.m21, nm22 = m22 * view.m22, nm23 = m23 * view.m22;
1690         double nm30 = m00 * view.m30, nm31 = m11 * view.m31, nm32 = m22 * view.m32 + m32, nm33 = m23 * view.m32;
1691         return dest
1692             ._m00(nm00)._m01(nm01)._m02(nm02)._m03(nm03)
1693             ._m10(nm10)._m11(nm11)._m12(nm12)._m13(nm13)
1694             ._m20(nm20)._m21(nm21)._m22(nm22)._m23(nm23)
1695             ._m30(nm30)._m31(nm31)._m32(nm32)._m33(nm33)
1696             ._properties(0);
1697     }
1698 
1699     /**
1700      * Multiply this matrix by the supplied <code>right</code> matrix, which is assumed to be {@link #isAffine() affine}, and store the result in <code>this</code>.
1701      * <p>
1702      * This method assumes that the given <code>right</code> matrix represents an {@link #isAffine() affine} transformation (i.e. its last row is equal to <code>(0, 0, 0, 1)</code>)
1703      * and can be used to speed up matrix multiplication if the matrix only represents affine transformations, such as translation, rotation, scaling and shearing (in any combination).
1704      * <p>
1705      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the <code>right</code> matrix,
1706      * then the new matrix will be <code>M * R</code>. So when transforming a
1707      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
1708      * transformation of the right matrix will be applied first!
1709      *
1710      * @param right
1711      *          the right operand of the matrix multiplication (the last row is assumed to be <code>(0, 0, 0, 1)</code>)
1712      * @return this
1713      */
1714     ref public Matrix4d mulAffineR(Matrix4d right) return {
1715        mulAffineR(right, this);
1716        return this;
1717     }
1718 
1719     public Matrix4d mulAffineR(Matrix4d right, ref Matrix4d dest) {
1720         double nm00 = Math.fma(m00, right.m00, Math.fma(m10, right.m01, m20 * right.m02));
1721         double nm01 = Math.fma(m01, right.m00, Math.fma(m11, right.m01, m21 * right.m02));
1722         double nm02 = Math.fma(m02, right.m00, Math.fma(m12, right.m01, m22 * right.m02));
1723         double nm03 = Math.fma(m03, right.m00, Math.fma(m13, right.m01, m23 * right.m02));
1724         double nm10 = Math.fma(m00, right.m10, Math.fma(m10, right.m11, m20 * right.m12));
1725         double nm11 = Math.fma(m01, right.m10, Math.fma(m11, right.m11, m21 * right.m12));
1726         double nm12 = Math.fma(m02, right.m10, Math.fma(m12, right.m11, m22 * right.m12));
1727         double nm13 = Math.fma(m03, right.m10, Math.fma(m13, right.m11, m23 * right.m12));
1728         double nm20 = Math.fma(m00, right.m20, Math.fma(m10, right.m21, m20 * right.m22));
1729         double nm21 = Math.fma(m01, right.m20, Math.fma(m11, right.m21, m21 * right.m22));
1730         double nm22 = Math.fma(m02, right.m20, Math.fma(m12, right.m21, m22 * right.m22));
1731         double nm23 = Math.fma(m03, right.m20, Math.fma(m13, right.m21, m23 * right.m22));
1732         double nm30 = Math.fma(m00, right.m30, Math.fma(m10, right.m31, Math.fma(m20, right.m32, m30)));
1733         double nm31 = Math.fma(m01, right.m30, Math.fma(m11, right.m31, Math.fma(m21, right.m32, m31)));
1734         double nm32 = Math.fma(m02, right.m30, Math.fma(m12, right.m31, Math.fma(m22, right.m32, m32)));
1735         double nm33 = Math.fma(m03, right.m30, Math.fma(m13, right.m31, Math.fma(m23, right.m32, m33)));
1736         dest._m00(nm00)
1737         ._m01(nm01)
1738         ._m02(nm02)
1739         ._m03(nm03)
1740         ._m10(nm10)
1741         ._m11(nm11)
1742         ._m12(nm12)
1743         ._m13(nm13)
1744         ._m20(nm20)
1745         ._m21(nm21)
1746         ._m22(nm22)
1747         ._m23(nm23)
1748         ._m30(nm30)
1749         ._m31(nm31)
1750         ._m32(nm32)
1751         ._m33(nm33)
1752         ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
1753         return dest;
1754     }
1755 
1756     /**
1757      * Multiply this matrix by the supplied <code>right</code> matrix, both of which are assumed to be {@link #isAffine() affine}, and store the result in <code>this</code>.
1758      * <p>
1759      * This method assumes that <code>this</code> matrix and the given <code>right</code> matrix both represent an {@link #isAffine() affine} transformation
1760      * (i.e. their last rows are equal to <code>(0, 0, 0, 1)</code>)
1761      * and can be used to speed up matrix multiplication if the matrices only represent affine transformations, such as translation, rotation, scaling and shearing (in any combination).
1762      * <p>
1763      * This method will not modify either the last row of <code>this</code> or the last row of <code>right</code>.
1764      * <p>
1765      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the <code>right</code> matrix,
1766      * then the new matrix will be <code>M * R</code>. So when transforming a
1767      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
1768      * transformation of the right matrix will be applied first!
1769      *
1770      * @param right
1771      *          the right operand of the matrix multiplication (the last row is assumed to be <code>(0, 0, 0, 1)</code>)
1772      * @return this
1773      */
1774     ref public Matrix4d mulAffine(Matrix4d right) return {
1775        mulAffine(right, this);
1776        return this;
1777     }
1778 
1779     public Matrix4d mulAffine(Matrix4d right, ref Matrix4d dest) {
1780         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
1781         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
1782         double m20 = this.m20, m21 = this.m21, m22 = this.m22;
1783         double rm00 = right.m00, rm01 = right.m01, rm02 = right.m02;
1784         double rm10 = right.m10, rm11 = right.m11, rm12 = right.m12;
1785         double rm20 = right.m20, rm21 = right.m21, rm22 = right.m22;
1786         double rm30 = right.m30, rm31 = right.m31, rm32 = right.m32;
1787         return dest
1788         ._m00(Math.fma(m00, rm00, Math.fma(m10, rm01, m20 * rm02)))
1789         ._m01(Math.fma(m01, rm00, Math.fma(m11, rm01, m21 * rm02)))
1790         ._m02(Math.fma(m02, rm00, Math.fma(m12, rm01, m22 * rm02)))
1791         ._m03(m03)
1792         ._m10(Math.fma(m00, rm10, Math.fma(m10, rm11, m20 * rm12)))
1793         ._m11(Math.fma(m01, rm10, Math.fma(m11, rm11, m21 * rm12)))
1794         ._m12(Math.fma(m02, rm10, Math.fma(m12, rm11, m22 * rm12)))
1795         ._m13(m13)
1796         ._m20(Math.fma(m00, rm20, Math.fma(m10, rm21, m20 * rm22)))
1797         ._m21(Math.fma(m01, rm20, Math.fma(m11, rm21, m21 * rm22)))
1798         ._m22(Math.fma(m02, rm20, Math.fma(m12, rm21, m22 * rm22)))
1799         ._m23(m23)
1800         ._m30(Math.fma(m00, rm30, Math.fma(m10, rm31, Math.fma(m20, rm32, m30))))
1801         ._m31(Math.fma(m01, rm30, Math.fma(m11, rm31, Math.fma(m21, rm32, m31))))
1802         ._m32(Math.fma(m02, rm30, Math.fma(m12, rm31, Math.fma(m22, rm32, m32))))
1803         ._m33(m33)
1804         ._properties(PROPERTY_AFFINE | (this.properties & right.properties & PROPERTY_ORTHONORMAL));
1805     }
1806 
1807     public Matrix4d mulTranslationAffine(Matrix4d right, ref Matrix4d dest) {
1808         return dest
1809         ._m00(right.m00)
1810         ._m01(right.m01)
1811         ._m02(right.m02)
1812         ._m03(m03)
1813         ._m10(right.m10)
1814         ._m11(right.m11)
1815         ._m12(right.m12)
1816         ._m13(m13)
1817         ._m20(right.m20)
1818         ._m21(right.m21)
1819         ._m22(right.m22)
1820         ._m23(m23)
1821         ._m30(right.m30 + m30)
1822         ._m31(right.m31 + m31)
1823         ._m32(right.m32 + m32)
1824         ._m33(m33)
1825         ._properties(PROPERTY_AFFINE | (right.properties & PROPERTY_ORTHONORMAL));
1826     }
1827 
1828     /**
1829      * Multiply <code>this</code> orthographic projection matrix by the supplied {@link #isAffine() affine} <code>view</code> matrix.
1830      * <p>
1831      * If <code>M</code> is <code>this</code> matrix and <code>V</code> the <code>view</code> matrix,
1832      * then the new matrix will be <code>M * V</code>. So when transforming a
1833      * vector <code>v</code> with the new matrix by using <code>M * V * v</code>, the
1834      * transformation of the <code>view</code> matrix will be applied first!
1835      *
1836      * @param view
1837      *          the affine matrix which to multiply <code>this</code> with
1838      * @return this
1839      */
1840     ref public Matrix4d mulOrthoAffine(Matrix4d view) return {
1841         mulOrthoAffine(view, this);
1842         return this;
1843     }
1844 
1845     public Matrix4d mulOrthoAffine(Matrix4d view, ref Matrix4d dest) {
1846         double nm00 = m00 * view.m00;
1847         double nm01 = m11 * view.m01;
1848         double nm02 = m22 * view.m02;
1849         double nm03 = 0.0;
1850         double nm10 = m00 * view.m10;
1851         double nm11 = m11 * view.m11;
1852         double nm12 = m22 * view.m12;
1853         double nm13 = 0.0;
1854         double nm20 = m00 * view.m20;
1855         double nm21 = m11 * view.m21;
1856         double nm22 = m22 * view.m22;
1857         double nm23 = 0.0;
1858         double nm30 = m00 * view.m30 + m30;
1859         double nm31 = m11 * view.m31 + m31;
1860         double nm32 = m22 * view.m32 + m32;
1861         double nm33 = 1.0;
1862         dest._m00(nm00)
1863         ._m01(nm01)
1864         ._m02(nm02)
1865         ._m03(nm03)
1866         ._m10(nm10)
1867         ._m11(nm11)
1868         ._m12(nm12)
1869         ._m13(nm13)
1870         ._m20(nm20)
1871         ._m21(nm21)
1872         ._m22(nm22)
1873         ._m23(nm23)
1874         ._m30(nm30)
1875         ._m31(nm31)
1876         ._m32(nm32)
1877         ._m33(nm33)
1878         ._properties(PROPERTY_AFFINE);
1879         return dest;
1880     }
1881 
1882     /**
1883      * Component-wise add the upper 4x3 submatrices of <code>this</code> and <code>other</code>
1884      * by first multiplying each component of <code>other</code>'s 4x3 submatrix by <code>otherFactor</code> and
1885      * adding that result to <code>this</code>.
1886      * <p>
1887      * The matrix <code>other</code> will not be changed.
1888      * 
1889      * @param other
1890      *          the other matrix
1891      * @param otherFactor
1892      *          the factor to multiply each of the other matrix's 4x3 components
1893      * @return this
1894      */
1895     ref public Matrix4d fma4x3(Matrix4d other, double otherFactor) return {
1896         fma4x3(other, otherFactor, this);
1897         return this;
1898     }
1899 
1900     public Matrix4d fma4x3(Matrix4d other, double otherFactor, ref Matrix4d dest) {
1901         dest._m00(Math.fma(other.m00, otherFactor, m00))
1902         ._m01(Math.fma(other.m01, otherFactor, m01))
1903         ._m02(Math.fma(other.m02, otherFactor, m02))
1904         ._m03(m03)
1905         ._m10(Math.fma(other.m10, otherFactor, m10))
1906         ._m11(Math.fma(other.m11, otherFactor, m11))
1907         ._m12(Math.fma(other.m12, otherFactor, m12))
1908         ._m13(m13)
1909         ._m20(Math.fma(other.m20, otherFactor, m20))
1910         ._m21(Math.fma(other.m21, otherFactor, m21))
1911         ._m22(Math.fma(other.m22, otherFactor, m22))
1912         ._m23(m23)
1913         ._m30(Math.fma(other.m30, otherFactor, m30))
1914         ._m31(Math.fma(other.m31, otherFactor, m31))
1915         ._m32(Math.fma(other.m32, otherFactor, m32))
1916         ._m33(m33)
1917         ._properties(0);
1918         return dest;
1919     }
1920 
1921     /**
1922      * Component-wise add <code>this</code> and <code>other</code>.
1923      * 
1924      * @param other
1925      *          the other addend
1926      * @return this
1927      */
1928     ref public Matrix4d add(Matrix4d other) return {
1929         add(other, this);
1930         return this;
1931     }
1932 
1933     public Matrix4d add(Matrix4d other, ref Matrix4d dest) {
1934         dest._m00(m00 + other.m00)
1935         ._m01(m01 + other.m01)
1936         ._m02(m02 + other.m02)
1937         ._m03(m03 + other.m03)
1938         ._m10(m10 + other.m10)
1939         ._m11(m11 + other.m11)
1940         ._m12(m12 + other.m12)
1941         ._m13(m13 + other.m13)
1942         ._m20(m20 + other.m20)
1943         ._m21(m21 + other.m21)
1944         ._m22(m22 + other.m22)
1945         ._m23(m23 + other.m23)
1946         ._m30(m30 + other.m30)
1947         ._m31(m31 + other.m31)
1948         ._m32(m32 + other.m32)
1949         ._m33(m33 + other.m33)
1950         ._properties(0);
1951         return dest;
1952     }
1953 
1954     /**
1955      * Component-wise subtract <code>subtrahend</code> from <code>this</code>.
1956      * 
1957      * @param subtrahend
1958      *          the subtrahend
1959      * @return this
1960      */
1961     ref public Matrix4d sub(Matrix4d subtrahend) return {
1962         sub(subtrahend, this);
1963         return this;
1964     }
1965 
1966     public Matrix4d sub(Matrix4d subtrahend, ref Matrix4d dest) {
1967         dest._m00(m00 - subtrahend.m00)
1968         ._m01(m01 - subtrahend.m01)
1969         ._m02(m02 - subtrahend.m02)
1970         ._m03(m03 - subtrahend.m03)
1971         ._m10(m10 - subtrahend.m10)
1972         ._m11(m11 - subtrahend.m11)
1973         ._m12(m12 - subtrahend.m12)
1974         ._m13(m13 - subtrahend.m13)
1975         ._m20(m20 - subtrahend.m20)
1976         ._m21(m21 - subtrahend.m21)
1977         ._m22(m22 - subtrahend.m22)
1978         ._m23(m23 - subtrahend.m23)
1979         ._m30(m30 - subtrahend.m30)
1980         ._m31(m31 - subtrahend.m31)
1981         ._m32(m32 - subtrahend.m32)
1982         ._m33(m33 - subtrahend.m33)
1983         ._properties(0);
1984         return dest;
1985     }
1986 
1987     /**
1988      * Component-wise multiply <code>this</code> by <code>other</code>.
1989      * 
1990      * @param other
1991      *          the other matrix
1992      * @return this
1993      */
1994     ref public Matrix4d mulComponentWise(Matrix4d other) return {
1995         mulComponentWise(other, this);
1996         return this;
1997     }
1998 
1999     public Matrix4d mulComponentWise(Matrix4d other, ref Matrix4d dest) {
2000         dest._m00(m00 * other.m00)
2001         ._m01(m01 * other.m01)
2002         ._m02(m02 * other.m02)
2003         ._m03(m03 * other.m03)
2004         ._m10(m10 * other.m10)
2005         ._m11(m11 * other.m11)
2006         ._m12(m12 * other.m12)
2007         ._m13(m13 * other.m13)
2008         ._m20(m20 * other.m20)
2009         ._m21(m21 * other.m21)
2010         ._m22(m22 * other.m22)
2011         ._m23(m23 * other.m23)
2012         ._m30(m30 * other.m30)
2013         ._m31(m31 * other.m31)
2014         ._m32(m32 * other.m32)
2015         ._m33(m33 * other.m33)
2016         ._properties(0);
2017         return dest;
2018     }
2019 
2020     /**
2021      * Component-wise add the upper 4x3 submatrices of <code>this</code> and <code>other</code>.
2022      * 
2023      * @param other
2024      *          the other addend
2025      * @return this
2026      */
2027     ref public Matrix4d add4x3(Matrix4d other) return {
2028         add4x3(other, this);
2029         return this;
2030     }
2031 
2032     public Matrix4d add4x3(Matrix4d other, ref Matrix4d dest) {
2033         dest._m00(m00 + other.m00)
2034         ._m01(m01 + other.m01)
2035         ._m02(m02 + other.m02)
2036         ._m03(m03)
2037         ._m10(m10 + other.m10)
2038         ._m11(m11 + other.m11)
2039         ._m12(m12 + other.m12)
2040         ._m13(m13)
2041         ._m20(m20 + other.m20)
2042         ._m21(m21 + other.m21)
2043         ._m22(m22 + other.m22)
2044         ._m23(m23)
2045         ._m30(m30 + other.m30)
2046         ._m31(m31 + other.m31)
2047         ._m32(m32 + other.m32)
2048         ._m33(m33)
2049         ._properties(0);
2050         return dest;
2051     }
2052 
2053     /**
2054      * Component-wise subtract the upper 4x3 submatrices of <code>subtrahend</code> from <code>this</code>.
2055      * 
2056      * @param subtrahend
2057      *          the subtrahend
2058      * @return this
2059      */
2060     ref public Matrix4d sub4x3(Matrix4d subtrahend) return {
2061         sub4x3(subtrahend, this);
2062         return this;
2063     }
2064 
2065     public Matrix4d sub4x3(Matrix4d subtrahend, ref Matrix4d dest) {
2066         dest._m00(m00 - subtrahend.m00)
2067         ._m01(m01 - subtrahend.m01)
2068         ._m02(m02 - subtrahend.m02)
2069         ._m03(m03)
2070         ._m10(m10 - subtrahend.m10)
2071         ._m11(m11 - subtrahend.m11)
2072         ._m12(m12 - subtrahend.m12)
2073         ._m13(m13)
2074         ._m20(m20 - subtrahend.m20)
2075         ._m21(m21 - subtrahend.m21)
2076         ._m22(m22 - subtrahend.m22)
2077         ._m23(m23)
2078         ._m30(m30 - subtrahend.m30)
2079         ._m31(m31 - subtrahend.m31)
2080         ._m32(m32 - subtrahend.m32)
2081         ._m33(m33)
2082         ._properties(0);
2083         return dest;
2084     }
2085 
2086     /**
2087      * Component-wise multiply the upper 4x3 submatrices of <code>this</code> by <code>other</code>.
2088      * 
2089      * @param other
2090      *          the other matrix
2091      * @return this
2092      */
2093     ref public Matrix4d mul4x3ComponentWise(Matrix4d other) return {
2094         mul4x3ComponentWise(other, this);
2095         return this;
2096     }
2097 
2098     public Matrix4d mul4x3ComponentWise(Matrix4d other, ref Matrix4d dest) {
2099         dest._m00(m00 * other.m00)
2100         ._m01(m01 * other.m01)
2101         ._m02(m02 * other.m02)
2102         ._m03(m03)
2103         ._m10(m10 * other.m10)
2104         ._m11(m11 * other.m11)
2105         ._m12(m12 * other.m12)
2106         ._m13(m13)
2107         ._m20(m20 * other.m20)
2108         ._m21(m21 * other.m21)
2109         ._m22(m22 * other.m22)
2110         ._m23(m23)
2111         ._m30(m30 * other.m30)
2112         ._m31(m31 * other.m31)
2113         ._m32(m32 * other.m32)
2114         ._m33(m33)
2115         ._properties(0);
2116         return dest;
2117     }
2118 
2119     /** Set the values within this matrix to the supplied double values. The matrix will look like this:<br><br>
2120      *  
2121      * m00, m10, m20, m30<br>
2122      * m01, m11, m21, m31<br>
2123      * m02, m12, m22, m32<br>
2124      * m03, m13, m23, m33
2125      *
2126      * @param m00
2127      *          the new value of m00
2128      * @param m01
2129      *          the new value of m01
2130      * @param m02
2131      *          the new value of m02
2132      * @param m03
2133      *          the new value of m03
2134      * @param m10
2135      *          the new value of m10
2136      * @param m11
2137      *          the new value of m11
2138      * @param m12
2139      *          the new value of m12
2140      * @param m13
2141      *          the new value of m13
2142      * @param m20
2143      *          the new value of m20
2144      * @param m21
2145      *          the new value of m21
2146      * @param m22
2147      *          the new value of m22
2148      * @param m23
2149      *          the new value of m23
2150      * @param m30
2151      *          the new value of m30
2152      * @param m31
2153      *          the new value of m31
2154      * @param m32
2155      *          the new value of m32
2156      * @param m33
2157      *          the new value of m33
2158      * @return this
2159      */
2160     ref public Matrix4d set(double m00, double m01, double m02,double m03,
2161                         double m10, double m11, double m12, double m13,
2162                         double m20, double m21, double m22, double m23, 
2163                         double m30, double m31, double m32, double m33) return {
2164         setm00(m00);
2165         setm10(m10);
2166         setm20(m20);
2167         setm30(m30);
2168         setm01(m01);
2169         setm11(m11);
2170         setm21(m21);
2171         setm31(m31);
2172         setm02(m02);
2173         setm12(m12);
2174         setm22(m22);
2175         setm32(m32);
2176         setm03(m03);
2177         setm13(m13);
2178         setm23(m23);
2179         setm33(m33);
2180         return determineProperties();
2181     }
2182 
2183     /**
2184      * Set the values in the matrix using a double array that contains the matrix elements in column-major order.
2185      * <p>
2186      * The results will look like this:<br><br>
2187      * 
2188      * 0, 4, 8, 12<br>
2189      * 1, 5, 9, 13<br>
2190      * 2, 6, 10, 14<br>
2191      * 3, 7, 11, 15<br>
2192      * 
2193      * @see #set(double[])
2194      * 
2195      * @param m
2196      *          the array to read the matrix values from
2197      * @param off
2198      *          the offset into the array
2199      * @return this
2200      */
2201     ref public Matrix4d set(double[] m, int off) return {
2202         return
2203         _m00(m[off+0]).
2204         _m01(m[off+1]).
2205         _m02(m[off+2]).
2206         _m03(m[off+3]).
2207         _m10(m[off+4]).
2208         _m11(m[off+5]).
2209         _m12(m[off+6]).
2210         _m13(m[off+7]).
2211         _m20(m[off+8]).
2212         _m21(m[off+9]).
2213         _m22(m[off+10]).
2214         _m23(m[off+11]).
2215         _m30(m[off+12]).
2216         _m31(m[off+13]).
2217         _m32(m[off+14]).
2218         _m33(m[off+15]).
2219         determineProperties();
2220     }
2221 
2222     /**
2223      * Set the values in the matrix using a double array that contains the matrix elements in column-major order.
2224      * <p>
2225      * The results will look like this:<br><br>
2226      * 
2227      * 0, 4, 8, 12<br>
2228      * 1, 5, 9, 13<br>
2229      * 2, 6, 10, 14<br>
2230      * 3, 7, 11, 15<br>
2231      * 
2232      * @see #set(double[], int)
2233      * 
2234      * @param m
2235      *          the array to read the matrix values from
2236      * @return this
2237      */
2238     ref public Matrix4d set(double[] m) return {
2239         return set(m, 0);
2240     }
2241 
2242     /**
2243      * Set the values in the matrix using a float array that contains the matrix elements in column-major order.
2244      * <p>
2245      * The results will look like this:<br><br>
2246      * 
2247      * 0, 4, 8, 12<br>
2248      * 1, 5, 9, 13<br>
2249      * 2, 6, 10, 14<br>
2250      * 3, 7, 11, 15<br>
2251      * 
2252      * @see #set(float[])
2253      * 
2254      * @param m
2255      *          the array to read the matrix values from
2256      * @param off
2257      *          the offset into the array
2258      * @return this
2259      */
2260     ref public Matrix4d set(float[] m, int off) return {
2261         return
2262         _m00(m[off+0]).
2263         _m01(m[off+1]).
2264         _m02(m[off+2]).
2265         _m03(m[off+3]).
2266         _m10(m[off+4]).
2267         _m11(m[off+5]).
2268         _m12(m[off+6]).
2269         _m13(m[off+7]).
2270         _m20(m[off+8]).
2271         _m21(m[off+9]).
2272         _m22(m[off+10]).
2273         _m23(m[off+11]).
2274         _m30(m[off+12]).
2275         _m31(m[off+13]).
2276         _m32(m[off+14]).
2277         _m33(m[off+15]).
2278         determineProperties();
2279     }
2280 
2281     /**
2282      * Set the values in the matrix using a float array that contains the matrix elements in column-major order.
2283      * <p>
2284      * The results will look like this:<br><br>
2285      * 
2286      * 0, 4, 8, 12<br>
2287      * 1, 5, 9, 13<br>
2288      * 2, 6, 10, 14<br>
2289      * 3, 7, 11, 15<br>
2290      * 
2291      * @see #set(float[], int)
2292      * 
2293      * @param m
2294      *          the array to read the matrix values from
2295      * @return this
2296      */
2297     ref public Matrix4d set(float[] m) return {
2298         return set(m, 0);
2299     }
2300 
2301 
2302     /**
2303      * Set the four columns of this matrix to the supplied vectors, respectively.
2304      * 
2305      * @param col0
2306      *          the first column
2307      * @param col1
2308      *          the second column
2309      * @param col2
2310      *          the third column
2311      * @param col3
2312      *          the fourth column
2313      * @return this
2314      */
2315     ref public Matrix4d set(ref Vector4d col0, Vector4d col1, Vector4d col2, Vector4d col3) return {
2316         return
2317         _m00(col0.x).
2318         _m01(col0.y).
2319         _m02(col0.z).
2320         _m03(col0.w).
2321         _m10(col1.x).
2322         _m11(col1.y).
2323         _m12(col1.z).
2324         _m13(col1.w).
2325         _m20(col2.x).
2326         _m21(col2.y).
2327         _m22(col2.z).
2328         _m23(col2.w).
2329         _m30(col3.x).
2330         _m31(col3.y).
2331         _m32(col3.z).
2332         _m33(col3.w).
2333         determineProperties();
2334     }
2335 
2336     public double determinant() {
2337         if ((properties & PROPERTY_AFFINE) != 0)
2338             return determinantAffine();
2339         return (m00 * m11 - m01 * m10) * (m22 * m33 - m23 * m32)
2340              + (m02 * m10 - m00 * m12) * (m21 * m33 - m23 * m31)
2341              + (m00 * m13 - m03 * m10) * (m21 * m32 - m22 * m31) 
2342              + (m01 * m12 - m02 * m11) * (m20 * m33 - m23 * m30)
2343              + (m03 * m11 - m01 * m13) * (m20 * m32 - m22 * m30) 
2344              + (m02 * m13 - m03 * m12) * (m20 * m31 - m21 * m30);
2345     }
2346 
2347     public double determinant3x3() {
2348         return (m00 * m11 - m01 * m10) * m22
2349              + (m02 * m10 - m00 * m12) * m21
2350              + (m01 * m12 - m02 * m11) * m20;
2351     }
2352 
2353     public double determinantAffine() {
2354         return (m00 * m11 - m01 * m10) * m22
2355              + (m02 * m10 - m00 * m12) * m21
2356              + (m01 * m12 - m02 * m11) * m20;
2357     }
2358 
2359     /**
2360      * Invert this matrix.
2361      * <p>
2362      * If <code>this</code> matrix represents an {@link #isAffine() affine} transformation, such as translation, rotation, scaling and shearing,
2363      * and thus its last row is equal to <code>(0, 0, 0, 1)</code>, then {@link #invertAffine()} can be used instead of this method.
2364      * 
2365      * @see #invertAffine()
2366      * 
2367      * @return this
2368      */
2369     ref public Matrix4d invert() return {
2370         invert(this);
2371         return this;
2372     }
2373 
2374     public Matrix4d invert(ref Matrix4d dest) {
2375         if ((properties & PROPERTY_IDENTITY) != 0)
2376             return dest.identity();
2377         else if ((properties & PROPERTY_TRANSLATION) != 0)
2378             return invertTranslation(dest);
2379         else if ((properties & PROPERTY_ORTHONORMAL) != 0)
2380             return invertOrthonormal(dest);
2381         else if ((properties & PROPERTY_AFFINE) != 0)
2382             return invertAffine(dest);
2383         else if ((properties & PROPERTY_PERSPECTIVE) != 0)
2384             return invertPerspective(dest);
2385         return invertGeneric(dest);
2386     }
2387     private Matrix4d invertTranslation(ref Matrix4d dest) {
2388         if (dest != this)
2389             dest.set(this);
2390         dest._m30(-m30)
2391         ._m31(-m31)
2392         ._m32(-m32)
2393         ._properties(PROPERTY_AFFINE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL);
2394         return dest;
2395     }
2396     private Matrix4d invertOrthonormal(ref Matrix4d dest) {
2397         double nm30 = -(m00 * m30 + m01 * m31 + m02 * m32);
2398         double nm31 = -(m10 * m30 + m11 * m31 + m12 * m32);
2399         double nm32 = -(m20 * m30 + m21 * m31 + m22 * m32);
2400         double m01 = this.m01;
2401         double m02 = this.m02;
2402         double m12 = this.m12;
2403         dest._m00(m00)
2404         ._m01(m10)
2405         ._m02(m20)
2406         ._m03(0.0)
2407         ._m10(m01)
2408         ._m11(m11)
2409         ._m12(m21)
2410         ._m13(0.0)
2411         ._m20(m02)
2412         ._m21(m12)
2413         ._m22(m22)
2414         ._m23(0.0)
2415         ._m30(nm30)
2416         ._m31(nm31)
2417         ._m32(nm32)
2418         ._m33(1.0)
2419         ._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL);
2420         return dest;
2421     }
2422     private Matrix4d invertGeneric(ref Matrix4d dest) {
2423         if (this != dest)
2424             return invertGenericNonThis(dest);
2425         return invertGenericThis(dest);
2426     }
2427     private Matrix4d invertGenericNonThis(ref Matrix4d dest) {
2428         double a = m00 * m11 - m01 * m10;
2429         double b = m00 * m12 - m02 * m10;
2430         double c = m00 * m13 - m03 * m10;
2431         double d = m01 * m12 - m02 * m11;
2432         double e = m01 * m13 - m03 * m11;
2433         double f = m02 * m13 - m03 * m12;
2434         double g = m20 * m31 - m21 * m30;
2435         double h = m20 * m32 - m22 * m30;
2436         double i = m20 * m33 - m23 * m30;
2437         double j = m21 * m32 - m22 * m31;
2438         double k = m21 * m33 - m23 * m31;
2439         double l = m22 * m33 - m23 * m32;
2440         double det = a * l - b * k + c * j + d * i - e * h + f * g;
2441         det = 1.0 / det;
2442         return dest
2443         ._m00(Math.fma( m11, l, Math.fma(-m12, k,  m13 * j)) * det)
2444         ._m01(Math.fma(-m01, l, Math.fma( m02, k, -m03 * j)) * det)
2445         ._m02(Math.fma( m31, f, Math.fma(-m32, e,  m33 * d)) * det)
2446         ._m03(Math.fma(-m21, f, Math.fma( m22, e, -m23 * d)) * det)
2447         ._m10(Math.fma(-m10, l, Math.fma( m12, i, -m13 * h)) * det)
2448         ._m11(Math.fma( m00, l, Math.fma(-m02, i,  m03 * h)) * det)
2449         ._m12(Math.fma(-m30, f, Math.fma( m32, c, -m33 * b)) * det)
2450         ._m13(Math.fma( m20, f, Math.fma(-m22, c,  m23 * b)) * det)
2451         ._m20(Math.fma( m10, k, Math.fma(-m11, i,  m13 * g)) * det)
2452         ._m21(Math.fma(-m00, k, Math.fma( m01, i, -m03 * g)) * det)
2453         ._m22(Math.fma( m30, e, Math.fma(-m31, c,  m33 * a)) * det)
2454         ._m23(Math.fma(-m20, e, Math.fma( m21, c, -m23 * a)) * det)
2455         ._m30(Math.fma(-m10, j, Math.fma( m11, h, -m12 * g)) * det)
2456         ._m31(Math.fma( m00, j, Math.fma(-m01, h,  m02 * g)) * det)
2457         ._m32(Math.fma(-m30, d, Math.fma( m31, b, -m32 * a)) * det)
2458         ._m33(Math.fma( m20, d, Math.fma(-m21, b,  m22 * a)) * det)
2459         ._properties(0);
2460     }
2461     private Matrix4d invertGenericThis(ref Matrix4d dest) {
2462         double a = m00 * m11 - m01 * m10;
2463         double b = m00 * m12 - m02 * m10;
2464         double c = m00 * m13 - m03 * m10;
2465         double d = m01 * m12 - m02 * m11;
2466         double e = m01 * m13 - m03 * m11;
2467         double f = m02 * m13 - m03 * m12;
2468         double g = m20 * m31 - m21 * m30;
2469         double h = m20 * m32 - m22 * m30;
2470         double i = m20 * m33 - m23 * m30;
2471         double j = m21 * m32 - m22 * m31;
2472         double k = m21 * m33 - m23 * m31;
2473         double l = m22 * m33 - m23 * m32;
2474         double det = a * l - b * k + c * j + d * i - e * h + f * g;
2475         det = 1.0 / det;
2476         double nm00 = Math.fma( m11, l, Math.fma(-m12, k,  m13 * j)) * det;
2477         double nm01 = Math.fma(-m01, l, Math.fma( m02, k, -m03 * j)) * det;
2478         double nm02 = Math.fma( m31, f, Math.fma(-m32, e,  m33 * d)) * det;
2479         double nm03 = Math.fma(-m21, f, Math.fma( m22, e, -m23 * d)) * det;
2480         double nm10 = Math.fma(-m10, l, Math.fma( m12, i, -m13 * h)) * det;
2481         double nm11 = Math.fma( m00, l, Math.fma(-m02, i,  m03 * h)) * det;
2482         double nm12 = Math.fma(-m30, f, Math.fma( m32, c, -m33 * b)) * det;
2483         double nm13 = Math.fma( m20, f, Math.fma(-m22, c,  m23 * b)) * det;
2484         double nm20 = Math.fma( m10, k, Math.fma(-m11, i,  m13 * g)) * det;
2485         double nm21 = Math.fma(-m00, k, Math.fma( m01, i, -m03 * g)) * det;
2486         double nm22 = Math.fma( m30, e, Math.fma(-m31, c,  m33 * a)) * det;
2487         double nm23 = Math.fma(-m20, e, Math.fma( m21, c, -m23 * a)) * det;
2488         double nm30 = Math.fma(-m10, j, Math.fma( m11, h, -m12 * g)) * det;
2489         double nm31 = Math.fma( m00, j, Math.fma(-m01, h,  m02 * g)) * det;
2490         double nm32 = Math.fma(-m30, d, Math.fma( m31, b, -m32 * a)) * det;
2491         double nm33 = Math.fma( m20, d, Math.fma(-m21, b,  m22 * a)) * det;
2492         return dest
2493         ._m00(nm00)
2494         ._m01(nm01)
2495         ._m02(nm02)
2496         ._m03(nm03)
2497         ._m10(nm10)
2498         ._m11(nm11)
2499         ._m12(nm12)
2500         ._m13(nm13)
2501         ._m20(nm20)
2502         ._m21(nm21)
2503         ._m22(nm22)
2504         ._m23(nm23)
2505         ._m30(nm30)
2506         ._m31(nm31)
2507         ._m32(nm32)
2508         ._m33(nm33)
2509         ._properties(0);
2510     }
2511 
2512     public Matrix4d invertPerspective(ref Matrix4d dest) {
2513         double a =  1.0 / (m00 * m11);
2514         double l = -1.0 / (m23 * m32);
2515         dest.set(m11 * a, 0, 0, 0,
2516                  0, m00 * a, 0, 0,
2517                  0, 0, 0, -m23 * l,
2518                  0, 0, -m32 * l, m22 * l);
2519         return dest;
2520     }
2521 
2522     /**
2523      * If <code>this</code> is a perspective projection matrix obtained via one of the {@link #perspective(double, double, double, double) perspective()} methods
2524      * or via {@link #setPerspective(double, double, double, double) setPerspective()}, that is, if <code>this</code> is a symmetrical perspective frustum transformation,
2525      * then this method builds the inverse of <code>this</code>.
2526      * <p>
2527      * This method can be used to quickly obtain the inverse of a perspective projection matrix when being obtained via {@link #perspective(double, double, double, double) perspective()}.
2528      * 
2529      * @see #perspective(double, double, double, double)
2530      * 
2531      * @return this
2532      */
2533     ref public Matrix4d invertPerspective() return {
2534         invertPerspective(this);
2535         return this;
2536     }
2537 
2538     public Matrix4d invertFrustum(ref Matrix4d dest) {
2539         double invM00 = 1.0 / m00;
2540         double invM11 = 1.0 / m11;
2541         double invM23 = 1.0 / m23;
2542         double invM32 = 1.0 / m32;
2543         dest.set(invM00, 0, 0, 0,
2544                  0, invM11, 0, 0,
2545                  0, 0, 0, invM32,
2546                  -m20 * invM00 * invM23, -m21 * invM11 * invM23, invM23, -m22 * invM23 * invM32);
2547         return dest;
2548     }
2549 
2550     /**
2551      * If <code>this</code> is an arbitrary perspective projection matrix obtained via one of the {@link #frustum(double, double, double, double, double, double) frustum()}  methods
2552      * or via {@link #setFrustum(double, double, double, double, double, double) setFrustum()},
2553      * then this method builds the inverse of <code>this</code>.
2554      * <p>
2555      * This method can be used to quickly obtain the inverse of a perspective projection matrix.
2556      * <p>
2557      * If this matrix represents a symmetric perspective frustum transformation, as obtained via {@link #perspective(double, double, double, double) perspective()}, then
2558      * {@link #invertPerspective()} should be used instead.
2559      * 
2560      * @see #frustum(double, double, double, double, double, double)
2561      * @see #invertPerspective()
2562      * 
2563      * @return this
2564      */
2565     ref public Matrix4d invertFrustum() return {
2566         invertFrustum(this);
2567         return this;
2568     }
2569 
2570     public Matrix4d invertOrtho(ref Matrix4d dest) {
2571         double invM00 = 1.0 / m00;
2572         double invM11 = 1.0 / m11;
2573         double invM22 = 1.0 / m22;
2574         dest.set(invM00, 0, 0, 0,
2575                  0, invM11, 0, 0,
2576                  0, 0, invM22, 0,
2577                  -m30 * invM00, -m31 * invM11, -m32 * invM22, 1)
2578         ._properties(PROPERTY_AFFINE | (this.properties & PROPERTY_ORTHONORMAL));
2579         return dest;
2580     }
2581 
2582     /**
2583      * Invert <code>this</code> orthographic projection matrix.
2584      * <p>
2585      * This method can be used to quickly obtain the inverse of an orthographic projection matrix.
2586      * 
2587      * @return this
2588      */
2589     ref public Matrix4d invertOrtho() return {
2590         invertOrtho(this);
2591         return this;
2592     }
2593 
2594     public Matrix4d invertPerspectiveView(Matrix4d view, ref Matrix4d dest) {
2595         double a =  1.0 / (m00 * m11);
2596         double l = -1.0 / (m23 * m32);
2597         double pm00 =  m11 * a;
2598         double pm11 =  m00 * a;
2599         double pm23 = -m23 * l;
2600         double pm32 = -m32 * l;
2601         double pm33 =  m22 * l;
2602         double vm30 = -view.m00 * view.m30 - view.m01 * view.m31 - view.m02 * view.m32;
2603         double vm31 = -view.m10 * view.m30 - view.m11 * view.m31 - view.m12 * view.m32;
2604         double vm32 = -view.m20 * view.m30 - view.m21 * view.m31 - view.m22 * view.m32;
2605         double nm10 = view.m01 * pm11;
2606         double nm30 = view.m02 * pm32 + vm30 * pm33;
2607         double nm31 = view.m12 * pm32 + vm31 * pm33;
2608         double nm32 = view.m22 * pm32 + vm32 * pm33;
2609         return dest
2610         ._m00(view.m00 * pm00)
2611         ._m01(view.m10 * pm00)
2612         ._m02(view.m20 * pm00)
2613         ._m03(0.0)
2614         ._m10(nm10)
2615         ._m11(view.m11 * pm11)
2616         ._m12(view.m21 * pm11)
2617         ._m13(0.0)
2618         ._m20(vm30 * pm23)
2619         ._m21(vm31 * pm23)
2620         ._m22(vm32 * pm23)
2621         ._m23(pm23)
2622         ._m30(nm30)
2623         ._m31(nm31)
2624         ._m32(nm32)
2625         ._m33(pm33)
2626         ._properties(0);
2627     }
2628 
2629     public Matrix4d invertPerspectiveView(Matrix4x3d view, ref Matrix4d dest) {
2630         double a =  1.0 / (m00 * m11);
2631         double l = -1.0 / (m23 * m32);
2632         double pm00 =  m11 * a;
2633         double pm11 =  m00 * a;
2634         double pm23 = -m23 * l;
2635         double pm32 = -m32 * l;
2636         double pm33 =  m22 * l;
2637         double vm30 = -view.m00 * view.m30 - view.m01 * view.m31 - view.m02 * view.m32;
2638         double vm31 = -view.m10 * view.m30 - view.m11 * view.m31 - view.m12 * view.m32;
2639         double vm32 = -view.m20 * view.m30 - view.m21 * view.m31 - view.m22 * view.m32;
2640         return dest
2641         ._m00(view.m00 * pm00)
2642         ._m01(view.m10 * pm00)
2643         ._m02(view.m20 * pm00)
2644         ._m03(0.0)
2645         ._m10(view.m01 * pm11)
2646         ._m11(view.m11 * pm11)
2647         ._m12(view.m21 * pm11)
2648         ._m13(0.0)
2649         ._m20(vm30 * pm23)
2650         ._m21(vm31 * pm23)
2651         ._m22(vm32 * pm23)
2652         ._m23(pm23)
2653         ._m30(view.m02 * pm32 + vm30 * pm33)
2654         ._m31(view.m12 * pm32 + vm31 * pm33)
2655         ._m32(view.m22 * pm32 + vm32 * pm33)
2656         ._m33(pm33)
2657         ._properties(0);
2658     }
2659 
2660     public Matrix4d invertAffine(ref Matrix4d dest) {
2661         double m11m00 = m00 * m11, m10m01 = m01 * m10, m10m02 = m02 * m10;
2662         double m12m00 = m00 * m12, m12m01 = m01 * m12, m11m02 = m02 * m11;
2663         double s = 1.0 / ((m11m00 - m10m01) * m22 + (m10m02 - m12m00) * m21 + (m12m01 - m11m02) * m20);
2664         double m10m22 = m10 * m22, m10m21 = m10 * m21, m11m22 = m11 * m22;
2665         double m11m20 = m11 * m20, m12m21 = m12 * m21, m12m20 = m12 * m20;
2666         double m20m02 = m20 * m02, m20m01 = m20 * m01, m21m02 = m21 * m02;
2667         double m21m00 = m21 * m00, m22m01 = m22 * m01, m22m00 = m22 * m00;
2668         double nm00 = (m11m22 - m12m21) * s;
2669         double nm01 = (m21m02 - m22m01) * s;
2670         double nm02 = (m12m01 - m11m02) * s;
2671         double nm10 = (m12m20 - m10m22) * s;
2672         double nm11 = (m22m00 - m20m02) * s;
2673         double nm12 = (m10m02 - m12m00) * s;
2674         double nm20 = (m10m21 - m11m20) * s;
2675         double nm21 = (m20m01 - m21m00) * s;
2676         double nm22 = (m11m00 - m10m01) * s;
2677         double nm30 = (m10m22 * m31 - m10m21 * m32 + m11m20 * m32 - m11m22 * m30 + m12m21 * m30 - m12m20 * m31) * s;
2678         double nm31 = (m20m02 * m31 - m20m01 * m32 + m21m00 * m32 - m21m02 * m30 + m22m01 * m30 - m22m00 * m31) * s;
2679         double nm32 = (m11m02 * m30 - m12m01 * m30 + m12m00 * m31 - m10m02 * m31 + m10m01 * m32 - m11m00 * m32) * s;
2680         dest._m00(nm00)
2681         ._m01(nm01)
2682         ._m02(nm02)
2683         ._m03(0.0)
2684         ._m10(nm10)
2685         ._m11(nm11)
2686         ._m12(nm12)
2687         ._m13(0.0)
2688         ._m20(nm20)
2689         ._m21(nm21)
2690         ._m22(nm22)
2691         ._m23(0.0)
2692         ._m30(nm30)
2693         ._m31(nm31)
2694         ._m32(nm32)
2695         ._m33(1.0)
2696         ._properties(PROPERTY_AFFINE);
2697         return dest;
2698     }
2699 
2700     /**
2701      * Invert this matrix by assuming that it is an {@link #isAffine() affine} transformation (i.e. its last row is equal to <code>(0, 0, 0, 1)</code>).
2702      * 
2703      * @return this
2704      */
2705     ref public Matrix4d invertAffine() return {
2706         invertAffine(this);
2707         return this;
2708     }
2709 
2710     /**
2711      * Transpose this matrix.
2712      * 
2713      * @return this
2714      */
2715     ref public Matrix4d transpose() return {
2716         transpose(this);
2717         return this;
2718     }
2719 
2720     public Matrix4d transpose(ref Matrix4d dest) {
2721         if ((properties & PROPERTY_IDENTITY) != 0)
2722             return dest.identity();
2723         else if (this != dest)
2724             return transposeNonThisGeneric(dest);
2725         return transposeThisGeneric(dest);
2726     }
2727     private Matrix4d transposeNonThisGeneric(ref Matrix4d dest) {
2728         return dest
2729         ._m00(m00)
2730         ._m01(m10)
2731         ._m02(m20)
2732         ._m03(m30)
2733         ._m10(m01)
2734         ._m11(m11)
2735         ._m12(m21)
2736         ._m13(m31)
2737         ._m20(m02)
2738         ._m21(m12)
2739         ._m22(m22)
2740         ._m23(m32)
2741         ._m30(m03)
2742         ._m31(m13)
2743         ._m32(m23)
2744         ._m33(m33)
2745         ._properties(0);
2746     }
2747     private Matrix4d transposeThisGeneric(ref Matrix4d dest) {
2748         double nm10 = m01;
2749         double nm20 = m02;
2750         double nm21 = m12;
2751         double nm30 = m03;
2752         double nm31 = m13;
2753         double nm32 = m23;
2754         return dest
2755         ._m01(m10)
2756         ._m02(m20)
2757         ._m03(m30)
2758         ._m10(nm10)
2759         ._m12(m21)
2760         ._m13(m31)
2761         ._m20(nm20)
2762         ._m21(nm21)
2763         ._m23(m32)
2764         ._m30(nm30)
2765         ._m31(nm31)
2766         ._m32(nm32)
2767         ._properties(0);
2768     }
2769 
2770     /**
2771      * Transpose only the upper left 3x3 submatrix of this matrix.
2772      * <p>
2773      * All other matrix elements are left unchanged.
2774      * 
2775      * @return this
2776      */
2777     ref public Matrix4d transpose3x3() return {
2778         transpose3x3(this);
2779         return this;
2780     }
2781 
2782     public Matrix4d transpose3x3(ref Matrix4d dest) {
2783         double nm10 = m01, nm20 = m02, nm21 = m12;
2784         return dest
2785         ._m00(m00)
2786         ._m01(m10)
2787         ._m02(m20)
2788         ._m10(nm10)
2789         ._m11(m11)
2790         ._m12(m21)
2791         ._m20(nm20)
2792         ._m21(nm21)
2793         ._m22(m22)
2794         ._properties(this.properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
2795     }
2796 
2797     public Matrix3d transpose3x3(ref Matrix3d dest) {
2798         return dest
2799         ._m00(m00)
2800         ._m01(m10)
2801         ._m02(m20)
2802         ._m10(m01)
2803         ._m11(m11)
2804         ._m12(m21)
2805         ._m20(m02)
2806         ._m21(m12)
2807         ._m22(m22);
2808     }
2809 
2810     /**
2811      * Set this matrix to be a simple translation matrix.
2812      * <p>
2813      * The resulting matrix can be multiplied against another transformation
2814      * matrix to obtain an additional translation.
2815      * 
2816      * @param x
2817      *          the offset to translate in x
2818      * @param y
2819      *          the offset to translate in y
2820      * @param z
2821      *          the offset to translate in z
2822      * @return this
2823      */
2824     ref public Matrix4d translation(double x, double y, double z) return {
2825         if ((properties & PROPERTY_IDENTITY) == 0)
2826             this._identity();
2827         return this.
2828         _m30(x).
2829         _m31(y).
2830         _m32(z).
2831         _m33(1.0).
2832         _properties(PROPERTY_AFFINE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL);
2833     }
2834 
2835 
2836     /**
2837      * Set this matrix to be a simple translation matrix.
2838      * <p>
2839      * The resulting matrix can be multiplied against another transformation
2840      * matrix to obtain an additional translation.
2841      *
2842      * @param offset
2843      *              the offsets in x, y and z to translate
2844      * @return this
2845      */
2846     ref public Matrix4d translation(ref Vector3d offset) return {
2847         return translation(offset.x, offset.y, offset.z);
2848     }
2849 
2850     /**
2851      * Set only the translation components <code>(m30, m31, m32)</code> of this matrix to the given values <code>(x, y, z)</code>.
2852      * <p>
2853      * To build a translation matrix instead, use {@link #translation(double, double, double)}.
2854      * To apply a translation, use {@link #translate(double, double, double)}.
2855      * 
2856      * @see #translation(double, double, double)
2857      * @see #translate(double, double, double)
2858      * 
2859      * @param x
2860      *          the units to translate in x
2861      * @param y
2862      *          the units to translate in y
2863      * @param z
2864      *          the units to translate in z
2865      * @return this
2866      */
2867     ref public Matrix4d setTranslation(double x, double y, double z) return {
2868         _m30(x).
2869         _m31(y).
2870         _m32(z).
2871         properties &= ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY);
2872         return this;
2873     }
2874 
2875     /**
2876      * Set only the translation components <code>(m30, m31, m32)</code> of this matrix to the given values <code>(xyz.x, xyz.y, xyz.z)</code>.
2877      * <p>
2878      * To build a translation matrix instead, use {@link #translation(ref Vector3d)}.
2879      * To apply a translation, use {@link #translate(ref Vector3d)}.
2880      * 
2881      * @see #translation(ref Vector3d)
2882      * @see #translate(ref Vector3d)
2883      * 
2884      * @param xyz
2885      *          the units to translate in <code>(x, y, z)</code>
2886      * @return this
2887      */
2888     ref public Matrix4d setTranslation(ref Vector3d xyz) return {
2889         return setTranslation(xyz.x, xyz.y, xyz.z);
2890     }
2891 
2892     public Vector3d getTranslation(ref Vector3d dest) {
2893         dest.x = m30;
2894         dest.y = m31;
2895         dest.z = m32;
2896         return dest;
2897     }
2898 
2899     public Vector3d getScale(ref Vector3d dest) {
2900         dest.x = Math.sqrt(m00 * m00 + m01 * m01 + m02 * m02);
2901         dest.y = Math.sqrt(m10 * m10 + m11 * m11 + m12 * m12);
2902         dest.z = Math.sqrt(m20 * m20 + m21 * m21 + m22 * m22);
2903         return dest;
2904     }
2905 
2906     public Matrix4d get(ref Matrix4d dest) {
2907         return dest.set(this);
2908     }
2909 
2910     public Matrix4x3d get4x3(ref Matrix4x3d dest) {
2911         return dest.set(this);
2912     }
2913 
2914     public Matrix3d get3x3(ref Matrix3d dest) {
2915         return dest.set(this);
2916     }
2917 
2918     public Quaterniond getUnnormalizedRotation(ref Quaterniond dest) {
2919         return dest.setFromUnnormalized(this);
2920     }
2921 
2922     public Quaterniond getNormalizedRotation(ref Quaterniond dest) {
2923         return dest.setFromNormalized(this);
2924     }
2925 
2926     public double[] get(ref double[] dest, int offset) {
2927         dest[offset+0]  = m00;
2928         dest[offset+1]  = m01;
2929         dest[offset+2]  = m02;
2930         dest[offset+3]  = m03;
2931         dest[offset+4]  = m10;
2932         dest[offset+5]  = m11;
2933         dest[offset+6]  = m12;
2934         dest[offset+7]  = m13;
2935         dest[offset+8]  = m20;
2936         dest[offset+9]  = m21;
2937         dest[offset+10] = m22;
2938         dest[offset+11] = m23;
2939         dest[offset+12] = m30;
2940         dest[offset+13] = m31;
2941         dest[offset+14] = m32;
2942         dest[offset+15] = m33;
2943         return dest;
2944     }
2945 
2946     public double[] get(ref double[] dest) {
2947         return get(dest, 0);
2948     }
2949 
2950     /**
2951      * Set all the values within this matrix to 0.
2952      * 
2953      * @return this
2954      */
2955     ref public Matrix4d zero() return {
2956         return
2957         _m00(0.0).
2958         _m01(0.0).
2959         _m02(0.0).
2960         _m03(0.0).
2961         _m10(0.0).
2962         _m11(0.0).
2963         _m12(0.0).
2964         _m13(0.0).
2965         _m20(0.0).
2966         _m21(0.0).
2967         _m22(0.0).
2968         _m23(0.0).
2969         _m30(0.0).
2970         _m31(0.0).
2971         _m32(0.0).
2972         _m33(0.0).
2973         _properties(0);
2974     }
2975 
2976     /**
2977      * Set this matrix to be a simple scale matrix, which scales all axes uniformly by the given factor.
2978      * <p>
2979      * The resulting matrix can be multiplied against another transformation
2980      * matrix to obtain an additional scaling.
2981      * <p>
2982      * In order to post-multiply a scaling transformation directly to a
2983      * matrix, use {@link #scale(double) scale()} instead.
2984      * 
2985      * @see #scale(double)
2986      * 
2987      * @param factor
2988      *             the scale factor in x, y and z
2989      * @return this
2990      */
2991     ref public Matrix4d scaling(double factor) return {
2992         return scaling(factor, factor, factor);
2993     }
2994 
2995     /**
2996      * Set this matrix to be a simple scale matrix.
2997      * 
2998      * @param x
2999      *          the scale in x
3000      * @param y
3001      *          the scale in y
3002      * @param z
3003      *          the scale in z         
3004      * @return this
3005      */
3006     ref public Matrix4d scaling(double x, double y, double z) return {
3007         if ((properties & PROPERTY_IDENTITY) == 0)
3008             identity();
3009         bool one = Math.absEqualsOne(x) && Math.absEqualsOne(y) && Math.absEqualsOne(z);
3010         _m00(x).
3011         _m11(y).
3012         _m22(z).
3013         properties = PROPERTY_AFFINE | (one ? PROPERTY_ORTHONORMAL : 0);
3014         return this;
3015     }
3016 
3017     /**
3018      * Set this matrix to be a simple scale matrix which scales the base axes by
3019      * <code>xyz.x</code>, <code>xyz.y</code> and <code>xyz.z</code>, respectively.
3020      * <p>
3021      * The resulting matrix can be multiplied against another transformation
3022      * matrix to obtain an additional scaling.
3023      * <p>
3024      * In order to post-multiply a scaling transformation directly to a
3025      * matrix use {@link #scale(ref Vector3d) scale()} instead.
3026      * 
3027      * @see #scale(ref Vector3d)
3028      * 
3029      * @param xyz
3030      *             the scale in x, y and z, respectively
3031      * @return this
3032      */
3033     ref public Matrix4d scaling(ref Vector3d xyz) return {
3034         return scaling(xyz.x, xyz.y, xyz.z);
3035     }
3036 
3037     /**
3038      * Set this matrix to a rotation matrix which rotates the given radians about a given axis.
3039      * <p>
3040      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3041      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3042      * When used with a left-handed coordinate system, the rotation is clockwise.
3043      * <p>
3044      * From <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">Wikipedia</a>
3045      * 
3046      * @param angle
3047      *          the angle in radians
3048      * @param x
3049      *          the x-coordinate of the axis to rotate about
3050      * @param y
3051      *          the y-coordinate of the axis to rotate about
3052      * @param z
3053      *          the z-coordinate of the axis to rotate about
3054      * @return this
3055      */
3056     ref public Matrix4d rotation(double angle, double x, double y, double z) return {
3057         if (y == 0.0 && z == 0.0 && Math.absEqualsOne(x))
3058             rotationX(x * angle);
3059         else if (x == 0.0 && z == 0.0 && Math.absEqualsOne(y))
3060             rotationY(y * angle);
3061         else if (x == 0.0 && y == 0.0 && Math.absEqualsOne(z))
3062             rotationZ(z * angle);
3063         else
3064             rotationInternal(angle, x, y, z);
3065         return this;
3066     }
3067     private Matrix4d rotationInternal(double angle, double x, double y, double z) {
3068         double sin = Math.sin(angle);
3069         double cos = Math.cosFromSin(sin, angle);
3070         double C = 1.0 - cos;
3071         double xy = x * y, xz = x * z, yz = y * z;
3072         if ((properties & PROPERTY_IDENTITY) == 0)
3073             this._identity();
3074         _m00(cos + x * x * C).
3075         _m10(xy * C - z * sin).
3076         _m20(xz * C + y * sin).
3077         _m01(xy * C + z * sin).
3078         _m11(cos + y * y * C).
3079         _m21(yz * C - x * sin).
3080         _m02(xz * C - y * sin).
3081         _m12(yz * C + x * sin).
3082         _m22(cos + z * z * C).
3083         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
3084         return this;
3085     }
3086 
3087     /**
3088      * Set this matrix to a rotation transformation about the X axis.
3089      * <p>
3090      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3091      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3092      * When used with a left-handed coordinate system, the rotation is clockwise.
3093      * <p>
3094      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">http://en.wikipedia.org</a>
3095      * 
3096      * @param ang
3097      *            the angle in radians
3098      * @return this
3099      */
3100     ref public Matrix4d rotationX(double ang) return {
3101         double sin, cos;
3102         sin = Math.sin(ang);
3103         cos = Math.cosFromSin(sin, ang);
3104         if ((properties & PROPERTY_IDENTITY) == 0)
3105             this._identity();
3106         _m11(cos).
3107         _m12(sin).
3108         _m21(-sin).
3109         _m22(cos).
3110         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
3111         return this;
3112     }
3113 
3114     /**
3115      * Set this matrix to a rotation transformation about the Y axis.
3116      * <p>
3117      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3118      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3119      * When used with a left-handed coordinate system, the rotation is clockwise.
3120      * <p>
3121      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">http://en.wikipedia.org</a>
3122      * 
3123      * @param ang
3124      *            the angle in radians
3125      * @return this
3126      */
3127     ref public Matrix4d rotationY(double ang) return {
3128         double sin, cos;
3129         sin = Math.sin(ang);
3130         cos = Math.cosFromSin(sin, ang);
3131         if ((properties & PROPERTY_IDENTITY) == 0)
3132             this._identity();
3133         _m00(cos).
3134         _m02(-sin).
3135         _m20(sin).
3136         _m22(cos).
3137         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
3138         return this;
3139     }
3140 
3141     /**
3142      * Set this matrix to a rotation transformation about the Z axis.
3143      * <p>
3144      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3145      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3146      * When used with a left-handed coordinate system, the rotation is clockwise.
3147      * <p>
3148      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">http://en.wikipedia.org</a>
3149      * 
3150      * @param ang
3151      *            the angle in radians
3152      * @return this
3153      */
3154     ref public Matrix4d rotationZ(double ang) return {
3155         double sin, cos;
3156         sin = Math.sin(ang);
3157         cos = Math.cosFromSin(sin, ang);
3158         if ((properties & PROPERTY_IDENTITY) == 0)
3159             this._identity();
3160         _m00(cos).
3161         _m01(sin).
3162         _m10(-sin).
3163         _m11(cos).
3164         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
3165         return this;
3166     }
3167 
3168     /**
3169      * Set this matrix to a rotation transformation about the Z axis to align the local <code>+X</code> towards <code>(dirX, dirY)</code>.
3170      * <p>
3171      * The vector <code>(dirX, dirY)</code> must be a unit vector.
3172      * 
3173      * @param dirX
3174      *            the x component of the normalized direction
3175      * @param dirY
3176      *            the y component of the normalized direction
3177      * @return this
3178      */
3179     ref public Matrix4d rotationTowardsXY(double dirX, double dirY) return {
3180         if ((properties & PROPERTY_IDENTITY) == 0)
3181             this._identity();
3182         setm00(dirY);
3183         setm01(dirX);
3184         setm10(-dirX);
3185         setm11(dirY);
3186         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
3187         return this;
3188     }
3189 
3190     /**
3191      * Set this matrix to a rotation of <code>angleX</code> radians about the X axis, followed by a rotation
3192      * of <code>angleY</code> radians about the Y axis and followed by a rotation of <code>angleZ</code> radians about the Z axis.
3193      * <p>
3194      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3195      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3196      * When used with a left-handed coordinate system, the rotation is clockwise.
3197      * <p>
3198      * This method is equivalent to calling: <code>rotationX(angleX).rotateY(angleY).rotateZ(angleZ)</code>
3199      * 
3200      * @param angleX
3201      *            the angle to rotate about X
3202      * @param angleY
3203      *            the angle to rotate about Y
3204      * @param angleZ
3205      *            the angle to rotate about Z
3206      * @return this
3207      */
3208     ref public Matrix4d rotationXYZ(double angleX, double angleY, double angleZ) return {
3209         double sinX = Math.sin(angleX);
3210         double cosX = Math.cosFromSin(sinX, angleX);
3211         double sinY = Math.sin(angleY);
3212         double cosY = Math.cosFromSin(sinY, angleY);
3213         double sinZ = Math.sin(angleZ);
3214         double cosZ = Math.cosFromSin(sinZ, angleZ);
3215         double m_sinX = -sinX;
3216         double m_sinY = -sinY;
3217         double m_sinZ = -sinZ;
3218         if ((properties & PROPERTY_IDENTITY) == 0)
3219             this._identity();
3220 
3221         // rotateX
3222         double nm11 = cosX;
3223         double nm12 = sinX;
3224         double nm21 = m_sinX;
3225         double nm22 = cosX;
3226         // rotateY
3227         double nm00 = cosY;
3228         double nm01 = nm21 * m_sinY;
3229         double nm02 = nm22 * m_sinY;
3230         _m20(sinY).
3231         _m21(nm21 * cosY).
3232         _m22(nm22 * cosY).
3233         // rotateZ
3234         _m00(nm00 * cosZ).
3235         _m01(nm01 * cosZ + nm11 * sinZ).
3236         _m02(nm02 * cosZ + nm12 * sinZ).
3237         _m10(nm00 * m_sinZ).
3238         _m11(nm01 * m_sinZ + nm11 * cosZ).
3239         _m12(nm02 * m_sinZ + nm12 * cosZ).
3240         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
3241         return this;
3242     }
3243 
3244     /**
3245      * Set this matrix to a rotation of <code>angleZ</code> radians about the Z axis, followed by a rotation
3246      * of <code>angleY</code> radians about the Y axis and followed by a rotation of <code>angleX</code> radians about the X axis.
3247      * <p>
3248      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3249      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3250      * When used with a left-handed coordinate system, the rotation is clockwise.
3251      * <p>
3252      * This method is equivalent to calling: <code>rotationZ(angleZ).rotateY(angleY).rotateX(angleX)</code>
3253      * 
3254      * @param angleZ
3255      *            the angle to rotate about Z
3256      * @param angleY
3257      *            the angle to rotate about Y
3258      * @param angleX
3259      *            the angle to rotate about X
3260      * @return this
3261      */
3262     ref public Matrix4d rotationZYX(double angleZ, double angleY, double angleX) return {
3263         double sinX = Math.sin(angleX);
3264         double cosX = Math.cosFromSin(sinX, angleX);
3265         double sinY = Math.sin(angleY);
3266         double cosY = Math.cosFromSin(sinY, angleY);
3267         double sinZ = Math.sin(angleZ);
3268         double cosZ = Math.cosFromSin(sinZ, angleZ);
3269         double m_sinZ = -sinZ;
3270         double m_sinY = -sinY;
3271         double m_sinX = -sinX;
3272         if ((properties & PROPERTY_IDENTITY) == 0)
3273             this._identity();
3274 
3275         // rotateZ
3276         double nm00 = cosZ;
3277         double nm01 = sinZ;
3278         double nm10 = m_sinZ;
3279         double nm11 = cosZ;
3280         // rotateY
3281         double nm20 = nm00 * sinY;
3282         double nm21 = nm01 * sinY;
3283         double nm22 = cosY;
3284         _m00(nm00 * cosY).
3285         _m01(nm01 * cosY).
3286         _m02(m_sinY).
3287         // rotateX
3288         _m10(nm10 * cosX + nm20 * sinX).
3289         _m11(nm11 * cosX + nm21 * sinX).
3290         _m12(nm22 * sinX).
3291         _m20(nm10 * m_sinX + nm20 * cosX).
3292         _m21(nm11 * m_sinX + nm21 * cosX).
3293         _m22(nm22 * cosX).
3294         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
3295         return this;
3296     }
3297 
3298     /**
3299      * Set this matrix to a rotation of <code>angleY</code> radians about the Y axis, followed by a rotation
3300      * of <code>angleX</code> radians about the X axis and followed by a rotation of <code>angleZ</code> radians about the Z axis.
3301      * <p>
3302      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3303      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3304      * When used with a left-handed coordinate system, the rotation is clockwise.
3305      * <p>
3306      * This method is equivalent to calling: <code>rotationY(angleY).rotateX(angleX).rotateZ(angleZ)</code>
3307      * 
3308      * @param angleY
3309      *            the angle to rotate about Y
3310      * @param angleX
3311      *            the angle to rotate about X
3312      * @param angleZ
3313      *            the angle to rotate about Z
3314      * @return this
3315      */
3316     ref public Matrix4d rotationYXZ(double angleY, double angleX, double angleZ) return {
3317         double sinX = Math.sin(angleX);
3318         double cosX = Math.cosFromSin(sinX, angleX);
3319         double sinY = Math.sin(angleY);
3320         double cosY = Math.cosFromSin(sinY, angleY);
3321         double sinZ = Math.sin(angleZ);
3322         double cosZ = Math.cosFromSin(sinZ, angleZ);
3323         double m_sinY = -sinY;
3324         double m_sinX = -sinX;
3325         double m_sinZ = -sinZ;
3326 
3327         // rotateY
3328         double nm00 = cosY;
3329         double nm02 = m_sinY;
3330         double nm20 = sinY;
3331         double nm22 = cosY;
3332         // rotateX
3333         double nm10 = nm20 * sinX;
3334         double nm11 = cosX;
3335         double nm12 = nm22 * sinX;
3336         _m20(nm20 * cosX).
3337         _m21(m_sinX).
3338         _m22(nm22 * cosX).
3339         _m23(0.0).
3340         // rotateZ
3341         _m00(nm00 * cosZ + nm10 * sinZ).
3342         _m01(nm11 * sinZ).
3343         _m02(nm02 * cosZ + nm12 * sinZ).
3344         _m03(0.0).
3345         _m10(nm00 * m_sinZ + nm10 * cosZ).
3346         _m11(nm11 * cosZ).
3347         _m12(nm02 * m_sinZ + nm12 * cosZ).
3348         _m13(0.0).
3349         // set last column to identity
3350         _m30(0.0).
3351         _m31(0.0).
3352         _m32(0.0).
3353         _m33(1.0).
3354         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
3355         return this;
3356     }
3357 
3358     /**
3359      * Set only the upper left 3x3 submatrix of this matrix to a rotation of <code>angleX</code> radians about the X axis, followed by a rotation
3360      * of <code>angleY</code> radians about the Y axis and followed by a rotation of <code>angleZ</code> radians about the Z axis.
3361      * <p>
3362      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3363      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3364      * When used with a left-handed coordinate system, the rotation is clockwise.
3365      * 
3366      * @param angleX
3367      *            the angle to rotate about X
3368      * @param angleY
3369      *            the angle to rotate about Y
3370      * @param angleZ
3371      *            the angle to rotate about Z
3372      * @return this
3373      */
3374     ref public Matrix4d setRotationXYZ(double angleX, double angleY, double angleZ) return {
3375         double sinX = Math.sin(angleX);
3376         double cosX = Math.cosFromSin(sinX, angleX);
3377         double sinY = Math.sin(angleY);
3378         double cosY = Math.cosFromSin(sinY, angleY);
3379         double sinZ = Math.sin(angleZ);
3380         double cosZ = Math.cosFromSin(sinZ, angleZ);
3381         double m_sinX = -sinX;
3382         double m_sinY = -sinY;
3383         double m_sinZ = -sinZ;
3384 
3385         // rotateX
3386         double nm11 = cosX;
3387         double nm12 = sinX;
3388         double nm21 = m_sinX;
3389         double nm22 = cosX;
3390         // rotateY
3391         double nm00 = cosY;
3392         double nm01 = nm21 * m_sinY;
3393         double nm02 = nm22 * m_sinY;
3394         _m20(sinY).
3395         _m21(nm21 * cosY).
3396         _m22(nm22 * cosY).
3397         // rotateZ
3398         _m00(nm00 * cosZ).
3399         _m01(nm01 * cosZ + nm11 * sinZ).
3400         _m02(nm02 * cosZ + nm12 * sinZ).
3401         _m10(nm00 * m_sinZ).
3402         _m11(nm01 * m_sinZ + nm11 * cosZ).
3403         _m12(nm02 * m_sinZ + nm12 * cosZ).
3404         properties &= ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION);
3405         return this;
3406     }
3407 
3408     /**
3409      * Set only the upper left 3x3 submatrix of this matrix to a rotation of <code>angleZ</code> radians about the Z axis, followed by a rotation
3410      * of <code>angleY</code> radians about the Y axis and followed by a rotation of <code>angleX</code> radians about the X axis.
3411      * <p>
3412      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3413      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3414      * When used with a left-handed coordinate system, the rotation is clockwise.
3415      * 
3416      * @param angleZ
3417      *            the angle to rotate about Z
3418      * @param angleY
3419      *            the angle to rotate about Y
3420      * @param angleX
3421      *            the angle to rotate about X
3422      * @return this
3423      */
3424     ref public Matrix4d setRotationZYX(double angleZ, double angleY, double angleX) return {
3425         double sinX = Math.sin(angleX);
3426         double cosX = Math.cosFromSin(sinX, angleX);
3427         double sinY = Math.sin(angleY);
3428         double cosY = Math.cosFromSin(sinY, angleY);
3429         double sinZ = Math.sin(angleZ);
3430         double cosZ = Math.cosFromSin(sinZ, angleZ);
3431         double m_sinZ = -sinZ;
3432         double m_sinY = -sinY;
3433         double m_sinX = -sinX;
3434 
3435         // rotateZ
3436         double nm00 = cosZ;
3437         double nm01 = sinZ;
3438         double nm10 = m_sinZ;
3439         double nm11 = cosZ;
3440         // rotateY
3441         double nm20 = nm00 * sinY;
3442         double nm21 = nm01 * sinY;
3443         double nm22 = cosY;
3444         _m00(nm00 * cosY).
3445         _m01(nm01 * cosY).
3446         _m02(m_sinY).
3447         // rotateX
3448         _m10(nm10 * cosX + nm20 * sinX).
3449         _m11(nm11 * cosX + nm21 * sinX).
3450         _m12(nm22 * sinX).
3451         _m20(nm10 * m_sinX + nm20 * cosX).
3452         _m21(nm11 * m_sinX + nm21 * cosX).
3453         _m22(nm22 * cosX).
3454         properties &= ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION);
3455         return this;
3456     }
3457 
3458     /**
3459      * Set only the upper left 3x3 submatrix of this matrix to a rotation of <code>angleY</code> radians about the Y axis, followed by a rotation
3460      * of <code>angleX</code> radians about the X axis and followed by a rotation of <code>angleZ</code> radians about the Z axis.
3461      * <p>
3462      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3463      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3464      * When used with a left-handed coordinate system, the rotation is clockwise.
3465      * 
3466      * @param angleY
3467      *            the angle to rotate about Y
3468      * @param angleX
3469      *            the angle to rotate about X
3470      * @param angleZ
3471      *            the angle to rotate about Z
3472      * @return this
3473      */
3474     ref public Matrix4d setRotationYXZ(double angleY, double angleX, double angleZ) return {
3475         double sinX = Math.sin(angleX);
3476         double cosX = Math.cosFromSin(sinX, angleX);
3477         double sinY = Math.sin(angleY);
3478         double cosY = Math.cosFromSin(sinY, angleY);
3479         double sinZ = Math.sin(angleZ);
3480         double cosZ = Math.cosFromSin(sinZ, angleZ);
3481         double m_sinY = -sinY;
3482         double m_sinX = -sinX;
3483         double m_sinZ = -sinZ;
3484 
3485         // rotateY
3486         double nm00 = cosY;
3487         double nm02 = m_sinY;
3488         double nm20 = sinY;
3489         double nm22 = cosY;
3490         // rotateX
3491         double nm10 = nm20 * sinX;
3492         double nm11 = cosX;
3493         double nm12 = nm22 * sinX;
3494         _m20(nm20 * cosX).
3495         _m21(m_sinX).
3496         _m22(nm22 * cosX).
3497         // rotateZ
3498         _m00(nm00 * cosZ + nm10 * sinZ).
3499         _m01(nm11 * sinZ).
3500         _m02(nm02 * cosZ + nm12 * sinZ).
3501         _m10(nm00 * m_sinZ + nm10 * cosZ).
3502         _m11(nm11 * cosZ).
3503         _m12(nm02 * m_sinZ + nm12 * cosZ).
3504         properties &= ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION);
3505         return this;
3506     }
3507 
3508     /**
3509      * Set this matrix to a rotation matrix which rotates the given radians about a given axis.
3510      * <p>
3511      * The axis described by the <code>axis</code> vector needs to be a unit vector.
3512      * <p>
3513      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3514      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3515      * When used with a left-handed coordinate system, the rotation is clockwise.
3516      * 
3517      * @param angle
3518      *          the angle in radians
3519      * @param axis
3520      *          the axis to rotate about
3521      * @return this
3522      */
3523     ref public Matrix4d rotation(double angle, Vector3d axis) return {
3524         return rotation(angle, axis.x, axis.y, axis.z);
3525     }
3526 
3527 
3528     public Vector4d transform(ref Vector4d v) {
3529         return v.mul(this);
3530     }
3531 
3532     public Vector4d transform(ref Vector4d v, ref Vector4d dest) {
3533         return v.mul(this, dest);
3534     }
3535 
3536     public Vector4d transform(double x, double y, double z, double w, ref Vector4d dest) {
3537         return dest.set(m00 * x + m10 * y + m20 * z + m30 * w,
3538                         m01 * x + m11 * y + m21 * z + m31 * w,
3539                         m02 * x + m12 * y + m22 * z + m32 * w,
3540                         m03 * x + m13 * y + m23 * z + m33 * w);
3541     }
3542 
3543     public Vector4d transformTranspose(ref Vector4d v) {
3544         return v.mulTranspose(this);
3545     }
3546     public Vector4d transformTranspose(ref Vector4d v, ref Vector4d dest) {
3547         return v.mulTranspose(this, dest);
3548     }
3549     public Vector4d transformTranspose(double x, double y, double z, double w, ref Vector4d dest) {
3550        return dest.set(x, y, z, w).mulTranspose(this);
3551     }
3552 
3553     public Vector4d transformProject(ref Vector4d v) {
3554         return v.mulProject(this);
3555     }
3556 
3557     public Vector4d transformProject(ref Vector4d v, ref Vector4d dest) {
3558         return v.mulProject(this, dest);
3559     }
3560 
3561     public Vector4d transformProject(double x, double y, double z, double w, ref Vector4d dest) {
3562         double invW = 1.0 / (m03 * x + m13 * y + m23 * z + m33 * w);
3563         return dest.set((m00 * x + m10 * y + m20 * z + m30 * w) * invW,
3564                         (m01 * x + m11 * y + m21 * z + m31 * w) * invW,
3565                         (m02 * x + m12 * y + m22 * z + m32 * w) * invW,
3566                         1.0);
3567     }
3568 
3569     public Vector3d transformProject(ref Vector3d v) {
3570         return v.mulProject(this);
3571     }
3572 
3573     public Vector3d transformProject(ref Vector3d v, ref Vector3d dest) {
3574         return v.mulProject(this, dest);
3575     }
3576 
3577     public Vector3d transformProject(double x, double y, double z, ref Vector3d dest) {
3578         double invW = 1.0 / (m03 * x + m13 * y + m23 * z + m33);
3579         return dest.set((m00 * x + m10 * y + m20 * z + m30) * invW,
3580                         (m01 * x + m11 * y + m21 * z + m31) * invW,
3581                         (m02 * x + m12 * y + m22 * z + m32) * invW);
3582     }
3583 
3584     public Vector3d transformProject(ref Vector4d v, ref Vector3d dest) {
3585         return v.mulProject(this, dest);
3586     }
3587 
3588     public Vector3d transformProject(double x, double y, double z, double w, ref Vector3d dest) {
3589         dest.x = x;
3590         dest.y = y;
3591         dest.z = z;
3592         return dest.mulProject(this, w, dest);
3593     }
3594 
3595     public Vector3d transformPosition(ref Vector3d dest) {
3596         return dest.set(m00 * dest.x + m10 * dest.y + m20 * dest.z + m30,
3597                         m01 * dest.x + m11 * dest.y + m21 * dest.z + m31,
3598                         m02 * dest.x + m12 * dest.y + m22 * dest.z + m32);
3599     }
3600 
3601     public Vector3d transformPosition(ref Vector3d v, ref Vector3d dest) {
3602         return transformPosition(v.x, v.y, v.z, dest);
3603     }
3604 
3605     public Vector3d transformPosition(double x, double y, double z, ref Vector3d dest) {
3606         return dest.set(m00 * x + m10 * y + m20 * z + m30,
3607                         m01 * x + m11 * y + m21 * z + m31,
3608                         m02 * x + m12 * y + m22 * z + m32);
3609     }
3610 
3611     public Vector3d transformDirection(ref Vector3d dest) {
3612         return dest.set(m00 * dest.x + m10 * dest.y + m20 * dest.z,
3613                         m01 * dest.x + m11 * dest.y + m21 * dest.z,
3614                         m02 * dest.x + m12 * dest.y + m22 * dest.z);
3615     }
3616 
3617     public Vector3d transformDirection(ref Vector3d v, ref Vector3d dest) {
3618         return dest.set(m00 * v.x + m10 * v.y + m20 * v.z,
3619                         m01 * v.x + m11 * v.y + m21 * v.z,
3620                         m02 * v.x + m12 * v.y + m22 * v.z);
3621     }
3622 
3623     public Vector3d transformDirection(double x, double y, double z, ref Vector3d dest) {
3624         return dest.set(m00 * x + m10 * y + m20 * z,
3625                         m01 * x + m11 * y + m21 * z,
3626                         m02 * x + m12 * y + m22 * z);
3627     }
3628 
3629     public Vector4d transformAffine(ref Vector4d dest) {
3630         return dest.mulAffine(this, dest);
3631     }
3632 
3633     public Vector4d transformAffine(ref Vector4d v, ref Vector4d dest) {
3634         return transformAffine(v.x, v.y, v.z, v.w, dest);
3635     }
3636 
3637     public Vector4d transformAffine(double x, double y, double z, double w, ref Vector4d dest) {
3638         double rx = m00 * x + m10 * y + m20 * z + m30 * w;
3639         double ry = m01 * x + m11 * y + m21 * z + m31 * w;
3640         double rz = m02 * x + m12 * y + m22 * z + m32 * w;
3641         dest.x = rx;
3642         dest.y = ry;
3643         dest.z = rz;
3644         dest.w = w;
3645         return dest;
3646     }
3647 
3648     /**
3649      * Set the upper left 3x3 submatrix of this {@link Matrix4d} to the given {@link Matrix3d} and don't change the other elements.
3650      * 
3651      * @param mat
3652      *          the 3x3 matrix
3653      * @return this
3654      */
3655     ref public Matrix4d set3x3(Matrix3d mat) return {
3656         return
3657         _m00(mat.m00).
3658         _m01(mat.m01).
3659         _m02(mat.m02).
3660         _m10(mat.m10).
3661         _m11(mat.m11).
3662         _m12(mat.m12).
3663         _m20(mat.m20).
3664         _m21(mat.m21).
3665         _m22(mat.m22).
3666         _properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
3667     }
3668 
3669     public Matrix4d scale(ref Vector3d xyz, ref Matrix4d dest) {
3670         return scale(xyz.x, xyz.y, xyz.z, dest);
3671     }
3672 
3673     /**
3674      * Apply scaling to this matrix by scaling the base axes by the given <code>xyz.x</code>,
3675      * <code>xyz.y</code> and <code>xyz.z</code> factors, respectively.
3676      * <p>
3677      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
3678      * then the new matrix will be <code>M * S</code>. So when transforming a
3679      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>, the
3680      * scaling will be applied first!
3681      * 
3682      * @param xyz
3683      *            the factors of the x, y and z component, respectively
3684      * @return this
3685      */
3686     ref public Matrix4d scale(ref Vector3d xyz) return {
3687         scale(xyz.x, xyz.y, xyz.z, this);
3688         return this;
3689     }
3690 
3691     public Matrix4d scale(double x, double y, double z, ref Matrix4d dest) {
3692         if ((properties & PROPERTY_IDENTITY) != 0)
3693             return dest.scaling(x, y, z);
3694         return scaleGeneric(x, y, z, dest);
3695     }
3696     private Matrix4d scaleGeneric(double x, double y, double z, ref Matrix4d dest) {
3697         bool one = Math.absEqualsOne(x) && Math.absEqualsOne(y) && Math.absEqualsOne(z);
3698         dest._m00(m00 * x)
3699         ._m01(m01 * x)
3700         ._m02(m02 * x)
3701         ._m03(m03 * x)
3702         ._m10(m10 * y)
3703         ._m11(m11 * y)
3704         ._m12(m12 * y)
3705         ._m13(m13 * y)
3706         ._m20(m20 * z)
3707         ._m21(m21 * z)
3708         ._m22(m22 * z)
3709         ._m23(m23 * z)
3710         ._m30(m30)
3711         ._m31(m31)
3712         ._m32(m32)
3713         ._m33(m33)
3714         ._properties(properties
3715                 & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | (one ? 0 : PROPERTY_ORTHONORMAL)));
3716         return dest;
3717     }
3718 
3719     /**
3720      * Apply scaling to <code>this</code> matrix by scaling the base axes by the given x,
3721      * y and z factors.
3722      * <p>
3723      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
3724      * then the new matrix will be <code>M * S</code>. So when transforming a
3725      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>
3726      * , the scaling will be applied first!
3727      * 
3728      * @param x
3729      *            the factor of the x component
3730      * @param y
3731      *            the factor of the y component
3732      * @param z
3733      *            the factor of the z component
3734      * @return this
3735      */
3736     ref public Matrix4d scale(double x, double y, double z) return {
3737         scale(x, y, z, this);
3738         return this;
3739     }
3740 
3741     public Matrix4d scale(double xyz, ref Matrix4d dest) {
3742         return scale(xyz, xyz, xyz, dest);
3743     }
3744 
3745     /**
3746      * Apply scaling to this matrix by uniformly scaling all base axes by the given xyz factor.
3747      * <p>
3748      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
3749      * then the new matrix will be <code>M * S</code>. So when transforming a
3750      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>
3751      * , the scaling will be applied first!
3752      * 
3753      * @see #scale(double, double, double)
3754      * 
3755      * @param xyz
3756      *            the factor for all components
3757      * @return this
3758      */
3759     ref public Matrix4d scale(double xyz) return {
3760         return scale(xyz, xyz, xyz);
3761     }
3762 
3763     public Matrix4d scaleXY(double x, double y, ref Matrix4d dest) {
3764         return scale(x, y, 1.0, dest);
3765     }
3766 
3767     /**
3768      * Apply scaling to this matrix by scaling the X axis by <code>x</code> and the Y axis by <code>y</code>.
3769      * <p>
3770      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
3771      * then the new matrix will be <code>M * S</code>. So when transforming a
3772      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>, the
3773      * scaling will be applied first!
3774      * 
3775      * @param x
3776      *            the factor of the x component
3777      * @param y
3778      *            the factor of the y component
3779      * @return this
3780      */
3781     ref public Matrix4d scaleXY(double x, double y) return {
3782         return scale(x, y, 1.0);
3783     }
3784 
3785     public Matrix4d scaleAround(double sx, double sy, double sz, double ox, double oy, double oz, ref Matrix4d dest) {
3786         double nm30 = m00 * ox + m10 * oy + m20 * oz + m30;
3787         double nm31 = m01 * ox + m11 * oy + m21 * oz + m31;
3788         double nm32 = m02 * ox + m12 * oy + m22 * oz + m32;
3789         double nm33 = m03 * ox + m13 * oy + m23 * oz + m33;
3790         bool one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz);
3791         return dest
3792         ._m00(m00 * sx)
3793         ._m01(m01 * sx)
3794         ._m02(m02 * sx)
3795         ._m03(m03 * sx)
3796         ._m10(m10 * sy)
3797         ._m11(m11 * sy)
3798         ._m12(m12 * sy)
3799         ._m13(m13 * sy)
3800         ._m20(m20 * sz)
3801         ._m21(m21 * sz)
3802         ._m22(m22 * sz)
3803         ._m23(m23 * sz)
3804         ._m30(-dest.m00 * ox - dest.m10 * oy - dest.m20 * oz + nm30)
3805         ._m31(-dest.m01 * ox - dest.m11 * oy - dest.m21 * oz + nm31)
3806         ._m32(-dest.m02 * ox - dest.m12 * oy - dest.m22 * oz + nm32)
3807         ._m33(-dest.m03 * ox - dest.m13 * oy - dest.m23 * oz + nm33)
3808         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION
3809             | (one ? 0 : PROPERTY_ORTHONORMAL)));
3810     }
3811 
3812     /**
3813      * Apply scaling to this matrix by scaling the base axes by the given sx,
3814      * sy and sz factors while using <code>(ox, oy, oz)</code> as the scaling origin.
3815      * <p>
3816      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
3817      * then the new matrix will be <code>M * S</code>. So when transforming a
3818      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>, the
3819      * scaling will be applied first!
3820      * <p>
3821      * This method is equivalent to calling: <code>translate(ox, oy, oz).scale(sx, sy, sz).translate(-ox, -oy, -oz)</code>
3822      * 
3823      * @param sx
3824      *            the scaling factor of the x component
3825      * @param sy
3826      *            the scaling factor of the y component
3827      * @param sz
3828      *            the scaling factor of the z component
3829      * @param ox
3830      *            the x coordinate of the scaling origin
3831      * @param oy
3832      *            the y coordinate of the scaling origin
3833      * @param oz
3834      *            the z coordinate of the scaling origin
3835      * @return this
3836      */
3837     ref public Matrix4d scaleAround(double sx, double sy, double sz, double ox, double oy, double oz) return {
3838         scaleAround(sx, sy, sz, ox, oy, oz, this);
3839         return this;
3840     }
3841 
3842     /**
3843      * Apply scaling to this matrix by scaling all three base axes by the given <code>factor</code>
3844      * while using <code>(ox, oy, oz)</code> as the scaling origin.
3845      * <p>
3846      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
3847      * then the new matrix will be <code>M * S</code>. So when transforming a
3848      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>, the
3849      * scaling will be applied first!
3850      * <p>
3851      * This method is equivalent to calling: <code>translate(ox, oy, oz).scale(factor).translate(-ox, -oy, -oz)</code>
3852      * 
3853      * @param factor
3854      *            the scaling factor for all three axes
3855      * @param ox
3856      *            the x coordinate of the scaling origin
3857      * @param oy
3858      *            the y coordinate of the scaling origin
3859      * @param oz
3860      *            the z coordinate of the scaling origin
3861      * @return this
3862      */
3863     ref public Matrix4d scaleAround(double factor, double ox, double oy, double oz) return {
3864         scaleAround(factor, factor, factor, ox, oy, oz, this);
3865         return this;
3866     }
3867 
3868     public Matrix4d scaleAround(double factor, double ox, double oy, double oz, ref Matrix4d dest) {
3869         return scaleAround(factor, factor, factor, ox, oy, oz, dest);
3870     }
3871 
3872     public Matrix4d scaleLocal(double x, double y, double z, ref Matrix4d dest) {
3873         if ((properties & PROPERTY_IDENTITY) != 0)
3874             return dest.scaling(x, y, z);
3875         return scaleLocalGeneric(x, y, z, dest);
3876     }
3877     private Matrix4d scaleLocalGeneric(double x, double y, double z, ref Matrix4d dest) {
3878         double nm00 = x * m00;
3879         double nm01 = y * m01;
3880         double nm02 = z * m02;
3881         double nm10 = x * m10;
3882         double nm11 = y * m11;
3883         double nm12 = z * m12;
3884         double nm20 = x * m20;
3885         double nm21 = y * m21;
3886         double nm22 = z * m22;
3887         double nm30 = x * m30;
3888         double nm31 = y * m31;
3889         double nm32 = z * m32;
3890         bool one = Math.absEqualsOne(x) && Math.absEqualsOne(y) && Math.absEqualsOne(z);
3891         dest._m00(nm00)
3892         ._m01(nm01)
3893         ._m02(nm02)
3894         ._m03(m03)
3895         ._m10(nm10)
3896         ._m11(nm11)
3897         ._m12(nm12)
3898         ._m13(m13)
3899         ._m20(nm20)
3900         ._m21(nm21)
3901         ._m22(nm22)
3902         ._m23(m23)
3903         ._m30(nm30)
3904         ._m31(nm31)
3905         ._m32(nm32)
3906         ._m33(m33)
3907         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION
3908                 | (one ? 0 : PROPERTY_ORTHONORMAL)));
3909         return dest;
3910     }
3911 
3912     public Matrix4d scaleLocal(double xyz, ref Matrix4d dest) {
3913         return scaleLocal(xyz, xyz, xyz, dest);
3914     }
3915 
3916     /**
3917      * Pre-multiply scaling to this matrix by scaling the base axes by the given xyz factor.
3918      * <p>
3919      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
3920      * then the new matrix will be <code>S * M</code>. So when transforming a
3921      * vector <code>v</code> with the new matrix by using <code>S * M * v</code>, the
3922      * scaling will be applied last!
3923      * 
3924      * @param xyz
3925      *            the factor of the x, y and z component
3926      * @return this
3927      */
3928     ref public Matrix4d scaleLocal(double xyz) return {
3929         scaleLocal(xyz, this);
3930         return this;
3931     }
3932 
3933     /**
3934      * Pre-multiply scaling to this matrix by scaling the base axes by the given x,
3935      * y and z factors.
3936      * <p>
3937      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
3938      * then the new matrix will be <code>S * M</code>. So when transforming a
3939      * vector <code>v</code> with the new matrix by using <code>S * M * v</code>, the
3940      * scaling will be applied last!
3941      * 
3942      * @param x
3943      *            the factor of the x component
3944      * @param y
3945      *            the factor of the y component
3946      * @param z
3947      *            the factor of the z component
3948      * @return this
3949      */
3950     ref public Matrix4d scaleLocal(double x, double y, double z) return {
3951         scaleLocal(x, y, z, this);
3952         return this;
3953     }
3954 
3955     public Matrix4d scaleAroundLocal(double sx, double sy, double sz, double ox, double oy, double oz, ref Matrix4d dest) {
3956         bool one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz);
3957         dest._m00(sx * (m00 - ox * m03) + ox * m03)
3958         ._m01(sy * (m01 - oy * m03) + oy * m03)
3959         ._m02(sz * (m02 - oz * m03) + oz * m03)
3960         ._m03(m03)
3961         ._m10(sx * (m10 - ox * m13) + ox * m13)
3962         ._m11(sy * (m11 - oy * m13) + oy * m13)
3963         ._m12(sz * (m12 - oz * m13) + oz * m13)
3964         ._m13(m13)
3965         ._m20(sx * (m20 - ox * m23) + ox * m23)
3966         ._m21(sy * (m21 - oy * m23) + oy * m23)
3967         ._m22(sz * (m22 - oz * m23) + oz * m23)
3968         ._m23(m23)
3969         ._m30(sx * (m30 - ox * m33) + ox * m33)
3970         ._m31(sy * (m31 - oy * m33) + oy * m33)
3971         ._m32(sz * (m32 - oz * m33) + oz * m33)
3972         ._m33(m33)
3973         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION
3974                 | (one ? 0 : PROPERTY_ORTHONORMAL)));
3975         return dest;
3976     }
3977 
3978     /**
3979      * Pre-multiply scaling to this matrix by scaling the base axes by the given sx,
3980      * sy and sz factors while using <code>(ox, oy, oz)</code> as the scaling origin.
3981      * <p>
3982      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
3983      * then the new matrix will be <code>S * M</code>. So when transforming a
3984      * vector <code>v</code> with the new matrix by using <code>S * M * v</code>, the
3985      * scaling will be applied last!
3986      * <p>
3987      * This method is equivalent to calling: <code>new Matrix4d().translate(ox, oy, oz).scale(sx, sy, sz).translate(-ox, -oy, -oz).mul(this, this)</code>
3988      * 
3989      * @param sx
3990      *            the scaling factor of the x component
3991      * @param sy
3992      *            the scaling factor of the y component
3993      * @param sz
3994      *            the scaling factor of the z component
3995      * @param ox
3996      *            the x coordinate of the scaling origin
3997      * @param oy
3998      *            the y coordinate of the scaling origin
3999      * @param oz
4000      *            the z coordinate of the scaling origin
4001      * @return this
4002      */
4003     ref public Matrix4d scaleAroundLocal(double sx, double sy, double sz, double ox, double oy, double oz) return {
4004         scaleAroundLocal(sx, sy, sz, ox, oy, oz, this);
4005         return this;
4006     }
4007 
4008     /**
4009      * Pre-multiply scaling to this matrix by scaling all three base axes by the given <code>factor</code>
4010      * while using <code>(ox, oy, oz)</code> as the scaling origin.
4011      * <p>
4012      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
4013      * then the new matrix will be <code>S * M</code>. So when transforming a
4014      * vector <code>v</code> with the new matrix by using <code>S * M * v</code>, the
4015      * scaling will be applied last!
4016      * <p>
4017      * This method is equivalent to calling: <code>new Matrix4d().translate(ox, oy, oz).scale(factor).translate(-ox, -oy, -oz).mul(this, this)</code>
4018      * 
4019      * @param factor
4020      *            the scaling factor for all three axes
4021      * @param ox
4022      *            the x coordinate of the scaling origin
4023      * @param oy
4024      *            the y coordinate of the scaling origin
4025      * @param oz
4026      *            the z coordinate of the scaling origin
4027      * @return this
4028      */
4029     ref public Matrix4d scaleAroundLocal(double factor, double ox, double oy, double oz) return {
4030         scaleAroundLocal(factor, factor, factor, ox, oy, oz, this);
4031         return this;
4032     }
4033 
4034     public Matrix4d scaleAroundLocal(double factor, double ox, double oy, double oz, ref Matrix4d dest) {
4035         return scaleAroundLocal(factor, factor, factor, ox, oy, oz, dest);
4036     }
4037 
4038     public Matrix4d rotate(double ang, double x, double y, double z, ref Matrix4d dest) {
4039         if ((properties & PROPERTY_IDENTITY) != 0)
4040             return dest.rotation(ang, x, y, z);
4041         else if ((properties & PROPERTY_TRANSLATION) != 0)
4042             return rotateTranslation(ang, x, y, z, dest);
4043         else if ((properties & PROPERTY_AFFINE) != 0)
4044             return rotateAffine(ang, x, y, z, dest);
4045         return rotateGeneric(ang, x, y, z, dest);
4046     }
4047     private Matrix4d rotateGeneric(double ang, double x, double y, double z, ref Matrix4d dest) {
4048         if (y == 0.0 && z == 0.0 && Math.absEqualsOne(x))
4049             return rotateX(x * ang, dest);
4050         else if (x == 0.0 && z == 0.0 && Math.absEqualsOne(y))
4051             return rotateY(y * ang, dest);
4052         else if (x == 0.0 && y == 0.0 && Math.absEqualsOne(z))
4053             return rotateZ(z * ang, dest);
4054         return rotateGenericInternal(ang, x, y, z, dest);
4055     }
4056     private Matrix4d rotateGenericInternal(double ang, double x, double y, double z, ref Matrix4d dest) {
4057         double s = Math.sin(ang);
4058         double c = Math.cosFromSin(s, ang);
4059         double C = 1.0 - c;
4060         double xx = x * x, xy = x * y, xz = x * z;
4061         double yy = y * y, yz = y * z;
4062         double zz = z * z;
4063         double rm00 = xx * C + c;
4064         double rm01 = xy * C + z * s;
4065         double rm02 = xz * C - y * s;
4066         double rm10 = xy * C - z * s;
4067         double rm11 = yy * C + c;
4068         double rm12 = yz * C + x * s;
4069         double rm20 = xz * C + y * s;
4070         double rm21 = yz * C - x * s;
4071         double rm22 = zz * C + c;
4072         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
4073         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
4074         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
4075         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02;
4076         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
4077         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
4078         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
4079         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12;
4080         dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
4081         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
4082         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
4083         ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22)
4084         ._m00(nm00)
4085         ._m01(nm01)
4086         ._m02(nm02)
4087         ._m03(nm03)
4088         ._m10(nm10)
4089         ._m11(nm11)
4090         ._m12(nm12)
4091         ._m13(nm13)
4092         ._m30(m30)
4093         ._m31(m31)
4094         ._m32(m32)
4095         ._m33(m33)
4096         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
4097         return dest;
4098     }
4099 
4100     /**
4101      * Apply rotation to this matrix by rotating the given amount of radians
4102      * about the given axis specified as x, y and z components.
4103      * <p>
4104      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4105      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4106      * When used with a left-handed coordinate system, the rotation is clockwise.
4107      * <p>
4108      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
4109      * then the new matrix will be <code>M * R</code>. So when transforming a
4110      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>
4111      * , the rotation will be applied first!
4112      * <p>
4113      * In order to set the matrix to a rotation matrix without post-multiplying the rotation
4114      * transformation, use {@link #rotation(double, double, double, double) rotation()}.
4115      * 
4116      * @see #rotation(double, double, double, double)
4117      *  
4118      * @param ang
4119      *            the angle is in radians
4120      * @param x
4121      *            the x component of the axis
4122      * @param y
4123      *            the y component of the axis
4124      * @param z
4125      *            the z component of the axis
4126      * @return this
4127      */
4128     ref public Matrix4d rotate(double ang, double x, double y, double z) return {
4129         rotate(ang, x, y, z, this);
4130         return this;
4131     }
4132 
4133     /**
4134      * Apply rotation to this matrix, which is assumed to only contain a translation, by rotating the given amount of radians
4135      * about the specified <code>(x, y, z)</code> axis and store the result in <code>dest</code>.
4136      * <p>
4137      * This method assumes <code>this</code> to only contain a translation.
4138      * <p>
4139      * The axis described by the three components needs to be a unit vector.
4140      * <p>
4141      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4142      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4143      * When used with a left-handed coordinate system, the rotation is clockwise.
4144      * <p>
4145      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
4146      * then the new matrix will be <code>M * R</code>. So when transforming a
4147      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
4148      * rotation will be applied first!
4149      * <p>
4150      * In order to set the matrix to a rotation matrix without post-multiplying the rotation
4151      * transformation, use {@link #rotation(double, double, double, double) rotation()}.
4152      * <p>
4153      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
4154      * 
4155      * @see #rotation(double, double, double, double)
4156      * 
4157      * @param ang
4158      *            the angle in radians
4159      * @param x
4160      *            the x component of the axis
4161      * @param y
4162      *            the y component of the axis
4163      * @param z
4164      *            the z component of the axis
4165      * @param dest
4166      *            will hold the result
4167      * @return dest
4168      */
4169     public Matrix4d rotateTranslation(double ang, double x, double y, double z, ref Matrix4d dest) {
4170         double tx = m30, ty = m31, tz = m32;
4171         if (y == 0.0 && z == 0.0 && Math.absEqualsOne(x))
4172             return dest.rotationX(x * ang).setTranslation(tx, ty, tz);
4173         else if (x == 0.0 && z == 0.0 && Math.absEqualsOne(y))
4174             return dest.rotationY(y * ang).setTranslation(tx, ty, tz);
4175         else if (x == 0.0 && y == 0.0 && Math.absEqualsOne(z))
4176             return dest.rotationZ(z * ang).setTranslation(tx, ty, tz);
4177         return rotateTranslationInternal(ang, x, y, z, dest);
4178     }
4179     private Matrix4d rotateTranslationInternal(double ang, double x, double y, double z, ref Matrix4d dest) {
4180         double s = Math.sin(ang);
4181         double c = Math.cosFromSin(s, ang);
4182         double C = 1.0 - c;
4183         double xx = x * x, xy = x * y, xz = x * z;
4184         double yy = y * y, yz = y * z;
4185         double zz = z * z;
4186         double rm00 = xx * C + c;
4187         double rm01 = xy * C + z * s;
4188         double rm02 = xz * C - y * s;
4189         double rm10 = xy * C - z * s;
4190         double rm11 = yy * C + c;
4191         double rm12 = yz * C + x * s;
4192         double rm20 = xz * C + y * s;
4193         double rm21 = yz * C - x * s;
4194         double rm22 = zz * C + c;
4195         return dest
4196         ._m20(rm20)
4197         ._m21(rm21)
4198         ._m22(rm22)
4199         ._m23(0.0)
4200         ._m00(rm00)
4201         ._m01(rm01)
4202         ._m02(rm02)
4203         ._m03(0.0)
4204         ._m10(rm10)
4205         ._m11(rm11)
4206         ._m12(rm12)
4207         ._m13(0.0)
4208         ._m30(m30)
4209         ._m31(m31)
4210         ._m32(m32)
4211         ._m33(1.0)
4212         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
4213     }
4214 
4215     /**
4216      * Apply rotation to this {@link #isAffine() affine} matrix by rotating the given amount of radians
4217      * about the specified <code>(x, y, z)</code> axis and store the result in <code>dest</code>.
4218      * <p>
4219      * This method assumes <code>this</code> to be {@link #isAffine() affine}.
4220      * <p>
4221      * The axis described by the three components needs to be a unit vector.
4222      * <p>
4223      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4224      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4225      * When used with a left-handed coordinate system, the rotation is clockwise.
4226      * <p>
4227      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
4228      * then the new matrix will be <code>M * R</code>. So when transforming a
4229      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
4230      * rotation will be applied first!
4231      * <p>
4232      * In order to set the matrix to a rotation matrix without post-multiplying the rotation
4233      * transformation, use {@link #rotation(double, double, double, double) rotation()}.
4234      * <p>
4235      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
4236      * 
4237      * @see #rotation(double, double, double, double)
4238      * 
4239      * @param ang
4240      *            the angle in radians
4241      * @param x
4242      *            the x component of the axis
4243      * @param y
4244      *            the y component of the axis
4245      * @param z
4246      *            the z component of the axis
4247      * @param dest
4248      *            will hold the result
4249      * @return dest
4250      */
4251     public Matrix4d rotateAffine(double ang, double x, double y, double z, ref Matrix4d dest) {
4252         if (y == 0.0 && z == 0.0 && Math.absEqualsOne(x))
4253             return rotateX(x * ang, dest);
4254         else if (x == 0.0 && z == 0.0 && Math.absEqualsOne(y))
4255             return rotateY(y * ang, dest);
4256         else if (x == 0.0 && y == 0.0 && Math.absEqualsOne(z))
4257             return rotateZ(z * ang, dest);
4258         return rotateAffineInternal(ang, x, y, z, dest);
4259     }
4260     private Matrix4d rotateAffineInternal(double ang, double x, double y, double z, ref Matrix4d dest) {
4261         double s = Math.sin(ang);
4262         double c = Math.cosFromSin(s, ang);
4263         double C = 1.0 - c;
4264         double xx = x * x, xy = x * y, xz = x * z;
4265         double yy = y * y, yz = y * z;
4266         double zz = z * z;
4267         double rm00 = xx * C + c;
4268         double rm01 = xy * C + z * s;
4269         double rm02 = xz * C - y * s;
4270         double rm10 = xy * C - z * s;
4271         double rm11 = yy * C + c;
4272         double rm12 = yz * C + x * s;
4273         double rm20 = xz * C + y * s;
4274         double rm21 = yz * C - x * s;
4275         double rm22 = zz * C + c;
4276         // add temporaries for dependent values
4277         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
4278         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
4279         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
4280         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
4281         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
4282         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
4283         // set non-dependent values directly
4284         dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
4285         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
4286         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
4287         ._m23(0.0)
4288         // set other values
4289         ._m00(nm00)
4290         ._m01(nm01)
4291         ._m02(nm02)
4292         ._m03(0.0)
4293         ._m10(nm10)
4294         ._m11(nm11)
4295         ._m12(nm12)
4296         ._m13(0.0)
4297         ._m30(m30)
4298         ._m31(m31)
4299         ._m32(m32)
4300         ._m33(m33)
4301         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
4302         return dest;
4303     }
4304 
4305     /**
4306      * Apply rotation to this {@link #isAffine() affine} matrix by rotating the given amount of radians
4307      * about the specified <code>(x, y, z)</code> axis.
4308      * <p>
4309      * This method assumes <code>this</code> to be {@link #isAffine() affine}.
4310      * <p>
4311      * The axis described by the three components needs to be a unit vector.
4312      * <p>
4313      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4314      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4315      * When used with a left-handed coordinate system, the rotation is clockwise.
4316      * <p>
4317      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
4318      * then the new matrix will be <code>M * R</code>. So when transforming a
4319      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
4320      * rotation will be applied first!
4321      * <p>
4322      * In order to set the matrix to a rotation matrix without post-multiplying the rotation
4323      * transformation, use {@link #rotation(double, double, double, double) rotation()}.
4324      * <p>
4325      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
4326      * 
4327      * @see #rotation(double, double, double, double)
4328      * 
4329      * @param ang
4330      *            the angle in radians
4331      * @param x
4332      *            the x component of the axis
4333      * @param y
4334      *            the y component of the axis
4335      * @param z
4336      *            the z component of the axis
4337      * @return this
4338      */
4339     ref public Matrix4d rotateAffine(double ang, double x, double y, double z) return {
4340         rotateAffine(ang, x, y, z, this);
4341         return this;
4342     }
4343 
4344     /**
4345      * Apply the rotation transformation of the given {@link Quaterniond} to this matrix while using <code>(ox, oy, oz)</code> as the rotation origin.
4346      * <p>
4347      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4348      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4349      * When used with a left-handed coordinate system, the rotation is clockwise.
4350      * <p>
4351      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
4352      * then the new matrix will be <code>M * Q</code>. So when transforming a
4353      * vector <code>v</code> with the new matrix by using <code>M * Q * v</code>,
4354      * the quaternion rotation will be applied first!
4355      * <p>
4356      * This method is equivalent to calling: <code>translate(ox, oy, oz).rotate(quat).translate(-ox, -oy, -oz)</code>
4357      * <p>
4358      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
4359      * 
4360      * @param quat
4361      *          the {@link Quaterniond}
4362      * @param ox
4363      *          the x coordinate of the rotation origin
4364      * @param oy
4365      *          the y coordinate of the rotation origin
4366      * @param oz
4367      *          the z coordinate of the rotation origin
4368      * @return this
4369      */
4370     ref public Matrix4d rotateAround(ref Quaterniond quat, double ox, double oy, double oz) return {
4371         rotateAround(quat, ox, oy, oz, this);
4372         return this;
4373     }
4374 
4375     public Matrix4d rotateAroundAffine(ref Quaterniond quat, double ox, double oy, double oz, ref Matrix4d dest) {
4376         double w2 = quat.w * quat.w, x2 = quat.x * quat.x;
4377         double y2 = quat.y * quat.y, z2 = quat.z * quat.z;
4378         double zw = quat.z * quat.w, dzw = zw + zw, xy = quat.x * quat.y, dxy = xy + xy;
4379         double xz = quat.x * quat.z, dxz = xz + xz, yw = quat.y * quat.w, dyw = yw + yw;
4380         double yz = quat.y * quat.z, dyz = yz + yz, xw = quat.x * quat.w, dxw = xw + xw;
4381         double rm00 = w2 + x2 - z2 - y2;
4382         double rm01 = dxy + dzw;
4383         double rm02 = dxz - dyw;
4384         double rm10 = -dzw + dxy;
4385         double rm11 = y2 - z2 + w2 - x2;
4386         double rm12 = dyz + dxw;
4387         double rm20 = dyw + dxz;
4388         double rm21 = dyz - dxw;
4389         double rm22 = z2 - y2 - x2 + w2;
4390         double tm30 = m00 * ox + m10 * oy + m20 * oz + m30;
4391         double tm31 = m01 * ox + m11 * oy + m21 * oz + m31;
4392         double tm32 = m02 * ox + m12 * oy + m22 * oz + m32;
4393         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
4394         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
4395         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
4396         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
4397         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
4398         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
4399         dest
4400         ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
4401         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
4402         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
4403         ._m23(0.0)
4404         ._m00(nm00)
4405         ._m01(nm01)
4406         ._m02(nm02)
4407         ._m03(0.0)
4408         ._m10(nm10)
4409         ._m11(nm11)
4410         ._m12(nm12)
4411         ._m13(0.0)
4412         ._m30(-nm00 * ox - nm10 * oy - m20 * oz + tm30)
4413         ._m31(-nm01 * ox - nm11 * oy - m21 * oz + tm31)
4414         ._m32(-nm02 * ox - nm12 * oy - m22 * oz + tm32)
4415         ._m33(1.0)
4416         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
4417         return dest;
4418     }
4419 
4420     public Matrix4d rotateAround(ref Quaterniond quat, double ox, double oy, double oz, ref Matrix4d dest) {
4421         if ((properties & PROPERTY_IDENTITY) != 0)
4422             return rotationAround(quat, ox, oy, oz);
4423         else if ((properties & PROPERTY_AFFINE) != 0)
4424             return rotateAroundAffine(quat, ox, oy, oz, this);
4425         return rotateAroundGeneric(quat, ox, oy, oz, this);
4426     }
4427     private Matrix4d rotateAroundGeneric(ref Quaterniond quat, double ox, double oy, double oz, ref Matrix4d dest) {
4428         double w2 = quat.w * quat.w, x2 = quat.x * quat.x;
4429         double y2 = quat.y * quat.y, z2 = quat.z * quat.z;
4430         double zw = quat.z * quat.w, dzw = zw + zw, xy = quat.x * quat.y, dxy = xy + xy;
4431         double xz = quat.x * quat.z, dxz = xz + xz, yw = quat.y * quat.w, dyw = yw + yw;
4432         double yz = quat.y * quat.z, dyz = yz + yz, xw = quat.x * quat.w, dxw = xw + xw;
4433         double rm00 = w2 + x2 - z2 - y2;
4434         double rm01 = dxy + dzw;
4435         double rm02 = dxz - dyw;
4436         double rm10 = -dzw + dxy;
4437         double rm11 = y2 - z2 + w2 - x2;
4438         double rm12 = dyz + dxw;
4439         double rm20 = dyw + dxz;
4440         double rm21 = dyz - dxw;
4441         double rm22 = z2 - y2 - x2 + w2;
4442         double tm30 = m00 * ox + m10 * oy + m20 * oz + m30;
4443         double tm31 = m01 * ox + m11 * oy + m21 * oz + m31;
4444         double tm32 = m02 * ox + m12 * oy + m22 * oz + m32;
4445         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
4446         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
4447         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
4448         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02;
4449         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
4450         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
4451         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
4452         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12;
4453         dest
4454         ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
4455         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
4456         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
4457         ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22)
4458         ._m00(nm00)
4459         ._m01(nm01)
4460         ._m02(nm02)
4461         ._m03(nm03)
4462         ._m10(nm10)
4463         ._m11(nm11)
4464         ._m12(nm12)
4465         ._m13(nm13)
4466         ._m30(-nm00 * ox - nm10 * oy - m20 * oz + tm30)
4467         ._m31(-nm01 * ox - nm11 * oy - m21 * oz + tm31)
4468         ._m32(-nm02 * ox - nm12 * oy - m22 * oz + tm32)
4469         ._m33(m33)
4470         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
4471         return dest;
4472     }
4473 
4474     /**
4475      * Set this matrix to a transformation composed of a rotation of the specified {@link Quaterniond} while using <code>(ox, oy, oz)</code> as the rotation origin.
4476      * <p>
4477      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4478      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4479      * When used with a left-handed coordinate system, the rotation is clockwise.
4480      * <p>
4481      * This method is equivalent to calling: <code>translation(ox, oy, oz).rotate(quat).translate(-ox, -oy, -oz)</code>
4482      * <p>
4483      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
4484      * 
4485      * @param quat
4486      *          the {@link Quaterniond}
4487      * @param ox
4488      *          the x coordinate of the rotation origin
4489      * @param oy
4490      *          the y coordinate of the rotation origin
4491      * @param oz
4492      *          the z coordinate of the rotation origin
4493      * @return this
4494      */
4495     ref public Matrix4d rotationAround(ref Quaterniond quat, double ox, double oy, double oz) return {
4496         double w2 = quat.w * quat.w, x2 = quat.x * quat.x;
4497         double y2 = quat.y * quat.y, z2 = quat.z * quat.z;
4498         double zw = quat.z * quat.w, dzw = zw + zw, xy = quat.x * quat.y, dxy = xy + xy;
4499         double xz = quat.x * quat.z, dxz = xz + xz, yw = quat.y * quat.w, dyw = yw + yw;
4500         double yz = quat.y * quat.z, dyz = yz + yz, xw = quat.x * quat.w, dxw = xw + xw;
4501         this._m20(dyw + dxz);
4502         this._m21(dyz - dxw);
4503         this._m22(z2 - y2 - x2 + w2);
4504         this._m23(0.0);
4505         this._m00(w2 + x2 - z2 - y2);
4506         this._m01(dxy + dzw);
4507         this._m02(dxz - dyw);
4508         this._m03(0.0);
4509         this._m10(-dzw + dxy);
4510         this._m11(y2 - z2 + w2 - x2);
4511         this._m12(dyz + dxw);
4512         this._m13(0.0);
4513         this._m30(-m00 * ox - m10 * oy - m20 * oz + ox);
4514         this._m31(-m01 * ox - m11 * oy - m21 * oz + oy);
4515         this._m32(-m02 * ox - m12 * oy - m22 * oz + oz);
4516         this._m33(1.0);
4517         this.properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
4518         return this;
4519     }
4520 
4521     /**
4522      * Pre-multiply a rotation to this matrix by rotating the given amount of radians
4523      * about the specified <code>(x, y, z)</code> axis and store the result in <code>dest</code>.
4524      * <p>
4525      * The axis described by the three components needs to be a unit vector.
4526      * <p>
4527      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4528      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4529      * When used with a left-handed coordinate system, the rotation is clockwise.
4530      * <p>
4531      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
4532      * then the new matrix will be <code>R * M</code>. So when transforming a
4533      * vector <code>v</code> with the new matrix by using <code>R * M * v</code>, the
4534      * rotation will be applied last!
4535      * <p>
4536      * In order to set the matrix to a rotation matrix without pre-multiplying the rotation
4537      * transformation, use {@link #rotation(double, double, double, double) rotation()}.
4538      * <p>
4539      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
4540      * 
4541      * @see #rotation(double, double, double, double)
4542      * 
4543      * @param ang
4544      *            the angle in radians
4545      * @param x
4546      *            the x component of the axis
4547      * @param y
4548      *            the y component of the axis
4549      * @param z
4550      *            the z component of the axis
4551      * @param dest
4552      *            will hold the result
4553      * @return dest
4554      */
4555     public Matrix4d rotateLocal(double ang, double x, double y, double z, ref Matrix4d dest) {
4556         if ((properties & PROPERTY_IDENTITY) != 0)
4557             return dest.rotation(ang, x, y, z);
4558         return rotateLocalGeneric(ang, x, y, z, dest);
4559     }
4560     private Matrix4d rotateLocalGeneric(double ang, double x, double y, double z, ref Matrix4d dest) {
4561         if (y == 0.0 && z == 0.0 && Math.absEqualsOne(x))
4562             return rotateLocalX(x * ang, dest);
4563         else if (x == 0.0 && z == 0.0 && Math.absEqualsOne(y))
4564             return rotateLocalY(y * ang, dest);
4565         else if (x == 0.0 && y == 0.0 && Math.absEqualsOne(z))
4566             return rotateLocalZ(z * ang, dest);
4567         return rotateLocalGenericInternal(ang, x, y, z, dest);
4568     }
4569     private Matrix4d rotateLocalGenericInternal(double ang, double x, double y, double z, ref Matrix4d dest) {
4570         double s = Math.sin(ang);
4571         double c = Math.cosFromSin(s, ang);
4572         double C = 1.0 - c;
4573         double xx = x * x, xy = x * y, xz = x * z;
4574         double yy = y * y, yz = y * z;
4575         double zz = z * z;
4576         double lm00 = xx * C + c;
4577         double lm01 = xy * C + z * s;
4578         double lm02 = xz * C - y * s;
4579         double lm10 = xy * C - z * s;
4580         double lm11 = yy * C + c;
4581         double lm12 = yz * C + x * s;
4582         double lm20 = xz * C + y * s;
4583         double lm21 = yz * C - x * s;
4584         double lm22 = zz * C + c;
4585         double nm00 = lm00 * m00 + lm10 * m01 + lm20 * m02;
4586         double nm01 = lm01 * m00 + lm11 * m01 + lm21 * m02;
4587         double nm02 = lm02 * m00 + lm12 * m01 + lm22 * m02;
4588         double nm10 = lm00 * m10 + lm10 * m11 + lm20 * m12;
4589         double nm11 = lm01 * m10 + lm11 * m11 + lm21 * m12;
4590         double nm12 = lm02 * m10 + lm12 * m11 + lm22 * m12;
4591         double nm20 = lm00 * m20 + lm10 * m21 + lm20 * m22;
4592         double nm21 = lm01 * m20 + lm11 * m21 + lm21 * m22;
4593         double nm22 = lm02 * m20 + lm12 * m21 + lm22 * m22;
4594         double nm30 = lm00 * m30 + lm10 * m31 + lm20 * m32;
4595         double nm31 = lm01 * m30 + lm11 * m31 + lm21 * m32;
4596         double nm32 = lm02 * m30 + lm12 * m31 + lm22 * m32;
4597         dest._m00(nm00)
4598         ._m01(nm01)
4599         ._m02(nm02)
4600         ._m03(m03)
4601         ._m10(nm10)
4602         ._m11(nm11)
4603         ._m12(nm12)
4604         ._m13(m13)
4605         ._m20(nm20)
4606         ._m21(nm21)
4607         ._m22(nm22)
4608         ._m23(m23)
4609         ._m30(nm30)
4610         ._m31(nm31)
4611         ._m32(nm32)
4612         ._m33(m33)
4613         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
4614         return dest;
4615     }
4616 
4617     /**
4618      * Pre-multiply a rotation to this matrix by rotating the given amount of radians
4619      * about the specified <code>(x, y, z)</code> axis.
4620      * <p>
4621      * The axis described by the three components needs to be a unit vector.
4622      * <p>
4623      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4624      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4625      * When used with a left-handed coordinate system, the rotation is clockwise.
4626      * <p>
4627      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
4628      * then the new matrix will be <code>R * M</code>. So when transforming a
4629      * vector <code>v</code> with the new matrix by using <code>R * M * v</code>, the
4630      * rotation will be applied last!
4631      * <p>
4632      * In order to set the matrix to a rotation matrix without pre-multiplying the rotation
4633      * transformation, use {@link #rotation(double, double, double, double) rotation()}.
4634      * <p>
4635      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
4636      * 
4637      * @see #rotation(double, double, double, double)
4638      * 
4639      * @param ang
4640      *            the angle in radians
4641      * @param x
4642      *            the x component of the axis
4643      * @param y
4644      *            the y component of the axis
4645      * @param z
4646      *            the z component of the axis
4647      * @return this
4648      */
4649     ref public Matrix4d rotateLocal(double ang, double x, double y, double z) return {
4650         rotateLocal(ang, x, y, z, this);
4651         return this;
4652     }
4653 
4654     public Matrix4d rotateAroundLocal(ref Quaterniond quat, double ox, double oy, double oz, ref Matrix4d dest) {
4655         double w2 = quat.w * quat.w;
4656         double x2 = quat.x * quat.x;
4657         double y2 = quat.y * quat.y;
4658         double z2 = quat.z * quat.z;
4659         double zw = quat.z * quat.w;
4660         double xy = quat.x * quat.y;
4661         double xz = quat.x * quat.z;
4662         double yw = quat.y * quat.w;
4663         double yz = quat.y * quat.z;
4664         double xw = quat.x * quat.w;
4665         double lm00 = w2 + x2 - z2 - y2;
4666         double lm01 = xy + zw + zw + xy;
4667         double lm02 = xz - yw + xz - yw;
4668         double lm10 = -zw + xy - zw + xy;
4669         double lm11 = y2 - z2 + w2 - x2;
4670         double lm12 = yz + yz + xw + xw;
4671         double lm20 = yw + xz + xz + yw;
4672         double lm21 = yz + yz - xw - xw;
4673         double lm22 = z2 - y2 - x2 + w2;
4674         double tm00 = m00 - ox * m03;
4675         double tm01 = m01 - oy * m03;
4676         double tm02 = m02 - oz * m03;
4677         double tm10 = m10 - ox * m13;
4678         double tm11 = m11 - oy * m13;
4679         double tm12 = m12 - oz * m13;
4680         double tm20 = m20 - ox * m23;
4681         double tm21 = m21 - oy * m23;
4682         double tm22 = m22 - oz * m23;
4683         double tm30 = m30 - ox * m33;
4684         double tm31 = m31 - oy * m33;
4685         double tm32 = m32 - oz * m33;
4686         dest._m00(lm00 * tm00 + lm10 * tm01 + lm20 * tm02 + ox * m03)
4687         ._m01(lm01 * tm00 + lm11 * tm01 + lm21 * tm02 + oy * m03)
4688         ._m02(lm02 * tm00 + lm12 * tm01 + lm22 * tm02 + oz * m03)
4689         ._m03(m03)
4690         ._m10(lm00 * tm10 + lm10 * tm11 + lm20 * tm12 + ox * m13)
4691         ._m11(lm01 * tm10 + lm11 * tm11 + lm21 * tm12 + oy * m13)
4692         ._m12(lm02 * tm10 + lm12 * tm11 + lm22 * tm12 + oz * m13)
4693         ._m13(m13)
4694         ._m20(lm00 * tm20 + lm10 * tm21 + lm20 * tm22 + ox * m23)
4695         ._m21(lm01 * tm20 + lm11 * tm21 + lm21 * tm22 + oy * m23)
4696         ._m22(lm02 * tm20 + lm12 * tm21 + lm22 * tm22 + oz * m23)
4697         ._m23(m23)
4698         ._m30(lm00 * tm30 + lm10 * tm31 + lm20 * tm32 + ox * m33)
4699         ._m31(lm01 * tm30 + lm11 * tm31 + lm21 * tm32 + oy * m33)
4700         ._m32(lm02 * tm30 + lm12 * tm31 + lm22 * tm32 + oz * m33)
4701         ._m33(m33)
4702         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
4703         return dest;
4704     }
4705 
4706     /**
4707      * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaterniond} to this matrix while using <code>(ox, oy, oz)</code>
4708      * as the rotation origin.
4709      * <p>
4710      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4711      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4712      * When used with a left-handed coordinate system, the rotation is clockwise.
4713      * <p>
4714      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
4715      * then the new matrix will be <code>Q * M</code>. So when transforming a
4716      * vector <code>v</code> with the new matrix by using <code>Q * M * v</code>,
4717      * the quaternion rotation will be applied last!
4718      * <p>
4719      * This method is equivalent to calling: <code>translateLocal(-ox, -oy, -oz).rotateLocal(quat).translateLocal(ox, oy, oz)</code>
4720      * <p>
4721      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
4722      * 
4723      * @param quat
4724      *          the {@link Quaterniond}
4725      * @param ox
4726      *          the x coordinate of the rotation origin
4727      * @param oy
4728      *          the y coordinate of the rotation origin
4729      * @param oz
4730      *          the z coordinate of the rotation origin
4731      * @return this
4732      */
4733     ref public Matrix4d rotateAroundLocal(ref Quaterniond quat, double ox, double oy, double oz) return {
4734         rotateAroundLocal(quat, ox, oy, oz, this);
4735         return this;
4736     }
4737 
4738     /**
4739      * Apply a translation to this matrix by translating by the given number of
4740      * units in x, y and z.
4741      * <p>
4742      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the translation
4743      * matrix, then the new matrix will be <code>M * T</code>. So when
4744      * transforming a vector <code>v</code> with the new matrix by using
4745      * <code>M * T * v</code>, the translation will be applied first!
4746      * <p>
4747      * In order to set the matrix to a translation transformation without post-multiplying
4748      * it, use {@link #translation(ref Vector3d)}.
4749      * 
4750      * @see #translation(ref Vector3d)
4751      * 
4752      * @param offset
4753      *          the number of units in x, y and z by which to translate
4754      * @return this
4755      */
4756     ref public Matrix4d translate(ref Vector3d offset) return {
4757         return translate(offset.x, offset.y, offset.z);
4758     }
4759 
4760     /**
4761      * Apply a translation to this matrix by translating by the given number of
4762      * units in x, y and z and store the result in <code>dest</code>.
4763      * <p>
4764      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the translation
4765      * matrix, then the new matrix will be <code>M * T</code>. So when
4766      * transforming a vector <code>v</code> with the new matrix by using
4767      * <code>M * T * v</code>, the translation will be applied first!
4768      * <p>
4769      * In order to set the matrix to a translation transformation without post-multiplying
4770      * it, use {@link #translation(ref Vector3d)}.
4771      * 
4772      * @see #translation(ref Vector3d)
4773      * 
4774      * @param offset
4775      *          the number of units in x, y and z by which to translate
4776      * @param dest
4777      *          will hold the result
4778      * @return dest
4779      */
4780     public Matrix4d translate(ref Vector3d offset, ref Matrix4d dest) {
4781         return translate(offset.x, offset.y, offset.z, dest);
4782     }
4783 
4784 
4785 
4786     /**
4787      * Apply a translation to this matrix by translating by the given number of
4788      * units in x, y and z and store the result in <code>dest</code>.
4789      * <p>
4790      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the translation
4791      * matrix, then the new matrix will be <code>M * T</code>. So when
4792      * transforming a vector <code>v</code> with the new matrix by using
4793      * <code>M * T * v</code>, the translation will be applied first!
4794      * <p>
4795      * In order to set the matrix to a translation transformation without post-multiplying
4796      * it, use {@link #translation(double, double, double)}.
4797      * 
4798      * @see #translation(double, double, double)
4799      * 
4800      * @param x
4801      *          the offset to translate in x
4802      * @param y
4803      *          the offset to translate in y
4804      * @param z
4805      *          the offset to translate in z
4806      * @param dest
4807      *          will hold the result
4808      * @return dest
4809      */
4810     public Matrix4d translate(double x, double y, double z, ref Matrix4d dest) {
4811         if ((properties & PROPERTY_IDENTITY) != 0)
4812             return dest.translation(x, y, z);
4813         return translateGeneric(x, y, z, dest);
4814     }
4815     private Matrix4d translateGeneric(double x, double y, double z, ref Matrix4d dest) {
4816         dest._m00(m00)
4817         ._m01(m01)
4818         ._m02(m02)
4819         ._m03(m03)
4820         ._m10(m10)
4821         ._m11(m11)
4822         ._m12(m12)
4823         ._m13(m13)
4824         ._m20(m20)
4825         ._m21(m21)
4826         ._m22(m22)
4827         ._m23(m23)
4828         ._m30(Math.fma(m00, x, Math.fma(m10, y, Math.fma(m20, z, m30))))
4829         ._m31(Math.fma(m01, x, Math.fma(m11, y, Math.fma(m21, z, m31))))
4830         ._m32(Math.fma(m02, x, Math.fma(m12, y, Math.fma(m22, z, m32))))
4831         ._m33(Math.fma(m03, x, Math.fma(m13, y, Math.fma(m23, z, m33))))
4832         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY));
4833         return dest;
4834     }
4835 
4836     /**
4837      * Apply a translation to this matrix by translating by the given number of
4838      * units in x, y and z.
4839      * <p>
4840      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the translation
4841      * matrix, then the new matrix will be <code>M * T</code>. So when
4842      * transforming a vector <code>v</code> with the new matrix by using
4843      * <code>M * T * v</code>, the translation will be applied first!
4844      * <p>
4845      * In order to set the matrix to a translation transformation without post-multiplying
4846      * it, use {@link #translation(double, double, double)}.
4847      * 
4848      * @see #translation(double, double, double)
4849      * 
4850      * @param x
4851      *          the offset to translate in x
4852      * @param y
4853      *          the offset to translate in y
4854      * @param z
4855      *          the offset to translate in z
4856      * @return this
4857      */
4858     ref public Matrix4d translate(double x, double y, double z) return {
4859         if ((properties & PROPERTY_IDENTITY) != 0)
4860             return translation(x, y, z);
4861         this._m30(Math.fma(m00, x, Math.fma(m10, y, Math.fma(m20, z, m30))));
4862         this._m31(Math.fma(m01, x, Math.fma(m11, y, Math.fma(m21, z, m31))));
4863         this._m32(Math.fma(m02, x, Math.fma(m12, y, Math.fma(m22, z, m32))));
4864         this._m33(Math.fma(m03, x, Math.fma(m13, y, Math.fma(m23, z, m33))));
4865         this.properties &= ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY);
4866         return this;
4867     }
4868 
4869     /**
4870      * Pre-multiply a translation to this matrix by translating by the given number of
4871      * units in x, y and z.
4872      * <p>
4873      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the translation
4874      * matrix, then the new matrix will be <code>T * M</code>. So when
4875      * transforming a vector <code>v</code> with the new matrix by using
4876      * <code>T * M * v</code>, the translation will be applied last!
4877      * <p>
4878      * In order to set the matrix to a translation transformation without pre-multiplying
4879      * it, use {@link #translation(ref Vector3d)}.
4880      * 
4881      * @see #translation(ref Vector3d)
4882      * 
4883      * @param offset
4884      *          the number of units in x, y and z by which to translate
4885      * @return this
4886      */
4887     ref public Matrix4d translateLocal(ref Vector3d offset) return {
4888         return translateLocal(offset.x, offset.y, offset.z);
4889     }
4890 
4891     /**
4892      * Pre-multiply a translation to this matrix by translating by the given number of
4893      * units in x, y and z and store the result in <code>dest</code>.
4894      * <p>
4895      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the translation
4896      * matrix, then the new matrix will be <code>T * M</code>. So when
4897      * transforming a vector <code>v</code> with the new matrix by using
4898      * <code>T * M * v</code>, the translation will be applied last!
4899      * <p>
4900      * In order to set the matrix to a translation transformation without pre-multiplying
4901      * it, use {@link #translation(ref Vector3d)}.
4902      * 
4903      * @see #translation(ref Vector3d)
4904      * 
4905      * @param offset
4906      *          the number of units in x, y and z by which to translate
4907      * @param dest
4908      *          will hold the result
4909      * @return dest
4910      */
4911     public Matrix4d translateLocal(ref Vector3d offset, ref Matrix4d dest) {
4912         return translateLocal(offset.x, offset.y, offset.z, dest);
4913     }
4914 
4915     /**
4916      * Pre-multiply a translation to this matrix by translating by the given number of
4917      * units in x, y and z and store the result in <code>dest</code>.
4918      * <p>
4919      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the translation
4920      * matrix, then the new matrix will be <code>T * M</code>. So when
4921      * transforming a vector <code>v</code> with the new matrix by using
4922      * <code>T * M * v</code>, the translation will be applied last!
4923      * <p>
4924      * In order to set the matrix to a translation transformation without pre-multiplying
4925      * it, use {@link #translation(double, double, double)}.
4926      * 
4927      * @see #translation(double, double, double)
4928      * 
4929      * @param x
4930      *          the offset to translate in x
4931      * @param y
4932      *          the offset to translate in y
4933      * @param z
4934      *          the offset to translate in z
4935      * @param dest
4936      *          will hold the result
4937      * @return dest
4938      */
4939     public Matrix4d translateLocal(double x, double y, double z, ref Matrix4d dest) {
4940         if ((properties & PROPERTY_IDENTITY) != 0)
4941             return dest.translation(x, y, z);
4942         return translateLocalGeneric(x, y, z, dest);
4943     }
4944     private Matrix4d translateLocalGeneric(double x, double y, double z, ref Matrix4d dest) {
4945         double nm00 = m00 + x * m03;
4946         double nm01 = m01 + y * m03;
4947         double nm02 = m02 + z * m03;
4948         double nm10 = m10 + x * m13;
4949         double nm11 = m11 + y * m13;
4950         double nm12 = m12 + z * m13;
4951         double nm20 = m20 + x * m23;
4952         double nm21 = m21 + y * m23;
4953         double nm22 = m22 + z * m23;
4954         double nm30 = m30 + x * m33;
4955         double nm31 = m31 + y * m33;
4956         double nm32 = m32 + z * m33;
4957         return dest
4958         ._m00(nm00)
4959         ._m01(nm01)
4960         ._m02(nm02)
4961         ._m03(m03)
4962         ._m10(nm10)
4963         ._m11(nm11)
4964         ._m12(nm12)
4965         ._m13(m13)
4966         ._m20(nm20)
4967         ._m21(nm21)
4968         ._m22(nm22)
4969         ._m23(m23)
4970         ._m30(nm30)
4971         ._m31(nm31)
4972         ._m32(nm32)
4973         ._m33(m33)
4974         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY));
4975     }
4976 
4977     /**
4978      * Pre-multiply a translation to this matrix by translating by the given number of
4979      * units in x, y and z.
4980      * <p>
4981      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the translation
4982      * matrix, then the new matrix will be <code>T * M</code>. So when
4983      * transforming a vector <code>v</code> with the new matrix by using
4984      * <code>T * M * v</code>, the translation will be applied last!
4985      * <p>
4986      * In order to set the matrix to a translation transformation without pre-multiplying
4987      * it, use {@link #translation(double, double, double)}.
4988      * 
4989      * @see #translation(double, double, double)
4990      * 
4991      * @param x
4992      *          the offset to translate in x
4993      * @param y
4994      *          the offset to translate in y
4995      * @param z
4996      *          the offset to translate in z
4997      * @return this
4998      */
4999     ref public Matrix4d translateLocal(double x, double y, double z) return {
5000         translateLocal(x, y, z, this);
5001         return this;
5002     }
5003 
5004     /**
5005      * Pre-multiply a rotation around the X axis to this matrix by rotating the given amount of radians
5006      * about the X axis and store the result in <code>dest</code>.
5007      * <p>
5008      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5009      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5010      * When used with a left-handed coordinate system, the rotation is clockwise.
5011      * <p>
5012      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5013      * then the new matrix will be <code>R * M</code>. So when transforming a
5014      * vector <code>v</code> with the new matrix by using <code>R * M * v</code>, the
5015      * rotation will be applied last!
5016      * <p>
5017      * In order to set the matrix to a rotation matrix without pre-multiplying the rotation
5018      * transformation, use {@link #rotationX(double) rotationX()}.
5019      * <p>
5020      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
5021      * 
5022      * @see #rotationX(double)
5023      * 
5024      * @param ang
5025      *            the angle in radians to rotate about the X axis
5026      * @param dest
5027      *            will hold the result
5028      * @return dest
5029      */
5030     public Matrix4d rotateLocalX(double ang, ref Matrix4d dest) {
5031         double sin = Math.sin(ang);
5032         double cos = Math.cosFromSin(sin, ang);
5033         double nm02 = sin * m01 + cos * m02;
5034         double nm12 = sin * m11 + cos * m12;
5035         double nm22 = sin * m21 + cos * m22;
5036         double nm32 = sin * m31 + cos * m32;
5037         dest
5038         ._m00(m00)
5039         ._m01(cos * m01 - sin * m02)
5040         ._m02(nm02)
5041         ._m03(m03)
5042         ._m10(m10)
5043         ._m11(cos * m11 - sin * m12)
5044         ._m12(nm12)
5045         ._m13(m13)
5046         ._m20(m20)
5047         ._m21(cos * m21 - sin * m22)
5048         ._m22(nm22)
5049         ._m23(m23)
5050         ._m30(m30)
5051         ._m31(cos * m31 - sin * m32)
5052         ._m32(nm32)
5053         ._m33(m33)
5054         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5055         return dest;
5056     }
5057 
5058     /**
5059      * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the X axis.
5060      * <p>
5061      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5062      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5063      * When used with a left-handed coordinate system, the rotation is clockwise.
5064      * <p>
5065      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5066      * then the new matrix will be <code>R * M</code>. So when transforming a
5067      * vector <code>v</code> with the new matrix by using <code>R * M * v</code>, the
5068      * rotation will be applied last!
5069      * <p>
5070      * In order to set the matrix to a rotation matrix without pre-multiplying the rotation
5071      * transformation, use {@link #rotationX(double) rotationX()}.
5072      * <p>
5073      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
5074      * 
5075      * @see #rotationX(double)
5076      * 
5077      * @param ang
5078      *            the angle in radians to rotate about the X axis
5079      * @return this
5080      */
5081     ref public Matrix4d rotateLocalX(double ang) return {
5082         rotateLocalX(ang, this);
5083         return this;
5084     }
5085 
5086     /**
5087      * Pre-multiply a rotation around the Y axis to this matrix by rotating the given amount of radians
5088      * about the Y axis and store the result in <code>dest</code>.
5089      * <p>
5090      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5091      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5092      * When used with a left-handed coordinate system, the rotation is clockwise.
5093      * <p>
5094      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5095      * then the new matrix will be <code>R * M</code>. So when transforming a
5096      * vector <code>v</code> with the new matrix by using <code>R * M * v</code>, the
5097      * rotation will be applied last!
5098      * <p>
5099      * In order to set the matrix to a rotation matrix without pre-multiplying the rotation
5100      * transformation, use {@link #rotationY(double) rotationY()}.
5101      * <p>
5102      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
5103      * 
5104      * @see #rotationY(double)
5105      * 
5106      * @param ang
5107      *            the angle in radians to rotate about the Y axis
5108      * @param dest
5109      *            will hold the result
5110      * @return dest
5111      */
5112     public Matrix4d rotateLocalY(double ang, ref Matrix4d dest) {
5113         double sin = Math.sin(ang);
5114         double cos = Math.cosFromSin(sin, ang);
5115         double nm02 = -sin * m00 + cos * m02;
5116         double nm12 = -sin * m10 + cos * m12;
5117         double nm22 = -sin * m20 + cos * m22;
5118         double nm32 = -sin * m30 + cos * m32;
5119         dest
5120         ._m00(cos * m00 + sin * m02)
5121         ._m01(m01)
5122         ._m02(nm02)
5123         ._m03(m03)
5124         ._m10(cos * m10 + sin * m12)
5125         ._m11(m11)
5126         ._m12(nm12)
5127         ._m13(m13)
5128         ._m20(cos * m20 + sin * m22)
5129         ._m21(m21)
5130         ._m22(nm22)
5131         ._m23(m23)
5132         ._m30(cos * m30 + sin * m32)
5133         ._m31(m31)
5134         ._m32(nm32)
5135         ._m33(m33)
5136         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5137         return dest;
5138     }
5139 
5140     /**
5141      * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the Y axis.
5142      * <p>
5143      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5144      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5145      * When used with a left-handed coordinate system, the rotation is clockwise.
5146      * <p>
5147      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5148      * then the new matrix will be <code>R * M</code>. So when transforming a
5149      * vector <code>v</code> with the new matrix by using <code>R * M * v</code>, the
5150      * rotation will be applied last!
5151      * <p>
5152      * In order to set the matrix to a rotation matrix without pre-multiplying the rotation
5153      * transformation, use {@link #rotationY(double) rotationY()}.
5154      * <p>
5155      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
5156      * 
5157      * @see #rotationY(double)
5158      * 
5159      * @param ang
5160      *            the angle in radians to rotate about the Y axis
5161      * @return this
5162      */
5163     ref public Matrix4d rotateLocalY(double ang) return {
5164         rotateLocalY(ang, this);
5165         return this;
5166     }
5167 
5168     /**
5169      * Pre-multiply a rotation around the Z axis to this matrix by rotating the given amount of radians
5170      * about the Z axis and store the result in <code>dest</code>.
5171      * <p>
5172      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5173      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5174      * When used with a left-handed coordinate system, the rotation is clockwise.
5175      * <p>
5176      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5177      * then the new matrix will be <code>R * M</code>. So when transforming a
5178      * vector <code>v</code> with the new matrix by using <code>R * M * v</code>, the
5179      * rotation will be applied last!
5180      * <p>
5181      * In order to set the matrix to a rotation matrix without pre-multiplying the rotation
5182      * transformation, use {@link #rotationZ(double) rotationZ()}.
5183      * <p>
5184      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
5185      * 
5186      * @see #rotationZ(double)
5187      * 
5188      * @param ang
5189      *            the angle in radians to rotate about the Z axis
5190      * @param dest
5191      *            will hold the result
5192      * @return dest
5193      */
5194     public Matrix4d rotateLocalZ(double ang, ref Matrix4d dest) {
5195         double sin = Math.sin(ang);
5196         double cos = Math.cosFromSin(sin, ang);
5197         double nm01 = sin * m00 + cos * m01;
5198         double nm11 = sin * m10 + cos * m11;
5199         double nm21 = sin * m20 + cos * m21;
5200         double nm31 = sin * m30 + cos * m31;
5201         dest
5202         ._m00(cos * m00 - sin * m01)
5203         ._m01(nm01)
5204         ._m02(m02)
5205         ._m03(m03)
5206         ._m10(cos * m10 - sin * m11)
5207         ._m11(nm11)
5208         ._m12(m12)
5209         ._m13(m13)
5210         ._m20(cos * m20 - sin * m21)
5211         ._m21(nm21)
5212         ._m22(m22)
5213         ._m23(m23)
5214         ._m30(cos * m30 - sin * m31)
5215         ._m31(nm31)
5216         ._m32(m32)
5217         ._m33(m33)
5218         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5219         return dest;
5220     }
5221 
5222     /**
5223      * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the Z axis.
5224      * <p>
5225      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5226      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5227      * When used with a left-handed coordinate system, the rotation is clockwise.
5228      * <p>
5229      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5230      * then the new matrix will be <code>R * M</code>. So when transforming a
5231      * vector <code>v</code> with the new matrix by using <code>R * M * v</code>, the
5232      * rotation will be applied last!
5233      * <p>
5234      * In order to set the matrix to a rotation matrix without pre-multiplying the rotation
5235      * transformation, use {@link #rotationZ(double) rotationY()}.
5236      * <p>
5237      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
5238      * 
5239      * @see #rotationY(double)
5240      * 
5241      * @param ang
5242      *            the angle in radians to rotate about the Z axis
5243      * @return this
5244      */
5245     ref public Matrix4d rotateLocalZ(double ang) return {
5246         rotateLocalZ(ang, this);
5247         return this;
5248     }
5249 
5250     public Matrix4d rotateX(double ang, ref Matrix4d dest) {
5251         if ((properties & PROPERTY_IDENTITY) != 0)
5252             return dest.rotationX(ang);
5253         else if ((properties & PROPERTY_TRANSLATION) != 0) {
5254             double x = m30, y = m31, z = m32;
5255             return dest.rotationX(ang).setTranslation(x, y, z);
5256         }
5257         return rotateXInternal(ang, dest);
5258     }
5259     private Matrix4d rotateXInternal(double ang, ref Matrix4d dest) {
5260         double sin, cos;
5261         sin = Math.sin(ang);
5262         cos = Math.cosFromSin(sin, ang);
5263         double rm11 = cos;
5264         double rm12 = sin;
5265         double rm21 = -sin;
5266         double rm22 = cos;
5267 
5268         // add temporaries for dependent values
5269         double nm10 = m10 * rm11 + m20 * rm12;
5270         double nm11 = m11 * rm11 + m21 * rm12;
5271         double nm12 = m12 * rm11 + m22 * rm12;
5272         double nm13 = m13 * rm11 + m23 * rm12;
5273         // set non-dependent values directly
5274         dest._m20(m10 * rm21 + m20 * rm22)
5275         ._m21(m11 * rm21 + m21 * rm22)
5276         ._m22(m12 * rm21 + m22 * rm22)
5277         ._m23(m13 * rm21 + m23 * rm22)
5278         // set other values
5279         ._m10(nm10)
5280         ._m11(nm11)
5281         ._m12(nm12)
5282         ._m13(nm13)
5283         ._m00(m00)
5284         ._m01(m01)
5285         ._m02(m02)
5286         ._m03(m03)
5287         ._m30(m30)
5288         ._m31(m31)
5289         ._m32(m32)
5290         ._m33(m33)
5291         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5292         return dest;
5293     }
5294 
5295     /**
5296      * Apply rotation about the X axis to this matrix by rotating the given amount of radians.
5297      * <p>
5298      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5299      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5300      * When used with a left-handed coordinate system, the rotation is clockwise.
5301      * <p>
5302      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5303      * then the new matrix will be <code>M * R</code>. So when transforming a
5304      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5305      * rotation will be applied first!
5306      * <p>
5307      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">http://en.wikipedia.org</a>
5308      * 
5309      * @param ang
5310      *            the angle in radians
5311      * @return this
5312      */
5313     ref public Matrix4d rotateX(double ang) return {
5314         rotateX(ang, this);
5315         return this;
5316     }
5317 
5318     public Matrix4d rotateY(double ang, ref Matrix4d dest) {
5319         if ((properties & PROPERTY_IDENTITY) != 0)
5320             return dest.rotationY(ang);
5321         else if ((properties & PROPERTY_TRANSLATION) != 0) {
5322             double x = m30, y = m31, z = m32;
5323             return dest.rotationY(ang).setTranslation(x, y, z);
5324         }
5325         return rotateYInternal(ang, dest);
5326     }
5327     private Matrix4d rotateYInternal(double ang, ref Matrix4d dest) {
5328         double sin, cos;
5329         sin = Math.sin(ang);
5330         cos = Math.cosFromSin(sin, ang);
5331         double rm00 = cos;
5332         double rm02 = -sin;
5333         double rm20 = sin;
5334         double rm22 = cos;
5335 
5336         // add temporaries for dependent values
5337         double nm00 = m00 * rm00 + m20 * rm02;
5338         double nm01 = m01 * rm00 + m21 * rm02;
5339         double nm02 = m02 * rm00 + m22 * rm02;
5340         double nm03 = m03 * rm00 + m23 * rm02;
5341         // set non-dependent values directly
5342         dest._m20(m00 * rm20 + m20 * rm22)
5343         ._m21(m01 * rm20 + m21 * rm22)
5344         ._m22(m02 * rm20 + m22 * rm22)
5345         ._m23(m03 * rm20 + m23 * rm22)
5346         // set other values
5347         ._m00(nm00)
5348         ._m01(nm01)
5349         ._m02(nm02)
5350         ._m03(nm03)
5351         ._m10(m10)
5352         ._m11(m11)
5353         ._m12(m12)
5354         ._m13(m13)
5355         ._m30(m30)
5356         ._m31(m31)
5357         ._m32(m32)
5358         ._m33(m33)
5359         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5360         return dest;
5361     }
5362 
5363     /**
5364      * Apply rotation about the Y axis to this matrix by rotating the given amount of radians.
5365      * <p>
5366      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5367      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5368      * When used with a left-handed coordinate system, the rotation is clockwise.
5369      * <p>
5370      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5371      * then the new matrix will be <code>M * R</code>. So when transforming a
5372      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5373      * rotation will be applied first!
5374      * <p>
5375      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">http://en.wikipedia.org</a>
5376      * 
5377      * @param ang
5378      *            the angle in radians
5379      * @return this
5380      */
5381     ref public Matrix4d rotateY(double ang) return {
5382         rotateY(ang, this);
5383         return this;
5384     }
5385 
5386     public Matrix4d rotateZ(double ang, ref Matrix4d dest) {
5387         if ((properties & PROPERTY_IDENTITY) != 0)
5388             return dest.rotationZ(ang);
5389         else if ((properties & PROPERTY_TRANSLATION) != 0) {
5390             double x = m30, y = m31, z = m32;
5391             return dest.rotationZ(ang).setTranslation(x, y, z);
5392         }
5393         return rotateZInternal(ang, dest);
5394     }
5395     private Matrix4d rotateZInternal(double ang, ref Matrix4d dest) {
5396         double sin = Math.sin(ang);
5397         double cos = Math.cosFromSin(sin, ang);
5398         return rotateTowardsXY(sin, cos, dest);
5399     }
5400 
5401     /**
5402      * Apply rotation about the Z axis to this matrix by rotating the given amount of radians.
5403      * <p>
5404      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5405      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5406      * When used with a left-handed coordinate system, the rotation is clockwise.
5407      * <p>
5408      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5409      * then the new matrix will be <code>M * R</code>. So when transforming a
5410      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5411      * rotation will be applied first!
5412      * <p>
5413      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">http://en.wikipedia.org</a>
5414      * 
5415      * @param ang
5416      *            the angle in radians
5417      * @return this
5418      */
5419     ref public Matrix4d rotateZ(double ang) return {
5420         rotateZ(ang, this);
5421         return this;
5422     }
5423 
5424     /**
5425      * Apply rotation about the Z axis to align the local <code>+X</code> towards <code>(dirX, dirY)</code>.
5426      * <p>
5427      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5428      * then the new matrix will be <code>M * R</code>. So when transforming a
5429      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5430      * rotation will be applied first!
5431      * <p>
5432      * The vector <code>(dirX, dirY)</code> must be a unit vector.
5433      * 
5434      * @param dirX
5435      *            the x component of the normalized direction
5436      * @param dirY
5437      *            the y component of the normalized direction
5438      * @return this
5439      */
5440     ref public Matrix4d rotateTowardsXY(double dirX, double dirY) return {
5441         rotateTowardsXY(dirX, dirY, this);
5442         return this;
5443     }
5444 
5445     public Matrix4d rotateTowardsXY(double dirX, double dirY, ref Matrix4d dest) {
5446         if ((properties & PROPERTY_IDENTITY) != 0)
5447             return dest.rotationTowardsXY(dirX, dirY);
5448         double rm00 = dirY;
5449         double rm01 = dirX;
5450         double rm10 = -dirX;
5451         double rm11 = dirY;
5452         double nm00 = m00 * rm00 + m10 * rm01;
5453         double nm01 = m01 * rm00 + m11 * rm01;
5454         double nm02 = m02 * rm00 + m12 * rm01;
5455         double nm03 = m03 * rm00 + m13 * rm01;
5456         dest._m10(m00 * rm10 + m10 * rm11)
5457         ._m11(m01 * rm10 + m11 * rm11)
5458         ._m12(m02 * rm10 + m12 * rm11)
5459         ._m13(m03 * rm10 + m13 * rm11)
5460         ._m00(nm00)
5461         ._m01(nm01)
5462         ._m02(nm02)
5463         ._m03(nm03)
5464         ._m20(m20)
5465         ._m21(m21)
5466         ._m22(m22)
5467         ._m23(m23)
5468         ._m30(m30)
5469         ._m31(m31)
5470         ._m32(m32)
5471         ._m33(m33)
5472         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5473         return dest;
5474     }
5475 
5476     /**
5477      * Apply rotation of <code>angles.x</code> radians about the X axis, followed by a rotation of <code>angles.y</code> radians about the Y axis and
5478      * followed by a rotation of <code>angles.z</code> radians about the Z axis.
5479      * <p>
5480      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5481      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5482      * When used with a left-handed coordinate system, the rotation is clockwise.
5483      * <p>
5484      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5485      * then the new matrix will be <code>M * R</code>. So when transforming a
5486      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5487      * rotation will be applied first!
5488      * <p>
5489      * This method is equivalent to calling: <code>rotateX(angles.x).rotateY(angles.y).rotateZ(angles.z)</code>
5490      * 
5491      * @param angles
5492      *            the Euler angles
5493      * @return this
5494      */
5495     ref public Matrix4d rotateXYZ(ref Vector3d angles) return {
5496         return rotateXYZ(angles.x, angles.y, angles.z);
5497     }
5498 
5499     /**
5500      * Apply rotation of <code>angleX</code> radians about the X axis, followed by a rotation of <code>angleY</code> radians about the Y axis and
5501      * followed by a rotation of <code>angleZ</code> radians about the Z axis.
5502      * <p>
5503      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5504      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5505      * When used with a left-handed coordinate system, the rotation is clockwise.
5506      * <p>
5507      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5508      * then the new matrix will be <code>M * R</code>. So when transforming a
5509      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5510      * rotation will be applied first!
5511      * <p>
5512      * This method is equivalent to calling: <code>rotateX(angleX).rotateY(angleY).rotateZ(angleZ)</code>
5513      * 
5514      * @param angleX
5515      *            the angle to rotate about X
5516      * @param angleY
5517      *            the angle to rotate about Y
5518      * @param angleZ
5519      *            the angle to rotate about Z
5520      * @return this
5521      */
5522     ref public Matrix4d rotateXYZ(double angleX, double angleY, double angleZ) return {
5523         rotateXYZ(angleX, angleY, angleZ, this);
5524         return this;
5525     }
5526 
5527     public Matrix4d rotateXYZ(double angleX, double angleY, double angleZ, ref Matrix4d dest) {
5528         if ((properties & PROPERTY_IDENTITY) != 0)
5529             return dest.rotationXYZ(angleX, angleY, angleZ);
5530         else if ((properties & PROPERTY_TRANSLATION) != 0) {
5531             double tx = m30, ty = m31, tz = m32;
5532             return dest.rotationXYZ(angleX, angleY, angleZ).setTranslation(tx, ty, tz);
5533         } else if ((properties & PROPERTY_AFFINE) != 0)
5534             return dest.rotateAffineXYZ(angleX, angleY, angleZ);
5535         return rotateXYZInternal(angleX, angleY, angleZ, dest);
5536     }
5537     private Matrix4d rotateXYZInternal(double angleX, double angleY, double angleZ, ref Matrix4d dest) {
5538         double sinX = Math.sin(angleX);
5539         double cosX = Math.cosFromSin(sinX, angleX);
5540         double sinY = Math.sin(angleY);
5541         double cosY = Math.cosFromSin(sinY, angleY);
5542         double sinZ = Math.sin(angleZ);
5543         double cosZ = Math.cosFromSin(sinZ, angleZ);
5544         double m_sinX = -sinX;
5545         double m_sinY = -sinY;
5546         double m_sinZ = -sinZ;
5547 
5548         // rotateX
5549         double nm10 = m10 * cosX + m20 * sinX;
5550         double nm11 = m11 * cosX + m21 * sinX;
5551         double nm12 = m12 * cosX + m22 * sinX;
5552         double nm13 = m13 * cosX + m23 * sinX;
5553         double nm20 = m10 * m_sinX + m20 * cosX;
5554         double nm21 = m11 * m_sinX + m21 * cosX;
5555         double nm22 = m12 * m_sinX + m22 * cosX;
5556         double nm23 = m13 * m_sinX + m23 * cosX;
5557         // rotateY
5558         double nm00 = m00 * cosY + nm20 * m_sinY;
5559         double nm01 = m01 * cosY + nm21 * m_sinY;
5560         double nm02 = m02 * cosY + nm22 * m_sinY;
5561         double nm03 = m03 * cosY + nm23 * m_sinY;
5562         dest._m20(m00 * sinY + nm20 * cosY)
5563         ._m21(m01 * sinY + nm21 * cosY)
5564         ._m22(m02 * sinY + nm22 * cosY)
5565         ._m23(m03 * sinY + nm23 * cosY)
5566         // rotateZ
5567         ._m00(nm00 * cosZ + nm10 * sinZ)
5568         ._m01(nm01 * cosZ + nm11 * sinZ)
5569         ._m02(nm02 * cosZ + nm12 * sinZ)
5570         ._m03(nm03 * cosZ + nm13 * sinZ)
5571         ._m10(nm00 * m_sinZ + nm10 * cosZ)
5572         ._m11(nm01 * m_sinZ + nm11 * cosZ)
5573         ._m12(nm02 * m_sinZ + nm12 * cosZ)
5574         ._m13(nm03 * m_sinZ + nm13 * cosZ)
5575         // copy last column from 'this'
5576         ._m30(m30)
5577         ._m31(m31)
5578         ._m32(m32)
5579         ._m33(m33)
5580         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5581         return dest;
5582     }
5583 
5584     /**
5585      * Apply rotation of <code>angleX</code> radians about the X axis, followed by a rotation of <code>angleY</code> radians about the Y axis and
5586      * followed by a rotation of <code>angleZ</code> radians about the Z axis.
5587      * <p>
5588      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5589      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5590      * When used with a left-handed coordinate system, the rotation is clockwise.
5591      * <p>
5592      * This method assumes that <code>this</code> matrix represents an {@link #isAffine() affine} transformation (i.e. its last row is equal to <code>(0, 0, 0, 1)</code>)
5593      * and can be used to speed up matrix multiplication if the matrix only represents affine transformations, such as translation, rotation, scaling and shearing (in any combination).
5594      * <p>
5595      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5596      * then the new matrix will be <code>M * R</code>. So when transforming a
5597      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5598      * rotation will be applied first!
5599      * <p>
5600      * This method is equivalent to calling: <code>rotateX(angleX).rotateY(angleY).rotateZ(angleZ)</code>
5601      * 
5602      * @param angleX
5603      *            the angle to rotate about X
5604      * @param angleY
5605      *            the angle to rotate about Y
5606      * @param angleZ
5607      *            the angle to rotate about Z
5608      * @return this
5609      */
5610     ref public Matrix4d rotateAffineXYZ(double angleX, double angleY, double angleZ) return {
5611         rotateAffineXYZ(angleX, angleY, angleZ, this);
5612         return this;
5613     }
5614 
5615     public Matrix4d rotateAffineXYZ(double angleX, double angleY, double angleZ, ref Matrix4d dest) {
5616         if ((properties & PROPERTY_IDENTITY) != 0)
5617             return dest.rotationXYZ(angleX, angleY, angleZ);
5618         else if ((properties & PROPERTY_TRANSLATION) != 0) {
5619             double tx = m30, ty = m31, tz = m32;
5620             return dest.rotationXYZ(angleX, angleY, angleZ).setTranslation(tx, ty, tz);
5621         }
5622         return rotateAffineXYZInternal(angleX, angleY, angleZ, dest);
5623     }
5624     private Matrix4d rotateAffineXYZInternal(double angleX, double angleY, double angleZ, ref Matrix4d dest) {
5625         double sinX = Math.sin(angleX);
5626         double cosX = Math.cosFromSin(sinX, angleX);
5627         double sinY = Math.sin(angleY);
5628         double cosY = Math.cosFromSin(sinY, angleY);
5629         double sinZ = Math.sin(angleZ);
5630         double cosZ = Math.cosFromSin(sinZ, angleZ);
5631         double m_sinX = -sinX;
5632         double m_sinY = -sinY;
5633         double m_sinZ = -sinZ;
5634 
5635         // rotateX
5636         double nm10 = m10 * cosX + m20 * sinX;
5637         double nm11 = m11 * cosX + m21 * sinX;
5638         double nm12 = m12 * cosX + m22 * sinX;
5639         double nm20 = m10 * m_sinX + m20 * cosX;
5640         double nm21 = m11 * m_sinX + m21 * cosX;
5641         double nm22 = m12 * m_sinX + m22 * cosX;
5642         // rotateY
5643         double nm00 = m00 * cosY + nm20 * m_sinY;
5644         double nm01 = m01 * cosY + nm21 * m_sinY;
5645         double nm02 = m02 * cosY + nm22 * m_sinY;
5646         dest._m20(m00 * sinY + nm20 * cosY)
5647         ._m21(m01 * sinY + nm21 * cosY)
5648         ._m22(m02 * sinY + nm22 * cosY)
5649         ._m23(0.0)
5650         // rotateZ
5651         ._m00(nm00 * cosZ + nm10 * sinZ)
5652         ._m01(nm01 * cosZ + nm11 * sinZ)
5653         ._m02(nm02 * cosZ + nm12 * sinZ)
5654         ._m03(0.0)
5655         ._m10(nm00 * m_sinZ + nm10 * cosZ)
5656         ._m11(nm01 * m_sinZ + nm11 * cosZ)
5657         ._m12(nm02 * m_sinZ + nm12 * cosZ)
5658         ._m13(0.0)
5659         // copy last column from 'this'
5660         ._m30(m30)
5661         ._m31(m31)
5662         ._m32(m32)
5663         ._m33(m33)
5664         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5665         return dest;
5666     }
5667 
5668     /**
5669      * Apply rotation of <code>angles.z</code> radians about the Z axis, followed by a rotation of <code>angles.y</code> radians about the Y axis and
5670      * followed by a rotation of <code>angles.x</code> radians about the X axis.
5671      * <p>
5672      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5673      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5674      * When used with a left-handed coordinate system, the rotation is clockwise.
5675      * <p>
5676      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5677      * then the new matrix will be <code>M * R</code>. So when transforming a
5678      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5679      * rotation will be applied first!
5680      * <p>
5681      * This method is equivalent to calling: <code>rotateZ(angles.z).rotateY(angles.y).rotateX(angles.x)</code>
5682      * 
5683      * @param angles
5684      *            the Euler angles
5685      * @return this
5686      */
5687     ref public Matrix4d rotateZYX(ref Vector3d angles) return {
5688         return rotateZYX(angles.z, angles.y, angles.x);
5689     }
5690 
5691     /**
5692      * Apply rotation of <code>angleZ</code> radians about the Z axis, followed by a rotation of <code>angleY</code> radians about the Y axis and
5693      * followed by a rotation of <code>angleX</code> radians about the X axis.
5694      * <p>
5695      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5696      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5697      * When used with a left-handed coordinate system, the rotation is clockwise.
5698      * <p>
5699      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5700      * then the new matrix will be <code>M * R</code>. So when transforming a
5701      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5702      * rotation will be applied first!
5703      * <p>
5704      * This method is equivalent to calling: <code>rotateZ(angleZ).rotateY(angleY).rotateX(angleX)</code>
5705      * 
5706      * @param angleZ
5707      *            the angle to rotate about Z
5708      * @param angleY
5709      *            the angle to rotate about Y
5710      * @param angleX
5711      *            the angle to rotate about X
5712      * @return this
5713      */
5714     ref public Matrix4d rotateZYX(double angleZ, double angleY, double angleX) return {
5715         rotateZYX(angleZ, angleY, angleX, this);
5716         return this;
5717     }
5718 
5719     public Matrix4d rotateZYX(double angleZ, double angleY, double angleX, ref Matrix4d dest) {
5720         if ((properties & PROPERTY_IDENTITY) != 0)
5721             return dest.rotationZYX(angleZ, angleY, angleX);
5722         else if ((properties & PROPERTY_TRANSLATION) != 0) {
5723             double tx = m30, ty = m31, tz = m32;
5724             return dest.rotationZYX(angleZ, angleY, angleX).setTranslation(tx, ty, tz);
5725         } else if ((properties & PROPERTY_AFFINE) != 0)
5726             return dest.rotateAffineZYX(angleZ, angleY, angleX);
5727         return rotateZYXInternal(angleZ, angleY, angleX, dest);
5728     }
5729     private Matrix4d rotateZYXInternal(double angleZ, double angleY, double angleX, ref Matrix4d dest) {
5730         double sinX = Math.sin(angleX);
5731         double cosX = Math.cosFromSin(sinX, angleX);
5732         double sinY = Math.sin(angleY);
5733         double cosY = Math.cosFromSin(sinY, angleY);
5734         double sinZ = Math.sin(angleZ);
5735         double cosZ = Math.cosFromSin(sinZ, angleZ);
5736         double m_sinZ = -sinZ;
5737         double m_sinY = -sinY;
5738         double m_sinX = -sinX;
5739 
5740         // rotateZ
5741         double nm00 = m00 * cosZ + m10 * sinZ;
5742         double nm01 = m01 * cosZ + m11 * sinZ;
5743         double nm02 = m02 * cosZ + m12 * sinZ;
5744         double nm03 = m03 * cosZ + m13 * sinZ;
5745         double nm10 = m00 * m_sinZ + m10 * cosZ;
5746         double nm11 = m01 * m_sinZ + m11 * cosZ;
5747         double nm12 = m02 * m_sinZ + m12 * cosZ;
5748         double nm13 = m03 * m_sinZ + m13 * cosZ;
5749         // rotateY
5750         double nm20 = nm00 * sinY + m20 * cosY;
5751         double nm21 = nm01 * sinY + m21 * cosY;
5752         double nm22 = nm02 * sinY + m22 * cosY;
5753         double nm23 = nm03 * sinY + m23 * cosY;
5754         dest._m00(nm00 * cosY + m20 * m_sinY)
5755         ._m01(nm01 * cosY + m21 * m_sinY)
5756         ._m02(nm02 * cosY + m22 * m_sinY)
5757         ._m03(nm03 * cosY + m23 * m_sinY)
5758         // rotateX
5759         ._m10(nm10 * cosX + nm20 * sinX)
5760         ._m11(nm11 * cosX + nm21 * sinX)
5761         ._m12(nm12 * cosX + nm22 * sinX)
5762         ._m13(nm13 * cosX + nm23 * sinX)
5763         ._m20(nm10 * m_sinX + nm20 * cosX)
5764         ._m21(nm11 * m_sinX + nm21 * cosX)
5765         ._m22(nm12 * m_sinX + nm22 * cosX)
5766         ._m23(nm13 * m_sinX + nm23 * cosX)
5767         // copy last column from 'this'
5768         ._m30(m30)
5769         ._m31(m31)
5770         ._m32(m32)
5771         ._m33(m33)
5772         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5773         return dest;
5774     }
5775 
5776     /**
5777      * Apply rotation of <code>angleZ</code> radians about the Z axis, followed by a rotation of <code>angleY</code> radians about the Y axis and
5778      * followed by a rotation of <code>angleX</code> radians about the X axis.
5779      * <p>
5780      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5781      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5782      * When used with a left-handed coordinate system, the rotation is clockwise.
5783      * <p>
5784      * This method assumes that <code>this</code> matrix represents an {@link #isAffine() affine} transformation (i.e. its last row is equal to <code>(0, 0, 0, 1)</code>)
5785      * and can be used to speed up matrix multiplication if the matrix only represents affine transformations, such as translation, rotation, scaling and shearing (in any combination).
5786      * <p>
5787      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5788      * then the new matrix will be <code>M * R</code>. So when transforming a
5789      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5790      * rotation will be applied first!
5791      * 
5792      * @param angleZ
5793      *            the angle to rotate about Z
5794      * @param angleY
5795      *            the angle to rotate about Y
5796      * @param angleX
5797      *            the angle to rotate about X
5798      * @return this
5799      */
5800     ref public Matrix4d rotateAffineZYX(double angleZ, double angleY, double angleX) return {
5801         rotateAffineZYX(angleZ, angleY, angleX, this);
5802         return this;
5803     }
5804 
5805     public Matrix4d rotateAffineZYX(double angleZ, double angleY, double angleX, ref Matrix4d dest) {
5806         double sinX = Math.sin(angleX);
5807         double cosX = Math.cosFromSin(sinX, angleX);
5808         double sinY = Math.sin(angleY);
5809         double cosY = Math.cosFromSin(sinY, angleY);
5810         double sinZ = Math.sin(angleZ);
5811         double cosZ = Math.cosFromSin(sinZ, angleZ);
5812         double m_sinZ = -sinZ;
5813         double m_sinY = -sinY;
5814         double m_sinX = -sinX;
5815 
5816         // rotateZ
5817         double nm00 = m00 * cosZ + m10 * sinZ;
5818         double nm01 = m01 * cosZ + m11 * sinZ;
5819         double nm02 = m02 * cosZ + m12 * sinZ;
5820         double nm10 = m00 * m_sinZ + m10 * cosZ;
5821         double nm11 = m01 * m_sinZ + m11 * cosZ;
5822         double nm12 = m02 * m_sinZ + m12 * cosZ;
5823         // rotateY
5824         double nm20 = nm00 * sinY + m20 * cosY;
5825         double nm21 = nm01 * sinY + m21 * cosY;
5826         double nm22 = nm02 * sinY + m22 * cosY;
5827         dest._m00(nm00 * cosY + m20 * m_sinY)
5828         ._m01(nm01 * cosY + m21 * m_sinY)
5829         ._m02(nm02 * cosY + m22 * m_sinY)
5830         ._m03(0.0)
5831         // rotateX
5832         ._m10(nm10 * cosX + nm20 * sinX)
5833         ._m11(nm11 * cosX + nm21 * sinX)
5834         ._m12(nm12 * cosX + nm22 * sinX)
5835         ._m13(0.0)
5836         ._m20(nm10 * m_sinX + nm20 * cosX)
5837         ._m21(nm11 * m_sinX + nm21 * cosX)
5838         ._m22(nm12 * m_sinX + nm22 * cosX)
5839         ._m23(0.0)
5840         // copy last column from 'this'
5841         ._m30(m30)
5842         ._m31(m31)
5843         ._m32(m32)
5844         ._m33(m33)
5845         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5846         return dest;
5847     }
5848 
5849     /**
5850      * Apply rotation of <code>angles.y</code> radians about the Y axis, followed by a rotation of <code>angles.x</code> radians about the X axis and
5851      * followed by a rotation of <code>angles.z</code> radians about the Z axis.
5852      * <p>
5853      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5854      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5855      * When used with a left-handed coordinate system, the rotation is clockwise.
5856      * <p>
5857      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5858      * then the new matrix will be <code>M * R</code>. So when transforming a
5859      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5860      * rotation will be applied first!
5861      * <p>
5862      * This method is equivalent to calling: <code>rotateY(angles.y).rotateX(angles.x).rotateZ(angles.z)</code>
5863      * 
5864      * @param angles
5865      *            the Euler angles
5866      * @return this
5867      */
5868     ref public Matrix4d rotateYXZ(ref Vector3d angles) return {
5869         return rotateYXZ(angles.y, angles.x, angles.z);
5870     }
5871 
5872     /**
5873      * Apply rotation of <code>angleY</code> radians about the Y axis, followed by a rotation of <code>angleX</code> radians about the X axis and
5874      * followed by a rotation of <code>angleZ</code> radians about the Z axis.
5875      * <p>
5876      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5877      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5878      * When used with a left-handed coordinate system, the rotation is clockwise.
5879      * <p>
5880      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5881      * then the new matrix will be <code>M * R</code>. So when transforming a
5882      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5883      * rotation will be applied first!
5884      * <p>
5885      * This method is equivalent to calling: <code>rotateY(angleY).rotateX(angleX).rotateZ(angleZ)</code>
5886      * 
5887      * @param angleY
5888      *            the angle to rotate about Y
5889      * @param angleX
5890      *            the angle to rotate about X
5891      * @param angleZ
5892      *            the angle to rotate about Z
5893      * @return this
5894      */
5895     ref public Matrix4d rotateYXZ(double angleY, double angleX, double angleZ) return {
5896         rotateYXZ(angleY, angleX, angleZ, this);
5897         return this;
5898     }
5899 
5900     public Matrix4d rotateYXZ(double angleY, double angleX, double angleZ, ref Matrix4d dest) {
5901         if ((properties & PROPERTY_IDENTITY) != 0)
5902             return dest.rotationYXZ(angleY, angleX, angleZ);
5903         else if ((properties & PROPERTY_TRANSLATION) != 0) {
5904             double tx = m30, ty = m31, tz = m32;
5905             return dest.rotationYXZ(angleY, angleX, angleZ).setTranslation(tx, ty, tz);
5906         } else if ((properties & PROPERTY_AFFINE) != 0)
5907             return dest.rotateAffineYXZ(angleY, angleX, angleZ);
5908         return rotateYXZInternal(angleY, angleX, angleZ, dest);
5909     }
5910     private Matrix4d rotateYXZInternal(double angleY, double angleX, double angleZ, ref Matrix4d dest) {
5911         double sinX = Math.sin(angleX);
5912         double cosX = Math.cosFromSin(sinX, angleX);
5913         double sinY = Math.sin(angleY);
5914         double cosY = Math.cosFromSin(sinY, angleY);
5915         double sinZ = Math.sin(angleZ);
5916         double cosZ = Math.cosFromSin(sinZ, angleZ);
5917         double m_sinY = -sinY;
5918         double m_sinX = -sinX;
5919         double m_sinZ = -sinZ;
5920 
5921         // rotateY
5922         double nm20 = m00 * sinY + m20 * cosY;
5923         double nm21 = m01 * sinY + m21 * cosY;
5924         double nm22 = m02 * sinY + m22 * cosY;
5925         double nm23 = m03 * sinY + m23 * cosY;
5926         double nm00 = m00 * cosY + m20 * m_sinY;
5927         double nm01 = m01 * cosY + m21 * m_sinY;
5928         double nm02 = m02 * cosY + m22 * m_sinY;
5929         double nm03 = m03 * cosY + m23 * m_sinY;
5930         // rotateX
5931         double nm10 = m10 * cosX + nm20 * sinX;
5932         double nm11 = m11 * cosX + nm21 * sinX;
5933         double nm12 = m12 * cosX + nm22 * sinX;
5934         double nm13 = m13 * cosX + nm23 * sinX;
5935         dest._m20(m10 * m_sinX + nm20 * cosX)
5936         ._m21(m11 * m_sinX + nm21 * cosX)
5937         ._m22(m12 * m_sinX + nm22 * cosX)
5938         ._m23(m13 * m_sinX + nm23 * cosX)
5939         // rotateZ
5940         ._m00(nm00 * cosZ + nm10 * sinZ)
5941         ._m01(nm01 * cosZ + nm11 * sinZ)
5942         ._m02(nm02 * cosZ + nm12 * sinZ)
5943         ._m03(nm03 * cosZ + nm13 * sinZ)
5944         ._m10(nm00 * m_sinZ + nm10 * cosZ)
5945         ._m11(nm01 * m_sinZ + nm11 * cosZ)
5946         ._m12(nm02 * m_sinZ + nm12 * cosZ)
5947         ._m13(nm03 * m_sinZ + nm13 * cosZ)
5948         // copy last column from 'this'
5949         ._m30(m30)
5950         ._m31(m31)
5951         ._m32(m32)
5952         ._m33(m33)
5953         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5954         return dest;
5955     }
5956 
5957     /**
5958      * Apply rotation of <code>angleY</code> radians about the Y axis, followed by a rotation of <code>angleX</code> radians about the X axis and
5959      * followed by a rotation of <code>angleZ</code> radians about the Z axis.
5960      * <p>
5961      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5962      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5963      * When used with a left-handed coordinate system, the rotation is clockwise.
5964      * <p>
5965      * This method assumes that <code>this</code> matrix represents an {@link #isAffine() affine} transformation (i.e. its last row is equal to <code>(0, 0, 0, 1)</code>)
5966      * and can be used to speed up matrix multiplication if the matrix only represents affine transformations, such as translation, rotation, scaling and shearing (in any combination).
5967      * <p>
5968      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5969      * then the new matrix will be <code>M * R</code>. So when transforming a
5970      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5971      * rotation will be applied first!
5972      * 
5973      * @param angleY
5974      *            the angle to rotate about Y
5975      * @param angleX
5976      *            the angle to rotate about X
5977      * @param angleZ
5978      *            the angle to rotate about Z
5979      * @return this
5980      */
5981     ref public Matrix4d rotateAffineYXZ(double angleY, double angleX, double angleZ) return {
5982         rotateAffineYXZ(angleY, angleX, angleZ, this);
5983         return this;
5984     }
5985 
5986     public Matrix4d rotateAffineYXZ(double angleY, double angleX, double angleZ, ref Matrix4d dest) {
5987         double sinX = Math.sin(angleX);
5988         double cosX = Math.cosFromSin(sinX, angleX);
5989         double sinY = Math.sin(angleY);
5990         double cosY = Math.cosFromSin(sinY, angleY);
5991         double sinZ = Math.sin(angleZ);
5992         double cosZ = Math.cosFromSin(sinZ, angleZ);
5993         double m_sinY = -sinY;
5994         double m_sinX = -sinX;
5995         double m_sinZ = -sinZ;
5996 
5997         // rotateY
5998         double nm20 = m00 * sinY + m20 * cosY;
5999         double nm21 = m01 * sinY + m21 * cosY;
6000         double nm22 = m02 * sinY + m22 * cosY;
6001         double nm00 = m00 * cosY + m20 * m_sinY;
6002         double nm01 = m01 * cosY + m21 * m_sinY;
6003         double nm02 = m02 * cosY + m22 * m_sinY;
6004         // rotateX
6005         double nm10 = m10 * cosX + nm20 * sinX;
6006         double nm11 = m11 * cosX + nm21 * sinX;
6007         double nm12 = m12 * cosX + nm22 * sinX;
6008         dest._m20(m10 * m_sinX + nm20 * cosX)
6009         ._m21(m11 * m_sinX + nm21 * cosX)
6010         ._m22(m12 * m_sinX + nm22 * cosX)
6011         ._m23(0.0)
6012         // rotateZ
6013         ._m00(nm00 * cosZ + nm10 * sinZ)
6014         ._m01(nm01 * cosZ + nm11 * sinZ)
6015         ._m02(nm02 * cosZ + nm12 * sinZ)
6016         ._m03(0.0)
6017         ._m10(nm00 * m_sinZ + nm10 * cosZ)
6018         ._m11(nm01 * m_sinZ + nm11 * cosZ)
6019         ._m12(nm02 * m_sinZ + nm12 * cosZ)
6020         ._m13(0.0)
6021         // copy last column from 'this'
6022         ._m30(m30)
6023         ._m31(m31)
6024         ._m32(m32)
6025         ._m33(m33)
6026         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
6027         return dest;
6028     }
6029 
6030     /**
6031      * Set this matrix to a rotation transformation using the given {@link AxisAngle4d}.
6032      * <p>
6033      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6034      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6035      * When used with a left-handed coordinate system, the rotation is clockwise.
6036      * <p>
6037      * The resulting matrix can be multiplied against another transformation
6038      * matrix to obtain an additional rotation.
6039      * <p>
6040      * In order to apply the rotation transformation to an existing transformation,
6041      * use {@link #rotate(ref AxisAngle4d) rotate()} instead.
6042      * <p>
6043      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Axis_and_angle">http://en.wikipedia.org</a>
6044      *
6045      * @see #rotate(ref AxisAngle4d)
6046      * 
6047      * @param angleAxis
6048      *          the {@link AxisAngle4d} (needs to be {@link AxisAngle4d#normalize() normalized})
6049      * @return this
6050      */
6051     ref public Matrix4d rotation(ref AxisAngle4d angleAxis) return {
6052         return rotation(angleAxis.angle, angleAxis.x, angleAxis.y, angleAxis.z);
6053     }
6054 
6055     /**
6056      * Set this matrix to the rotation - and possibly scaling - transformation of the given {@link Quaterniond}.
6057      * <p>
6058      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6059      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6060      * When used with a left-handed coordinate system, the rotation is clockwise.
6061      * <p>
6062      * The resulting matrix can be multiplied against another transformation
6063      * matrix to obtain an additional rotation.
6064      * <p>
6065      * In order to apply the rotation transformation to an existing transformation,
6066      * use {@link #rotate(ref Quaterniond) rotate()} instead.
6067      * <p>
6068      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
6069      * 
6070      * @see #rotate(ref Quaterniond)
6071      * 
6072      * @param quat
6073      *          the {@link Quaterniond}
6074      * @return this
6075      */
6076     ref public Matrix4d rotation(ref Quaterniond quat) return {
6077         double w2 = quat.w * quat.w;
6078         double x2 = quat.x * quat.x;
6079         double y2 = quat.y * quat.y;
6080         double z2 = quat.z * quat.z;
6081         double zw = quat.z * quat.w, dzw = zw + zw;
6082         double xy = quat.x * quat.y, dxy = xy + xy;
6083         double xz = quat.x * quat.z, dxz = xz + xz;
6084         double yw = quat.y * quat.w, dyw = yw + yw;
6085         double yz = quat.y * quat.z, dyz = yz + yz;
6086         double xw = quat.x * quat.w, dxw = xw + xw;
6087         if ((properties & PROPERTY_IDENTITY) == 0)
6088             this._identity();
6089         _m00(w2 + x2 - z2 - y2).
6090         _m01(dxy + dzw).
6091         _m02(dxz - dyw).
6092         _m10(-dzw + dxy).
6093         _m11(y2 - z2 + w2 - x2).
6094         _m12(dyz + dxw).
6095         _m20(dyw + dxz).
6096         _m21(dyz - dxw).
6097         _m22(z2 - y2 - x2 + w2).
6098         _properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL);
6099         return this;
6100     }
6101 
6102 
6103     /**
6104      * Set <code>this</code> matrix to <code>T * R * S</code>, where <code>T</code> is a translation by the given <code>(tx, ty, tz)</code>,
6105      * <code>R</code> is a rotation transformation specified by the quaternion <code>(qx, qy, qz, qw)</code>, and <code>S</code> is a scaling transformation
6106      * which scales the three axes x, y and z by <code>(sx, sy, sz)</code>.
6107      * <p>
6108      * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and
6109      * at last the translation.
6110      * <p>
6111      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6112      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6113      * When used with a left-handed coordinate system, the rotation is clockwise.
6114      * <p>
6115      * This method is equivalent to calling: <code>translation(tx, ty, tz).rotate(quat).scale(sx, sy, sz)</code>
6116      * 
6117      * @see #translation(double, double, double)
6118      * @see #rotate(ref Quaterniond)
6119      * @see #scale(double, double, double)
6120      * 
6121      * @param tx
6122      *          the number of units by which to translate the x-component
6123      * @param ty
6124      *          the number of units by which to translate the y-component
6125      * @param tz
6126      *          the number of units by which to translate the z-component
6127      * @param qx
6128      *          the x-coordinate of the vector part of the quaternion
6129      * @param qy
6130      *          the y-coordinate of the vector part of the quaternion
6131      * @param qz
6132      *          the z-coordinate of the vector part of the quaternion
6133      * @param qw
6134      *          the scalar part of the quaternion
6135      * @param sx
6136      *          the scaling factor for the x-axis
6137      * @param sy
6138      *          the scaling factor for the y-axis
6139      * @param sz
6140      *          the scaling factor for the z-axis
6141      * @return this
6142      */
6143     ref public Matrix4d translationRotateScale(double tx, double ty, double tz, 
6144                                            double qx, double qy, double qz, double qw, 
6145                                            double sx, double sy, double sz) return {
6146         double dqx = qx + qx, dqy = qy + qy, dqz = qz + qz;
6147         double q00 = dqx * qx;
6148         double q11 = dqy * qy;
6149         double q22 = dqz * qz;
6150         double q01 = dqx * qy;
6151         double q02 = dqx * qz;
6152         double q03 = dqx * qw;
6153         double q12 = dqy * qz;
6154         double q13 = dqy * qw;
6155         double q23 = dqz * qw;
6156         bool one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz);
6157         _m00(sx - (q11 + q22) * sx).
6158         _m01((q01 + q23) * sx).
6159         _m02((q02 - q13) * sx).
6160         _m03(0.0).
6161         _m10((q01 - q23) * sy).
6162         _m11(sy - (q22 + q00) * sy).
6163         _m12((q12 + q03) * sy).
6164         _m13(0.0).
6165         _m20((q02 + q13) * sz).
6166         _m21((q12 - q03) * sz).
6167         _m22(sz - (q11 + q00) * sz).
6168         _m23(0.0).
6169         _m30(tx).
6170         _m31(ty).
6171         _m32(tz).
6172         _m33(1.0).
6173         properties = PROPERTY_AFFINE | (one ? PROPERTY_ORTHONORMAL : 0);
6174         return this;
6175     }
6176 
6177     /**
6178      * Set <code>this</code> matrix to <code>T * R * S</code>, where <code>T</code> is the given <code>translation</code>,
6179      * <code>R</code> is a rotation transformation specified by the given quaternion, and <code>S</code> is a scaling transformation
6180      * which scales the axes by <code>scale</code>.
6181      * <p>
6182      * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and
6183      * at last the translation.
6184      * <p>
6185      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6186      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6187      * When used with a left-handed coordinate system, the rotation is clockwise.
6188      * <p>
6189      * This method is equivalent to calling: <code>translation(translation).rotate(quat).scale(scale)</code>
6190      * 
6191      * @see #translation(ref Vector3d)
6192      * @see #rotate(ref Quaterniond)
6193      * @see #scale(ref Vector3d)
6194      * 
6195      * @param translation
6196      *          the translation
6197      * @param quat
6198      *          the quaternion representing a rotation
6199      * @param scale
6200      *          the scaling factors
6201      * @return this
6202      */
6203     ref public Matrix4d translationRotateScale(ref Vector3d translation, 
6204                                            Quaterniond quat, 
6205                                            Vector3d scale) return {
6206         return translationRotateScale(translation.x, translation.y, translation.z, quat.x, quat.y, quat.z, quat.w, scale.x, scale.y, scale.z);
6207     }
6208 
6209     /**
6210      * Set <code>this</code> matrix to <code>T * R * S</code>, where <code>T</code> is a translation by the given <code>(tx, ty, tz)</code>,
6211      * <code>R</code> is a rotation transformation specified by the quaternion <code>(qx, qy, qz, qw)</code>, and <code>S</code> is a scaling transformation
6212      * which scales all three axes by <code>scale</code>.
6213      * <p>
6214      * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and
6215      * at last the translation.
6216      * <p>
6217      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6218      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6219      * When used with a left-handed coordinate system, the rotation is clockwise.
6220      * <p>
6221      * This method is equivalent to calling: <code>translation(tx, ty, tz).rotate(quat).scale(scale)</code>
6222      * 
6223      * @see #translation(double, double, double)
6224      * @see #rotate(ref Quaterniond)
6225      * @see #scale(double)
6226      * 
6227      * @param tx
6228      *          the number of units by which to translate the x-component
6229      * @param ty
6230      *          the number of units by which to translate the y-component
6231      * @param tz
6232      *          the number of units by which to translate the z-component
6233      * @param qx
6234      *          the x-coordinate of the vector part of the quaternion
6235      * @param qy
6236      *          the y-coordinate of the vector part of the quaternion
6237      * @param qz
6238      *          the z-coordinate of the vector part of the quaternion
6239      * @param qw
6240      *          the scalar part of the quaternion
6241      * @param scale
6242      *          the scaling factor for all three axes
6243      * @return this
6244      */
6245     ref public Matrix4d translationRotateScale(double tx, double ty, double tz, 
6246                                            double qx, double qy, double qz, double qw, 
6247                                            double scale) return {
6248         return translationRotateScale(tx, ty, tz, qx, qy, qz, qw, scale, scale, scale);
6249     }
6250 
6251     /**
6252      * Set <code>this</code> matrix to <code>T * R * S</code>, where <code>T</code> is the given <code>translation</code>,
6253      * <code>R</code> is a rotation transformation specified by the given quaternion, and <code>S</code> is a scaling transformation
6254      * which scales all three axes by <code>scale</code>.
6255      * <p>
6256      * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and
6257      * at last the translation.
6258      * <p>
6259      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6260      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6261      * When used with a left-handed coordinate system, the rotation is clockwise.
6262      * <p>
6263      * This method is equivalent to calling: <code>translation(translation).rotate(quat).scale(scale)</code>
6264      * 
6265      * @see #translation(ref Vector3d)
6266      * @see #rotate(ref Quaterniond)
6267      * @see #scale(double)
6268      * 
6269      * @param translation
6270      *          the translation
6271      * @param quat
6272      *          the quaternion representing a rotation
6273      * @param scale
6274      *          the scaling factors
6275      * @return this
6276      */
6277     ref public Matrix4d translationRotateScale(ref Vector3d translation, 
6278                                            Quaterniond quat, 
6279                                            double scale) return {
6280         return translationRotateScale(translation.x, translation.y, translation.z, quat.x, quat.y, quat.z, quat.w, scale, scale, scale);
6281     }
6282 
6283 
6284     /**
6285      * Set <code>this</code> matrix to <code>(T * R * S)<sup>-1</sup></code>, where <code>T</code> is a translation by the given <code>(tx, ty, tz)</code>,
6286      * <code>R</code> is a rotation transformation specified by the quaternion <code>(qx, qy, qz, qw)</code>, and <code>S</code> is a scaling transformation
6287      * which scales the three axes x, y and z by <code>(sx, sy, sz)</code>.
6288      * <p>
6289      * This method is equivalent to calling: <code>translationRotateScale(...).invert()</code>
6290      * 
6291      * @see #translationRotateScale(double, double, double, double, double, double, double, double, double, double)
6292      * @see #invert()
6293      * 
6294      * @param tx
6295      *          the number of units by which to translate the x-component
6296      * @param ty
6297      *          the number of units by which to translate the y-component
6298      * @param tz
6299      *          the number of units by which to translate the z-component
6300      * @param qx
6301      *          the x-coordinate of the vector part of the quaternion
6302      * @param qy
6303      *          the y-coordinate of the vector part of the quaternion
6304      * @param qz
6305      *          the z-coordinate of the vector part of the quaternion
6306      * @param qw
6307      *          the scalar part of the quaternion
6308      * @param sx
6309      *          the scaling factor for the x-axis
6310      * @param sy
6311      *          the scaling factor for the y-axis
6312      * @param sz
6313      *          the scaling factor for the z-axis
6314      * @return this
6315      */
6316     ref public Matrix4d translationRotateScaleInvert(double tx, double ty, double tz, 
6317                                                  double qx, double qy, double qz, double qw, 
6318                                                  double sx, double sy, double sz) return {
6319         bool one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz);
6320         if (one)
6321             return translationRotateInvert(tx, ty, tz, qx, qy, qz, qw);
6322         double nqx = -qx, nqy = -qy, nqz = -qz;
6323         double dqx = nqx + nqx;
6324         double dqy = nqy + nqy;
6325         double dqz = nqz + nqz;
6326         double q00 = dqx * nqx;
6327         double q11 = dqy * nqy;
6328         double q22 = dqz * nqz;
6329         double q01 = dqx * nqy;
6330         double q02 = dqx * nqz;
6331         double q03 = dqx * qw;
6332         double q12 = dqy * nqz;
6333         double q13 = dqy * qw;
6334         double q23 = dqz * qw;
6335         double isx = 1/sx, isy = 1/sy, isz = 1/sz;
6336         _m00(isx * (1.0 - q11 - q22)).
6337         _m01(isy * (q01 + q23)).
6338         _m02(isz * (q02 - q13)).
6339         _m03(0.0).
6340         _m10(isx * (q01 - q23)).
6341         _m11(isy * (1.0 - q22 - q00)).
6342         _m12(isz * (q12 + q03)).
6343         _m13(0.0).
6344         _m20(isx * (q02 + q13)).
6345         _m21(isy * (q12 - q03)).
6346         _m22(isz * (1.0 - q11 - q00)).
6347         _m23(0.0).
6348         _m30(-m00 * tx - m10 * ty - m20 * tz).
6349         _m31(-m01 * tx - m11 * ty - m21 * tz).
6350         _m32(-m02 * tx - m12 * ty - m22 * tz).
6351         _m33(1.0).
6352         properties = PROPERTY_AFFINE;
6353         return this;
6354     }
6355 
6356     /**
6357      * Set <code>this</code> matrix to <code>(T * R * S)<sup>-1</sup></code>, where <code>T</code> is the given <code>translation</code>,
6358      * <code>R</code> is a rotation transformation specified by the given quaternion, and <code>S</code> is a scaling transformation
6359      * which scales the axes by <code>scale</code>.
6360      * <p>
6361      * This method is equivalent to calling: <code>translationRotateScale(...).invert()</code>
6362      * 
6363      * @see #translationRotateScale(ref Vector3d, Quaterniond, Vector3d)
6364      * @see #invert()
6365      * 
6366      * @param translation
6367      *          the translation
6368      * @param quat
6369      *          the quaternion representing a rotation
6370      * @param scale
6371      *          the scaling factors
6372      * @return this
6373      */
6374     ref public Matrix4d translationRotateScaleInvert(ref Vector3d translation, 
6375                                                  Quaterniond quat, 
6376                                                  Vector3d scale) return {
6377         return translationRotateScaleInvert(translation.x, translation.y, translation.z, quat.x, quat.y, quat.z, quat.w, scale.x, scale.y, scale.z);
6378     }
6379 
6380     
6381     /**
6382      * Set <code>this</code> matrix to <code>(T * R * S)<sup>-1</sup></code>, where <code>T</code> is the given <code>translation</code>,
6383      * <code>R</code> is a rotation transformation specified by the given quaternion, and <code>S</code> is a scaling transformation
6384      * which scales all three axes by <code>scale</code>.
6385      * <p>
6386      * This method is equivalent to calling: <code>translationRotateScale(...).invert()</code>
6387      * 
6388      * @see #translationRotateScale(ref Vector3d, Quaterniond, double)
6389      * @see #invert()
6390      * 
6391      * @param translation
6392      *          the translation
6393      * @param quat
6394      *          the quaternion representing a rotation
6395      * @param scale
6396      *          the scaling factors
6397      * @return this
6398      */
6399     ref public Matrix4d translationRotateScaleInvert(ref Vector3d translation, 
6400                                                  Quaterniond quat, 
6401                                                  double scale) return {
6402         return translationRotateScaleInvert(translation.x, translation.y, translation.z, quat.x, quat.y, quat.z, quat.w, scale, scale, scale);
6403     }
6404 
6405 
6406     /**
6407      * Set <code>this</code> matrix to <code>T * R * S * M</code>, where <code>T</code> is a translation by the given <code>(tx, ty, tz)</code>,
6408      * <code>R</code> is a rotation - and possibly scaling - transformation specified by the quaternion <code>(qx, qy, qz, qw)</code>, <code>S</code> is a scaling transformation
6409      * which scales the three axes x, y and z by <code>(sx, sy, sz)</code> and <code>M</code> is an {@link #isAffine() affine} matrix.
6410      * <p>
6411      * When transforming a vector by the resulting matrix the transformation described by <code>M</code> will be applied first, then the scaling, then rotation and
6412      * at last the translation.
6413      * <p>
6414      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6415      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6416      * When used with a left-handed coordinate system, the rotation is clockwise.
6417      * <p>
6418      * This method is equivalent to calling: <code>translation(tx, ty, tz).rotate(quat).scale(sx, sy, sz).mulAffine(m)</code>
6419      * 
6420      * @see #translation(double, double, double)
6421      * @see #rotate(ref Quaterniond)
6422      * @see #scale(double, double, double)
6423      * @see #mulAffine(Matrix4d)
6424      * 
6425      * @param tx
6426      *          the number of units by which to translate the x-component
6427      * @param ty
6428      *          the number of units by which to translate the y-component
6429      * @param tz
6430      *          the number of units by which to translate the z-component
6431      * @param qx
6432      *          the x-coordinate of the vector part of the quaternion
6433      * @param qy
6434      *          the y-coordinate of the vector part of the quaternion
6435      * @param qz
6436      *          the z-coordinate of the vector part of the quaternion
6437      * @param qw
6438      *          the scalar part of the quaternion
6439      * @param sx
6440      *          the scaling factor for the x-axis
6441      * @param sy
6442      *          the scaling factor for the y-axis
6443      * @param sz
6444      *          the scaling factor for the z-axis
6445      * @param m
6446      *          the {@link #isAffine() affine} matrix to multiply by
6447      * @return this
6448      */
6449     ref public Matrix4d translationRotateScaleMulAffine(double tx, double ty, double tz, 
6450                                                     double qx, double qy, double qz, double qw, 
6451                                                     double sx, double sy, double sz,
6452                                                     Matrix4d m) return {
6453         double w2 = qw * qw;
6454         double x2 = qx * qx;
6455         double y2 = qy * qy;
6456         double z2 = qz * qz;
6457         double zw = qz * qw;
6458         double xy = qx * qy;
6459         double xz = qx * qz;
6460         double yw = qy * qw;
6461         double yz = qy * qz;
6462         double xw = qx * qw;
6463         double nm00 = w2 + x2 - z2 - y2;
6464         double nm01 = xy + zw + zw + xy;
6465         double nm02 = xz - yw + xz - yw;
6466         double nm10 = -zw + xy - zw + xy;
6467         double nm11 = y2 - z2 + w2 - x2;
6468         double nm12 = yz + yz + xw + xw;
6469         double nm20 = yw + xz + xz + yw;
6470         double nm21 = yz + yz - xw - xw;
6471         double nm22 = z2 - y2 - x2 + w2;
6472         double m00 = nm00 * m.m00 + nm10 * m.m01 + nm20 * m.m02;
6473         double m01 = nm01 * m.m00 + nm11 * m.m01 + nm21 * m.m02;
6474         setm02(nm02 * m.m00 + nm12 * m.m01 + nm22 * m.m02);
6475         setm00(m00);
6476         setm01(m01);
6477         setm03(0.0);
6478         double m10 = nm00 * m.m10 + nm10 * m.m11 + nm20 * m.m12;
6479         double m11 = nm01 * m.m10 + nm11 * m.m11 + nm21 * m.m12;
6480         setm12(nm02 * m.m10 + nm12 * m.m11 + nm22 * m.m12);
6481         setm10(m10);
6482         setm11(m11);
6483         setm13(0.0);
6484         double m20 = nm00 * m.m20 + nm10 * m.m21 + nm20 * m.m22;
6485         double m21 = nm01 * m.m20 + nm11 * m.m21 + nm21 * m.m22;
6486         setm22(nm02 * m.m20 + nm12 * m.m21 + nm22 * m.m22);
6487         setm20(m20);
6488         setm21(m21);
6489         setm23(0.0);
6490         double m30 = nm00 * m.m30 + nm10 * m.m31 + nm20 * m.m32 + tx;
6491         double m31 = nm01 * m.m30 + nm11 * m.m31 + nm21 * m.m32 + ty;
6492         setm32(nm02 * m.m30 + nm12 * m.m31 + nm22 * m.m32 + tz);
6493         setm30(m30);
6494         setm31(m31);
6495         setm33(1.0);
6496         bool one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz);
6497         properties = PROPERTY_AFFINE | (one && (m.properties & PROPERTY_ORTHONORMAL) != 0 ? PROPERTY_ORTHONORMAL : 0);
6498         return this;
6499     }
6500 
6501    
6502 
6503     /**
6504      * Set <code>this</code> matrix to <code>T * R</code>, where <code>T</code> is a translation by the given <code>(tx, ty, tz)</code> and
6505      * <code>R</code> is a rotation - and possibly scaling - transformation specified by the quaternion <code>(qx, qy, qz, qw)</code>.
6506      * <p>
6507      * When transforming a vector by the resulting matrix the rotation - and possibly scaling - transformation will be applied first and then the translation.
6508      * <p>
6509      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6510      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6511      * When used with a left-handed coordinate system, the rotation is clockwise.
6512      * <p>
6513      * This method is equivalent to calling: <code>translation(tx, ty, tz).rotate(quat)</code>
6514      * 
6515      * @see #translation(double, double, double)
6516      * @see #rotate(ref Quaterniond)
6517      * 
6518      * @param tx
6519      *          the number of units by which to translate the x-component
6520      * @param ty
6521      *          the number of units by which to translate the y-component
6522      * @param tz
6523      *          the number of units by which to translate the z-component
6524      * @param qx
6525      *          the x-coordinate of the vector part of the quaternion
6526      * @param qy
6527      *          the y-coordinate of the vector part of the quaternion
6528      * @param qz
6529      *          the z-coordinate of the vector part of the quaternion
6530      * @param qw
6531      *          the scalar part of the quaternion
6532      * @return this
6533      */
6534     ref public Matrix4d translationRotate(double tx, double ty, double tz, double qx, double qy, double qz, double qw) return {
6535         double w2 = qw * qw;
6536         double x2 = qx * qx;
6537         double y2 = qy * qy;
6538         double z2 = qz * qz;
6539         double zw = qz * qw;
6540         double xy = qx * qy;
6541         double xz = qx * qz;
6542         double yw = qy * qw;
6543         double yz = qy * qz;
6544         double xw = qx * qw;
6545         setm00(w2 + x2 - z2 - y2);
6546         setm01(xy + zw + zw + xy);
6547         setm02(xz - yw + xz - yw);
6548         setm10(-zw + xy - zw + xy);
6549         setm11(y2 - z2 + w2 - x2);
6550         setm12(yz + yz + xw + xw);
6551         setm20(yw + xz + xz + yw);
6552         setm21(yz + yz - xw - xw);
6553         setm22(z2 - y2 - x2 + w2);
6554         setm30(tx);
6555         setm31(ty);
6556         setm32(tz);
6557         setm33(1.0);
6558         this.properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
6559         return this;
6560     }
6561 
6562     /**
6563      * Set <code>this</code> matrix to <code>T * R</code>, where <code>T</code> is a translation by the given <code>(tx, ty, tz)</code> and
6564      * <code>R</code> is a rotation - and possibly scaling - transformation specified by the given quaternion.
6565      * <p>
6566      * When transforming a vector by the resulting matrix the rotation - and possibly scaling - transformation will be applied first and then the translation.
6567      * <p>
6568      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6569      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6570      * When used with a left-handed coordinate system, the rotation is clockwise.
6571      * <p>
6572      * This method is equivalent to calling: <code>translation(tx, ty, tz).rotate(quat)</code>
6573      * 
6574      * @see #translation(double, double, double)
6575      * @see #rotate(ref Quaterniond)
6576      * 
6577      * @param tx
6578      *          the number of units by which to translate the x-component
6579      * @param ty
6580      *          the number of units by which to translate the y-component
6581      * @param tz
6582      *          the number of units by which to translate the z-component
6583      * @param quat
6584      *          the quaternion representing a rotation
6585      * @return this
6586      */
6587     ref public Matrix4d translationRotate(double tx, double ty, double tz, Quaterniond quat) return {
6588         return translationRotate(tx, ty, tz, quat.x, quat.y, quat.z, quat.w);
6589     }
6590 
6591     /**
6592      * Set <code>this</code> matrix to <code>T * R</code>, where <code>T</code> is the given <code>translation</code> and
6593      * <code>R</code> is a rotation transformation specified by the given quaternion.
6594      * <p>
6595      * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and
6596      * at last the translation.
6597      * <p>
6598      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6599      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6600      * When used with a left-handed coordinate system, the rotation is clockwise.
6601      * <p>
6602      * This method is equivalent to calling: <code>translation(translation).rotate(quat)</code>
6603      * 
6604      * @see #translation(ref Vector3d)
6605      * @see #rotate(ref Quaterniond)
6606      * 
6607      * @param translation
6608      *          the translation
6609      * @param quat
6610      *          the quaternion representing a rotation
6611      * @return this
6612      */
6613     ref public Matrix4d translationRotate(ref Vector3d translation, 
6614                                       Quaterniond quat) return {
6615         return translationRotate(translation.x, translation.y, translation.z, quat.x, quat.y, quat.z, quat.w);
6616     }
6617 
6618     /**
6619      * Set <code>this</code> matrix to <code>(T * R)<sup>-1</sup></code>, where <code>T</code> is a translation by the given <code>(tx, ty, tz)</code> and
6620      * <code>R</code> is a rotation transformation specified by the quaternion <code>(qx, qy, qz, qw)</code>.
6621      * <p>
6622      * This method is equivalent to calling: <code>translationRotate(...).invert()</code>
6623      * 
6624      * @see #translationRotate(double, double, double, double, double, double, double)
6625      * @see #invert()
6626      * 
6627      * @param tx
6628      *          the number of units by which to translate the x-component
6629      * @param ty
6630      *          the number of units by which to translate the y-component
6631      * @param tz
6632      *          the number of units by which to translate the z-component
6633      * @param qx
6634      *          the x-coordinate of the vector part of the quaternion
6635      * @param qy
6636      *          the y-coordinate of the vector part of the quaternion
6637      * @param qz
6638      *          the z-coordinate of the vector part of the quaternion
6639      * @param qw
6640      *          the scalar part of the quaternion
6641      * @return this
6642      */
6643     ref public Matrix4d translationRotateInvert(double tx, double ty, double tz, double qx, double qy, double qz, double qw) return {
6644         double nqx = -qx, nqy = -qy, nqz = -qz;
6645         double dqx = nqx + nqx;
6646         double dqy = nqy + nqy;
6647         double dqz = nqz + nqz;
6648         double q00 = dqx * nqx;
6649         double q11 = dqy * nqy;
6650         double q22 = dqz * nqz;
6651         double q01 = dqx * nqy;
6652         double q02 = dqx * nqz;
6653         double q03 = dqx * qw;
6654         double q12 = dqy * nqz;
6655         double q13 = dqy * qw;
6656         double q23 = dqz * qw;
6657         return this
6658         ._m00(1.0 - q11 - q22)
6659         ._m01(q01 + q23)
6660         ._m02(q02 - q13)
6661         ._m03(0.0)
6662         ._m10(q01 - q23)
6663         ._m11(1.0 - q22 - q00)
6664         ._m12(q12 + q03)
6665         ._m13(0.0)
6666         ._m20(q02 + q13)
6667         ._m21(q12 - q03)
6668         ._m22(1.0 - q11 - q00)
6669         ._m23(0.0)
6670         ._m30(-m00 * tx - m10 * ty - m20 * tz)
6671         ._m31(-m01 * tx - m11 * ty - m21 * tz)
6672         ._m32(-m02 * tx - m12 * ty - m22 * tz)
6673         ._m33(1.0)
6674         ._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL);
6675     }
6676 
6677     
6678 
6679     /**
6680      * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniond} to this matrix and store
6681      * the result in <code>dest</code>.
6682      * <p>
6683      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6684      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6685      * When used with a left-handed coordinate system, the rotation is clockwise.
6686      * <p>
6687      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
6688      * then the new matrix will be <code>M * Q</code>. So when transforming a
6689      * vector <code>v</code> with the new matrix by using <code>M * Q * v</code>,
6690      * the quaternion rotation will be applied first!
6691      * <p>
6692      * In order to set the matrix to a rotation transformation without post-multiplying,
6693      * use {@link #rotation(ref Quaterniond)}.
6694      * <p>
6695      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
6696      * 
6697      * @see #rotation(ref Quaterniond)
6698      * 
6699      * @param quat
6700      *          the {@link Quaterniond}
6701      * @param dest
6702      *          will hold the result
6703      * @return dest
6704      */
6705     public Matrix4d rotate(ref Quaterniond quat, ref Matrix4d dest) {
6706         if ((properties & PROPERTY_IDENTITY) != 0)
6707             return dest.rotation(quat);
6708         else if ((properties & PROPERTY_TRANSLATION) != 0)
6709             return rotateTranslation(quat, dest);
6710         else if ((properties & PROPERTY_AFFINE) != 0)
6711             return rotateAffine(quat, dest);
6712         return rotateGeneric(quat, dest);
6713     }
6714     private Matrix4d rotateGeneric(ref Quaterniond quat, ref Matrix4d dest) {
6715         double w2 = quat.w * quat.w, x2 = quat.x * quat.x;
6716         double y2 = quat.y * quat.y, z2 = quat.z * quat.z;
6717         double zw = quat.z * quat.w, dzw = zw + zw, xy = quat.x * quat.y, dxy = xy + xy;
6718         double xz = quat.x * quat.z, dxz = xz + xz, yw = quat.y * quat.w, dyw = yw + yw;
6719         double yz = quat.y * quat.z, dyz = yz + yz, xw = quat.x * quat.w, dxw = xw + xw;
6720         double rm00 = w2 + x2 - z2 - y2;
6721         double rm01 = dxy + dzw;
6722         double rm02 = dxz - dyw;
6723         double rm10 = -dzw + dxy;
6724         double rm11 = y2 - z2 + w2 - x2;
6725         double rm12 = dyz + dxw;
6726         double rm20 = dyw + dxz;
6727         double rm21 = dyz - dxw;
6728         double rm22 = z2 - y2 - x2 + w2;
6729         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
6730         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
6731         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
6732         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02;
6733         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
6734         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
6735         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
6736         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12;
6737         dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
6738         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
6739         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
6740         ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22)
6741         ._m00(nm00)
6742         ._m01(nm01)
6743         ._m02(nm02)
6744         ._m03(nm03)
6745         ._m10(nm10)
6746         ._m11(nm11)
6747         ._m12(nm12)
6748         ._m13(nm13)
6749         ._m30(m30)
6750         ._m31(m31)
6751         ._m32(m32)
6752         ._m33(m33)
6753         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
6754         return dest;
6755     }
6756 
6757 
6758     /**
6759      * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniond} to this matrix.
6760      * <p>
6761      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6762      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6763      * When used with a left-handed coordinate system, the rotation is clockwise.
6764      * <p>
6765      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
6766      * then the new matrix will be <code>M * Q</code>. So when transforming a
6767      * vector <code>v</code> with the new matrix by using <code>M * Q * v</code>,
6768      * the quaternion rotation will be applied first!
6769      * <p>
6770      * In order to set the matrix to a rotation transformation without post-multiplying,
6771      * use {@link #rotation(ref Quaterniond)}.
6772      * <p>
6773      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
6774      * 
6775      * @see #rotation(ref Quaterniond)
6776      * 
6777      * @param quat
6778      *          the {@link Quaterniond}
6779      * @return this
6780      */
6781     ref public Matrix4d rotate(ref Quaterniond quat) return {
6782         rotate(quat, this);
6783         return this;
6784     }
6785 
6786 
6787     /**
6788      * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniond} to this {@link #isAffine() affine} matrix and store
6789      * the result in <code>dest</code>.
6790      * <p>
6791      * This method assumes <code>this</code> to be {@link #isAffine() affine}.
6792      * <p>
6793      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6794      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6795      * When used with a left-handed coordinate system, the rotation is clockwise.
6796      * <p>
6797      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
6798      * then the new matrix will be <code>M * Q</code>. So when transforming a
6799      * vector <code>v</code> with the new matrix by using <code>M * Q * v</code>,
6800      * the quaternion rotation will be applied first!
6801      * <p>
6802      * In order to set the matrix to a rotation transformation without post-multiplying,
6803      * use {@link #rotation(ref Quaterniond)}.
6804      * <p>
6805      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
6806      * 
6807      * @see #rotation(ref Quaterniond)
6808      * 
6809      * @param quat
6810      *          the {@link Quaterniond}
6811      * @param dest
6812      *          will hold the result
6813      * @return dest
6814      */
6815     public Matrix4d rotateAffine(ref Quaterniond quat, ref Matrix4d dest) {
6816         double w2 = quat.w * quat.w, x2 = quat.x * quat.x;
6817         double y2 = quat.y * quat.y, z2 = quat.z * quat.z;
6818         double zw = quat.z * quat.w, dzw = zw + zw, xy = quat.x * quat.y, dxy = xy + xy;
6819         double xz = quat.x * quat.z, dxz = xz + xz, yw = quat.y * quat.w, dyw = yw + yw;
6820         double yz = quat.y * quat.z, dyz = yz + yz, xw = quat.x * quat.w, dxw = xw + xw;
6821         double rm00 = w2 + x2 - z2 - y2;
6822         double rm01 = dxy + dzw;
6823         double rm02 = dxz - dyw;
6824         double rm10 = -dzw + dxy;
6825         double rm11 = y2 - z2 + w2 - x2;
6826         double rm12 = dyz + dxw;
6827         double rm20 = dyw + dxz;
6828         double rm21 = dyz - dxw;
6829         double rm22 = z2 - y2 - x2 + w2;
6830         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
6831         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
6832         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
6833         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
6834         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
6835         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
6836         dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
6837         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
6838         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
6839         ._m23(0.0)
6840         ._m00(nm00)
6841         ._m01(nm01)
6842         ._m02(nm02)
6843         ._m03(0.0)
6844         ._m10(nm10)
6845         ._m11(nm11)
6846         ._m12(nm12)
6847         ._m13(0.0)
6848         ._m30(m30)
6849         ._m31(m31)
6850         ._m32(m32)
6851         ._m33(m33)
6852         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
6853         return dest;
6854     }
6855 
6856     /**
6857      * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniond} to this matrix.
6858      * <p>
6859      * This method assumes <code>this</code> to be {@link #isAffine() affine}.
6860      * <p>
6861      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6862      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6863      * When used with a left-handed coordinate system, the rotation is clockwise.
6864      * <p>
6865      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
6866      * then the new matrix will be <code>M * Q</code>. So when transforming a
6867      * vector <code>v</code> with the new matrix by using <code>M * Q * v</code>,
6868      * the quaternion rotation will be applied first!
6869      * <p>
6870      * In order to set the matrix to a rotation transformation without post-multiplying,
6871      * use {@link #rotation(ref Quaterniond)}.
6872      * <p>
6873      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
6874      * 
6875      * @see #rotation(ref Quaterniond)
6876      * 
6877      * @param quat
6878      *          the {@link Quaterniond}
6879      * @return this
6880      */
6881     ref public Matrix4d rotateAffine(ref Quaterniond quat) return {
6882         rotateAffine(quat, this);
6883         return this;
6884     }
6885 
6886     /**
6887      * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniond} to this matrix, which is assumed to only contain a translation, and store
6888      * the result in <code>dest</code>.
6889      * <p>
6890      * This method assumes <code>this</code> to only contain a translation.
6891      * <p>
6892      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6893      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6894      * When used with a left-handed coordinate system, the rotation is clockwise.
6895      * <p>
6896      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
6897      * then the new matrix will be <code>M * Q</code>. So when transforming a
6898      * vector <code>v</code> with the new matrix by using <code>M * Q * v</code>,
6899      * the quaternion rotation will be applied first!
6900      * <p>
6901      * In order to set the matrix to a rotation transformation without post-multiplying,
6902      * use {@link #rotation(ref Quaterniond)}.
6903      * <p>
6904      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
6905      * 
6906      * @see #rotation(ref Quaterniond)
6907      * 
6908      * @param quat
6909      *          the {@link Quaterniond}
6910      * @param dest
6911      *          will hold the result
6912      * @return dest
6913      */
6914     public Matrix4d rotateTranslation(ref Quaterniond quat, ref Matrix4d dest) {
6915         double w2 = quat.w * quat.w, x2 = quat.x * quat.x;
6916         double y2 = quat.y * quat.y, z2 = quat.z * quat.z;
6917         double zw = quat.z * quat.w, dzw = zw + zw, xy = quat.x * quat.y, dxy = xy + xy;
6918         double xz = quat.x * quat.z, dxz = xz + xz, yw = quat.y * quat.w, dyw = yw + yw;
6919         double yz = quat.y * quat.z, dyz = yz + yz, xw = quat.x * quat.w, dxw = xw + xw;
6920         double rm00 = w2 + x2 - z2 - y2;
6921         double rm01 = dxy + dzw;
6922         double rm02 = dxz - dyw;
6923         double rm10 = -dzw + dxy;
6924         double rm11 = y2 - z2 + w2 - x2;
6925         double rm12 = dyz + dxw;
6926         double rm20 = dyw + dxz;
6927         double rm21 = dyz - dxw;
6928         double rm22 = z2 - y2 - x2 + w2;
6929         dest._m20(rm20)
6930         ._m21(rm21)
6931         ._m22(rm22)
6932         ._m23(0.0)
6933         ._m00(rm00)
6934         ._m01(rm01)
6935         ._m02(rm02)
6936         ._m03(0.0)
6937         ._m10(rm10)
6938         ._m11(rm11)
6939         ._m12(rm12)
6940         ._m13(0.0)
6941         ._m30(m30)
6942         ._m31(m31)
6943         ._m32(m32)
6944         ._m33(1.0)
6945         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
6946         return dest;
6947     }
6948 
6949 
6950     /**
6951      * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaterniond} to this matrix and store
6952      * the result in <code>dest</code>.
6953      * <p>
6954      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6955      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6956      * When used with a left-handed coordinate system, the rotation is clockwise.
6957      * <p>
6958      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
6959      * then the new matrix will be <code>Q * M</code>. So when transforming a
6960      * vector <code>v</code> with the new matrix by using <code>Q * M * v</code>,
6961      * the quaternion rotation will be applied last!
6962      * <p>
6963      * In order to set the matrix to a rotation transformation without pre-multiplying,
6964      * use {@link #rotation(ref Quaterniond)}.
6965      * <p>
6966      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
6967      * 
6968      * @see #rotation(ref Quaterniond)
6969      * 
6970      * @param quat
6971      *          the {@link Quaterniond}
6972      * @param dest
6973      *          will hold the result
6974      * @return dest
6975      */
6976     public Matrix4d rotateLocal(ref Quaterniond quat, ref Matrix4d dest) {
6977         double w2 = quat.w * quat.w, x2 = quat.x * quat.x;
6978         double y2 = quat.y * quat.y, z2 = quat.z * quat.z;
6979         double zw = quat.z * quat.w, dzw = zw + zw, xy = quat.x * quat.y, dxy = xy + xy;
6980         double xz = quat.x * quat.z, dxz = xz + xz, yw = quat.y * quat.w, dyw = yw + yw;
6981         double yz = quat.y * quat.z, dyz = yz + yz, xw = quat.x * quat.w, dxw = xw + xw;
6982         double lm00 = w2 + x2 - z2 - y2;
6983         double lm01 = dxy + dzw;
6984         double lm02 = dxz - dyw;
6985         double lm10 = -dzw + dxy;
6986         double lm11 = y2 - z2 + w2 - x2;
6987         double lm12 = dyz + dxw;
6988         double lm20 = dyw + dxz;
6989         double lm21 = dyz - dxw;
6990         double lm22 = z2 - y2 - x2 + w2;
6991         double nm00 = lm00 * m00 + lm10 * m01 + lm20 * m02;
6992         double nm01 = lm01 * m00 + lm11 * m01 + lm21 * m02;
6993         double nm02 = lm02 * m00 + lm12 * m01 + lm22 * m02;
6994         double nm03 = m03;
6995         double nm10 = lm00 * m10 + lm10 * m11 + lm20 * m12;
6996         double nm11 = lm01 * m10 + lm11 * m11 + lm21 * m12;
6997         double nm12 = lm02 * m10 + lm12 * m11 + lm22 * m12;
6998         double nm13 = m13;
6999         double nm20 = lm00 * m20 + lm10 * m21 + lm20 * m22;
7000         double nm21 = lm01 * m20 + lm11 * m21 + lm21 * m22;
7001         double nm22 = lm02 * m20 + lm12 * m21 + lm22 * m22;
7002         double nm23 = m23;
7003         double nm30 = lm00 * m30 + lm10 * m31 + lm20 * m32;
7004         double nm31 = lm01 * m30 + lm11 * m31 + lm21 * m32;
7005         double nm32 = lm02 * m30 + lm12 * m31 + lm22 * m32;
7006         dest._m00(nm00)
7007         ._m01(nm01)
7008         ._m02(nm02)
7009         ._m03(nm03)
7010         ._m10(nm10)
7011         ._m11(nm11)
7012         ._m12(nm12)
7013         ._m13(nm13)
7014         ._m20(nm20)
7015         ._m21(nm21)
7016         ._m22(nm22)
7017         ._m23(nm23)
7018         ._m30(nm30)
7019         ._m31(nm31)
7020         ._m32(nm32)
7021         ._m33(m33)
7022         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
7023         return dest;
7024     }
7025 
7026     /**
7027      * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaterniond} to this matrix.
7028      * <p>
7029      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
7030      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
7031      * When used with a left-handed coordinate system, the rotation is clockwise.
7032      * <p>
7033      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
7034      * then the new matrix will be <code>Q * M</code>. So when transforming a
7035      * vector <code>v</code> with the new matrix by using <code>Q * M * v</code>,
7036      * the quaternion rotation will be applied last!
7037      * <p>
7038      * In order to set the matrix to a rotation transformation without pre-multiplying,
7039      * use {@link #rotation(ref Quaterniond)}.
7040      * <p>
7041      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
7042      * 
7043      * @see #rotation(ref Quaterniond)
7044      * 
7045      * @param quat
7046      *          the {@link Quaterniond}
7047      * @return this
7048      */
7049     ref public Matrix4d rotateLocal(ref Quaterniond quat) return {
7050         rotateLocal(quat, this);
7051         return this;
7052     }
7053 
7054 
7055     /**
7056      * Apply a rotation transformation, rotating about the given {@link AxisAngle4d}, to this matrix.
7057      * <p>
7058      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
7059      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
7060      * When used with a left-handed coordinate system, the rotation is clockwise.
7061      * <p>
7062      * If <code>M</code> is <code>this</code> matrix and <code>A</code> the rotation matrix obtained from the given {@link AxisAngle4d},
7063      * then the new matrix will be <code>M * A</code>. So when transforming a
7064      * vector <code>v</code> with the new matrix by using <code>M * A * v</code>,
7065      * the {@link AxisAngle4d} rotation will be applied first!
7066      * <p>
7067      * In order to set the matrix to a rotation transformation without post-multiplying,
7068      * use {@link #rotation(ref AxisAngle4d)}.
7069      * <p>
7070      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Axis_and_angle">http://en.wikipedia.org</a>
7071      * 
7072      * @see #rotate(double, double, double, double)
7073      * @see #rotation(ref AxisAngle4d)
7074      * 
7075      * @param axisAngle
7076      *          the {@link AxisAngle4d} (needs to be {@link AxisAngle4d#normalize() normalized})
7077      * @return this
7078      */
7079     ref public Matrix4d rotate(ref AxisAngle4d axisAngle) return {
7080         return rotate(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z);
7081     }
7082 
7083     /**
7084      * Apply a rotation transformation, rotating about the given {@link AxisAngle4d} and store the result in <code>dest</code>.
7085      * <p>
7086      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
7087      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
7088      * When used with a left-handed coordinate system, the rotation is clockwise.
7089      * <p>
7090      * If <code>M</code> is <code>this</code> matrix and <code>A</code> the rotation matrix obtained from the given {@link AxisAngle4d},
7091      * then the new matrix will be <code>M * A</code>. So when transforming a
7092      * vector <code>v</code> with the new matrix by using <code>M * A * v</code>,
7093      * the {@link AxisAngle4d} rotation will be applied first!
7094      * <p>
7095      * In order to set the matrix to a rotation transformation without post-multiplying,
7096      * use {@link #rotation(ref AxisAngle4d)}.
7097      * <p>
7098      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Axis_and_angle">http://en.wikipedia.org</a>
7099      * 
7100      * @see #rotate(double, double, double, double)
7101      * @see #rotation(ref AxisAngle4d)
7102      * 
7103      * @param axisAngle
7104      *          the {@link AxisAngle4d} (needs to be {@link AxisAngle4d#normalize() normalized})
7105      * @param dest
7106      *          will hold the result
7107      * @return dest
7108      */
7109     public Matrix4d rotate(ref AxisAngle4d axisAngle, ref Matrix4d dest) {
7110         return rotate(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z, dest);
7111     }
7112 
7113     /**
7114      * Apply a rotation transformation, rotating the given radians about the specified axis, to this matrix.
7115      * <p>
7116      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
7117      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
7118      * When used with a left-handed coordinate system, the rotation is clockwise.
7119      * <p>
7120      * If <code>M</code> is <code>this</code> matrix and <code>A</code> the rotation matrix obtained from the given angle and axis,
7121      * then the new matrix will be <code>M * A</code>. So when transforming a
7122      * vector <code>v</code> with the new matrix by using <code>M * A * v</code>,
7123      * the axis-angle rotation will be applied first!
7124      * <p>
7125      * In order to set the matrix to a rotation transformation without post-multiplying,
7126      * use {@link #rotation(double, Vector3d)}.
7127      * <p>
7128      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Axis_and_angle">http://en.wikipedia.org</a>
7129      * 
7130      * @see #rotate(double, double, double, double)
7131      * @see #rotation(double, Vector3d)
7132      * 
7133      * @param angle
7134      *          the angle in radians
7135      * @param axis
7136      *          the rotation axis (needs to be {@link Vector3d#normalize() normalized})
7137      * @return this
7138      */
7139     ref public Matrix4d rotate(double angle, Vector3d axis) return {
7140         return rotate(angle, axis.x, axis.y, axis.z);
7141     }
7142 
7143     /**
7144      * Apply a rotation transformation, rotating the given radians about the specified axis and store the result in <code>dest</code>.
7145      * <p>
7146      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
7147      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
7148      * When used with a left-handed coordinate system, the rotation is clockwise.
7149      * <p>
7150      * If <code>M</code> is <code>this</code> matrix and <code>A</code> the rotation matrix obtained from the given angle and axis,
7151      * then the new matrix will be <code>M * A</code>. So when transforming a
7152      * vector <code>v</code> with the new matrix by using <code>M * A * v</code>,
7153      * the axis-angle rotation will be applied first!
7154      * <p>
7155      * In order to set the matrix to a rotation transformation without post-multiplying,
7156      * use {@link #rotation(double, Vector3d)}.
7157      * <p>
7158      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Axis_and_angle">http://en.wikipedia.org</a>
7159      * 
7160      * @see #rotate(double, double, double, double)
7161      * @see #rotation(double, Vector3d)
7162      * 
7163      * @param angle
7164      *          the angle in radians
7165      * @param axis
7166      *          the rotation axis (needs to be {@link Vector3d#normalize() normalized})
7167      * @param dest
7168      *          will hold the result
7169      * @return dest
7170      */
7171     public Matrix4d rotate(double angle, Vector3d axis, ref Matrix4d dest) {
7172         return rotate(angle, axis.x, axis.y, axis.z, dest);
7173     }
7174 
7175     public Vector4d getRow(int row, ref Vector4d dest) {
7176         switch (row) {
7177         case 0:
7178             dest.x = m00;
7179             dest.y = m10;
7180             dest.z = m20;
7181             dest.w = m30;
7182             break;
7183         case 1:
7184             dest.x = m01;
7185             dest.y = m11;
7186             dest.z = m21;
7187             dest.w = m31;
7188             break;
7189         case 2:
7190             dest.x = m02;
7191             dest.y = m12;
7192             dest.z = m22;
7193             dest.w = m32;
7194             break;
7195         case 3:
7196             dest.x = m03;
7197             dest.y = m13;
7198             dest.z = m23;
7199             dest.w = m33;
7200             break;
7201         default: {}
7202         }
7203         return dest;
7204     }
7205 
7206     public Vector3d getRow(int row, ref Vector3d dest){
7207         switch (row) {
7208         case 0:
7209             dest.x = m00;
7210             dest.y = m10;
7211             dest.z = m20;
7212             break;
7213         case 1:
7214             dest.x = m01;
7215             dest.y = m11;
7216             dest.z = m21;
7217             break;
7218         case 2:
7219             dest.x = m02;
7220             dest.y = m12;
7221             dest.z = m22;
7222             break;
7223         case 3:
7224             dest.x = m03;
7225             dest.y = m13;
7226             dest.z = m23;
7227             break;
7228         default: {}
7229         }
7230         return dest;
7231     }
7232 
7233     /**
7234      * Set the row at the given <code>row</code> index, starting with <code>0</code>.
7235      * 
7236      * @param row
7237      *          the row index in <code>[0..3]</code>
7238      * @param src
7239      *          the row components to set
7240      * @return this
7241      * @throws IndexOutOfBoundsException if <code>row</code> is not in <code>[0..3]</code>
7242      */
7243     ref public Matrix4d setRow(int row, Vector4d src) return {
7244         switch (row) {
7245         case 0:
7246             return _m00(src.x)._m10(src.y)._m20(src.z)._m30(src.w)._properties(0);
7247         case 1:
7248             return _m01(src.x)._m11(src.y)._m21(src.z)._m31(src.w)._properties(0);
7249         case 2:
7250             return _m02(src.x)._m12(src.y)._m22(src.z)._m32(src.w)._properties(0);
7251         case 3:
7252             return _m03(src.x)._m13(src.y)._m23(src.z)._m33(src.w)._properties(0);
7253         default:
7254             return this;
7255         }
7256     }
7257 
7258     public Vector4d getColumn(int column, ref Vector4d dest) {
7259         switch (column) {
7260         case 0:
7261             dest.x = m00;
7262             dest.y = m01;
7263             dest.z = m02;
7264             dest.w = m03;
7265             break;
7266         case 1:
7267             dest.x = m10;
7268             dest.y = m11;
7269             dest.z = m12;
7270             dest.w = m13;
7271             break;
7272         case 2:
7273             dest.x = m20;
7274             dest.y = m21;
7275             dest.z = m22;
7276             dest.w = m23;
7277             break;
7278         case 3:
7279             dest.x = m30;
7280             dest.y = m31;
7281             dest.z = m32;
7282             dest.w = m33;
7283             break;
7284         default: {}
7285         }
7286         return dest;
7287     }
7288 
7289     public Vector3d getColumn(int column, ref Vector3d dest) {
7290         switch (column) {
7291         case 0:
7292             dest.x = m00;
7293             dest.y = m01;
7294             dest.z = m02;
7295             break;
7296         case 1:
7297             dest.x = m10;
7298             dest.y = m11;
7299             dest.z = m12;
7300             break;
7301         case 2:
7302             dest.x = m20;
7303             dest.y = m21;
7304             dest.z = m22;
7305             break;
7306         case 3:
7307             dest.x = m30;
7308             dest.y = m31;
7309             dest.z = m32;
7310             break;
7311         default: {}
7312         }
7313         return dest;
7314     }
7315 
7316     /**
7317      * Set the column at the given <code>column</code> index, starting with <code>0</code>.
7318      * 
7319      * @param column
7320      *          the column index in <code>[0..3]</code>
7321      * @param src
7322      *          the column components to set
7323      * @return this
7324      * @throws IndexOutOfBoundsException if <code>column</code> is not in <code>[0..3]</code>
7325      */
7326     ref public Matrix4d setColumn(int column, Vector4d src) return {
7327         switch (column) {
7328         case 0:
7329             return _m00(src.x)._m01(src.y)._m02(src.z)._m03(src.w)._properties(0);
7330         case 1:
7331             return _m10(src.x)._m11(src.y)._m12(src.z)._m13(src.w)._properties(0);
7332         case 2:
7333             return _m20(src.x)._m21(src.y)._m22(src.z)._m23(src.w)._properties(0);
7334         case 3:
7335             return _m30(src.x)._m31(src.y)._m32(src.z)._m33(src.w)._properties(0);
7336         default:
7337             return this;
7338         }
7339     }
7340 
7341     public double get(int column, int row) {
7342         return MemUtil.get(this, column, row);
7343     }
7344 
7345     /**
7346      * Set the matrix element at the given column and row to the specified value.
7347      * 
7348      * @param column
7349      *          the colum index in <code>[0..3]</code>
7350      * @param row
7351      *          the row index in <code>[0..3]</code>
7352      * @param value
7353      *          the value
7354      * @return this
7355      */
7356     ref public Matrix4d set(int column, int row, double value) return {
7357         MemUtil.set(this, column, row, value);
7358         return this;
7359     }
7360 
7361     public double getRowColumn(int row, int column) {
7362         return MemUtil.get(this, column, row);
7363     }
7364 
7365     /**
7366      * Set the matrix element at the given row and column to the specified value.
7367      * 
7368      * @param row
7369      *          the row index in <code>[0..3]</code>
7370      * @param column
7371      *          the colum index in <code>[0..3]</code>
7372      * @param value
7373      *          the value
7374      * @return this
7375      */
7376     ref public Matrix4d setRowColumn(int row, int column, double value) return {
7377         MemUtil.set(this, column, row, value);
7378         return this;
7379     }
7380 
7381     /**
7382      * Compute a normal matrix from the upper left 3x3 submatrix of <code>this</code>
7383      * and store it into the upper left 3x3 submatrix of <code>this</code>.
7384      * All other values of <code>this</code> will be set to {@link #identity() identity}.
7385      * <p>
7386      * The normal matrix of <code>m</code> is the transpose of the inverse of <code>m</code>.
7387      * <p>
7388      * Please note that, if <code>this</code> is an orthogonal matrix or a matrix whose columns are orthogonal vectors, 
7389      * then this method <i>need not</i> be invoked, since in that case <code>this</code> itself is its normal matrix.
7390      * In that case, use {@link #set3x3(Matrix4d)} to set a given Matrix4f to only the upper left 3x3 submatrix
7391      * of this matrix.
7392      * 
7393      * @see #set3x3(Matrix4d)
7394      * 
7395      * @return this
7396      */
7397     ref public Matrix4d normal() return {
7398         normal(this);
7399         return this;
7400     }
7401 
7402     /**
7403      * Compute a normal matrix from the upper left 3x3 submatrix of <code>this</code>
7404      * and store it into the upper left 3x3 submatrix of <code>dest</code>.
7405      * All other values of <code>dest</code> will be set to {@link #identity() identity}.
7406      * <p>
7407      * The normal matrix of <code>m</code> is the transpose of the inverse of <code>m</code>.
7408      * <p>
7409      * Please note that, if <code>this</code> is an orthogonal matrix or a matrix whose columns are orthogonal vectors, 
7410      * then this method <i>need not</i> be invoked, since in that case <code>this</code> itself is its normal matrix.
7411      * In that case, use {@link #set3x3(Matrix4d)} to set a given Matrix4d to only the upper left 3x3 submatrix
7412      * of a given matrix.
7413      * 
7414      * @see #set3x3(Matrix4d)
7415      * 
7416      * @param dest
7417      *             will hold the result
7418      * @return dest
7419      */
7420     public Matrix4d normal(ref Matrix4d dest) {
7421         if ((properties & PROPERTY_IDENTITY) != 0)
7422             return dest.identity();
7423         else if ((properties & PROPERTY_ORTHONORMAL) != 0)
7424             return normalOrthonormal(dest);
7425         return normalGeneric(dest);
7426     }
7427     private Matrix4d normalOrthonormal(ref Matrix4d dest) {
7428         if (dest != this)
7429             dest.set(this);
7430         return dest._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL);
7431     }
7432     private Matrix4d normalGeneric(ref Matrix4d dest) {
7433         double m00m11 = m00 * m11;
7434         double m01m10 = m01 * m10;
7435         double m02m10 = m02 * m10;
7436         double m00m12 = m00 * m12;
7437         double m01m12 = m01 * m12;
7438         double m02m11 = m02 * m11;
7439         double det = (m00m11 - m01m10) * m22 + (m02m10 - m00m12) * m21 + (m01m12 - m02m11) * m20;
7440         double s = 1.0 / det;
7441         /* Invert and transpose in one go */
7442         double nm00 = (m11 * m22 - m21 * m12) * s;
7443         double nm01 = (m20 * m12 - m10 * m22) * s;
7444         double nm02 = (m10 * m21 - m20 * m11) * s;
7445         double nm10 = (m21 * m02 - m01 * m22) * s;
7446         double nm11 = (m00 * m22 - m20 * m02) * s;
7447         double nm12 = (m20 * m01 - m00 * m21) * s;
7448         double nm20 = (m01m12 - m02m11) * s;
7449         double nm21 = (m02m10 - m00m12) * s;
7450         double nm22 = (m00m11 - m01m10) * s;
7451         return dest
7452         ._m00(nm00)
7453         ._m01(nm01)
7454         ._m02(nm02)
7455         ._m03(0.0)
7456         ._m10(nm10)
7457         ._m11(nm11)
7458         ._m12(nm12)
7459         ._m13(0.0)
7460         ._m20(nm20)
7461         ._m21(nm21)
7462         ._m22(nm22)
7463         ._m23(0.0)
7464         ._m30(0.0)
7465         ._m31(0.0)
7466         ._m32(0.0)
7467         ._m33(1.0)
7468         ._properties((properties | PROPERTY_AFFINE) & ~(PROPERTY_TRANSLATION | PROPERTY_PERSPECTIVE));
7469     }
7470 
7471     /**
7472      * Compute a normal matrix from the upper left 3x3 submatrix of <code>this</code>
7473      * and store it into <code>dest</code>.
7474      * <p>
7475      * The normal matrix of <code>m</code> is the transpose of the inverse of <code>m</code>.
7476      * <p>
7477      * Please note that, if <code>this</code> is an orthogonal matrix or a matrix whose columns are orthogonal vectors, 
7478      * then this method <i>need not</i> be invoked, since in that case <code>this</code> itself is its normal matrix.
7479      * In that case, use {@link Matrix3d#set(Matrix4d)} to set a given Matrix3d to only the upper left 3x3 submatrix
7480      * of this matrix.
7481      * 
7482      * @see Matrix3d#set(Matrix4d)
7483      * @see #get3x3(Matrix3d)
7484      * 
7485      * @param dest
7486      *             will hold the result
7487      * @return dest
7488      */
7489     public Matrix3d normal(ref Matrix3d dest) {
7490         if ((properties & PROPERTY_ORTHONORMAL) != 0)
7491             return normalOrthonormal(dest);
7492         return normalGeneric(dest);
7493     }
7494     private Matrix3d normalOrthonormal(ref Matrix3d dest) {
7495         dest.set(this);
7496         return dest;
7497     }
7498     private Matrix3d normalGeneric(ref Matrix3d dest) {
7499         double m00m11 = m00 * m11;
7500         double m01m10 = m01 * m10;
7501         double m02m10 = m02 * m10;
7502         double m00m12 = m00 * m12;
7503         double m01m12 = m01 * m12;
7504         double m02m11 = m02 * m11;
7505         double det = (m00m11 - m01m10) * m22 + (m02m10 - m00m12) * m21 + (m01m12 - m02m11) * m20;
7506         double s = 1.0 / det;
7507         /* Invert and transpose in one go */
7508         return dest._m00((m11 * m22 - m21 * m12) * s)
7509         ._m01((m20 * m12 - m10 * m22) * s)
7510         ._m02((m10 * m21 - m20 * m11) * s)
7511         ._m10((m21 * m02 - m01 * m22) * s)
7512         ._m11((m00 * m22 - m20 * m02) * s)
7513         ._m12((m20 * m01 - m00 * m21) * s)
7514         ._m20((m01m12 - m02m11) * s)
7515         ._m21((m02m10 - m00m12) * s)
7516         ._m22((m00m11 - m01m10) * s);
7517     }
7518 
7519     /**
7520      * Compute the cofactor matrix of the upper left 3x3 submatrix of <code>this</code>.
7521      * <p>
7522      * The cofactor matrix can be used instead of {@link #normal()} to transform normals
7523      * when the orientation of the normals with respect to the surface should be preserved.
7524      * 
7525      * @return this
7526      */
7527     ref public Matrix4d cofactor3x3() return {
7528         cofactor3x3(this);
7529         return this;
7530     }
7531 
7532     /**
7533      * Compute the cofactor matrix of the upper left 3x3 submatrix of <code>this</code>
7534      * and store it into <code>dest</code>.
7535      * <p>
7536      * The cofactor matrix can be used instead of {@link #normal(Matrix3d)} to transform normals
7537      * when the orientation of the normals with respect to the surface should be preserved.
7538      * 
7539      * @param dest
7540      *             will hold the result
7541      * @return dest
7542      */
7543     public Matrix3d cofactor3x3(ref Matrix3d dest) {
7544         return dest._m00(m11 * m22 - m21 * m12)
7545         ._m01(m20 * m12 - m10 * m22)
7546         ._m02(m10 * m21 - m20 * m11)
7547         ._m10(m21 * m02 - m01 * m22)
7548         ._m11(m00 * m22 - m20 * m02)
7549         ._m12(m20 * m01 - m00 * m21)
7550         ._m20(m01 * m12 - m02 * m11)
7551         ._m21(m02 * m10 - m00 * m12)
7552         ._m22(m00 * m11 - m01 * m10);
7553     }
7554 
7555     /**
7556      * Compute the cofactor matrix of the upper left 3x3 submatrix of <code>this</code>
7557      * and store it into <code>dest</code>.
7558      * All other values of <code>dest</code> will be set to {@link #identity() identity}.
7559      * <p>
7560      * The cofactor matrix can be used instead of {@link #normal(Matrix4d)} to transform normals
7561      * when the orientation of the normals with respect to the surface should be preserved.
7562      * 
7563      * @param dest
7564      *             will hold the result
7565      * @return dest
7566      */
7567     public Matrix4d cofactor3x3(ref Matrix4d dest) {
7568         double nm10 = m21 * m02 - m01 * m22;
7569         double nm11 = m00 * m22 - m20 * m02;
7570         double nm12 = m20 * m01 - m00 * m21;
7571         double nm20 = m01 * m12 - m11 * m02;
7572         double nm21 = m02 * m10 - m12 * m00;
7573         double nm22 = m00 * m11 - m10 * m01;
7574         return dest
7575         ._m00(m11 * m22 - m21 * m12)
7576         ._m01(m20 * m12 - m10 * m22)
7577         ._m02(m10 * m21 - m20 * m11)
7578         ._m03(0.0)
7579         ._m10(nm10)
7580         ._m11(nm11)
7581         ._m12(nm12)
7582         ._m13(0.0)
7583         ._m20(nm20)
7584         ._m21(nm21)
7585         ._m22(nm22)
7586         ._m23(0.0)
7587         ._m30(0.0)
7588         ._m31(0.0)
7589         ._m32(0.0)
7590         ._m33(1.0)
7591         ._properties((properties | PROPERTY_AFFINE) & ~(PROPERTY_TRANSLATION | PROPERTY_PERSPECTIVE));
7592     }
7593 
7594     /**
7595      * Normalize the upper left 3x3 submatrix of this matrix.
7596      * <p>
7597      * The resulting matrix will map unit vectors to unit vectors, though a pair of orthogonal input unit
7598      * vectors need not be mapped to a pair of orthogonal output vectors if the original matrix was not orthogonal itself
7599      * (i.e. had <i>skewing</i>).
7600      * 
7601      * @return this
7602      */
7603     ref public Matrix4d normalize3x3() return {
7604         normalize3x3(this);
7605         return this;
7606     }
7607 
7608     public Matrix4d normalize3x3(ref Matrix4d dest) {
7609         double invXlen = Math.invsqrt(m00 * m00 + m01 * m01 + m02 * m02);
7610         double invYlen = Math.invsqrt(m10 * m10 + m11 * m11 + m12 * m12);
7611         double invZlen = Math.invsqrt(m20 * m20 + m21 * m21 + m22 * m22);
7612         dest._m00(m00 * invXlen)._m01(m01 * invXlen)._m02(m02 * invXlen)
7613             ._m10(m10 * invYlen)._m11(m11 * invYlen)._m12(m12 * invYlen)
7614             ._m20(m20 * invZlen)._m21(m21 * invZlen)._m22(m22 * invZlen)
7615             ._m30(m30)._m31(m31)._m32(m32)._m33(m33)
7616             ._properties(properties);
7617         return dest;
7618     }
7619 
7620     public Matrix3d normalize3x3(ref Matrix3d dest) {
7621         double invXlen = Math.invsqrt(m00 * m00 + m01 * m01 + m02 * m02);
7622         double invYlen = Math.invsqrt(m10 * m10 + m11 * m11 + m12 * m12);
7623         double invZlen = Math.invsqrt(m20 * m20 + m21 * m21 + m22 * m22);
7624         dest.m00 = (m00 * invXlen);
7625         dest.m01 = (m01 * invXlen);
7626         dest.m02 = (m02 * invXlen);
7627 
7628         dest.m10 = (m10 * invYlen);
7629         dest.m11 = (m11 * invYlen);
7630         dest.m12 = (m12 * invYlen);
7631 
7632         dest.m20 = (m20 * invZlen);
7633         dest.m21 = (m21 * invZlen);
7634         dest.m22 = (m22 * invZlen);
7635         return dest;
7636     }
7637 
7638     public Vector4d unproject(double winX, double winY, double winZ, int[] viewport, ref Vector4d dest) {
7639         double a = m00 * m11 - m01 * m10;
7640         double b = m00 * m12 - m02 * m10;
7641         double c = m00 * m13 - m03 * m10;
7642         double d = m01 * m12 - m02 * m11;
7643         double e = m01 * m13 - m03 * m11;
7644         double f = m02 * m13 - m03 * m12;
7645         double g = m20 * m31 - m21 * m30;
7646         double h = m20 * m32 - m22 * m30;
7647         double i = m20 * m33 - m23 * m30;
7648         double j = m21 * m32 - m22 * m31;
7649         double k = m21 * m33 - m23 * m31;
7650         double l = m22 * m33 - m23 * m32;
7651         double det = a * l - b * k + c * j + d * i - e * h + f * g;
7652         det = 1.0 / det;
7653         double im00 = ( m11 * l - m12 * k + m13 * j) * det;
7654         double im01 = (-m01 * l + m02 * k - m03 * j) * det;
7655         double im02 = ( m31 * f - m32 * e + m33 * d) * det;
7656         double im03 = (-m21 * f + m22 * e - m23 * d) * det;
7657         double im10 = (-m10 * l + m12 * i - m13 * h) * det;
7658         double im11 = ( m00 * l - m02 * i + m03 * h) * det;
7659         double im12 = (-m30 * f + m32 * c - m33 * b) * det;
7660         double im13 = ( m20 * f - m22 * c + m23 * b) * det;
7661         double im20 = ( m10 * k - m11 * i + m13 * g) * det;
7662         double im21 = (-m00 * k + m01 * i - m03 * g) * det;
7663         double im22 = ( m30 * e - m31 * c + m33 * a) * det;
7664         double im23 = (-m20 * e + m21 * c - m23 * a) * det;
7665         double im30 = (-m10 * j + m11 * h - m12 * g) * det;
7666         double im31 = ( m00 * j - m01 * h + m02 * g) * det;
7667         double im32 = (-m30 * d + m31 * b - m32 * a) * det;
7668         double im33 = ( m20 * d - m21 * b + m22 * a) * det;
7669         double ndcX = (winX-viewport[0])/viewport[2]*2.0-1.0;
7670         double ndcY = (winY-viewport[1])/viewport[3]*2.0-1.0;
7671         double ndcZ = winZ+winZ-1.0;
7672         double invW = 1.0 / (im03 * ndcX + im13 * ndcY + im23 * ndcZ + im33);
7673         dest.x = (im00 * ndcX + im10 * ndcY + im20 * ndcZ + im30) * invW;
7674         dest.y = (im01 * ndcX + im11 * ndcY + im21 * ndcZ + im31) * invW;
7675         dest.z = (im02 * ndcX + im12 * ndcY + im22 * ndcZ + im32) * invW;
7676         dest.w = 1.0;
7677         return dest;
7678     }
7679 
7680     public Vector3d unproject(double winX, double winY, double winZ, int[] viewport, ref Vector3d dest) {
7681         double a = m00 * m11 - m01 * m10;
7682         double b = m00 * m12 - m02 * m10;
7683         double c = m00 * m13 - m03 * m10;
7684         double d = m01 * m12 - m02 * m11;
7685         double e = m01 * m13 - m03 * m11;
7686         double f = m02 * m13 - m03 * m12;
7687         double g = m20 * m31 - m21 * m30;
7688         double h = m20 * m32 - m22 * m30;
7689         double i = m20 * m33 - m23 * m30;
7690         double j = m21 * m32 - m22 * m31;
7691         double k = m21 * m33 - m23 * m31;
7692         double l = m22 * m33 - m23 * m32;
7693         double det = a * l - b * k + c * j + d * i - e * h + f * g;
7694         det = 1.0 / det;
7695         double im00 = ( m11 * l - m12 * k + m13 * j) * det;
7696         double im01 = (-m01 * l + m02 * k - m03 * j) * det;
7697         double im02 = ( m31 * f - m32 * e + m33 * d) * det;
7698         double im03 = (-m21 * f + m22 * e - m23 * d) * det;
7699         double im10 = (-m10 * l + m12 * i - m13 * h) * det;
7700         double im11 = ( m00 * l - m02 * i + m03 * h) * det;
7701         double im12 = (-m30 * f + m32 * c - m33 * b) * det;
7702         double im13 = ( m20 * f - m22 * c + m23 * b) * det;
7703         double im20 = ( m10 * k - m11 * i + m13 * g) * det;
7704         double im21 = (-m00 * k + m01 * i - m03 * g) * det;
7705         double im22 = ( m30 * e - m31 * c + m33 * a) * det;
7706         double im23 = (-m20 * e + m21 * c - m23 * a) * det;
7707         double im30 = (-m10 * j + m11 * h - m12 * g) * det;
7708         double im31 = ( m00 * j - m01 * h + m02 * g) * det;
7709         double im32 = (-m30 * d + m31 * b - m32 * a) * det;
7710         double im33 = ( m20 * d - m21 * b + m22 * a) * det;
7711         double ndcX = (winX-viewport[0])/viewport[2]*2.0-1.0;
7712         double ndcY = (winY-viewport[1])/viewport[3]*2.0-1.0;
7713         double ndcZ = winZ+winZ-1.0;
7714         double invW = 1.0 / (im03 * ndcX + im13 * ndcY + im23 * ndcZ + im33);
7715         dest.x = (im00 * ndcX + im10 * ndcY + im20 * ndcZ + im30) * invW;
7716         dest.y = (im01 * ndcX + im11 * ndcY + im21 * ndcZ + im31) * invW;
7717         dest.z = (im02 * ndcX + im12 * ndcY + im22 * ndcZ + im32) * invW;
7718         return dest;
7719     }
7720 
7721     public Vector4d unproject(ref Vector3d winCoords, int[] viewport, ref Vector4d dest) {
7722         return unproject(winCoords.x, winCoords.y, winCoords.z, viewport, dest);
7723     }
7724 
7725     public Vector3d unproject(ref Vector3d winCoords, int[] viewport, ref Vector3d dest) {
7726         return unproject(winCoords.x, winCoords.y, winCoords.z, viewport, dest);
7727     }
7728 
7729     ref public Matrix4d unprojectRay(double winX, double winY, int[] viewport, ref Vector3d originDest, ref Vector3d dirDest) return {
7730         double a = m00 * m11 - m01 * m10;
7731         double b = m00 * m12 - m02 * m10;
7732         double c = m00 * m13 - m03 * m10;
7733         double d = m01 * m12 - m02 * m11;
7734         double e = m01 * m13 - m03 * m11;
7735         double f = m02 * m13 - m03 * m12;
7736         double g = m20 * m31 - m21 * m30;
7737         double h = m20 * m32 - m22 * m30;
7738         double i = m20 * m33 - m23 * m30;
7739         double j = m21 * m32 - m22 * m31;
7740         double k = m21 * m33 - m23 * m31;
7741         double l = m22 * m33 - m23 * m32;
7742         double det = a * l - b * k + c * j + d * i - e * h + f * g;
7743         det = 1.0 / det;
7744         double im00 = ( m11 * l - m12 * k + m13 * j) * det;
7745         double im01 = (-m01 * l + m02 * k - m03 * j) * det;
7746         double im02 = ( m31 * f - m32 * e + m33 * d) * det;
7747         double im03 = (-m21 * f + m22 * e - m23 * d) * det;
7748         double im10 = (-m10 * l + m12 * i - m13 * h) * det;
7749         double im11 = ( m00 * l - m02 * i + m03 * h) * det;
7750         double im12 = (-m30 * f + m32 * c - m33 * b) * det;
7751         double im13 = ( m20 * f - m22 * c + m23 * b) * det;
7752         double im20 = ( m10 * k - m11 * i + m13 * g) * det;
7753         double im21 = (-m00 * k + m01 * i - m03 * g) * det;
7754         double im22 = ( m30 * e - m31 * c + m33 * a) * det;
7755         double im23 = (-m20 * e + m21 * c - m23 * a) * det;
7756         double im30 = (-m10 * j + m11 * h - m12 * g) * det;
7757         double im31 = ( m00 * j - m01 * h + m02 * g) * det;
7758         double im32 = (-m30 * d + m31 * b - m32 * a) * det;
7759         double im33 = ( m20 * d - m21 * b + m22 * a) * det;
7760         double ndcX = (winX-viewport[0])/viewport[2]*2.0-1.0;
7761         double ndcY = (winY-viewport[1])/viewport[3]*2.0-1.0;
7762         double px = im00 * ndcX + im10 * ndcY + im30;
7763         double py = im01 * ndcX + im11 * ndcY + im31;
7764         double pz = im02 * ndcX + im12 * ndcY + im32;
7765         double invNearW = 1.0 / (im03 * ndcX + im13 * ndcY - im23 + im33);
7766         double nearX = (px - im20) * invNearW;
7767         double nearY = (py - im21) * invNearW;
7768         double nearZ = (pz - im22) * invNearW;
7769         double invW0 = 1.0 / (im03 * ndcX + im13 * ndcY + im33);
7770         double x0 = px * invW0;
7771         double y0 = py * invW0;
7772         double z0 = pz * invW0;
7773         originDest.x = nearX; originDest.y = nearY; originDest.z = nearZ;
7774         dirDest.x = x0 - nearX; dirDest.y = y0 - nearY; dirDest.z = z0 - nearZ;
7775         return this;
7776     }
7777 
7778     public Matrix4d unprojectRay(ref Vector2d winCoords, int[] viewport, ref Vector3d originDest, ref Vector3d dirDest) {
7779         return unprojectRay(winCoords.x, winCoords.y, viewport, originDest, dirDest);
7780     }
7781 
7782     public Vector4d unprojectInv(ref Vector3d winCoords, int[] viewport, ref Vector4d dest) {
7783         return unprojectInv(winCoords.x, winCoords.y, winCoords.z, viewport, dest);
7784     }
7785 
7786     public Vector4d unprojectInv(double winX, double winY, double winZ, int[] viewport, ref Vector4d dest) {
7787         double ndcX = (winX-viewport[0])/viewport[2]*2.0-1.0;
7788         double ndcY = (winY-viewport[1])/viewport[3]*2.0-1.0;
7789         double ndcZ = winZ+winZ-1.0;
7790         double invW = 1.0 / (m03 * ndcX + m13 * ndcY + m23 * ndcZ + m33);
7791         dest.x = (m00 * ndcX + m10 * ndcY + m20 * ndcZ + m30) * invW;
7792         dest.y = (m01 * ndcX + m11 * ndcY + m21 * ndcZ + m31) * invW;
7793         dest.z = (m02 * ndcX + m12 * ndcY + m22 * ndcZ + m32) * invW;
7794         dest.w = 1.0;
7795         return dest;
7796     }
7797 
7798     public Vector3d unprojectInv(ref Vector3d winCoords, int[] viewport, ref Vector3d dest) {
7799         return unprojectInv(winCoords.x, winCoords.y, winCoords.z, viewport, dest);
7800     }
7801 
7802     public Vector3d unprojectInv(double winX, double winY, double winZ, int[] viewport, ref Vector3d dest) {
7803         double ndcX = (winX-viewport[0])/viewport[2]*2.0-1.0;
7804         double ndcY = (winY-viewport[1])/viewport[3]*2.0-1.0;
7805         double ndcZ = winZ+winZ-1.0;
7806         double invW = 1.0 / (m03 * ndcX + m13 * ndcY + m23 * ndcZ + m33);
7807         dest.x = (m00 * ndcX + m10 * ndcY + m20 * ndcZ + m30) * invW;
7808         dest.y = (m01 * ndcX + m11 * ndcY + m21 * ndcZ + m31) * invW;
7809         dest.z = (m02 * ndcX + m12 * ndcY + m22 * ndcZ + m32) * invW;
7810         return dest;
7811     }
7812 
7813     public Matrix4d unprojectInvRay(ref Vector2d winCoords, int[] viewport, ref Vector3d originDest, ref Vector3d dirDest) {
7814         return unprojectInvRay(winCoords.x, winCoords.y, viewport, originDest, dirDest);
7815     }
7816 
7817     ref public Matrix4d unprojectInvRay(double winX, double winY, int[] viewport, ref Vector3d originDest, ref Vector3d dirDest) return  {
7818         double ndcX = (winX-viewport[0])/viewport[2]*2.0-1.0;
7819         double ndcY = (winY-viewport[1])/viewport[3]*2.0-1.0;
7820         double px = m00 * ndcX + m10 * ndcY + m30;
7821         double py = m01 * ndcX + m11 * ndcY + m31;
7822         double pz = m02 * ndcX + m12 * ndcY + m32;
7823         double invNearW = 1.0 / (m03 * ndcX + m13 * ndcY - m23 + m33);
7824         double nearX = (px - m20) * invNearW;
7825         double nearY = (py - m21) * invNearW;
7826         double nearZ = (pz - m22) * invNearW;
7827         double invW0 = 1.0 / (m03 * ndcX + m13 * ndcY + m33);
7828         double x0 = px * invW0;
7829         double y0 = py * invW0;
7830         double z0 = pz * invW0;
7831         originDest.x = nearX; originDest.y = nearY; originDest.z = nearZ;
7832         dirDest.x = x0 - nearX; dirDest.y = y0 - nearY; dirDest.z = z0 - nearZ;
7833         return this;
7834     }
7835 
7836     public Vector4d project(double x, double y, double z, int[] viewport, ref Vector4d winCoordsDest) {
7837         double invW = 1.0 / Math.fma(m03, x, Math.fma(m13, y, Math.fma(m23, z, m33)));
7838         double nx = Math.fma(m00, x, Math.fma(m10, y, Math.fma(m20, z, m30))) * invW;
7839         double ny = Math.fma(m01, x, Math.fma(m11, y, Math.fma(m21, z, m31))) * invW;
7840         double nz = Math.fma(m02, x, Math.fma(m12, y, Math.fma(m22, z, m32))) * invW;
7841         return winCoordsDest.set(Math.fma(Math.fma(nx, 0.5, 0.5), viewport[2], viewport[0]),
7842                                  Math.fma(Math.fma(ny, 0.5, 0.5), viewport[3], viewport[1]),
7843                                  Math.fma(0.5, nz, 0.5),
7844                                  1.0);
7845     }
7846 
7847     public Vector3d project(double x, double y, double z, int[] viewport, ref Vector3d winCoordsDest) {
7848         double invW = 1.0 / Math.fma(m03, x, Math.fma(m13, y, Math.fma(m23, z, m33)));
7849         double nx = Math.fma(m00, x, Math.fma(m10, y, Math.fma(m20, z, m30))) * invW;
7850         double ny = Math.fma(m01, x, Math.fma(m11, y, Math.fma(m21, z, m31))) * invW;
7851         double nz = Math.fma(m02, x, Math.fma(m12, y, Math.fma(m22, z, m32))) * invW;
7852         winCoordsDest.x = Math.fma(Math.fma(nx, 0.5, 0.5), viewport[2], viewport[0]);
7853         winCoordsDest.y = Math.fma(Math.fma(ny, 0.5, 0.5), viewport[3], viewport[1]);
7854         winCoordsDest.z = Math.fma(0.5, nz, 0.5);
7855         return winCoordsDest;
7856     }
7857 
7858     public Vector4d project(ref Vector3d position, int[] viewport, ref Vector4d dest) {
7859         return project(position.x, position.y, position.z, viewport, dest);
7860     }
7861 
7862     public Vector3d project(ref Vector3d position, int[] viewport, ref Vector3d dest) {
7863         return project(position.x, position.y, position.z, viewport, dest);
7864     }
7865 
7866     public Matrix4d reflect(double a, double b, double c, double d, ref Matrix4d dest) {
7867         if ((properties & PROPERTY_IDENTITY) != 0)
7868             return dest.reflection(a, b, c, d);
7869         if ((properties & PROPERTY_IDENTITY) != 0)
7870             return dest.reflection(a, b, c, d);
7871         else if ((properties & PROPERTY_AFFINE) != 0)
7872             return reflectAffine(a, b, c, d, dest);
7873         return reflectGeneric(a, b, c, d, dest);
7874     }
7875     private Matrix4d reflectAffine(double a, double b, double c, double d, ref Matrix4d dest) {
7876         double da = a + a, db = b + b, dc = c + c, dd = d + d;
7877         double rm00 = 1.0 - da * a;
7878         double rm01 = -da * b;
7879         double rm02 = -da * c;
7880         double rm10 = -db * a;
7881         double rm11 = 1.0 - db * b;
7882         double rm12 = -db * c;
7883         double rm20 = -dc * a;
7884         double rm21 = -dc * b;
7885         double rm22 = 1.0 - dc * c;
7886         double rm30 = -dd * a;
7887         double rm31 = -dd * b;
7888         double rm32 = -dd * c;
7889         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
7890         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
7891         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
7892         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
7893         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
7894         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
7895         // matrix multiplication
7896         dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30)
7897         ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31)
7898         ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32)
7899         ._m33(m33)
7900         ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
7901         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
7902         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
7903         ._m23(0.0)
7904         ._m00(nm00)
7905         ._m01(nm01)
7906         ._m02(nm02)
7907         ._m03(0.0)
7908         ._m10(nm10)
7909         ._m11(nm11)
7910         ._m12(nm12)
7911         ._m13(0.0)
7912         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
7913         return dest;
7914     }
7915     private Matrix4d reflectGeneric(double a, double b, double c, double d, ref Matrix4d dest) {
7916         double da = a + a, db = b + b, dc = c + c, dd = d + d;
7917         double rm00 = 1.0 - da * a;
7918         double rm01 = -da * b;
7919         double rm02 = -da * c;
7920         double rm10 = -db * a;
7921         double rm11 = 1.0 - db * b;
7922         double rm12 = -db * c;
7923         double rm20 = -dc * a;
7924         double rm21 = -dc * b;
7925         double rm22 = 1.0 - dc * c;
7926         double rm30 = -dd * a;
7927         double rm31 = -dd * b;
7928         double rm32 = -dd * c;
7929         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
7930         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
7931         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
7932         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02;
7933         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
7934         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
7935         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
7936         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12;
7937         // matrix multiplication
7938         dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30)
7939         ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31)
7940         ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32)
7941         ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33)
7942         ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
7943         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
7944         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
7945         ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22)
7946         ._m00(nm00)
7947         ._m01(nm01)
7948         ._m02(nm02)
7949         ._m03(nm03)
7950         ._m10(nm10)
7951         ._m11(nm11)
7952         ._m12(nm12)
7953         ._m13(nm13)
7954         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
7955         return dest;
7956     }
7957 
7958     /**
7959      * Apply a mirror/reflection transformation to this matrix that reflects about the given plane
7960      * specified via the equation <code>x*a + y*b + z*c + d = 0</code>.
7961      * <p>
7962      * The vector <code>(a, b, c)</code> must be a unit vector.
7963      * <p>
7964      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the reflection matrix,
7965      * then the new matrix will be <code>M * R</code>. So when transforming a
7966      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
7967      * reflection will be applied first!
7968      * <p>
7969      * Reference: <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/bb281733(v=vs.85).aspx">msdn.microsoft.com</a>
7970      * 
7971      * @param a
7972      *          the x factor in the plane equation
7973      * @param b
7974      *          the y factor in the plane equation
7975      * @param c
7976      *          the z factor in the plane equation
7977      * @param d
7978      *          the constant in the plane equation
7979      * @return this
7980      */
7981     ref public Matrix4d reflect(double a, double b, double c, double d) return {
7982         reflect(a, b, c, d, this);
7983         return this;
7984     }
7985 
7986     /**
7987      * Apply a mirror/reflection transformation to this matrix that reflects about the given plane
7988      * specified via the plane normal and a point on the plane.
7989      * <p>
7990      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the reflection matrix,
7991      * then the new matrix will be <code>M * R</code>. So when transforming a
7992      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
7993      * reflection will be applied first!
7994      * 
7995      * @param nx
7996      *          the x-coordinate of the plane normal
7997      * @param ny
7998      *          the y-coordinate of the plane normal
7999      * @param nz
8000      *          the z-coordinate of the plane normal
8001      * @param px
8002      *          the x-coordinate of a point on the plane
8003      * @param py
8004      *          the y-coordinate of a point on the plane
8005      * @param pz
8006      *          the z-coordinate of a point on the plane
8007      * @return this
8008      */
8009     ref public Matrix4d reflect(double nx, double ny, double nz, double px, double py, double pz) return {
8010         reflect(nx, ny, nz, px, py, pz, this);
8011         return this;
8012     }
8013 
8014     public Matrix4d reflect(double nx, double ny, double nz, double px, double py, double pz, ref Matrix4d dest) {
8015         double invLength = Math.invsqrt(nx * nx + ny * ny + nz * nz);
8016         double nnx = nx * invLength;
8017         double nny = ny * invLength;
8018         double nnz = nz * invLength;
8019         /* See: http://mathworld.wolfram.com/Plane.html */
8020         return reflect(nnx, nny, nnz, -nnx * px - nny * py - nnz * pz, dest);
8021     }
8022 
8023     /**
8024      * Apply a mirror/reflection transformation to this matrix that reflects about the given plane
8025      * specified via the plane normal and a point on the plane.
8026      * <p>
8027      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the reflection matrix,
8028      * then the new matrix will be <code>M * R</code>. So when transforming a
8029      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
8030      * reflection will be applied first!
8031      * 
8032      * @param normal
8033      *          the plane normal
8034      * @param point
8035      *          a point on the plane
8036      * @return this
8037      */
8038     ref public Matrix4d reflect(ref Vector3d normal, Vector3d point) return {
8039         return reflect(normal.x, normal.y, normal.z, point.x, point.y, point.z);
8040     }
8041 
8042     /**
8043      * Apply a mirror/reflection transformation to this matrix that reflects about a plane
8044      * specified via the plane orientation and a point on the plane.
8045      * <p>
8046      * This method can be used to build a reflection transformation based on the orientation of a mirror object in the scene.
8047      * It is assumed that the default mirror plane's normal is <code>(0, 0, 1)</code>. So, if the given {@link Quaterniond} is
8048      * the identity (does not apply any additional rotation), the reflection plane will be <code>z=0</code>, offset by the given <code>point</code>.
8049      * <p>
8050      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the reflection matrix,
8051      * then the new matrix will be <code>M * R</code>. So when transforming a
8052      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
8053      * reflection will be applied first!
8054      * 
8055      * @param orientation
8056      *          the plane orientation relative to an implied normal vector of <code>(0, 0, 1)</code>
8057      * @param point
8058      *          a point on the plane
8059      * @return this
8060      */
8061     ref public Matrix4d reflect(ref Quaterniond orientation, Vector3d point) return {
8062         reflect(orientation, point, this);
8063         return this;
8064     }
8065 
8066     public Matrix4d reflect(ref Quaterniond orientation, Vector3d point, ref Matrix4d dest) {
8067         double num1 = orientation.x + orientation.x;
8068         double num2 = orientation.y + orientation.y;
8069         double num3 = orientation.z + orientation.z;
8070         double normalX = orientation.x * num3 + orientation.w * num2;
8071         double normalY = orientation.y * num3 - orientation.w * num1;
8072         double normalZ = 1.0 - (orientation.x * num1 + orientation.y * num2);
8073         return reflect(normalX, normalY, normalZ, point.x, point.y, point.z, dest);
8074     }
8075 
8076     public Matrix4d reflect(ref Vector3d normal, Vector3d point, ref Matrix4d dest) {
8077         return reflect(normal.x, normal.y, normal.z, point.x, point.y, point.z, dest);
8078     }
8079 
8080     /**
8081      * Set this matrix to a mirror/reflection transformation that reflects about the given plane
8082      * specified via the equation <code>x*a + y*b + z*c + d = 0</code>.
8083      * <p>
8084      * The vector <code>(a, b, c)</code> must be a unit vector.
8085      * <p>
8086      * Reference: <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/bb281733(v=vs.85).aspx">msdn.microsoft.com</a>
8087      * 
8088      * @param a
8089      *          the x factor in the plane equation
8090      * @param b
8091      *          the y factor in the plane equation
8092      * @param c
8093      *          the z factor in the plane equation
8094      * @param d
8095      *          the constant in the plane equation
8096      * @return this
8097      */
8098     ref public Matrix4d reflection(double a, double b, double c, double d) return {
8099         double da = a + a, db = b + b, dc = c + c, dd = d + d;
8100         _m00(1.0 - da * a).
8101         _m01(-da * b).
8102         _m02(-da * c).
8103         _m03(0.0).
8104         _m10(-db * a).
8105         _m11(1.0 - db * b).
8106         _m12(-db * c).
8107         _m13(0.0).
8108         _m20(-dc * a).
8109         _m21(-dc * b).
8110         _m22(1.0 - dc * c).
8111         _m23(0.0).
8112         _m30(-dd * a).
8113         _m31(-dd * b).
8114         _m32(-dd * c).
8115         _m33(1.0).
8116         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
8117         return this;
8118     }
8119 
8120     /**
8121      * Set this matrix to a mirror/reflection transformation that reflects about the given plane
8122      * specified via the plane normal and a point on the plane.
8123      * 
8124      * @param nx
8125      *          the x-coordinate of the plane normal
8126      * @param ny
8127      *          the y-coordinate of the plane normal
8128      * @param nz
8129      *          the z-coordinate of the plane normal
8130      * @param px
8131      *          the x-coordinate of a point on the plane
8132      * @param py
8133      *          the y-coordinate of a point on the plane
8134      * @param pz
8135      *          the z-coordinate of a point on the plane
8136      * @return this
8137      */
8138     ref public Matrix4d reflection(double nx, double ny, double nz, double px, double py, double pz) return {
8139         double invLength = Math.invsqrt(nx * nx + ny * ny + nz * nz);
8140         double nnx = nx * invLength;
8141         double nny = ny * invLength;
8142         double nnz = nz * invLength;
8143         /* See: http://mathworld.wolfram.com/Plane.html */
8144         return reflection(nnx, nny, nnz, -nnx * px - nny * py - nnz * pz);
8145     }
8146 
8147     /**
8148      * Set this matrix to a mirror/reflection transformation that reflects about the given plane
8149      * specified via the plane normal and a point on the plane.
8150      * 
8151      * @param normal
8152      *          the plane normal
8153      * @param point
8154      *          a point on the plane
8155      * @return this
8156      */
8157     ref public Matrix4d reflection(ref Vector3d normal, Vector3d point) return {
8158         return reflection(normal.x, normal.y, normal.z, point.x, point.y, point.z);
8159     }
8160 
8161     /**
8162      * Set this matrix to a mirror/reflection transformation that reflects about a plane
8163      * specified via the plane orientation and a point on the plane.
8164      * <p>
8165      * This method can be used to build a reflection transformation based on the orientation of a mirror object in the scene.
8166      * It is assumed that the default mirror plane's normal is <code>(0, 0, 1)</code>. So, if the given {@link Quaterniond} is
8167      * the identity (does not apply any additional rotation), the reflection plane will be <code>z=0</code>, offset by the given <code>point</code>.
8168      * 
8169      * @param orientation
8170      *          the plane orientation
8171      * @param point
8172      *          a point on the plane
8173      * @return this
8174      */
8175     ref public Matrix4d reflection(ref Quaterniond orientation, Vector3d point) return {
8176         double num1 = orientation.x + orientation.x;
8177         double num2 = orientation.y + orientation.y;
8178         double num3 = orientation.z + orientation.z;
8179         double normalX = orientation.x * num3 + orientation.w * num2;
8180         double normalY = orientation.y * num3 - orientation.w * num1;
8181         double normalZ = 1.0 - (orientation.x * num1 + orientation.y * num2);
8182         return reflection(normalX, normalY, normalZ, point.x, point.y, point.z);
8183     }
8184 
8185     /**
8186      * Apply an orthographic projection transformation for a right-handed coordinate system
8187      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
8188      * <p>
8189      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8190      * then the new matrix will be <code>M * O</code>. So when transforming a
8191      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8192      * orthographic projection transformation will be applied first!
8193      * <p>
8194      * In order to set the matrix to an orthographic projection without post-multiplying it,
8195      * use {@link #setOrtho(double, double, double, double, double, double, bool) setOrtho()}.
8196      * <p>
8197      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8198      * 
8199      * @see #setOrtho(double, double, double, double, double, double, bool)
8200      * 
8201      * @param left
8202      *            the distance from the center to the left frustum edge
8203      * @param right
8204      *            the distance from the center to the right frustum edge
8205      * @param bottom
8206      *            the distance from the center to the bottom frustum edge
8207      * @param top
8208      *            the distance from the center to the top frustum edge
8209      * @param zNear
8210      *            near clipping plane distance
8211      * @param zFar
8212      *            far clipping plane distance
8213      * @param zZeroToOne
8214      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8215      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8216      * @param dest
8217      *            will hold the result
8218      * @return dest
8219      */
8220     public Matrix4d ortho(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
8221         if ((properties & PROPERTY_IDENTITY) != 0)
8222             return dest.setOrtho(left, right, bottom, top, zNear, zFar, zZeroToOne);
8223         return orthoGeneric(left, right, bottom, top, zNear, zFar, zZeroToOne, dest);
8224     }
8225     private Matrix4d orthoGeneric(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
8226         // calculate right matrix elements
8227         double rm00 = 2.0 / (right - left);
8228         double rm11 = 2.0 / (top - bottom);
8229         double rm22 = (zZeroToOne ? 1.0 : 2.0) / (zNear - zFar);
8230         double rm30 = (left + right) / (left - right);
8231         double rm31 = (top + bottom) / (bottom - top);
8232         double rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar);
8233         // perform optimized multiplication
8234         // compute the last column first, because other columns do not depend on it
8235         dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30)
8236         ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31)
8237         ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32)
8238         ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33)
8239         ._m00(m00 * rm00)
8240         ._m01(m01 * rm00)
8241         ._m02(m02 * rm00)
8242         ._m03(m03 * rm00)
8243         ._m10(m10 * rm11)
8244         ._m11(m11 * rm11)
8245         ._m12(m12 * rm11)
8246         ._m13(m13 * rm11)
8247         ._m20(m20 * rm22)
8248         ._m21(m21 * rm22)
8249         ._m22(m22 * rm22)
8250         ._m23(m23 * rm22)
8251         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
8252         return dest;
8253     }
8254 
8255     /**
8256      * Apply an orthographic projection transformation for a right-handed coordinate system
8257      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
8258      * <p>
8259      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8260      * then the new matrix will be <code>M * O</code>. So when transforming a
8261      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8262      * orthographic projection transformation will be applied first!
8263      * <p>
8264      * In order to set the matrix to an orthographic projection without post-multiplying it,
8265      * use {@link #setOrtho(double, double, double, double, double, double) setOrtho()}.
8266      * <p>
8267      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8268      * 
8269      * @see #setOrtho(double, double, double, double, double, double)
8270      * 
8271      * @param left
8272      *            the distance from the center to the left frustum edge
8273      * @param right
8274      *            the distance from the center to the right frustum edge
8275      * @param bottom
8276      *            the distance from the center to the bottom frustum edge
8277      * @param top
8278      *            the distance from the center to the top frustum edge
8279      * @param zNear
8280      *            near clipping plane distance
8281      * @param zFar
8282      *            far clipping plane distance
8283      * @param dest
8284      *            will hold the result
8285      * @return dest
8286      */
8287     public Matrix4d ortho(double left, double right, double bottom, double top, double zNear, double zFar, ref Matrix4d dest) {
8288         return ortho(left, right, bottom, top, zNear, zFar, false, dest);
8289     }
8290 
8291     /**
8292      * Apply an orthographic projection transformation for a right-handed coordinate system
8293      * using the given NDC z range to this matrix.
8294      * <p>
8295      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8296      * then the new matrix will be <code>M * O</code>. So when transforming a
8297      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8298      * orthographic projection transformation will be applied first!
8299      * <p>
8300      * In order to set the matrix to an orthographic projection without post-multiplying it,
8301      * use {@link #setOrtho(double, double, double, double, double, double, bool) setOrtho()}.
8302      * <p>
8303      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8304      * 
8305      * @see #setOrtho(double, double, double, double, double, double, bool)
8306      * 
8307      * @param left
8308      *            the distance from the center to the left frustum edge
8309      * @param right
8310      *            the distance from the center to the right frustum edge
8311      * @param bottom
8312      *            the distance from the center to the bottom frustum edge
8313      * @param top
8314      *            the distance from the center to the top frustum edge
8315      * @param zNear
8316      *            near clipping plane distance
8317      * @param zFar
8318      *            far clipping plane distance
8319      * @param zZeroToOne
8320      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8321      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8322      * @return this
8323      */
8324     ref public Matrix4d ortho(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne) return {
8325         ortho(left, right, bottom, top, zNear, zFar, zZeroToOne, this);
8326         return this;
8327     }
8328 
8329     /**
8330      * Apply an orthographic projection transformation for a right-handed coordinate system
8331      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
8332      * <p>
8333      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8334      * then the new matrix will be <code>M * O</code>. So when transforming a
8335      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8336      * orthographic projection transformation will be applied first!
8337      * <p>
8338      * In order to set the matrix to an orthographic projection without post-multiplying it,
8339      * use {@link #setOrtho(double, double, double, double, double, double) setOrtho()}.
8340      * <p>
8341      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8342      * 
8343      * @see #setOrtho(double, double, double, double, double, double)
8344      * 
8345      * @param left
8346      *            the distance from the center to the left frustum edge
8347      * @param right
8348      *            the distance from the center to the right frustum edge
8349      * @param bottom
8350      *            the distance from the center to the bottom frustum edge
8351      * @param top
8352      *            the distance from the center to the top frustum edge
8353      * @param zNear
8354      *            near clipping plane distance
8355      * @param zFar
8356      *            far clipping plane distance
8357      * @return this
8358      */
8359     ref public Matrix4d ortho(double left, double right, double bottom, double top, double zNear, double zFar) return {
8360         return ortho(left, right, bottom, top, zNear, zFar, false);
8361     }
8362 
8363     /**
8364      * Apply an orthographic projection transformation for a left-handed coordiante system
8365      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
8366      * <p>
8367      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8368      * then the new matrix will be <code>M * O</code>. So when transforming a
8369      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8370      * orthographic projection transformation will be applied first!
8371      * <p>
8372      * In order to set the matrix to an orthographic projection without post-multiplying it,
8373      * use {@link #setOrthoLH(double, double, double, double, double, double, bool) setOrthoLH()}.
8374      * <p>
8375      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8376      * 
8377      * @see #setOrthoLH(double, double, double, double, double, double, bool)
8378      * 
8379      * @param left
8380      *            the distance from the center to the left frustum edge
8381      * @param right
8382      *            the distance from the center to the right frustum edge
8383      * @param bottom
8384      *            the distance from the center to the bottom frustum edge
8385      * @param top
8386      *            the distance from the center to the top frustum edge
8387      * @param zNear
8388      *            near clipping plane distance
8389      * @param zFar
8390      *            far clipping plane distance
8391      * @param zZeroToOne
8392      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8393      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8394      * @param dest
8395      *            will hold the result
8396      * @return dest
8397      */
8398     public Matrix4d orthoLH(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
8399         if ((properties & PROPERTY_IDENTITY) != 0)
8400             return dest.setOrthoLH(left, right, bottom, top, zNear, zFar, zZeroToOne);
8401         return orthoLHGeneric(left, right, bottom, top, zNear, zFar, zZeroToOne, dest);
8402     }
8403     private Matrix4d orthoLHGeneric(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
8404         // calculate right matrix elements
8405         double rm00 = 2.0 / (right - left);
8406         double rm11 = 2.0 / (top - bottom);
8407         double rm22 = (zZeroToOne ? 1.0 : 2.0) / (zFar - zNear);
8408         double rm30 = (left + right) / (left - right);
8409         double rm31 = (top + bottom) / (bottom - top);
8410         double rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar);
8411         // perform optimized multiplication
8412         // compute the last column first, because other columns do not depend on it
8413         dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30)
8414         ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31)
8415         ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32)
8416         ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33)
8417         ._m00(m00 * rm00)
8418         ._m01(m01 * rm00)
8419         ._m02(m02 * rm00)
8420         ._m03(m03 * rm00)
8421         ._m10(m10 * rm11)
8422         ._m11(m11 * rm11)
8423         ._m12(m12 * rm11)
8424         ._m13(m13 * rm11)
8425         ._m20(m20 * rm22)
8426         ._m21(m21 * rm22)
8427         ._m22(m22 * rm22)
8428         ._m23(m23 * rm22)
8429         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
8430         return dest;
8431     }
8432 
8433     /**
8434      * Apply an orthographic projection transformation for a left-handed coordiante system
8435      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
8436      * <p>
8437      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8438      * then the new matrix will be <code>M * O</code>. So when transforming a
8439      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8440      * orthographic projection transformation will be applied first!
8441      * <p>
8442      * In order to set the matrix to an orthographic projection without post-multiplying it,
8443      * use {@link #setOrthoLH(double, double, double, double, double, double) setOrthoLH()}.
8444      * <p>
8445      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8446      * 
8447      * @see #setOrthoLH(double, double, double, double, double, double)
8448      * 
8449      * @param left
8450      *            the distance from the center to the left frustum edge
8451      * @param right
8452      *            the distance from the center to the right frustum edge
8453      * @param bottom
8454      *            the distance from the center to the bottom frustum edge
8455      * @param top
8456      *            the distance from the center to the top frustum edge
8457      * @param zNear
8458      *            near clipping plane distance
8459      * @param zFar
8460      *            far clipping plane distance
8461      * @param dest
8462      *            will hold the result
8463      * @return dest
8464      */
8465     public Matrix4d orthoLH(double left, double right, double bottom, double top, double zNear, double zFar, ref Matrix4d dest) {
8466         return orthoLH(left, right, bottom, top, zNear, zFar, false, dest);
8467     }
8468 
8469     /**
8470      * Apply an orthographic projection transformation for a left-handed coordiante system
8471      * using the given NDC z range to this matrix.
8472      * <p>
8473      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8474      * then the new matrix will be <code>M * O</code>. So when transforming a
8475      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8476      * orthographic projection transformation will be applied first!
8477      * <p>
8478      * In order to set the matrix to an orthographic projection without post-multiplying it,
8479      * use {@link #setOrthoLH(double, double, double, double, double, double, bool) setOrthoLH()}.
8480      * <p>
8481      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8482      * 
8483      * @see #setOrthoLH(double, double, double, double, double, double, bool)
8484      * 
8485      * @param left
8486      *            the distance from the center to the left frustum edge
8487      * @param right
8488      *            the distance from the center to the right frustum edge
8489      * @param bottom
8490      *            the distance from the center to the bottom frustum edge
8491      * @param top
8492      *            the distance from the center to the top frustum edge
8493      * @param zNear
8494      *            near clipping plane distance
8495      * @param zFar
8496      *            far clipping plane distance
8497      * @param zZeroToOne
8498      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8499      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8500      * @return this
8501      */
8502     ref public Matrix4d orthoLH(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne) return {
8503         orthoLH(left, right, bottom, top, zNear, zFar, zZeroToOne, this);
8504         return this;
8505     }
8506 
8507     /**
8508      * Apply an orthographic projection transformation for a left-handed coordiante system
8509      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
8510      * <p>
8511      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8512      * then the new matrix will be <code>M * O</code>. So when transforming a
8513      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8514      * orthographic projection transformation will be applied first!
8515      * <p>
8516      * In order to set the matrix to an orthographic projection without post-multiplying it,
8517      * use {@link #setOrthoLH(double, double, double, double, double, double) setOrthoLH()}.
8518      * <p>
8519      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8520      * 
8521      * @see #setOrthoLH(double, double, double, double, double, double)
8522      * 
8523      * @param left
8524      *            the distance from the center to the left frustum edge
8525      * @param right
8526      *            the distance from the center to the right frustum edge
8527      * @param bottom
8528      *            the distance from the center to the bottom frustum edge
8529      * @param top
8530      *            the distance from the center to the top frustum edge
8531      * @param zNear
8532      *            near clipping plane distance
8533      * @param zFar
8534      *            far clipping plane distance
8535      * @return this
8536      */
8537     ref public Matrix4d orthoLH(double left, double right, double bottom, double top, double zNear, double zFar) return {
8538         return orthoLH(left, right, bottom, top, zNear, zFar, false);
8539     }
8540 
8541     /**
8542      * Set this matrix to be an orthographic projection transformation for a right-handed coordinate system
8543      * using the given NDC z range.
8544      * <p>
8545      * In order to apply the orthographic projection to an already existing transformation,
8546      * use {@link #ortho(double, double, double, double, double, double, bool) ortho()}.
8547      * <p>
8548      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8549      * 
8550      * @see #ortho(double, double, double, double, double, double, bool)
8551      * 
8552      * @param left
8553      *            the distance from the center to the left frustum edge
8554      * @param right
8555      *            the distance from the center to the right frustum edge
8556      * @param bottom
8557      *            the distance from the center to the bottom frustum edge
8558      * @param top
8559      *            the distance from the center to the top frustum edge
8560      * @param zNear
8561      *            near clipping plane distance
8562      * @param zFar
8563      *            far clipping plane distance
8564      * @param zZeroToOne
8565      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8566      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8567      * @return this
8568      */
8569     ref public Matrix4d setOrtho(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne) return {
8570         if ((properties & PROPERTY_IDENTITY) == 0)
8571             _identity();
8572         _m00(2.0 / (right - left)).
8573         _m11(2.0 / (top - bottom)).
8574         _m22((zZeroToOne ? 1.0 : 2.0) / (zNear - zFar)).
8575         _m30((right + left) / (left - right)).
8576         _m31((top + bottom) / (bottom - top)).
8577         _m32((zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar)).
8578         properties = PROPERTY_AFFINE;
8579         return this;
8580     }
8581 
8582     /**
8583      * Set this matrix to be an orthographic projection transformation for a right-handed coordinate system
8584      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
8585      * <p>
8586      * In order to apply the orthographic projection to an already existing transformation,
8587      * use {@link #ortho(double, double, double, double, double, double) ortho()}.
8588      * <p>
8589      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8590      * 
8591      * @see #ortho(double, double, double, double, double, double)
8592      * 
8593      * @param left
8594      *            the distance from the center to the left frustum edge
8595      * @param right
8596      *            the distance from the center to the right frustum edge
8597      * @param bottom
8598      *            the distance from the center to the bottom frustum edge
8599      * @param top
8600      *            the distance from the center to the top frustum edge
8601      * @param zNear
8602      *            near clipping plane distance
8603      * @param zFar
8604      *            far clipping plane distance
8605      * @return this
8606      */
8607     ref public Matrix4d setOrtho(double left, double right, double bottom, double top, double zNear, double zFar) return {
8608         return setOrtho(left, right, bottom, top, zNear, zFar, false);
8609     }
8610 
8611     /**
8612      * Set this matrix to be an orthographic projection transformation for a left-handed coordinate system
8613      * using the given NDC z range.
8614      * <p>
8615      * In order to apply the orthographic projection to an already existing transformation,
8616      * use {@link #orthoLH(double, double, double, double, double, double, bool) orthoLH()}.
8617      * <p>
8618      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8619      * 
8620      * @see #orthoLH(double, double, double, double, double, double, bool)
8621      * 
8622      * @param left
8623      *            the distance from the center to the left frustum edge
8624      * @param right
8625      *            the distance from the center to the right frustum edge
8626      * @param bottom
8627      *            the distance from the center to the bottom frustum edge
8628      * @param top
8629      *            the distance from the center to the top frustum edge
8630      * @param zNear
8631      *            near clipping plane distance
8632      * @param zFar
8633      *            far clipping plane distance
8634      * @param zZeroToOne
8635      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8636      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8637      * @return this
8638      */
8639     ref public Matrix4d setOrthoLH(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne) return {
8640         if ((properties & PROPERTY_IDENTITY) == 0)
8641             _identity();
8642         _m00(2.0 / (right - left)).
8643         _m11(2.0 / (top - bottom)).
8644         _m22((zZeroToOne ? 1.0 : 2.0) / (zFar - zNear)).
8645         _m30((right + left) / (left - right)).
8646         _m31((top + bottom) / (bottom - top)).
8647         _m32((zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar)).
8648         properties = PROPERTY_AFFINE;
8649         return this;
8650     }
8651 
8652     /**
8653      * Set this matrix to be an orthographic projection transformation for a left-handed coordinate system
8654      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
8655      * <p>
8656      * In order to apply the orthographic projection to an already existing transformation,
8657      * use {@link #orthoLH(double, double, double, double, double, double) orthoLH()}.
8658      * <p>
8659      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8660      * 
8661      * @see #orthoLH(double, double, double, double, double, double)
8662      * 
8663      * @param left
8664      *            the distance from the center to the left frustum edge
8665      * @param right
8666      *            the distance from the center to the right frustum edge
8667      * @param bottom
8668      *            the distance from the center to the bottom frustum edge
8669      * @param top
8670      *            the distance from the center to the top frustum edge
8671      * @param zNear
8672      *            near clipping plane distance
8673      * @param zFar
8674      *            far clipping plane distance
8675      * @return this
8676      */
8677     ref public Matrix4d setOrthoLH(double left, double right, double bottom, double top, double zNear, double zFar) return {
8678         return setOrthoLH(left, right, bottom, top, zNear, zFar, false);
8679     }
8680 
8681     /**
8682      * Apply a symmetric orthographic projection transformation for a right-handed coordinate system
8683      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
8684      * <p>
8685      * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double, bool, Matrix4d) ortho()} with
8686      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
8687      * <p>
8688      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8689      * then the new matrix will be <code>M * O</code>. So when transforming a
8690      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8691      * orthographic projection transformation will be applied first!
8692      * <p>
8693      * In order to set the matrix to a symmetric orthographic projection without post-multiplying it,
8694      * use {@link #setOrthoSymmetric(double, double, double, double, bool) setOrthoSymmetric()}.
8695      * <p>
8696      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8697      * 
8698      * @see #setOrthoSymmetric(double, double, double, double, bool)
8699      * 
8700      * @param width
8701      *            the distance between the right and left frustum edges
8702      * @param height
8703      *            the distance between the top and bottom frustum edges
8704      * @param zNear
8705      *            near clipping plane distance
8706      * @param zFar
8707      *            far clipping plane distance
8708      * @param dest
8709      *            will hold the result
8710      * @param zZeroToOne
8711      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8712      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8713      * @return dest
8714      */
8715     public Matrix4d orthoSymmetric(double width, double height, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
8716         if ((properties & PROPERTY_IDENTITY) != 0)
8717             return dest.setOrthoSymmetric(width, height, zNear, zFar, zZeroToOne);
8718         return orthoSymmetricGeneric(width, height, zNear, zFar, zZeroToOne, dest);
8719     }
8720     private Matrix4d orthoSymmetricGeneric(double width, double height, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
8721         // calculate right matrix elements
8722         double rm00 = 2.0 / width;
8723         double rm11 = 2.0 / height;
8724         double rm22 = (zZeroToOne ? 1.0 : 2.0) / (zNear - zFar);
8725         double rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar);
8726         // perform optimized multiplication
8727         // compute the last column first, because other columns do not depend on it
8728         dest._m30(m20 * rm32 + m30)
8729         ._m31(m21 * rm32 + m31)
8730         ._m32(m22 * rm32 + m32)
8731         ._m33(m23 * rm32 + m33)
8732         ._m00(m00 * rm00)
8733         ._m01(m01 * rm00)
8734         ._m02(m02 * rm00)
8735         ._m03(m03 * rm00)
8736         ._m10(m10 * rm11)
8737         ._m11(m11 * rm11)
8738         ._m12(m12 * rm11)
8739         ._m13(m13 * rm11)
8740         ._m20(m20 * rm22)
8741         ._m21(m21 * rm22)
8742         ._m22(m22 * rm22)
8743         ._m23(m23 * rm22)
8744         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
8745         return dest;
8746     }
8747 
8748     /**
8749      * Apply a symmetric orthographic projection transformation for a right-handed coordinate system
8750      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
8751      * <p>
8752      * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double, Matrix4d) ortho()} with
8753      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
8754      * <p>
8755      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8756      * then the new matrix will be <code>M * O</code>. So when transforming a
8757      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8758      * orthographic projection transformation will be applied first!
8759      * <p>
8760      * In order to set the matrix to a symmetric orthographic projection without post-multiplying it,
8761      * use {@link #setOrthoSymmetric(double, double, double, double) setOrthoSymmetric()}.
8762      * <p>
8763      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8764      * 
8765      * @see #setOrthoSymmetric(double, double, double, double)
8766      * 
8767      * @param width
8768      *            the distance between the right and left frustum edges
8769      * @param height
8770      *            the distance between the top and bottom frustum edges
8771      * @param zNear
8772      *            near clipping plane distance
8773      * @param zFar
8774      *            far clipping plane distance
8775      * @param dest
8776      *            will hold the result
8777      * @return dest
8778      */
8779     public Matrix4d orthoSymmetric(double width, double height, double zNear, double zFar, ref Matrix4d dest) {
8780         return orthoSymmetric(width, height, zNear, zFar, false, dest);
8781     }
8782 
8783     /**
8784      * Apply a symmetric orthographic projection transformation for a right-handed coordinate system
8785      * using the given NDC z range to this matrix.
8786      * <p>
8787      * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double, bool) ortho()} with
8788      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
8789      * <p>
8790      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8791      * then the new matrix will be <code>M * O</code>. So when transforming a
8792      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8793      * orthographic projection transformation will be applied first!
8794      * <p>
8795      * In order to set the matrix to a symmetric orthographic projection without post-multiplying it,
8796      * use {@link #setOrthoSymmetric(double, double, double, double, bool) setOrthoSymmetric()}.
8797      * <p>
8798      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8799      * 
8800      * @see #setOrthoSymmetric(double, double, double, double, bool)
8801      * 
8802      * @param width
8803      *            the distance between the right and left frustum edges
8804      * @param height
8805      *            the distance between the top and bottom frustum edges
8806      * @param zNear
8807      *            near clipping plane distance
8808      * @param zFar
8809      *            far clipping plane distance
8810      * @param zZeroToOne
8811      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8812      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8813      * @return this
8814      */
8815     ref public Matrix4d orthoSymmetric(double width, double height, double zNear, double zFar, bool zZeroToOne) return {
8816         orthoSymmetric(width, height, zNear, zFar, zZeroToOne, this);
8817         return this;
8818     }
8819 
8820     /**
8821      * Apply a symmetric orthographic projection transformation for a right-handed coordinate system
8822      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
8823      * <p>
8824      * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double) ortho()} with
8825      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
8826      * <p>
8827      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8828      * then the new matrix will be <code>M * O</code>. So when transforming a
8829      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8830      * orthographic projection transformation will be applied first!
8831      * <p>
8832      * In order to set the matrix to a symmetric orthographic projection without post-multiplying it,
8833      * use {@link #setOrthoSymmetric(double, double, double, double) setOrthoSymmetric()}.
8834      * <p>
8835      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8836      * 
8837      * @see #setOrthoSymmetric(double, double, double, double)
8838      * 
8839      * @param width
8840      *            the distance between the right and left frustum edges
8841      * @param height
8842      *            the distance between the top and bottom frustum edges
8843      * @param zNear
8844      *            near clipping plane distance
8845      * @param zFar
8846      *            far clipping plane distance
8847      * @return this
8848      */
8849     ref public Matrix4d orthoSymmetric(double width, double height, double zNear, double zFar) return {
8850         orthoSymmetric(width, height, zNear, zFar, false, this);
8851         return this;
8852     }
8853 
8854     /**
8855      * Apply a symmetric orthographic projection transformation for a left-handed coordinate system
8856      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
8857      * <p>
8858      * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double, bool, Matrix4d) orthoLH()} with
8859      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
8860      * <p>
8861      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8862      * then the new matrix will be <code>M * O</code>. So when transforming a
8863      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8864      * orthographic projection transformation will be applied first!
8865      * <p>
8866      * In order to set the matrix to a symmetric orthographic projection without post-multiplying it,
8867      * use {@link #setOrthoSymmetricLH(double, double, double, double, bool) setOrthoSymmetricLH()}.
8868      * <p>
8869      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8870      * 
8871      * @see #setOrthoSymmetricLH(double, double, double, double, bool)
8872      * 
8873      * @param width
8874      *            the distance between the right and left frustum edges
8875      * @param height
8876      *            the distance between the top and bottom frustum edges
8877      * @param zNear
8878      *            near clipping plane distance
8879      * @param zFar
8880      *            far clipping plane distance
8881      * @param dest
8882      *            will hold the result
8883      * @param zZeroToOne
8884      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8885      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8886      * @return dest
8887      */
8888     public Matrix4d orthoSymmetricLH(double width, double height, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
8889         if ((properties & PROPERTY_IDENTITY) != 0)
8890             return dest.setOrthoSymmetricLH(width, height, zNear, zFar, zZeroToOne);
8891         return orthoSymmetricLHGeneric(width, height, zNear, zFar, zZeroToOne, dest);
8892     }
8893     private Matrix4d orthoSymmetricLHGeneric(double width, double height, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
8894         // calculate right matrix elements
8895         double rm00 = 2.0 / width;
8896         double rm11 = 2.0 / height;
8897         double rm22 = (zZeroToOne ? 1.0 : 2.0) / (zFar - zNear);
8898         double rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar);
8899         // perform optimized multiplication
8900         // compute the last column first, because other columns do not depend on it
8901         dest._m30(m20 * rm32 + m30)
8902         ._m31(m21 * rm32 + m31)
8903         ._m32(m22 * rm32 + m32)
8904         ._m33(m23 * rm32 + m33)
8905         ._m00(m00 * rm00)
8906         ._m01(m01 * rm00)
8907         ._m02(m02 * rm00)
8908         ._m03(m03 * rm00)
8909         ._m10(m10 * rm11)
8910         ._m11(m11 * rm11)
8911         ._m12(m12 * rm11)
8912         ._m13(m13 * rm11)
8913         ._m20(m20 * rm22)
8914         ._m21(m21 * rm22)
8915         ._m22(m22 * rm22)
8916         ._m23(m23 * rm22)
8917         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
8918         return dest;
8919     }
8920 
8921     /**
8922      * Apply a symmetric orthographic projection transformation for a left-handed coordinate system
8923      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
8924      * <p>
8925      * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double, Matrix4d) orthoLH()} with
8926      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
8927      * <p>
8928      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8929      * then the new matrix will be <code>M * O</code>. So when transforming a
8930      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8931      * orthographic projection transformation will be applied first!
8932      * <p>
8933      * In order to set the matrix to a symmetric orthographic projection without post-multiplying it,
8934      * use {@link #setOrthoSymmetricLH(double, double, double, double) setOrthoSymmetricLH()}.
8935      * <p>
8936      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8937      * 
8938      * @see #setOrthoSymmetricLH(double, double, double, double)
8939      * 
8940      * @param width
8941      *            the distance between the right and left frustum edges
8942      * @param height
8943      *            the distance between the top and bottom frustum edges
8944      * @param zNear
8945      *            near clipping plane distance
8946      * @param zFar
8947      *            far clipping plane distance
8948      * @param dest
8949      *            will hold the result
8950      * @return dest
8951      */
8952     public Matrix4d orthoSymmetricLH(double width, double height, double zNear, double zFar, ref Matrix4d dest) {
8953         return orthoSymmetricLH(width, height, zNear, zFar, false, dest);
8954     }
8955 
8956     /**
8957      * Apply a symmetric orthographic projection transformation for a left-handed coordinate system
8958      * using the given NDC z range to this matrix.
8959      * <p>
8960      * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double, bool) orthoLH()} with
8961      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
8962      * <p>
8963      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8964      * then the new matrix will be <code>M * O</code>. So when transforming a
8965      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8966      * orthographic projection transformation will be applied first!
8967      * <p>
8968      * In order to set the matrix to a symmetric orthographic projection without post-multiplying it,
8969      * use {@link #setOrthoSymmetricLH(double, double, double, double, bool) setOrthoSymmetricLH()}.
8970      * <p>
8971      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8972      * 
8973      * @see #setOrthoSymmetricLH(double, double, double, double, bool)
8974      * 
8975      * @param width
8976      *            the distance between the right and left frustum edges
8977      * @param height
8978      *            the distance between the top and bottom frustum edges
8979      * @param zNear
8980      *            near clipping plane distance
8981      * @param zFar
8982      *            far clipping plane distance
8983      * @param zZeroToOne
8984      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8985      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8986      * @return this
8987      */
8988     ref public Matrix4d orthoSymmetricLH(double width, double height, double zNear, double zFar, bool zZeroToOne) return {
8989         orthoSymmetricLH(width, height, zNear, zFar, zZeroToOne, this);
8990         return this;
8991     }
8992 
8993     /**
8994      * Apply a symmetric orthographic projection transformation for a left-handed coordinate system
8995      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
8996      * <p>
8997      * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double) orthoLH()} with
8998      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
8999      * <p>
9000      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
9001      * then the new matrix will be <code>M * O</code>. So when transforming a
9002      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
9003      * orthographic projection transformation will be applied first!
9004      * <p>
9005      * In order to set the matrix to a symmetric orthographic projection without post-multiplying it,
9006      * use {@link #setOrthoSymmetricLH(double, double, double, double) setOrthoSymmetricLH()}.
9007      * <p>
9008      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9009      * 
9010      * @see #setOrthoSymmetricLH(double, double, double, double)
9011      * 
9012      * @param width
9013      *            the distance between the right and left frustum edges
9014      * @param height
9015      *            the distance between the top and bottom frustum edges
9016      * @param zNear
9017      *            near clipping plane distance
9018      * @param zFar
9019      *            far clipping plane distance
9020      * @return this
9021      */
9022     ref public Matrix4d orthoSymmetricLH(double width, double height, double zNear, double zFar) return {
9023         orthoSymmetricLH(width, height, zNear, zFar, false, this);
9024         return this;
9025     }
9026 
9027     /**
9028      * Set this matrix to be a symmetric orthographic projection transformation for a right-handed coordinate system
9029      * using the given NDC z range.
9030      * <p>
9031      * This method is equivalent to calling {@link #setOrtho(double, double, double, double, double, double, bool) setOrtho()} with
9032      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
9033      * <p>
9034      * In order to apply the symmetric orthographic projection to an already existing transformation,
9035      * use {@link #orthoSymmetric(double, double, double, double, bool) orthoSymmetric()}.
9036      * <p>
9037      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9038      * 
9039      * @see #orthoSymmetric(double, double, double, double, bool)
9040      * 
9041      * @param width
9042      *            the distance between the right and left frustum edges
9043      * @param height
9044      *            the distance between the top and bottom frustum edges
9045      * @param zNear
9046      *            near clipping plane distance
9047      * @param zFar
9048      *            far clipping plane distance
9049      * @param zZeroToOne
9050      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
9051      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
9052      * @return this
9053      */
9054     ref public Matrix4d setOrthoSymmetric(double width, double height, double zNear, double zFar, bool zZeroToOne) return {
9055         if ((properties & PROPERTY_IDENTITY) == 0)
9056             _identity();
9057         _m00(2.0 / width).
9058         _m11(2.0 / height).
9059         _m22((zZeroToOne ? 1.0 : 2.0) / (zNear - zFar)).
9060         _m32((zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar)).
9061         properties = PROPERTY_AFFINE;
9062         return this;
9063     }
9064 
9065     /**
9066      * Set this matrix to be a symmetric orthographic projection transformation for a right-handed coordinate system
9067      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
9068      * <p>
9069      * This method is equivalent to calling {@link #setOrtho(double, double, double, double, double, double) setOrtho()} with
9070      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
9071      * <p>
9072      * In order to apply the symmetric orthographic projection to an already existing transformation,
9073      * use {@link #orthoSymmetric(double, double, double, double) orthoSymmetric()}.
9074      * <p>
9075      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9076      * 
9077      * @see #orthoSymmetric(double, double, double, double)
9078      * 
9079      * @param width
9080      *            the distance between the right and left frustum edges
9081      * @param height
9082      *            the distance between the top and bottom frustum edges
9083      * @param zNear
9084      *            near clipping plane distance
9085      * @param zFar
9086      *            far clipping plane distance
9087      * @return this
9088      */
9089     ref public Matrix4d setOrthoSymmetric(double width, double height, double zNear, double zFar) return {
9090         return setOrthoSymmetric(width, height, zNear, zFar, false);
9091     }
9092 
9093     /**
9094      * Set this matrix to be a symmetric orthographic projection transformation for a left-handed coordinate system using the given NDC z range.
9095      * <p>
9096      * This method is equivalent to calling {@link #setOrtho(double, double, double, double, double, double, bool) setOrtho()} with
9097      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
9098      * <p>
9099      * In order to apply the symmetric orthographic projection to an already existing transformation,
9100      * use {@link #orthoSymmetricLH(double, double, double, double, bool) orthoSymmetricLH()}.
9101      * <p>
9102      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9103      * 
9104      * @see #orthoSymmetricLH(double, double, double, double, bool)
9105      * 
9106      * @param width
9107      *            the distance between the right and left frustum edges
9108      * @param height
9109      *            the distance between the top and bottom frustum edges
9110      * @param zNear
9111      *            near clipping plane distance
9112      * @param zFar
9113      *            far clipping plane distance
9114      * @param zZeroToOne
9115      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
9116      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
9117      * @return this
9118      */
9119     ref public Matrix4d setOrthoSymmetricLH(double width, double height, double zNear, double zFar, bool zZeroToOne) return {
9120         if ((properties & PROPERTY_IDENTITY) == 0)
9121             _identity();
9122         _m00(2.0 / width).
9123         _m11(2.0 / height).
9124         _m22((zZeroToOne ? 1.0 : 2.0) / (zFar - zNear)).
9125         _m32((zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar)).
9126         properties = PROPERTY_AFFINE;
9127         return this;
9128     }
9129 
9130     /**
9131      * Set this matrix to be a symmetric orthographic projection transformation for a left-handed coordinate system
9132      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
9133      * <p>
9134      * This method is equivalent to calling {@link #setOrthoLH(double, double, double, double, double, double) setOrthoLH()} with
9135      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
9136      * <p>
9137      * In order to apply the symmetric orthographic projection to an already existing transformation,
9138      * use {@link #orthoSymmetricLH(double, double, double, double) orthoSymmetricLH()}.
9139      * <p>
9140      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9141      * 
9142      * @see #orthoSymmetricLH(double, double, double, double)
9143      * 
9144      * @param width
9145      *            the distance between the right and left frustum edges
9146      * @param height
9147      *            the distance between the top and bottom frustum edges
9148      * @param zNear
9149      *            near clipping plane distance
9150      * @param zFar
9151      *            far clipping plane distance
9152      * @return this
9153      */
9154     ref public Matrix4d setOrthoSymmetricLH(double width, double height, double zNear, double zFar) return {
9155         return setOrthoSymmetricLH(width, height, zNear, zFar, false);
9156     }
9157 
9158     /**
9159      * Apply an orthographic projection transformation for a right-handed coordinate system
9160      * to this matrix and store the result in <code>dest</code>.
9161      * <p>
9162      * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double, Matrix4d) ortho()} with
9163      * <code>zNear=-1</code> and <code>zFar=+1</code>.
9164      * <p>
9165      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
9166      * then the new matrix will be <code>M * O</code>. So when transforming a
9167      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
9168      * orthographic projection transformation will be applied first!
9169      * <p>
9170      * In order to set the matrix to an orthographic projection without post-multiplying it,
9171      * use {@link #setOrtho2D(double, double, double, double) setOrtho()}.
9172      * <p>
9173      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9174      * 
9175      * @see #ortho(double, double, double, double, double, double, Matrix4d)
9176      * @see #setOrtho2D(double, double, double, double)
9177      * 
9178      * @param left
9179      *            the distance from the center to the left frustum edge
9180      * @param right
9181      *            the distance from the center to the right frustum edge
9182      * @param bottom
9183      *            the distance from the center to the bottom frustum edge
9184      * @param top
9185      *            the distance from the center to the top frustum edge
9186      * @param dest
9187      *            will hold the result
9188      * @return dest
9189      */
9190     public Matrix4d ortho2D(double left, double right, double bottom, double top, ref Matrix4d dest) {
9191         if ((properties & PROPERTY_IDENTITY) != 0)
9192             return dest.setOrtho2D(left, right, bottom, top);
9193         return ortho2DGeneric(left, right, bottom, top, dest);
9194     }
9195     private Matrix4d ortho2DGeneric(double left, double right, double bottom, double top, ref Matrix4d dest) {
9196         // calculate right matrix elements
9197         double rm00 = 2.0 / (right - left);
9198         double rm11 = 2.0 / (top - bottom);
9199         double rm30 = (right + left) / (left - right);
9200         double rm31 = (top + bottom) / (bottom - top);
9201         // perform optimized multiplication
9202         // compute the last column first, because other columns do not depend on it
9203         dest._m30(m00 * rm30 + m10 * rm31 + m30)
9204         ._m31(m01 * rm30 + m11 * rm31 + m31)
9205         ._m32(m02 * rm30 + m12 * rm31 + m32)
9206         ._m33(m03 * rm30 + m13 * rm31 + m33)
9207         ._m00(m00 * rm00)
9208         ._m01(m01 * rm00)
9209         ._m02(m02 * rm00)
9210         ._m03(m03 * rm00)
9211         ._m10(m10 * rm11)
9212         ._m11(m11 * rm11)
9213         ._m12(m12 * rm11)
9214         ._m13(m13 * rm11)
9215         ._m20(-m20)
9216         ._m21(-m21)
9217         ._m22(-m22)
9218         ._m23(-m23)
9219         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
9220         return dest;
9221     }
9222 
9223     /**
9224      * Apply an orthographic projection transformation for a right-handed coordinate system to this matrix.
9225      * <p>
9226      * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double) ortho()} with
9227      * <code>zNear=-1</code> and <code>zFar=+1</code>.
9228      * <p>
9229      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
9230      * then the new matrix will be <code>M * O</code>. So when transforming a
9231      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
9232      * orthographic projection transformation will be applied first!
9233      * <p>
9234      * In order to set the matrix to an orthographic projection without post-multiplying it,
9235      * use {@link #setOrtho2D(double, double, double, double) setOrtho2D()}.
9236      * <p>
9237      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9238      * 
9239      * @see #ortho(double, double, double, double, double, double)
9240      * @see #setOrtho2D(double, double, double, double)
9241      * 
9242      * @param left
9243      *            the distance from the center to the left frustum edge
9244      * @param right
9245      *            the distance from the center to the right frustum edge
9246      * @param bottom
9247      *            the distance from the center to the bottom frustum edge
9248      * @param top
9249      *            the distance from the center to the top frustum edge
9250      * @return this
9251      */
9252     ref public Matrix4d ortho2D(double left, double right, double bottom, double top) return {
9253         ortho2D(left, right, bottom, top, this);
9254         return this;
9255     }
9256 
9257     /**
9258      * Apply an orthographic projection transformation for a left-handed coordinate system to this matrix and store the result in <code>dest</code>.
9259      * <p>
9260      * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double, Matrix4d) orthoLH()} with
9261      * <code>zNear=-1</code> and <code>zFar=+1</code>.
9262      * <p>
9263      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
9264      * then the new matrix will be <code>M * O</code>. So when transforming a
9265      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
9266      * orthographic projection transformation will be applied first!
9267      * <p>
9268      * In order to set the matrix to an orthographic projection without post-multiplying it,
9269      * use {@link #setOrtho2DLH(double, double, double, double) setOrthoLH()}.
9270      * <p>
9271      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9272      * 
9273      * @see #orthoLH(double, double, double, double, double, double, Matrix4d)
9274      * @see #setOrtho2DLH(double, double, double, double)
9275      * 
9276      * @param left
9277      *            the distance from the center to the left frustum edge
9278      * @param right
9279      *            the distance from the center to the right frustum edge
9280      * @param bottom
9281      *            the distance from the center to the bottom frustum edge
9282      * @param top
9283      *            the distance from the center to the top frustum edge
9284      * @param dest
9285      *            will hold the result
9286      * @return dest
9287      */
9288     public Matrix4d ortho2DLH(double left, double right, double bottom, double top, ref Matrix4d dest) {
9289         if ((properties & PROPERTY_IDENTITY) != 0)
9290             return dest.setOrtho2DLH(left, right, bottom, top);
9291         return ortho2DLHGeneric(left, right, bottom, top, dest);
9292     }
9293     private Matrix4d ortho2DLHGeneric(double left, double right, double bottom, double top, ref Matrix4d dest) {
9294         // calculate right matrix elements
9295         double rm00 = 2.0 / (right - left);
9296         double rm11 = 2.0 / (top - bottom);
9297         double rm30 = (right + left) / (left - right);
9298         double rm31 = (top + bottom) / (bottom - top);
9299         // perform optimized multiplication
9300         // compute the last column first, because other columns do not depend on it
9301         dest._m30(m00 * rm30 + m10 * rm31 + m30)
9302         ._m31(m01 * rm30 + m11 * rm31 + m31)
9303         ._m32(m02 * rm30 + m12 * rm31 + m32)
9304         ._m33(m03 * rm30 + m13 * rm31 + m33)
9305         ._m00(m00 * rm00)
9306         ._m01(m01 * rm00)
9307         ._m02(m02 * rm00)
9308         ._m03(m03 * rm00)
9309         ._m10(m10 * rm11)
9310         ._m11(m11 * rm11)
9311         ._m12(m12 * rm11)
9312         ._m13(m13 * rm11)
9313         ._m20(m20)
9314         ._m21(m21)
9315         ._m22(m22)
9316         ._m23(m23)
9317         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
9318         return dest;
9319     }
9320 
9321     /**
9322      * Apply an orthographic projection transformation for a left-handed coordinate system to this matrix.
9323      * <p>
9324      * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double) orthoLH()} with
9325      * <code>zNear=-1</code> and <code>zFar=+1</code>.
9326      * <p>
9327      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
9328      * then the new matrix will be <code>M * O</code>. So when transforming a
9329      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
9330      * orthographic projection transformation will be applied first!
9331      * <p>
9332      * In order to set the matrix to an orthographic projection without post-multiplying it,
9333      * use {@link #setOrtho2DLH(double, double, double, double) setOrtho2DLH()}.
9334      * <p>
9335      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9336      * 
9337      * @see #orthoLH(double, double, double, double, double, double)
9338      * @see #setOrtho2DLH(double, double, double, double)
9339      * 
9340      * @param left
9341      *            the distance from the center to the left frustum edge
9342      * @param right
9343      *            the distance from the center to the right frustum edge
9344      * @param bottom
9345      *            the distance from the center to the bottom frustum edge
9346      * @param top
9347      *            the distance from the center to the top frustum edge
9348      * @return this
9349      */
9350     ref public Matrix4d ortho2DLH(double left, double right, double bottom, double top) return {
9351         ortho2DLH(left, right, bottom, top, this);
9352         return this;
9353     }
9354 
9355     /**
9356      * Set this matrix to be an orthographic projection transformation for a right-handed coordinate system.
9357      * <p>
9358      * This method is equivalent to calling {@link #setOrtho(double, double, double, double, double, double) setOrtho()} with
9359      * <code>zNear=-1</code> and <code>zFar=+1</code>.
9360      * <p>
9361      * In order to apply the orthographic projection to an already existing transformation,
9362      * use {@link #ortho2D(double, double, double, double) ortho2D()}.
9363      * <p>
9364      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9365      * 
9366      * @see #setOrtho(double, double, double, double, double, double)
9367      * @see #ortho2D(double, double, double, double)
9368      * 
9369      * @param left
9370      *            the distance from the center to the left frustum edge
9371      * @param right
9372      *            the distance from the center to the right frustum edge
9373      * @param bottom
9374      *            the distance from the center to the bottom frustum edge
9375      * @param top
9376      *            the distance from the center to the top frustum edge
9377      * @return this
9378      */
9379     ref public Matrix4d setOrtho2D(double left, double right, double bottom, double top) return {
9380         if ((properties & PROPERTY_IDENTITY) == 0)
9381             _identity();
9382         _m00(2.0 / (right - left)).
9383         _m11(2.0 / (top - bottom)).
9384         _m22(-1.0).
9385         _m30((right + left) / (left - right)).
9386         _m31((top + bottom) / (bottom - top)).
9387         properties = PROPERTY_AFFINE;
9388         return this;
9389     }
9390 
9391     /**
9392      * Set this matrix to be an orthographic projection transformation for a left-handed coordinate system.
9393      * <p>
9394      * This method is equivalent to calling {@link #setOrtho(double, double, double, double, double, double) setOrthoLH()} with
9395      * <code>zNear=-1</code> and <code>zFar=+1</code>.
9396      * <p>
9397      * In order to apply the orthographic projection to an already existing transformation,
9398      * use {@link #ortho2DLH(double, double, double, double) ortho2DLH()}.
9399      * <p>
9400      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9401      * 
9402      * @see #setOrthoLH(double, double, double, double, double, double)
9403      * @see #ortho2DLH(double, double, double, double)
9404      * 
9405      * @param left
9406      *            the distance from the center to the left frustum edge
9407      * @param right
9408      *            the distance from the center to the right frustum edge
9409      * @param bottom
9410      *            the distance from the center to the bottom frustum edge
9411      * @param top
9412      *            the distance from the center to the top frustum edge
9413      * @return this
9414      */
9415     ref public Matrix4d setOrtho2DLH(double left, double right, double bottom, double top) return {
9416         if ((properties & PROPERTY_IDENTITY) == 0)
9417             _identity();
9418         _m00(2.0 / (right - left)).
9419         _m11(2.0 / (top - bottom)).
9420         _m30((right + left) / (left - right)).
9421         _m31((top + bottom) / (bottom - top)).
9422         properties = PROPERTY_AFFINE;
9423         return this;
9424     }
9425 
9426     /**
9427      * Apply a rotation transformation to this matrix to make <code>-z</code> point along <code>dir</code>. 
9428      * <p>
9429      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookalong rotation matrix,
9430      * then the new matrix will be <code>M * L</code>. So when transforming a
9431      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>, the
9432      * lookalong rotation transformation will be applied first!
9433      * <p>
9434      * This is equivalent to calling
9435      * {@link #lookAt(ref Vector3d, Vector3d, Vector3d) lookAt}
9436      * with <code>eye = (0, 0, 0)</code> and <code>center = dir</code>.
9437      * <p>
9438      * In order to set the matrix to a lookalong transformation without post-multiplying it,
9439      * use {@link #setLookAlong(ref Vector3d, Vector3d) setLookAlong()}.
9440      * 
9441      * @see #lookAlong(double, double, double, double, double, double)
9442      * @see #lookAt(ref Vector3d, Vector3d, Vector3d)
9443      * @see #setLookAlong(ref Vector3d, Vector3d)
9444      * 
9445      * @param dir
9446      *            the direction in space to look along
9447      * @param up
9448      *            the direction of 'up'
9449      * @return this
9450      */
9451     ref public Matrix4d lookAlong(ref Vector3d dir, Vector3d up) return {
9452         lookAlong(dir.x, dir.y, dir.z, up.x, up.y, up.z, this);
9453         return this;
9454     }
9455 
9456     /**
9457      * Apply a rotation transformation to this matrix to make <code>-z</code> point along <code>dir</code>
9458      * and store the result in <code>dest</code>. 
9459      * <p>
9460      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookalong rotation matrix,
9461      * then the new matrix will be <code>M * L</code>. So when transforming a
9462      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>, the
9463      * lookalong rotation transformation will be applied first!
9464      * <p>
9465      * This is equivalent to calling
9466      * {@link #lookAt(ref Vector3d, Vector3d, Vector3d) lookAt}
9467      * with <code>eye = (0, 0, 0)</code> and <code>center = dir</code>.
9468      * <p>
9469      * In order to set the matrix to a lookalong transformation without post-multiplying it,
9470      * use {@link #setLookAlong(ref Vector3d, Vector3d) setLookAlong()}.
9471      * 
9472      * @see #lookAlong(double, double, double, double, double, double)
9473      * @see #lookAt(ref Vector3d, Vector3d, Vector3d)
9474      * @see #setLookAlong(ref Vector3d, Vector3d)
9475      * 
9476      * @param dir
9477      *            the direction in space to look along
9478      * @param up
9479      *            the direction of 'up'
9480      * @param dest
9481      *            will hold the result
9482      * @return dest
9483      */
9484     public Matrix4d lookAlong(ref Vector3d dir, Vector3d up, ref Matrix4d dest) {
9485         return lookAlong(dir.x, dir.y, dir.z, up.x, up.y, up.z, dest);
9486     }
9487 
9488     /**
9489      * Apply a rotation transformation to this matrix to make <code>-z</code> point along <code>dir</code>
9490      * and store the result in <code>dest</code>. 
9491      * <p>
9492      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookalong rotation matrix,
9493      * then the new matrix will be <code>M * L</code>. So when transforming a
9494      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>, the
9495      * lookalong rotation transformation will be applied first!
9496      * <p>
9497      * This is equivalent to calling
9498      * {@link #lookAt(double, double, double, double, double, double, double, double, double) lookAt()}
9499      * with <code>eye = (0, 0, 0)</code> and <code>center = dir</code>.
9500      * <p>
9501      * In order to set the matrix to a lookalong transformation without post-multiplying it,
9502      * use {@link #setLookAlong(double, double, double, double, double, double) setLookAlong()}
9503      * 
9504      * @see #lookAt(double, double, double, double, double, double, double, double, double)
9505      * @see #setLookAlong(double, double, double, double, double, double)
9506      * 
9507      * @param dirX
9508      *              the x-coordinate of the direction to look along
9509      * @param dirY
9510      *              the y-coordinate of the direction to look along
9511      * @param dirZ
9512      *              the z-coordinate of the direction to look along
9513      * @param upX
9514      *              the x-coordinate of the up vector
9515      * @param upY
9516      *              the y-coordinate of the up vector
9517      * @param upZ
9518      *              the z-coordinate of the up vector
9519      * @param dest
9520      *              will hold the result
9521      * @return dest
9522      */
9523     public Matrix4d lookAlong(double dirX, double dirY, double dirZ, double upX, double upY, double upZ, ref Matrix4d dest) {
9524         if ((properties & PROPERTY_IDENTITY) != 0)
9525             return dest.setLookAlong(dirX, dirY, dirZ, upX, upY, upZ);
9526         return lookAlongGeneric(dirX, dirY, dirZ, upX, upY, upZ, dest);
9527     }
9528 
9529     private Matrix4d lookAlongGeneric(double dirX, double dirY, double dirZ, double upX, double upY, double upZ, ref Matrix4d dest) {
9530         // Normalize direction
9531         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
9532         dirX *= -invDirLength;
9533         dirY *= -invDirLength;
9534         dirZ *= -invDirLength;
9535         // left = up x direction
9536         double leftX, leftY, leftZ;
9537         leftX = upY * dirZ - upZ * dirY;
9538         leftY = upZ * dirX - upX * dirZ;
9539         leftZ = upX * dirY - upY * dirX;
9540         // normalize left
9541         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
9542         leftX *= invLeftLength;
9543         leftY *= invLeftLength;
9544         leftZ *= invLeftLength;
9545         // up = direction x left
9546         double upnX = dirY * leftZ - dirZ * leftY;
9547         double upnY = dirZ * leftX - dirX * leftZ;
9548         double upnZ = dirX * leftY - dirY * leftX;
9549         // calculate right matrix elements
9550         double rm00 = leftX;
9551         double rm01 = upnX;
9552         double rm02 = dirX;
9553         double rm10 = leftY;
9554         double rm11 = upnY;
9555         double rm12 = dirY;
9556         double rm20 = leftZ;
9557         double rm21 = upnZ;
9558         double rm22 = dirZ;
9559         // perform optimized matrix multiplication
9560         // introduce temporaries for dependent results
9561         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
9562         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
9563         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
9564         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02;
9565         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
9566         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
9567         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
9568         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12;
9569         dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
9570         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
9571         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
9572         ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22)
9573         // set the rest of the matrix elements
9574         ._m00(nm00)
9575         ._m01(nm01)
9576         ._m02(nm02)
9577         ._m03(nm03)
9578         ._m10(nm10)
9579         ._m11(nm11)
9580         ._m12(nm12)
9581         ._m13(nm13)
9582         ._m30(m30)
9583         ._m31(m31)
9584         ._m32(m32)
9585         ._m33(m33)
9586         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
9587         return dest;
9588     }
9589 
9590     /**
9591      * Apply a rotation transformation to this matrix to make <code>-z</code> point along <code>dir</code>. 
9592      * <p>
9593      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookalong rotation matrix,
9594      * then the new matrix will be <code>M * L</code>. So when transforming a
9595      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>, the
9596      * lookalong rotation transformation will be applied first!
9597      * <p>
9598      * This is equivalent to calling
9599      * {@link #lookAt(double, double, double, double, double, double, double, double, double) lookAt()}
9600      * with <code>eye = (0, 0, 0)</code> and <code>center = dir</code>.
9601      * <p>
9602      * In order to set the matrix to a lookalong transformation without post-multiplying it,
9603      * use {@link #setLookAlong(double, double, double, double, double, double) setLookAlong()}
9604      * 
9605      * @see #lookAt(double, double, double, double, double, double, double, double, double)
9606      * @see #setLookAlong(double, double, double, double, double, double)
9607      * 
9608      * @param dirX
9609      *              the x-coordinate of the direction to look along
9610      * @param dirY
9611      *              the y-coordinate of the direction to look along
9612      * @param dirZ
9613      *              the z-coordinate of the direction to look along
9614      * @param upX
9615      *              the x-coordinate of the up vector
9616      * @param upY
9617      *              the y-coordinate of the up vector
9618      * @param upZ
9619      *              the z-coordinate of the up vector
9620      * @return this
9621      */
9622     ref public Matrix4d lookAlong(double dirX, double dirY, double dirZ,
9623                               double upX, double upY, double upZ) return {
9624         lookAlong(dirX, dirY, dirZ, upX, upY, upZ, this);
9625         return this;
9626     }
9627 
9628     /**
9629      * Set this matrix to a rotation transformation to make <code>-z</code>
9630      * point along <code>dir</code>.
9631      * <p>
9632      * This is equivalent to calling
9633      * {@link #setLookAt(ref Vector3d, Vector3d, Vector3d) setLookAt()} 
9634      * with <code>eye = (0, 0, 0)</code> and <code>center = dir</code>.
9635      * <p>
9636      * In order to apply the lookalong transformation to any previous existing transformation,
9637      * use {@link #lookAlong(ref Vector3d, Vector3d)}.
9638      * 
9639      * @see #setLookAlong(ref Vector3d, Vector3d)
9640      * @see #lookAlong(ref Vector3d, Vector3d)
9641      * 
9642      * @param dir
9643      *            the direction in space to look along
9644      * @param up
9645      *            the direction of 'up'
9646      * @return this
9647      */
9648     ref public Matrix4d setLookAlong(ref Vector3d dir, Vector3d up) return {
9649         return setLookAlong(dir.x, dir.y, dir.z, up.x, up.y, up.z);
9650     }
9651 
9652     /**
9653      * Set this matrix to a rotation transformation to make <code>-z</code>
9654      * point along <code>dir</code>.
9655      * <p>
9656      * This is equivalent to calling
9657      * {@link #setLookAt(double, double, double, double, double, double, double, double, double)
9658      * setLookAt()} with <code>eye = (0, 0, 0)</code> and <code>center = dir</code>.
9659      * <p>
9660      * In order to apply the lookalong transformation to any previous existing transformation,
9661      * use {@link #lookAlong(double, double, double, double, double, double) lookAlong()}
9662      * 
9663      * @see #setLookAlong(double, double, double, double, double, double)
9664      * @see #lookAlong(double, double, double, double, double, double)
9665      * 
9666      * @param dirX
9667      *              the x-coordinate of the direction to look along
9668      * @param dirY
9669      *              the y-coordinate of the direction to look along
9670      * @param dirZ
9671      *              the z-coordinate of the direction to look along
9672      * @param upX
9673      *              the x-coordinate of the up vector
9674      * @param upY
9675      *              the y-coordinate of the up vector
9676      * @param upZ
9677      *              the z-coordinate of the up vector
9678      * @return this
9679      */
9680     ref public Matrix4d setLookAlong(double dirX, double dirY, double dirZ,
9681                                  double upX, double upY, double upZ) return {
9682         // Normalize direction
9683         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
9684         dirX *= -invDirLength;
9685         dirY *= -invDirLength;
9686         dirZ *= -invDirLength;
9687         // left = up x direction
9688         double leftX, leftY, leftZ;
9689         leftX = upY * dirZ - upZ * dirY;
9690         leftY = upZ * dirX - upX * dirZ;
9691         leftZ = upX * dirY - upY * dirX;
9692         // normalize left
9693         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
9694         leftX *= invLeftLength;
9695         leftY *= invLeftLength;
9696         leftZ *= invLeftLength;
9697         // up = direction x left
9698         double upnX = dirY * leftZ - dirZ * leftY;
9699         double upnY = dirZ * leftX - dirX * leftZ;
9700         double upnZ = dirX * leftY - dirY * leftX;
9701         _m00(leftX).
9702         _m01(upnX).
9703         _m02(dirX).
9704         _m03(0.0).
9705         _m10(leftY).
9706         _m11(upnY).
9707         _m12(dirY).
9708         _m13(0.0).
9709         _m20(leftZ).
9710         _m21(upnZ).
9711         _m22(dirZ).
9712         _m23(0.0).
9713         _m30(0.0).
9714         _m31(0.0).
9715         _m32(0.0).
9716         _m33(1.0).
9717         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
9718         return this;
9719     }
9720 
9721     /**
9722      * Set this matrix to be a "lookat" transformation for a right-handed coordinate system, that aligns
9723      * <code>-z</code> with <code>center - eye</code>.
9724      * <p>
9725      * In order to not make use of vectors to specify <code>eye</code>, <code>center</code> and <code>up</code> but use primitives,
9726      * like in the GLU function, use {@link #setLookAt(double, double, double, double, double, double, double, double, double) setLookAt()}
9727      * instead.
9728      * <p>
9729      * In order to apply the lookat transformation to a previous existing transformation,
9730      * use {@link #lookAt(ref Vector3d, Vector3d, Vector3d) lookAt()}.
9731      * 
9732      * @see #setLookAt(double, double, double, double, double, double, double, double, double)
9733      * @see #lookAt(ref Vector3d, Vector3d, Vector3d)
9734      * 
9735      * @param eye
9736      *            the position of the camera
9737      * @param center
9738      *            the point in space to look at
9739      * @param up
9740      *            the direction of 'up'
9741      * @return this
9742      */
9743     ref public Matrix4d setLookAt(ref Vector3d eye, Vector3d center, Vector3d up) return {
9744         return setLookAt(eye.x, eye.y, eye.z, center.x, center.y, center.z, up.x, up.y, up.z);
9745     }
9746 
9747     /**
9748      * Set this matrix to be a "lookat" transformation for a right-handed coordinate system, 
9749      * that aligns <code>-z</code> with <code>center - eye</code>.
9750      * <p>
9751      * In order to apply the lookat transformation to a previous existing transformation,
9752      * use {@link #lookAt(double, double, double, double, double, double, double, double, double) lookAt}.
9753      * 
9754      * @see #setLookAt(ref Vector3d, Vector3d, Vector3d)
9755      * @see #lookAt(double, double, double, double, double, double, double, double, double)
9756      * 
9757      * @param eyeX
9758      *              the x-coordinate of the eye/camera location
9759      * @param eyeY
9760      *              the y-coordinate of the eye/camera location
9761      * @param eyeZ
9762      *              the z-coordinate of the eye/camera location
9763      * @param centerX
9764      *              the x-coordinate of the point to look at
9765      * @param centerY
9766      *              the y-coordinate of the point to look at
9767      * @param centerZ
9768      *              the z-coordinate of the point to look at
9769      * @param upX
9770      *              the x-coordinate of the up vector
9771      * @param upY
9772      *              the y-coordinate of the up vector
9773      * @param upZ
9774      *              the z-coordinate of the up vector
9775      * @return this
9776      */
9777     ref public Matrix4d setLookAt(double eyeX, double eyeY, double eyeZ,
9778                               double centerX, double centerY, double centerZ,
9779                               double upX, double upY, double upZ) return {
9780         // Compute direction from position to lookAt
9781         double dirX, dirY, dirZ;
9782         dirX = eyeX - centerX;
9783         dirY = eyeY - centerY;
9784         dirZ = eyeZ - centerZ;
9785         // Normalize direction
9786         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
9787         dirX *= invDirLength;
9788         dirY *= invDirLength;
9789         dirZ *= invDirLength;
9790         // left = up x direction
9791         double leftX, leftY, leftZ;
9792         leftX = upY * dirZ - upZ * dirY;
9793         leftY = upZ * dirX - upX * dirZ;
9794         leftZ = upX * dirY - upY * dirX;
9795         // normalize left
9796         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
9797         leftX *= invLeftLength;
9798         leftY *= invLeftLength;
9799         leftZ *= invLeftLength;
9800         // up = direction x left
9801         double upnX = dirY * leftZ - dirZ * leftY;
9802         double upnY = dirZ * leftX - dirX * leftZ;
9803         double upnZ = dirX * leftY - dirY * leftX;
9804         return this.
9805         _m00(leftX).
9806         _m01(upnX).
9807         _m02(dirX).
9808         _m03(0.0).
9809         _m10(leftY).
9810         _m11(upnY).
9811         _m12(dirY).
9812         _m13(0.0).
9813         _m20(leftZ).
9814         _m21(upnZ).
9815         _m22(dirZ).
9816         _m23(0.0).
9817         _m30(-(leftX * eyeX + leftY * eyeY + leftZ * eyeZ)).
9818         _m31(-(upnX * eyeX + upnY * eyeY + upnZ * eyeZ)).
9819         _m32(-(dirX * eyeX + dirY * eyeY + dirZ * eyeZ)).
9820         _m33(1.0).
9821         _properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL);
9822     }
9823 
9824     /**
9825      * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, 
9826      * that aligns <code>-z</code> with <code>center - eye</code> and store the result in <code>dest</code>.
9827      * <p>
9828      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
9829      * then the new matrix will be <code>M * L</code>. So when transforming a
9830      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
9831      * the lookat transformation will be applied first!
9832      * <p>
9833      * In order to set the matrix to a lookat transformation without post-multiplying it,
9834      * use {@link #setLookAt(ref Vector3d, Vector3d, Vector3d)}.
9835      * 
9836      * @see #lookAt(double, double, double, double, double, double, double, double, double)
9837      * @see #setLookAlong(ref Vector3d, Vector3d)
9838      * 
9839      * @param eye
9840      *            the position of the camera
9841      * @param center
9842      *            the point in space to look at
9843      * @param up
9844      *            the direction of 'up'
9845      * @param dest
9846      *            will hold the result
9847      * @return dest
9848      */
9849     public Matrix4d lookAt(ref Vector3d eye, Vector3d center, Vector3d up, ref Matrix4d dest) {
9850         return lookAt(eye.x, eye.y, eye.z, center.x, center.y, center.z, up.x, up.y, up.z, dest);
9851     }
9852 
9853     /**
9854      * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, 
9855      * that aligns <code>-z</code> with <code>center - eye</code>.
9856      * <p>
9857      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
9858      * then the new matrix will be <code>M * L</code>. So when transforming a
9859      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
9860      * the lookat transformation will be applied first!
9861      * <p>
9862      * In order to set the matrix to a lookat transformation without post-multiplying it,
9863      * use {@link #setLookAt(ref Vector3d, Vector3d, Vector3d)}.
9864      * 
9865      * @see #lookAt(double, double, double, double, double, double, double, double, double)
9866      * @see #setLookAlong(ref Vector3d, Vector3d)
9867      * 
9868      * @param eye
9869      *            the position of the camera
9870      * @param center
9871      *            the point in space to look at
9872      * @param up
9873      *            the direction of 'up'
9874      * @return this
9875      */
9876     ref public Matrix4d lookAt(ref Vector3d eye, Vector3d center, Vector3d up) return {
9877         lookAt(eye.x, eye.y, eye.z, center.x, center.y, center.z, up.x, up.y, up.z, this);
9878         return this;
9879     }
9880 
9881     /**
9882      * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, 
9883      * that aligns <code>-z</code> with <code>center - eye</code> and store the result in <code>dest</code>.
9884      * <p>
9885      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
9886      * then the new matrix will be <code>M * L</code>. So when transforming a
9887      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
9888      * the lookat transformation will be applied first!
9889      * <p>
9890      * In order to set the matrix to a lookat transformation without post-multiplying it,
9891      * use {@link #setLookAt(double, double, double, double, double, double, double, double, double) setLookAt()}.
9892      * 
9893      * @see #lookAt(ref Vector3d, Vector3d, Vector3d)
9894      * @see #setLookAt(double, double, double, double, double, double, double, double, double)
9895      * 
9896      * @param eyeX
9897      *              the x-coordinate of the eye/camera location
9898      * @param eyeY
9899      *              the y-coordinate of the eye/camera location
9900      * @param eyeZ
9901      *              the z-coordinate of the eye/camera location
9902      * @param centerX
9903      *              the x-coordinate of the point to look at
9904      * @param centerY
9905      *              the y-coordinate of the point to look at
9906      * @param centerZ
9907      *              the z-coordinate of the point to look at
9908      * @param upX
9909      *              the x-coordinate of the up vector
9910      * @param upY
9911      *              the y-coordinate of the up vector
9912      * @param upZ
9913      *              the z-coordinate of the up vector
9914      * @param dest
9915      *          will hold the result
9916      * @return dest
9917      */
9918     public Matrix4d lookAt(double eyeX, double eyeY, double eyeZ,
9919                            double centerX, double centerY, double centerZ,
9920                            double upX, double upY, double upZ, ref Matrix4d dest) {
9921         if ((properties & PROPERTY_IDENTITY) != 0)
9922             return dest.setLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);
9923         else if ((properties & PROPERTY_PERSPECTIVE) != 0)
9924             return lookAtPerspective(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, dest);
9925         return lookAtGeneric(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, dest);
9926     }
9927     private Matrix4d lookAtGeneric(double eyeX, double eyeY, double eyeZ,
9928                                    double centerX, double centerY, double centerZ,
9929                                    double upX, double upY, double upZ, ref Matrix4d dest) {
9930         // Compute direction from position to lookAt
9931         double dirX, dirY, dirZ;
9932         dirX = eyeX - centerX;
9933         dirY = eyeY - centerY;
9934         dirZ = eyeZ - centerZ;
9935         // Normalize direction
9936         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
9937         dirX *= invDirLength;
9938         dirY *= invDirLength;
9939         dirZ *= invDirLength;
9940         // left = up x direction
9941         double leftX, leftY, leftZ;
9942         leftX = upY * dirZ - upZ * dirY;
9943         leftY = upZ * dirX - upX * dirZ;
9944         leftZ = upX * dirY - upY * dirX;
9945         // normalize left
9946         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
9947         leftX *= invLeftLength;
9948         leftY *= invLeftLength;
9949         leftZ *= invLeftLength;
9950         // up = direction x left
9951         double upnX = dirY * leftZ - dirZ * leftY;
9952         double upnY = dirZ * leftX - dirX * leftZ;
9953         double upnZ = dirX * leftY - dirY * leftX;
9954         // calculate right matrix elements
9955         double rm00 = leftX;
9956         double rm01 = upnX;
9957         double rm02 = dirX;
9958         double rm10 = leftY;
9959         double rm11 = upnY;
9960         double rm12 = dirY;
9961         double rm20 = leftZ;
9962         double rm21 = upnZ;
9963         double rm22 = dirZ;
9964         double rm30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ);
9965         double rm31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ);
9966         double rm32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ);
9967         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
9968         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
9969         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
9970         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02;
9971         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
9972         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
9973         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
9974         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12;
9975         // perform optimized matrix multiplication
9976         // compute last column first, because others do not depend on it
9977         dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30)
9978         ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31)
9979         ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32)
9980         ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33)
9981         // introduce temporaries for dependent results
9982         ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
9983         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
9984         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
9985         ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22)
9986         // set the rest of the matrix elements
9987         ._m00(nm00)
9988         ._m01(nm01)
9989         ._m02(nm02)
9990         ._m03(nm03)
9991         ._m10(nm10)
9992         ._m11(nm11)
9993         ._m12(nm12)
9994         ._m13(nm13)
9995         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
9996         return dest;
9997     }
9998 
9999     /**
10000      * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, 
10001      * that aligns <code>-z</code> with <code>center - eye</code>.
10002      * <p>
10003      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
10004      * then the new matrix will be <code>M * L</code>. So when transforming a
10005      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
10006      * the lookat transformation will be applied first!
10007      * <p>
10008      * In order to set the matrix to a lookat transformation without post-multiplying it,
10009      * use {@link #setLookAt(double, double, double, double, double, double, double, double, double) setLookAt()}.
10010      * 
10011      * @see #lookAt(ref Vector3d, Vector3d, Vector3d)
10012      * @see #setLookAt(double, double, double, double, double, double, double, double, double)
10013      * 
10014      * @param eyeX
10015      *              the x-coordinate of the eye/camera location
10016      * @param eyeY
10017      *              the y-coordinate of the eye/camera location
10018      * @param eyeZ
10019      *              the z-coordinate of the eye/camera location
10020      * @param centerX
10021      *              the x-coordinate of the point to look at
10022      * @param centerY
10023      *              the y-coordinate of the point to look at
10024      * @param centerZ
10025      *              the z-coordinate of the point to look at
10026      * @param upX
10027      *              the x-coordinate of the up vector
10028      * @param upY
10029      *              the y-coordinate of the up vector
10030      * @param upZ
10031      *              the z-coordinate of the up vector
10032      * @return this
10033      */
10034     ref public Matrix4d lookAt(double eyeX, double eyeY, double eyeZ,
10035                            double centerX, double centerY, double centerZ,
10036                            double upX, double upY, double upZ) return {
10037         lookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, this);
10038         return this;
10039     }
10040 
10041     /**
10042      * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, 
10043      * that aligns <code>-z</code> with <code>center - eye</code> and store the result in <code>dest</code>.
10044      * <p>
10045      * This method assumes <code>this</code> to be a perspective transformation, obtained via
10046      * {@link #frustum(double, double, double, double, double, double) frustum()} or {@link #perspective(double, double, double, double) perspective()} or
10047      * one of their overloads.
10048      * <p>
10049      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
10050      * then the new matrix will be <code>M * L</code>. So when transforming a
10051      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
10052      * the lookat transformation will be applied first!
10053      * <p>
10054      * In order to set the matrix to a lookat transformation without post-multiplying it,
10055      * use {@link #setLookAt(double, double, double, double, double, double, double, double, double) setLookAt()}.
10056      * 
10057      * @see #setLookAt(double, double, double, double, double, double, double, double, double)
10058      * 
10059      * @param eyeX
10060      *              the x-coordinate of the eye/camera location
10061      * @param eyeY
10062      *              the y-coordinate of the eye/camera location
10063      * @param eyeZ
10064      *              the z-coordinate of the eye/camera location
10065      * @param centerX
10066      *              the x-coordinate of the point to look at
10067      * @param centerY
10068      *              the y-coordinate of the point to look at
10069      * @param centerZ
10070      *              the z-coordinate of the point to look at
10071      * @param upX
10072      *              the x-coordinate of the up vector
10073      * @param upY
10074      *              the y-coordinate of the up vector
10075      * @param upZ
10076      *              the z-coordinate of the up vector
10077      * @param dest
10078      *          will hold the result
10079      * @return dest
10080      */
10081     public Matrix4d lookAtPerspective(double eyeX, double eyeY, double eyeZ,
10082             double centerX, double centerY, double centerZ,
10083             double upX, double upY, double upZ, ref Matrix4d dest) {
10084         // Compute direction from position to lookAt
10085         double dirX, dirY, dirZ;
10086         dirX = eyeX - centerX;
10087         dirY = eyeY - centerY;
10088         dirZ = eyeZ - centerZ;
10089         // Normalize direction
10090         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
10091         dirX *= invDirLength;
10092         dirY *= invDirLength;
10093         dirZ *= invDirLength;
10094         // left = up x direction
10095         double leftX, leftY, leftZ;
10096         leftX = upY * dirZ - upZ * dirY;
10097         leftY = upZ * dirX - upX * dirZ;
10098         leftZ = upX * dirY - upY * dirX;
10099         // normalize left
10100         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
10101         leftX *= invLeftLength;
10102         leftY *= invLeftLength;
10103         leftZ *= invLeftLength;
10104         // up = direction x left
10105         double upnX = dirY * leftZ - dirZ * leftY;
10106         double upnY = dirZ * leftX - dirX * leftZ;
10107         double upnZ = dirX * leftY - dirY * leftX;
10108         double rm30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ);
10109         double rm31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ);
10110         double rm32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ);
10111         double nm10 = m00 * leftY;
10112         double nm20 = m00 * leftZ;
10113         double nm21 = m11 * upnZ;
10114         double nm30 = m00 * rm30;
10115         double nm31 = m11 * rm31;
10116         double nm32 = m22 * rm32 + m32;
10117         double nm33 = m23 * rm32;
10118         return dest
10119         ._m00(m00 * leftX)
10120         ._m01(m11 * upnX)
10121         ._m02(m22 * dirX)
10122         ._m03(m23 * dirX)
10123         ._m10(nm10)
10124         ._m11(m11 * upnY)
10125         ._m12(m22 * dirY)
10126         ._m13(m23 * dirY)
10127         ._m20(nm20)
10128         ._m21(nm21)
10129         ._m22(m22 * dirZ)
10130         ._m23(m23 * dirZ)
10131         ._m30(nm30)
10132         ._m31(nm31)
10133         ._m32(nm32)
10134         ._m33(nm33)
10135         ._properties(0);
10136     }
10137 
10138     /**
10139      * Set this matrix to be a "lookat" transformation for a left-handed coordinate system, that aligns
10140      * <code>+z</code> with <code>center - eye</code>.
10141      * <p>
10142      * In order to not make use of vectors to specify <code>eye</code>, <code>center</code> and <code>up</code> but use primitives,
10143      * like in the GLU function, use {@link #setLookAtLH(double, double, double, double, double, double, double, double, double) setLookAtLH()}
10144      * instead.
10145      * <p>
10146      * In order to apply the lookat transformation to a previous existing transformation,
10147      * use {@link #lookAtLH(ref Vector3d, Vector3d, Vector3d) lookAt()}.
10148      * 
10149      * @see #setLookAtLH(double, double, double, double, double, double, double, double, double)
10150      * @see #lookAtLH(ref Vector3d, Vector3d, Vector3d)
10151      * 
10152      * @param eye
10153      *            the position of the camera
10154      * @param center
10155      *            the point in space to look at
10156      * @param up
10157      *            the direction of 'up'
10158      * @return this
10159      */
10160     ref public Matrix4d setLookAtLH(ref Vector3d eye, Vector3d center, Vector3d up) return {
10161         return setLookAtLH(eye.x, eye.y, eye.z, center.x, center.y, center.z, up.x, up.y, up.z);
10162     }
10163 
10164     /**
10165      * Set this matrix to be a "lookat" transformation for a left-handed coordinate system, 
10166      * that aligns <code>+z</code> with <code>center - eye</code>.
10167      * <p>
10168      * In order to apply the lookat transformation to a previous existing transformation,
10169      * use {@link #lookAtLH(double, double, double, double, double, double, double, double, double) lookAtLH}.
10170      * 
10171      * @see #setLookAtLH(ref Vector3d, Vector3d, Vector3d)
10172      * @see #lookAtLH(double, double, double, double, double, double, double, double, double)
10173      * 
10174      * @param eyeX
10175      *              the x-coordinate of the eye/camera location
10176      * @param eyeY
10177      *              the y-coordinate of the eye/camera location
10178      * @param eyeZ
10179      *              the z-coordinate of the eye/camera location
10180      * @param centerX
10181      *              the x-coordinate of the point to look at
10182      * @param centerY
10183      *              the y-coordinate of the point to look at
10184      * @param centerZ
10185      *              the z-coordinate of the point to look at
10186      * @param upX
10187      *              the x-coordinate of the up vector
10188      * @param upY
10189      *              the y-coordinate of the up vector
10190      * @param upZ
10191      *              the z-coordinate of the up vector
10192      * @return this
10193      */
10194     ref public Matrix4d setLookAtLH(double eyeX, double eyeY, double eyeZ,
10195                                 double centerX, double centerY, double centerZ,
10196                                 double upX, double upY, double upZ) return {
10197         // Compute direction from position to lookAt
10198         double dirX, dirY, dirZ;
10199         dirX = centerX - eyeX;
10200         dirY = centerY - eyeY;
10201         dirZ = centerZ - eyeZ;
10202         // Normalize direction
10203         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
10204         dirX *= invDirLength;
10205         dirY *= invDirLength;
10206         dirZ *= invDirLength;
10207         // left = up x direction
10208         double leftX, leftY, leftZ;
10209         leftX = upY * dirZ - upZ * dirY;
10210         leftY = upZ * dirX - upX * dirZ;
10211         leftZ = upX * dirY - upY * dirX;
10212         // normalize left
10213         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
10214         leftX *= invLeftLength;
10215         leftY *= invLeftLength;
10216         leftZ *= invLeftLength;
10217         // up = direction x left
10218         double upnX = dirY * leftZ - dirZ * leftY;
10219         double upnY = dirZ * leftX - dirX * leftZ;
10220         double upnZ = dirX * leftY - dirY * leftX;
10221         _m00(leftX).
10222         _m01(upnX).
10223         _m02(dirX).
10224         _m03(0.0).
10225         _m10(leftY).
10226         _m11(upnY).
10227         _m12(dirY).
10228         _m13(0.0).
10229         _m20(leftZ).
10230         _m21(upnZ).
10231         _m22(dirZ).
10232         _m23(0.0).
10233         _m30(-(leftX * eyeX + leftY * eyeY + leftZ * eyeZ)).
10234         _m31(-(upnX * eyeX + upnY * eyeY + upnZ * eyeZ)).
10235         _m32(-(dirX * eyeX + dirY * eyeY + dirZ * eyeZ)).
10236         _m33(1.0).
10237         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
10238         return this;
10239     }
10240 
10241     /**
10242      * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, 
10243      * that aligns <code>+z</code> with <code>center - eye</code> and store the result in <code>dest</code>.
10244      * <p>
10245      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
10246      * then the new matrix will be <code>M * L</code>. So when transforming a
10247      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
10248      * the lookat transformation will be applied first!
10249      * <p>
10250      * In order to set the matrix to a lookat transformation without post-multiplying it,
10251      * use {@link #setLookAtLH(ref Vector3d, Vector3d, Vector3d)}.
10252      * 
10253      * @see #lookAtLH(double, double, double, double, double, double, double, double, double)
10254      * @see #setLookAtLH(ref Vector3d, Vector3d, Vector3d)
10255      * 
10256      * @param eye
10257      *            the position of the camera
10258      * @param center
10259      *            the point in space to look at
10260      * @param up
10261      *            the direction of 'up'
10262      * @param dest
10263      *            will hold the result
10264      * @return dest
10265      */
10266     public Matrix4d lookAtLH(ref Vector3d eye, Vector3d center, Vector3d up, ref Matrix4d dest) {
10267         return lookAtLH(eye.x, eye.y, eye.z, center.x, center.y, center.z, up.x, up.y, up.z, dest);
10268     }
10269 
10270     /**
10271      * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, 
10272      * that aligns <code>+z</code> with <code>center - eye</code>.
10273      * <p>
10274      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
10275      * then the new matrix will be <code>M * L</code>. So when transforming a
10276      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
10277      * the lookat transformation will be applied first!
10278      * <p>
10279      * In order to set the matrix to a lookat transformation without post-multiplying it,
10280      * use {@link #setLookAtLH(ref Vector3d, Vector3d, Vector3d)}.
10281      * 
10282      * @see #lookAtLH(double, double, double, double, double, double, double, double, double)
10283      * 
10284      * @param eye
10285      *            the position of the camera
10286      * @param center
10287      *            the point in space to look at
10288      * @param up
10289      *            the direction of 'up'
10290      * @return this
10291      */
10292     ref public Matrix4d lookAtLH(ref Vector3d eye, Vector3d center, Vector3d up) return {
10293         lookAtLH(eye.x, eye.y, eye.z, center.x, center.y, center.z, up.x, up.y, up.z, this);
10294         return this;
10295     }
10296 
10297     /**
10298      * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, 
10299      * that aligns <code>+z</code> with <code>center - eye</code> and store the result in <code>dest</code>.
10300      * <p>
10301      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
10302      * then the new matrix will be <code>M * L</code>. So when transforming a
10303      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
10304      * the lookat transformation will be applied first!
10305      * <p>
10306      * In order to set the matrix to a lookat transformation without post-multiplying it,
10307      * use {@link #setLookAtLH(double, double, double, double, double, double, double, double, double) setLookAtLH()}.
10308      * 
10309      * @see #lookAtLH(ref Vector3d, Vector3d, Vector3d)
10310      * @see #setLookAtLH(double, double, double, double, double, double, double, double, double)
10311      * 
10312      * @param eyeX
10313      *              the x-coordinate of the eye/camera location
10314      * @param eyeY
10315      *              the y-coordinate of the eye/camera location
10316      * @param eyeZ
10317      *              the z-coordinate of the eye/camera location
10318      * @param centerX
10319      *              the x-coordinate of the point to look at
10320      * @param centerY
10321      *              the y-coordinate of the point to look at
10322      * @param centerZ
10323      *              the z-coordinate of the point to look at
10324      * @param upX
10325      *              the x-coordinate of the up vector
10326      * @param upY
10327      *              the y-coordinate of the up vector
10328      * @param upZ
10329      *              the z-coordinate of the up vector
10330      * @param dest
10331      *          will hold the result
10332      * @return dest
10333      */
10334     public Matrix4d lookAtLH(double eyeX, double eyeY, double eyeZ,
10335                              double centerX, double centerY, double centerZ,
10336                              double upX, double upY, double upZ, ref Matrix4d dest) {
10337         if ((properties & PROPERTY_IDENTITY) != 0)
10338             return dest.setLookAtLH(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);
10339         else if ((properties & PROPERTY_PERSPECTIVE) != 0)
10340             return lookAtPerspectiveLH(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, dest);
10341         return lookAtLHGeneric(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, dest);
10342     }
10343     private Matrix4d lookAtLHGeneric(double eyeX, double eyeY, double eyeZ,
10344                                      double centerX, double centerY, double centerZ,
10345                                      double upX, double upY, double upZ, ref Matrix4d dest) {
10346         // Compute direction from position to lookAt
10347         double dirX, dirY, dirZ;
10348         dirX = centerX - eyeX;
10349         dirY = centerY - eyeY;
10350         dirZ = centerZ - eyeZ;
10351         // Normalize direction
10352         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
10353         dirX *= invDirLength;
10354         dirY *= invDirLength;
10355         dirZ *= invDirLength;
10356         // left = up x direction
10357         double leftX, leftY, leftZ;
10358         leftX = upY * dirZ - upZ * dirY;
10359         leftY = upZ * dirX - upX * dirZ;
10360         leftZ = upX * dirY - upY * dirX;
10361         // normalize left
10362         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
10363         leftX *= invLeftLength;
10364         leftY *= invLeftLength;
10365         leftZ *= invLeftLength;
10366         // up = direction x left
10367         double upnX = dirY * leftZ - dirZ * leftY;
10368         double upnY = dirZ * leftX - dirX * leftZ;
10369         double upnZ = dirX * leftY - dirY * leftX;
10370         // calculate right matrix elements
10371         double rm00 = leftX;
10372         double rm01 = upnX;
10373         double rm02 = dirX;
10374         double rm10 = leftY;
10375         double rm11 = upnY;
10376         double rm12 = dirY;
10377         double rm20 = leftZ;
10378         double rm21 = upnZ;
10379         double rm22 = dirZ;
10380         double rm30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ);
10381         double rm31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ);
10382         double rm32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ);
10383         // introduce temporaries for dependent results
10384         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
10385         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
10386         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
10387         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02;
10388         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
10389         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
10390         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
10391         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12;
10392         // perform optimized matrix multiplication
10393         // compute last column first, because others do not depend on it
10394         dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30)
10395         ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31)
10396         ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32)
10397         ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33)
10398         ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
10399         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
10400         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
10401         ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22)
10402         // set the rest of the matrix elements
10403         ._m00(nm00)
10404         ._m01(nm01)
10405         ._m02(nm02)
10406         ._m03(nm03)
10407         ._m10(nm10)
10408         ._m11(nm11)
10409         ._m12(nm12)
10410         ._m13(nm13)
10411         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
10412         return dest;
10413     }
10414 
10415     /**
10416      * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, 
10417      * that aligns <code>+z</code> with <code>center - eye</code>.
10418      * <p>
10419      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
10420      * then the new matrix will be <code>M * L</code>. So when transforming a
10421      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
10422      * the lookat transformation will be applied first!
10423      * <p>
10424      * In order to set the matrix to a lookat transformation without post-multiplying it,
10425      * use {@link #setLookAtLH(double, double, double, double, double, double, double, double, double) setLookAtLH()}.
10426      * 
10427      * @see #lookAtLH(ref Vector3d, Vector3d, Vector3d)
10428      * @see #setLookAtLH(double, double, double, double, double, double, double, double, double)
10429      * 
10430      * @param eyeX
10431      *              the x-coordinate of the eye/camera location
10432      * @param eyeY
10433      *              the y-coordinate of the eye/camera location
10434      * @param eyeZ
10435      *              the z-coordinate of the eye/camera location
10436      * @param centerX
10437      *              the x-coordinate of the point to look at
10438      * @param centerY
10439      *              the y-coordinate of the point to look at
10440      * @param centerZ
10441      *              the z-coordinate of the point to look at
10442      * @param upX
10443      *              the x-coordinate of the up vector
10444      * @param upY
10445      *              the y-coordinate of the up vector
10446      * @param upZ
10447      *              the z-coordinate of the up vector
10448      * @return this
10449      */
10450     ref public Matrix4d lookAtLH(double eyeX, double eyeY, double eyeZ,
10451                              double centerX, double centerY, double centerZ,
10452                              double upX, double upY, double upZ) return {
10453         lookAtLH(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, this);
10454         return this;
10455     }
10456 
10457     /**
10458      * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, 
10459      * that aligns <code>+z</code> with <code>center - eye</code> and store the result in <code>dest</code>.
10460      * <p>
10461      * This method assumes <code>this</code> to be a perspective transformation, obtained via
10462      * {@link #frustumLH(double, double, double, double, double, double) frustumLH()} or {@link #perspectiveLH(double, double, double, double) perspectiveLH()} or
10463      * one of their overloads.
10464      * <p>
10465      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
10466      * then the new matrix will be <code>M * L</code>. So when transforming a
10467      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
10468      * the lookat transformation will be applied first!
10469      * <p>
10470      * In order to set the matrix to a lookat transformation without post-multiplying it,
10471      * use {@link #setLookAtLH(double, double, double, double, double, double, double, double, double) setLookAtLH()}.
10472      * 
10473      * @see #setLookAtLH(double, double, double, double, double, double, double, double, double)
10474      * 
10475      * @param eyeX
10476      *              the x-coordinate of the eye/camera location
10477      * @param eyeY
10478      *              the y-coordinate of the eye/camera location
10479      * @param eyeZ
10480      *              the z-coordinate of the eye/camera location
10481      * @param centerX
10482      *              the x-coordinate of the point to look at
10483      * @param centerY
10484      *              the y-coordinate of the point to look at
10485      * @param centerZ
10486      *              the z-coordinate of the point to look at
10487      * @param upX
10488      *              the x-coordinate of the up vector
10489      * @param upY
10490      *              the y-coordinate of the up vector
10491      * @param upZ
10492      *              the z-coordinate of the up vector
10493      * @param dest
10494      *          will hold the result
10495      * @return dest
10496      */
10497     public Matrix4d lookAtPerspectiveLH(double eyeX, double eyeY, double eyeZ,
10498             double centerX, double centerY, double centerZ,
10499             double upX, double upY, double upZ, ref Matrix4d dest) {
10500         // Compute direction from position to lookAt
10501         double dirX, dirY, dirZ;
10502         dirX = centerX - eyeX;
10503         dirY = centerY - eyeY;
10504         dirZ = centerZ - eyeZ;
10505         // Normalize direction
10506         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
10507         dirX *= invDirLength;
10508         dirY *= invDirLength;
10509         dirZ *= invDirLength;
10510         // left = up x direction
10511         double leftX, leftY, leftZ;
10512         leftX = upY * dirZ - upZ * dirY;
10513         leftY = upZ * dirX - upX * dirZ;
10514         leftZ = upX * dirY - upY * dirX;
10515         // normalize left
10516         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
10517         leftX *= invLeftLength;
10518         leftY *= invLeftLength;
10519         leftZ *= invLeftLength;
10520         // up = direction x left
10521         double upnX = dirY * leftZ - dirZ * leftY;
10522         double upnY = dirZ * leftX - dirX * leftZ;
10523         double upnZ = dirX * leftY - dirY * leftX;
10524 
10525         // calculate right matrix elements
10526         double rm00 = leftX;
10527         double rm01 = upnX;
10528         double rm02 = dirX;
10529         double rm10 = leftY;
10530         double rm11 = upnY;
10531         double rm12 = dirY;
10532         double rm20 = leftZ;
10533         double rm21 = upnZ;
10534         double rm22 = dirZ;
10535         double rm30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ);
10536         double rm31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ);
10537         double rm32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ);
10538 
10539         double nm00 = m00 * rm00;
10540         double nm01 = m11 * rm01;
10541         double nm02 = m22 * rm02;
10542         double nm03 = m23 * rm02;
10543         double nm10 = m00 * rm10;
10544         double nm11 = m11 * rm11;
10545         double nm12 = m22 * rm12;
10546         double nm13 = m23 * rm12;
10547         double nm20 = m00 * rm20;
10548         double nm21 = m11 * rm21;
10549         double nm22 = m22 * rm22;
10550         double nm23 = m23 * rm22;
10551         double nm30 = m00 * rm30;
10552         double nm31 = m11 * rm31;
10553         double nm32 = m22 * rm32 + m32;
10554         double nm33 = m23 * rm32;
10555         dest._m00(nm00)
10556         ._m01(nm01)
10557         ._m02(nm02)
10558         ._m03(nm03)
10559         ._m10(nm10)
10560         ._m11(nm11)
10561         ._m12(nm12)
10562         ._m13(nm13)
10563         ._m20(nm20)
10564         ._m21(nm21)
10565         ._m22(nm22)
10566         ._m23(nm23)
10567         ._m30(nm30)
10568         ._m31(nm31)
10569         ._m32(nm32)
10570         ._m33(nm33)
10571         ._properties(0);
10572 
10573         return dest;
10574     }
10575 
10576     /**
10577      * This method is equivalent to calling: <code>translate(w-1-2*x, h-1-2*y, 0).scale(w, h, 1)</code>
10578      * <p>
10579      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the created transformation matrix,
10580      * then the new matrix will be <code>M * T</code>. So when transforming a
10581      * vector <code>v</code> with the new matrix by using <code>M * T * v</code>, the
10582      * created transformation will be applied first!
10583      * 
10584      * @param x
10585      *             the tile's x coordinate/index (should be in <code>[0..w)</code>)
10586      * @param y
10587      *             the tile's y coordinate/index (should be in <code>[0..h)</code>)
10588      * @param w
10589      *             the number of tiles along the x axis
10590      * @param h
10591      *             the number of tiles along the y axis
10592      * @return this
10593      */
10594     ref public Matrix4d tile(int x, int y, int w, int h) return {
10595         tile(x, y, w, h, this);
10596         return this;
10597     }
10598     public Matrix4d tile(int x, int y, int w, int h, ref Matrix4d dest) {
10599         float tx = w - 1 - (x<<1), ty = h - 1 - (y<<1);
10600         return dest
10601         ._m30(Math.fma(m00, tx, Math.fma(m10, ty, m30)))
10602         ._m31(Math.fma(m01, tx, Math.fma(m11, ty, m31)))
10603         ._m32(Math.fma(m02, tx, Math.fma(m12, ty, m32)))
10604         ._m33(Math.fma(m03, tx, Math.fma(m13, ty, m33)))
10605         ._m00(m00 * w)
10606         ._m01(m01 * w)
10607         ._m02(m02 * w)
10608         ._m03(m03 * w)
10609         ._m10(m10 * h)
10610         ._m11(m11 * h)
10611         ._m12(m12 * h)
10612         ._m13(m13 * h)
10613         ._m20(m20)
10614         ._m21(m21)
10615         ._m22(m22)
10616         ._m23(m23)
10617         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
10618     }
10619 
10620     /**
10621      * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system
10622      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
10623      * <p>
10624      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
10625      * then the new matrix will be <code>M * P</code>. So when transforming a
10626      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
10627      * the perspective projection will be applied first!
10628      * <p>
10629      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
10630      * use {@link #setPerspective(double, double, double, double, bool) setPerspective}.
10631      * 
10632      * @see #setPerspective(double, double, double, double, bool)
10633      * 
10634      * @param fovy
10635      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
10636      * @param aspect
10637      *            the aspect ratio (i.e. width / height; must be greater than zero)
10638      * @param zNear
10639      *            near clipping plane distance. This value must be greater than zero.
10640      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
10641      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
10642      * @param zFar
10643      *            far clipping plane distance. This value must be greater than zero.
10644      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
10645      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
10646      * @param dest
10647      *            will hold the result
10648      * @param zZeroToOne
10649      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
10650      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
10651      * @return dest
10652      */
10653     public Matrix4d perspective(double fovy, double aspect, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
10654         if ((properties & PROPERTY_IDENTITY) != 0)
10655             return dest.setPerspective(fovy, aspect, zNear, zFar, zZeroToOne);
10656         return perspectiveGeneric(fovy, aspect, zNear, zFar, zZeroToOne, dest);
10657     }
10658     private Matrix4d perspectiveGeneric(double fovy, double aspect, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
10659         double h = Math.tan(fovy * 0.5);
10660         // calculate right matrix elements
10661         double rm00 = 1.0 / (h * aspect);
10662         double rm11 = 1.0 / h;
10663         double rm22;
10664         double rm32;
10665         bool farInf = zFar > 0 && Math.isInfinite(zFar);
10666         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
10667         if (farInf) {
10668             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
10669             double e = 1E-6;
10670             rm22 = e - 1.0;
10671             rm32 = (e - (zZeroToOne ? 1.0 : 2.0)) * zNear;
10672         } else if (nearInf) {
10673             double e = 1E-6;
10674             rm22 = (zZeroToOne ? 0.0 : 1.0) - e;
10675             rm32 = ((zZeroToOne ? 1.0 : 2.0) - e) * zFar;
10676         } else {
10677             rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar);
10678             rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar);
10679         }
10680         // perform optimized matrix multiplication
10681         double nm20 = m20 * rm22 - m30;
10682         double nm21 = m21 * rm22 - m31;
10683         double nm22 = m22 * rm22 - m32;
10684         double nm23 = m23 * rm22 - m33;
10685         dest._m00(m00 * rm00)
10686         ._m01(m01 * rm00)
10687         ._m02(m02 * rm00)
10688         ._m03(m03 * rm00)
10689         ._m10(m10 * rm11)
10690         ._m11(m11 * rm11)
10691         ._m12(m12 * rm11)
10692         ._m13(m13 * rm11)
10693         ._m30(m20 * rm32)
10694         ._m31(m21 * rm32)
10695         ._m32(m22 * rm32)
10696         ._m33(m23 * rm32)
10697         ._m20(nm20)
10698         ._m21(nm21)
10699         ._m22(nm22)
10700         ._m23(nm23)
10701         ._properties(properties & ~(PROPERTY_AFFINE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
10702         return dest;
10703     }
10704 
10705     /**
10706      * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system
10707      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
10708      * <p>
10709      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
10710      * then the new matrix will be <code>M * P</code>. So when transforming a
10711      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
10712      * the perspective projection will be applied first!
10713      * <p>
10714      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
10715      * use {@link #setPerspective(double, double, double, double) setPerspective}.
10716      * 
10717      * @see #setPerspective(double, double, double, double)
10718      * 
10719      * @param fovy
10720      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
10721      * @param aspect
10722      *            the aspect ratio (i.e. width / height; must be greater than zero)
10723      * @param zNear
10724      *            near clipping plane distance. This value must be greater than zero.
10725      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
10726      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
10727      * @param zFar
10728      *            far clipping plane distance. This value must be greater than zero.
10729      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
10730      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
10731      * @param dest
10732      *            will hold the result
10733      * @return dest
10734      */
10735     public Matrix4d perspective(double fovy, double aspect, double zNear, double zFar, ref Matrix4d dest) {
10736         return perspective(fovy, aspect, zNear, zFar, false, dest);
10737     }
10738 
10739     /**
10740      * Apply a symmetric perspective projection frustum transformation using for a right-handed coordinate system
10741      * using the given NDC z range to this matrix.
10742      * <p>
10743      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
10744      * then the new matrix will be <code>M * P</code>. So when transforming a
10745      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
10746      * the perspective projection will be applied first!
10747      * <p>
10748      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
10749      * use {@link #setPerspective(double, double, double, double, bool) setPerspective}.
10750      * 
10751      * @see #setPerspective(double, double, double, double, bool)
10752      * 
10753      * @param fovy
10754      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
10755      * @param aspect
10756      *            the aspect ratio (i.e. width / height; must be greater than zero)
10757      * @param zNear
10758      *            near clipping plane distance. This value must be greater than zero.
10759      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
10760      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
10761      * @param zFar
10762      *            far clipping plane distance. This value must be greater than zero.
10763      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
10764      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
10765      * @param zZeroToOne
10766      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
10767      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
10768      * @return this
10769      */
10770     ref public Matrix4d perspective(double fovy, double aspect, double zNear, double zFar, bool zZeroToOne) return {
10771         perspective(fovy, aspect, zNear, zFar, zZeroToOne, this);
10772         return this;
10773     }
10774 
10775     /**
10776      * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system
10777      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
10778      * <p>
10779      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
10780      * then the new matrix will be <code>M * P</code>. So when transforming a
10781      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
10782      * the perspective projection will be applied first!
10783      * <p>
10784      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
10785      * use {@link #setPerspective(double, double, double, double) setPerspective}.
10786      * 
10787      * @see #setPerspective(double, double, double, double)
10788      * 
10789      * @param fovy
10790      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
10791      * @param aspect
10792      *            the aspect ratio (i.e. width / height; must be greater than zero)
10793      * @param zNear
10794      *            near clipping plane distance. This value must be greater than zero.
10795      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
10796      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
10797      * @param zFar
10798      *            far clipping plane distance. This value must be greater than zero.
10799      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
10800      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
10801      * @return this
10802      */    
10803     ref public Matrix4d perspective(double fovy, double aspect, double zNear, double zFar) return {
10804         perspective(fovy, aspect, zNear, zFar, this);
10805         return this;
10806     }
10807 
10808     /**
10809      * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system
10810      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
10811      * <p>
10812      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
10813      * then the new matrix will be <code>M * P</code>. So when transforming a
10814      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
10815      * the perspective projection will be applied first!
10816      * <p>
10817      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
10818      * use {@link #setPerspectiveRect(double, double, double, double, bool) setPerspectiveRect}.
10819      * 
10820      * @see #setPerspectiveRect(double, double, double, double, bool)
10821      * 
10822      * @param width
10823      *            the width of the near frustum plane
10824      * @param height
10825      *            the height of the near frustum plane
10826      * @param zNear
10827      *            near clipping plane distance. This value must be greater than zero.
10828      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
10829      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
10830      * @param zFar
10831      *            far clipping plane distance. This value must be greater than zero.
10832      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
10833      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
10834      * @param dest
10835      *            will hold the result
10836      * @param zZeroToOne
10837      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
10838      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
10839      * @return dest
10840      */
10841     public Matrix4d perspectiveRect(double width, double height, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
10842         if ((properties & PROPERTY_IDENTITY) != 0)
10843             return dest.setPerspectiveRect(width, height, zNear, zFar, zZeroToOne);
10844         return perspectiveRectGeneric(width, height, zNear, zFar, zZeroToOne, dest);
10845     }
10846     private Matrix4d perspectiveRectGeneric(double width, double height, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
10847         double rm00 = (zNear + zNear) / width;
10848         double rm11 = (zNear + zNear) / height;
10849         double rm22, rm32;
10850         bool farInf = zFar > 0 && Math.isInfinite(zFar);
10851         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
10852         if (farInf) {
10853             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
10854             double e = 1E-6f;
10855             rm22 = e - 1.0;
10856             rm32 = (e - (zZeroToOne ? 1.0 : 2.0)) * zNear;
10857         } else if (nearInf) {
10858             double e = 1E-6f;
10859             rm22 = (zZeroToOne ? 0.0 : 1.0) - e;
10860             rm32 = ((zZeroToOne ? 1.0 : 2.0) - e) * zFar;
10861         } else {
10862             rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar);
10863             rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar);
10864         }
10865         // perform optimized matrix multiplication
10866         double nm20 = m20 * rm22 - m30;
10867         double nm21 = m21 * rm22 - m31;
10868         double nm22 = m22 * rm22 - m32;
10869         double nm23 = m23 * rm22 - m33;
10870         dest._m00(m00 * rm00)
10871         ._m01(m01 * rm00)
10872         ._m02(m02 * rm00)
10873         ._m03(m03 * rm00)
10874         ._m10(m10 * rm11)
10875         ._m11(m11 * rm11)
10876         ._m12(m12 * rm11)
10877         ._m13(m13 * rm11)
10878         ._m30(m20 * rm32)
10879         ._m31(m21 * rm32)
10880         ._m32(m22 * rm32)
10881         ._m33(m23 * rm32)
10882         ._m20(nm20)
10883         ._m21(nm21)
10884         ._m22(nm22)
10885         ._m23(nm23)
10886         ._properties(properties & ~(PROPERTY_AFFINE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
10887         return dest;
10888     }
10889 
10890     /**
10891      * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system
10892      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
10893      * <p>
10894      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
10895      * then the new matrix will be <code>M * P</code>. So when transforming a
10896      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
10897      * the perspective projection will be applied first!
10898      * <p>
10899      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
10900      * use {@link #setPerspectiveRect(double, double, double, double) setPerspectiveRect}.
10901      * 
10902      * @see #setPerspectiveRect(double, double, double, double)
10903      * 
10904      * @param width
10905      *            the width of the near frustum plane
10906      * @param height
10907      *            the height of the near frustum plane
10908      * @param zNear
10909      *            near clipping plane distance. This value must be greater than zero.
10910      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
10911      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
10912      * @param zFar
10913      *            far clipping plane distance. This value must be greater than zero.
10914      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
10915      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
10916      * @param dest
10917      *            will hold the result
10918      * @return dest
10919      */
10920     public Matrix4d perspectiveRect(double width, double height, double zNear, double zFar, ref Matrix4d dest) {
10921         return perspectiveRect(width, height, zNear, zFar, false, dest);
10922     }
10923 
10924     /**
10925      * Apply a symmetric perspective projection frustum transformation using for a right-handed coordinate system
10926      * using the given NDC z range to this matrix.
10927      * <p>
10928      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
10929      * then the new matrix will be <code>M * P</code>. So when transforming a
10930      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
10931      * the perspective projection will be applied first!
10932      * <p>
10933      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
10934      * use {@link #setPerspectiveRect(double, double, double, double, bool) setPerspectiveRect}.
10935      * 
10936      * @see #setPerspectiveRect(double, double, double, double, bool)
10937      * 
10938      * @param width
10939      *            the width of the near frustum plane
10940      * @param height
10941      *            the height of the near frustum plane
10942      * @param zNear
10943      *            near clipping plane distance. This value must be greater than zero.
10944      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
10945      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
10946      * @param zFar
10947      *            far clipping plane distance. This value must be greater than zero.
10948      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
10949      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
10950      * @param zZeroToOne
10951      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
10952      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
10953      * @return this
10954      */
10955     ref public Matrix4d perspectiveRect(double width, double height, double zNear, double zFar, bool zZeroToOne) return {
10956         perspectiveRect(width, height, zNear, zFar, zZeroToOne, this);
10957         return this;
10958     }
10959 
10960     /**
10961      * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system
10962      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
10963      * <p>
10964      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
10965      * then the new matrix will be <code>M * P</code>. So when transforming a
10966      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
10967      * the perspective projection will be applied first!
10968      * <p>
10969      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
10970      * use {@link #setPerspectiveRect(double, double, double, double) setPerspectiveRect}.
10971      * 
10972      * @see #setPerspectiveRect(double, double, double, double)
10973      * 
10974      * @param width
10975      *            the width of the near frustum plane
10976      * @param height
10977      *            the height of the near frustum plane
10978      * @param zNear
10979      *            near clipping plane distance. This value must be greater than zero.
10980      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
10981      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
10982      * @param zFar
10983      *            far clipping plane distance. This value must be greater than zero.
10984      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
10985      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
10986      * @return this
10987      */
10988     ref public Matrix4d perspectiveRect(double width, double height, double zNear, double zFar) return {
10989         perspectiveRect(width, height, zNear, zFar, this);
10990         return this;
10991     }
10992 
10993     /**
10994      * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system
10995      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
10996      * <p>
10997      * The given angles <code>offAngleX</code> and <code>offAngleY</code> are the horizontal and vertical angles between
10998      * the line of sight and the line given by the center of the near and far frustum planes. So, when <code>offAngleY</code>
10999      * is just <code>fovy/2</code> then the projection frustum is rotated towards +Y and the bottom frustum plane 
11000      * is parallel to the XZ-plane.
11001      * <p>
11002      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11003      * then the new matrix will be <code>M * P</code>. So when transforming a
11004      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11005      * the perspective projection will be applied first!
11006      * <p>
11007      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11008      * use {@link #setPerspectiveOffCenter(double, double, double, double, double, double, bool) setPerspectiveOffCenter}.
11009      * 
11010      * @see #setPerspectiveOffCenter(double, double, double, double, double, double, bool)
11011      * 
11012      * @param fovy
11013      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11014      * @param offAngleX
11015      *            the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes
11016      * @param offAngleY
11017      *            the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes
11018      * @param aspect
11019      *            the aspect ratio (i.e. width / height; must be greater than zero)
11020      * @param zNear
11021      *            near clipping plane distance. This value must be greater than zero.
11022      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11023      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11024      * @param zFar
11025      *            far clipping plane distance. This value must be greater than zero.
11026      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11027      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11028      * @param dest
11029      *            will hold the result
11030      * @param zZeroToOne
11031      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11032      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11033      * @return dest
11034      */
11035     public Matrix4d perspectiveOffCenter(double fovy, double offAngleX, double offAngleY, double aspect, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
11036         if ((properties & PROPERTY_IDENTITY) != 0)
11037             return dest.setPerspectiveOffCenter(fovy, offAngleX, offAngleY, aspect, zNear, zFar, zZeroToOne);
11038         return perspectiveOffCenterGeneric(fovy, offAngleX, offAngleY, aspect, zNear, zFar, zZeroToOne, dest);
11039     }
11040     private Matrix4d perspectiveOffCenterGeneric(double fovy, double offAngleX, double offAngleY, double aspect, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
11041         double h = Math.tan(fovy * 0.5);
11042         // calculate right matrix elements
11043         double xScale = 1.0 / (h * aspect);
11044         double yScale = 1.0 / h;
11045         double rm00 = xScale;
11046         double rm11 = yScale;
11047         double offX = Math.tan(offAngleX), offY = Math.tan(offAngleY);
11048         double rm20 = offX * xScale;
11049         double rm21 = offY * yScale;
11050         double rm22;
11051         double rm32;
11052         bool farInf = zFar > 0 && Math.isInfinite(zFar);
11053         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
11054         if (farInf) {
11055             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
11056             double e = 1E-6;
11057             rm22 = e - 1.0;
11058             rm32 = (e - (zZeroToOne ? 1.0 : 2.0)) * zNear;
11059         } else if (nearInf) {
11060             double e = 1E-6;
11061             rm22 = (zZeroToOne ? 0.0 : 1.0) - e;
11062             rm32 = ((zZeroToOne ? 1.0 : 2.0) - e) * zFar;
11063         } else {
11064             rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar);
11065             rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar);
11066         }
11067         // perform optimized matrix multiplication
11068         double nm20 = m00 * rm20 + m10 * rm21 + m20 * rm22 - m30;
11069         double nm21 = m01 * rm20 + m11 * rm21 + m21 * rm22 - m31;
11070         double nm22 = m02 * rm20 + m12 * rm21 + m22 * rm22 - m32;
11071         double nm23 = m03 * rm20 + m13 * rm21 + m23 * rm22 - m33;
11072         dest._m00(m00 * rm00)
11073         ._m01(m01 * rm00)
11074         ._m02(m02 * rm00)
11075         ._m03(m03 * rm00)
11076         ._m10(m10 * rm11)
11077         ._m11(m11 * rm11)
11078         ._m12(m12 * rm11)
11079         ._m13(m13 * rm11)
11080         ._m30(m20 * rm32)
11081         ._m31(m21 * rm32)
11082         ._m32(m22 * rm32)
11083         ._m33(m23 * rm32)
11084         ._m20(nm20)
11085         ._m21(nm21)
11086         ._m22(nm22)
11087         ._m23(nm23)
11088         ._properties(properties & ~(PROPERTY_AFFINE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION
11089                 | PROPERTY_ORTHONORMAL | (rm20 == 0.0 && rm21 == 0.0 ? 0 : PROPERTY_PERSPECTIVE)));
11090         return dest;
11091     }
11092 
11093     /**
11094      * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system
11095      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
11096      * <p>
11097      * The given angles <code>offAngleX</code> and <code>offAngleY</code> are the horizontal and vertical angles between
11098      * the line of sight and the line given by the center of the near and far frustum planes. So, when <code>offAngleY</code>
11099      * is just <code>fovy/2</code> then the projection frustum is rotated towards +Y and the bottom frustum plane 
11100      * is parallel to the XZ-plane.
11101      * <p>
11102      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11103      * then the new matrix will be <code>M * P</code>. So when transforming a
11104      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11105      * the perspective projection will be applied first!
11106      * <p>
11107      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11108      * use {@link #setPerspectiveOffCenter(double, double, double, double, double, double) setPerspectiveOffCenter}.
11109      * 
11110      * @see #setPerspectiveOffCenter(double, double, double, double, double, double)
11111      * 
11112      * @param fovy
11113      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11114      * @param offAngleX
11115      *            the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes
11116      * @param offAngleY
11117      *            the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes
11118      * @param aspect
11119      *            the aspect ratio (i.e. width / height; must be greater than zero)
11120      * @param zNear
11121      *            near clipping plane distance. This value must be greater than zero.
11122      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11123      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11124      * @param zFar
11125      *            far clipping plane distance. This value must be greater than zero.
11126      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11127      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11128      * @param dest
11129      *            will hold the result
11130      * @return dest
11131      */
11132     public Matrix4d perspectiveOffCenter(double fovy, double offAngleX, double offAngleY, double aspect, double zNear, double zFar, ref Matrix4d dest) {
11133         return perspectiveOffCenter(fovy, offAngleX, offAngleY, aspect, zNear, zFar, false, dest);
11134     }
11135 
11136     /**
11137      * Apply an asymmetric off-center perspective projection frustum transformation using for a right-handed coordinate system
11138      * using the given NDC z range to this matrix.
11139      * <p>
11140      * The given angles <code>offAngleX</code> and <code>offAngleY</code> are the horizontal and vertical angles between
11141      * the line of sight and the line given by the center of the near and far frustum planes. So, when <code>offAngleY</code>
11142      * is just <code>fovy/2</code> then the projection frustum is rotated towards +Y and the bottom frustum plane 
11143      * is parallel to the XZ-plane.
11144      * <p>
11145      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11146      * then the new matrix will be <code>M * P</code>. So when transforming a
11147      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11148      * the perspective projection will be applied first!
11149      * <p>
11150      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11151      * use {@link #setPerspectiveOffCenter(double, double, double, double, double, double, bool) setPerspectiveOffCenter}.
11152      * 
11153      * @see #setPerspectiveOffCenter(double, double, double, double, double, double, bool)
11154      * 
11155      * @param fovy
11156      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11157      * @param offAngleX
11158      *            the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes
11159      * @param offAngleY
11160      *            the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes
11161      * @param aspect
11162      *            the aspect ratio (i.e. width / height; must be greater than zero)
11163      * @param zNear
11164      *            near clipping plane distance. This value must be greater than zero.
11165      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11166      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11167      * @param zFar
11168      *            far clipping plane distance. This value must be greater than zero.
11169      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11170      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11171      * @param zZeroToOne
11172      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11173      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11174      * @return this
11175      */
11176     ref public Matrix4d perspectiveOffCenter(double fovy, double offAngleX, double offAngleY, double aspect, double zNear, double zFar, bool zZeroToOne) return {
11177         perspectiveOffCenter(fovy, offAngleX, offAngleY, aspect, zNear, zFar, zZeroToOne, this);
11178         return this;
11179     }
11180 
11181     /**
11182      * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system
11183      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
11184      * <p>
11185      * The given angles <code>offAngleX</code> and <code>offAngleY</code> are the horizontal and vertical angles between
11186      * the line of sight and the line given by the center of the near and far frustum planes. So, when <code>offAngleY</code>
11187      * is just <code>fovy/2</code> then the projection frustum is rotated towards +Y and the bottom frustum plane 
11188      * is parallel to the XZ-plane.
11189      * <p>
11190      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11191      * then the new matrix will be <code>M * P</code>. So when transforming a
11192      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11193      * the perspective projection will be applied first!
11194      * <p>
11195      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11196      * use {@link #setPerspectiveOffCenter(double, double, double, double, double, double) setPerspectiveOffCenter}.
11197      * 
11198      * @see #setPerspectiveOffCenter(double, double, double, double, double, double)
11199      * 
11200      * @param fovy
11201      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11202      * @param offAngleX
11203      *            the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes
11204      * @param offAngleY
11205      *            the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes
11206      * @param aspect
11207      *            the aspect ratio (i.e. width / height; must be greater than zero)
11208      * @param zNear
11209      *            near clipping plane distance. This value must be greater than zero.
11210      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11211      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11212      * @param zFar
11213      *            far clipping plane distance. This value must be greater than zero.
11214      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11215      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11216      * @return this
11217      */
11218     ref public Matrix4d perspectiveOffCenter(double fovy, double offAngleX, double offAngleY, double aspect, double zNear, double zFar) return {
11219         perspectiveOffCenter(fovy, offAngleX, offAngleY, aspect, zNear, zFar, this);
11220         return this;
11221     }
11222 
11223     /**
11224      * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system
11225      * using the given NDC z range to this matrix.
11226      * <p>
11227      * The given angles <code>angleLeft</code> and <code>angleRight</code> are the horizontal angles between
11228      * the left and right frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11229      * The angles <code>angleDown</code> and <code>angleUp</code> are the vertical angles between
11230      * the bottom and top frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11231      * <p>
11232      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11233      * then the new matrix will be <code>M * P</code>. So when transforming a
11234      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11235      * the perspective projection will be applied first!
11236      * <p>
11237      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11238      * use {@link #setPerspectiveOffCenterFov(double, double, double, double, double, double, bool) setPerspectiveOffCenterFov}.
11239      * 
11240      * @see #setPerspectiveOffCenterFov(double, double, double, double, double, double, bool)
11241      * 
11242      * @param angleLeft
11243      *            the horizontal angle between left frustum plane and a line perpendicular to the near/far frustum planes.
11244      *            For a symmetric frustum, this value is negative.
11245      * @param angleRight
11246      *            the horizontal angle between right frustum plane and a line perpendicular to the near/far frustum planes
11247      * @param angleDown
11248      *            the vertical angle between bottom frustum plane and a line perpendicular to the near/far frustum planes.
11249      *            For a symmetric frustum, this value is negative.
11250      * @param angleUp
11251      *            the vertical angle between top frustum plane and a line perpendicular to the near/far frustum planes
11252      * @param zNear
11253      *            near clipping plane distance. This value must be greater than zero.
11254      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11255      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11256      * @param zFar
11257      *            far clipping plane distance. This value must be greater than zero.
11258      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11259      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11260      * @param zZeroToOne
11261      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11262      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11263      * @return this
11264      */
11265     ref public Matrix4d perspectiveOffCenterFov(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar, bool zZeroToOne) return {
11266         perspectiveOffCenterFov(angleLeft, angleRight, angleDown, angleUp, zNear, zFar, zZeroToOne, this);
11267         return this;
11268     }
11269     public Matrix4d perspectiveOffCenterFov(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
11270         return frustum(Math.tan(angleLeft)*zNear, Math.tan(angleRight)*zNear, Math.tan(angleDown)*zNear, Math.tan(angleUp)*zNear, zNear, zFar, zZeroToOne, dest);
11271     }
11272 
11273     /**
11274      * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system
11275      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
11276      * <p>
11277      * The given angles <code>angleLeft</code> and <code>angleRight</code> are the horizontal angles between
11278      * the left and right frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11279      * The angles <code>angleDown</code> and <code>angleUp</code> are the vertical angles between
11280      * the bottom and top frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11281      * <p>
11282      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11283      * then the new matrix will be <code>M * P</code>. So when transforming a
11284      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11285      * the perspective projection will be applied first!
11286      * <p>
11287      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11288      * use {@link #setPerspectiveOffCenterFov(double, double, double, double, double, double) setPerspectiveOffCenterFov}.
11289      * 
11290      * @see #setPerspectiveOffCenterFov(double, double, double, double, double, double)
11291      * 
11292      * @param angleLeft
11293      *            the horizontal angle between left frustum plane and a line perpendicular to the near/far frustum planes.
11294      *            For a symmetric frustum, this value is negative.
11295      * @param angleRight
11296      *            the horizontal angle between right frustum plane and a line perpendicular to the near/far frustum planes
11297      * @param angleDown
11298      *            the vertical angle between bottom frustum plane and a line perpendicular to the near/far frustum planes.
11299      *            For a symmetric frustum, this value is negative.
11300      * @param angleUp
11301      *            the vertical angle between top frustum plane and a line perpendicular to the near/far frustum planes
11302      * @param zNear
11303      *            near clipping plane distance. This value must be greater than zero.
11304      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11305      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11306      * @param zFar
11307      *            far clipping plane distance. This value must be greater than zero.
11308      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11309      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11310      * @return this
11311      */
11312     ref public Matrix4d perspectiveOffCenterFov(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar) return {
11313         perspectiveOffCenterFov(angleLeft, angleRight, angleDown, angleUp, zNear, zFar, this);
11314         return this;
11315     }
11316     public Matrix4d perspectiveOffCenterFov(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar, ref Matrix4d dest) {
11317         return frustum(Math.tan(angleLeft)*zNear, Math.tan(angleRight)*zNear, Math.tan(angleDown)*zNear, Math.tan(angleUp)*zNear, zNear, zFar, dest);
11318     }
11319 
11320     /**
11321      * Apply an asymmetric off-center perspective projection frustum transformation for a left-handed coordinate system
11322      * using the given NDC z range to this matrix.
11323      * <p>
11324      * The given angles <code>angleLeft</code> and <code>angleRight</code> are the horizontal angles between
11325      * the left and right frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11326      * The angles <code>angleDown</code> and <code>angleUp</code> are the vertical angles between
11327      * the bottom and top frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11328      * <p>
11329      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11330      * then the new matrix will be <code>M * P</code>. So when transforming a
11331      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11332      * the perspective projection will be applied first!
11333      * <p>
11334      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11335      * use {@link #setPerspectiveOffCenterFovLH(double, double, double, double, double, double, bool) setPerspectiveOffCenterFovLH}.
11336      * 
11337      * @see #setPerspectiveOffCenterFovLH(double, double, double, double, double, double, bool)
11338      * 
11339      * @param angleLeft
11340      *            the horizontal angle between left frustum plane and a line perpendicular to the near/far frustum planes.
11341      *            For a symmetric frustum, this value is negative.
11342      * @param angleRight
11343      *            the horizontal angle between right frustum plane and a line perpendicular to the near/far frustum planes
11344      * @param angleDown
11345      *            the vertical angle between bottom frustum plane and a line perpendicular to the near/far frustum planes.
11346      *            For a symmetric frustum, this value is negative.
11347      * @param angleUp
11348      *            the vertical angle between top frustum plane and a line perpendicular to the near/far frustum planes
11349      * @param zNear
11350      *            near clipping plane distance. This value must be greater than zero.
11351      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11352      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11353      * @param zFar
11354      *            far clipping plane distance. This value must be greater than zero.
11355      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11356      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11357      * @param zZeroToOne
11358      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11359      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11360      * @return this
11361      */
11362     ref public Matrix4d perspectiveOffCenterFovLH(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar, bool zZeroToOne) return {
11363         perspectiveOffCenterFovLH(angleLeft, angleRight, angleDown, angleUp, zNear, zFar, zZeroToOne, this);
11364         return this;
11365     }
11366     public Matrix4d perspectiveOffCenterFovLH(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
11367         return frustumLH(Math.tan(angleLeft)*zNear, Math.tan(angleRight)*zNear, Math.tan(angleDown)*zNear, Math.tan(angleUp)*zNear, zNear, zFar, zZeroToOne, dest);
11368     }
11369 
11370     /**
11371      * Apply an asymmetric off-center perspective projection frustum transformation for a left-handed coordinate system
11372      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
11373      * <p>
11374      * The given angles <code>angleLeft</code> and <code>angleRight</code> are the horizontal angles between
11375      * the left and right frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11376      * The angles <code>angleDown</code> and <code>angleUp</code> are the vertical angles between
11377      * the bottom and top frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11378      * <p>
11379      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11380      * then the new matrix will be <code>M * P</code>. So when transforming a
11381      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11382      * the perspective projection will be applied first!
11383      * <p>
11384      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11385      * use {@link #setPerspectiveOffCenterFovLH(double, double, double, double, double, double) setPerspectiveOffCenterFovLH}.
11386      * 
11387      * @see #setPerspectiveOffCenterFovLH(double, double, double, double, double, double)
11388      * 
11389      * @param angleLeft
11390      *            the horizontal angle between left frustum plane and a line perpendicular to the near/far frustum planes.
11391      *            For a symmetric frustum, this value is negative.
11392      * @param angleRight
11393      *            the horizontal angle between right frustum plane and a line perpendicular to the near/far frustum planes
11394      * @param angleDown
11395      *            the vertical angle between bottom frustum plane and a line perpendicular to the near/far frustum planes.
11396      *            For a symmetric frustum, this value is negative.
11397      * @param angleUp
11398      *            the vertical angle between top frustum plane and a line perpendicular to the near/far frustum planes
11399      * @param zNear
11400      *            near clipping plane distance. This value must be greater than zero.
11401      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11402      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11403      * @param zFar
11404      *            far clipping plane distance. This value must be greater than zero.
11405      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11406      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11407      * @return this
11408      */
11409     ref public Matrix4d perspectiveOffCenterFovLH(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar) return {
11410         perspectiveOffCenterFovLH(angleLeft, angleRight, angleDown, angleUp, zNear, zFar, this);
11411         return this;
11412     }
11413     public Matrix4d perspectiveOffCenterFovLH(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar, ref Matrix4d dest) {
11414         return frustumLH(Math.tan(angleLeft)*zNear, Math.tan(angleRight)*zNear, Math.tan(angleDown)*zNear, Math.tan(angleUp)*zNear, zNear, zFar, dest);
11415     }
11416 
11417     /**
11418      * Set this matrix to be a symmetric perspective projection frustum transformation for a right-handed coordinate system
11419      * using the given NDC z range.
11420      * <p>
11421      * In order to apply the perspective projection transformation to an existing transformation,
11422      * use {@link #perspective(double, double, double, double, bool) perspective()}.
11423      * 
11424      * @see #perspective(double, double, double, double, bool)
11425      * 
11426      * @param fovy
11427      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11428      * @param aspect
11429      *            the aspect ratio (i.e. width / height; must be greater than zero)
11430      * @param zNear
11431      *            near clipping plane distance. This value must be greater than zero.
11432      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11433      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11434      * @param zFar
11435      *            far clipping plane distance. This value must be greater than zero.
11436      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11437      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11438      * @param zZeroToOne
11439      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11440      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11441      * @return this
11442      */
11443     ref public Matrix4d setPerspective(double fovy, double aspect, double zNear, double zFar, bool zZeroToOne) return {
11444         double h = Math.tan(fovy * 0.5);
11445         _m00(1.0 / (h * aspect)).
11446         _m01(0.0).
11447         _m02(0.0).
11448         _m03(0.0).
11449         _m10(0.0).
11450         _m11(1.0 / h).
11451         _m12(0.0).
11452         _m13(0.0).
11453         _m20(0.0).
11454         _m21(0.0);
11455         bool farInf = zFar > 0 && Math.isInfinite(zFar);
11456         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
11457         if (farInf) {
11458             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
11459             double e = 1E-6;
11460             _m22(e - 1.0).
11461             _m32((e - (zZeroToOne ? 1.0 : 2.0)) * zNear);
11462         } else if (nearInf) {
11463             double e = 1E-6;
11464             _m22((zZeroToOne ? 0.0 : 1.0) - e).
11465             _m32(((zZeroToOne ? 1.0 : 2.0) - e) * zFar);
11466         } else {
11467             _m22((zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar)).
11468             _m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar));
11469         }
11470         _m23(-1.0).
11471         _m30(0.0).
11472         _m31(0.0).
11473         _m33(0.0).
11474         properties = PROPERTY_PERSPECTIVE;
11475         return this;
11476     }
11477 
11478     /**
11479      * Set this matrix to be a symmetric perspective projection frustum transformation for a right-handed coordinate system
11480      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
11481      * <p>
11482      * In order to apply the perspective projection transformation to an existing transformation,
11483      * use {@link #perspective(double, double, double, double) perspective()}.
11484      * 
11485      * @see #perspective(double, double, double, double)
11486      * 
11487      * @param fovy
11488      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11489      * @param aspect
11490      *            the aspect ratio (i.e. width / height; must be greater than zero)
11491      * @param zNear
11492      *            near clipping plane distance. This value must be greater than zero.
11493      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11494      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11495      * @param zFar
11496      *            far clipping plane distance. This value must be greater than zero.
11497      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11498      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11499      * @return this
11500      */
11501     ref public Matrix4d setPerspective(double fovy, double aspect, double zNear, double zFar) return {
11502         return setPerspective(fovy, aspect, zNear, zFar, false);
11503     }
11504 
11505     /**
11506      * Set this matrix to be a symmetric perspective projection frustum transformation for a right-handed coordinate system
11507      * using the given NDC z range.
11508      * <p>
11509      * In order to apply the perspective projection transformation to an existing transformation,
11510      * use {@link #perspectiveRect(double, double, double, double, bool) perspectiveRect()}.
11511      * 
11512      * @see #perspectiveRect(double, double, double, double, bool)
11513      * 
11514      * @param width
11515      *            the width of the near frustum plane
11516      * @param height
11517      *            the height of the near frustum plane
11518      * @param zNear
11519      *            near clipping plane distance. This value must be greater than zero.
11520      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11521      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11522      * @param zFar
11523      *            far clipping plane distance. This value must be greater than zero.
11524      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11525      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11526      * @param zZeroToOne
11527      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11528      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11529      * @return this
11530      */
11531     ref public Matrix4d setPerspectiveRect(double width, double height, double zNear, double zFar, bool zZeroToOne) return {
11532         this.zero();
11533         this._m00((zNear + zNear) / width);
11534         this._m11((zNear + zNear) / height);
11535         bool farInf = zFar > 0 && Math.isInfinite(zFar);
11536         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
11537         if (farInf) {
11538             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
11539             double e = 1E-6;
11540             this._m22(e - 1.0);
11541             this._m32((e - (zZeroToOne ? 1.0 : 2.0)) * zNear);
11542         } else if (nearInf) {
11543             double e = 1E-6f;
11544             this._m22((zZeroToOne ? 0.0 : 1.0) - e);
11545             this._m32(((zZeroToOne ? 1.0 : 2.0) - e) * zFar);
11546         } else {
11547             this._m22((zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar));
11548             this._m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar));
11549         }
11550         this._m23(-1.0);
11551         properties = PROPERTY_PERSPECTIVE;
11552         return this;
11553     }
11554 
11555     /**
11556      * Set this matrix to be a symmetric perspective projection frustum transformation for a right-handed coordinate system
11557      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
11558      * <p>
11559      * In order to apply the perspective projection transformation to an existing transformation,
11560      * use {@link #perspectiveRect(double, double, double, double) perspectiveRect()}.
11561      * 
11562      * @see #perspectiveRect(double, double, double, double)
11563      * 
11564      * @param width
11565      *            the width of the near frustum plane
11566      * @param height
11567      *            the height of the near frustum plane
11568      * @param zNear
11569      *            near clipping plane distance. This value must be greater than zero.
11570      *            If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11571      *            In that case, <code>zFar</code> may not also be {@link Float#POSITIVE_INFINITY}.
11572      * @param zFar
11573      *            far clipping plane distance. This value must be greater than zero.
11574      *            If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11575      *            In that case, <code>zNear</code> may not also be {@link Float#POSITIVE_INFINITY}.
11576      * @return this
11577      */
11578     ref public Matrix4d setPerspectiveRect(double width, double height, double zNear, double zFar) return {
11579         return setPerspectiveRect(width, height, zNear, zFar, false);
11580     }
11581 
11582     /**
11583      * Set this matrix to be an asymmetric off-center perspective projection frustum transformation for a right-handed
11584      * coordinate system using OpenGL's NDC z range of <code>[-1..+1]</code>.
11585      * <p>
11586      * The given angles <code>offAngleX</code> and <code>offAngleY</code> are the horizontal and vertical angles between
11587      * the line of sight and the line given by the center of the near and far frustum planes. So, when <code>offAngleY</code>
11588      * is just <code>fovy/2</code> then the projection frustum is rotated towards +Y and the bottom frustum plane 
11589      * is parallel to the XZ-plane.
11590      * <p>
11591      * In order to apply the perspective projection transformation to an existing transformation,
11592      * use {@link #perspectiveOffCenter(double, double, double, double, double, double) perspectiveOffCenter()}.
11593      * 
11594      * @see #perspectiveOffCenter(double, double, double, double, double, double)
11595      * 
11596      * @param fovy
11597      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11598      * @param offAngleX
11599      *            the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes
11600      * @param offAngleY
11601      *            the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes
11602      * @param aspect
11603      *            the aspect ratio (i.e. width / height; must be greater than zero)
11604      * @param zNear
11605      *            near clipping plane distance. This value must be greater than zero.
11606      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11607      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11608      * @param zFar
11609      *            far clipping plane distance. This value must be greater than zero.
11610      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11611      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11612      * @return this
11613      */
11614     ref public Matrix4d setPerspectiveOffCenter(double fovy, double offAngleX, double offAngleY,
11615             double aspect, double zNear, double zFar) return {
11616         return setPerspectiveOffCenter(fovy, offAngleX, offAngleY, aspect, zNear, zFar, false);
11617     }
11618     /**
11619      * Set this matrix to be an asymmetric off-center perspective projection frustum transformation for a right-handed
11620      * coordinate system using the given NDC z range.
11621      * <p>
11622      * The given angles <code>offAngleX</code> and <code>offAngleY</code> are the horizontal and vertical angles between
11623      * the line of sight and the line given by the center of the near and far frustum planes. So, when <code>offAngleY</code>
11624      * is just <code>fovy/2</code> then the projection frustum is rotated towards +Y and the bottom frustum plane 
11625      * is parallel to the XZ-plane.
11626      * <p>
11627      * In order to apply the perspective projection transformation to an existing transformation,
11628      * use {@link #perspectiveOffCenter(double, double, double, double, double, double) perspectiveOffCenter()}.
11629      * 
11630      * @see #perspectiveOffCenter(double, double, double, double, double, double)
11631      * 
11632      * @param fovy
11633      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11634      * @param offAngleX
11635      *            the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes
11636      * @param offAngleY
11637      *            the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes
11638      * @param aspect
11639      *            the aspect ratio (i.e. width / height; must be greater than zero)
11640      * @param zNear
11641      *            near clipping plane distance. This value must be greater than zero.
11642      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11643      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11644      * @param zFar
11645      *            far clipping plane distance. This value must be greater than zero.
11646      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11647      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11648      * @param zZeroToOne
11649      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11650      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11651      * @return this
11652      */
11653     ref public Matrix4d setPerspectiveOffCenter(double fovy, double offAngleX, double offAngleY,
11654                                             double aspect, double zNear, double zFar, bool zZeroToOne) return {
11655         this.zero();
11656         double h = Math.tan(fovy * 0.5);
11657         double xScale = 1.0 / (h * aspect), yScale = 1.0 / h;
11658         _m00(xScale).
11659         _m11(yScale);
11660         double offX = Math.tan(offAngleX), offY = Math.tan(offAngleY);
11661         _m20(offX * xScale).
11662         _m21(offY * yScale);
11663         bool farInf = zFar > 0 && Math.isInfinite(zFar);
11664         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
11665         if (farInf) {
11666             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
11667             double e = 1E-6;
11668             _m22(e - 1.0).
11669             _m32((e - (zZeroToOne ? 1.0 : 2.0)) * zNear);
11670         } else if (nearInf) {
11671             double e = 1E-6;
11672             _m22((zZeroToOne ? 0.0 : 1.0) - e).
11673             _m32(((zZeroToOne ? 1.0 : 2.0) - e) * zFar);
11674         } else {
11675             _m22((zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar)).
11676             _m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar));
11677         }
11678         _m23(-1.0).
11679         _m30(0.0).
11680         _m31(0.0).
11681         _m33(0.0).
11682         properties = offAngleX == 0.0 && offAngleY == 0.0 ? PROPERTY_PERSPECTIVE : 0;
11683         return this;
11684     }
11685 
11686     /**
11687      * Set this matrix to be an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate
11688      * system using OpenGL's NDC z range of <code>[-1..+1]</code>.
11689      * <p>
11690      * The given angles <code>angleLeft</code> and <code>angleRight</code> are the horizontal angles between
11691      * the left and right frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11692      * The angles <code>angleDown</code> and <code>angleUp</code> are the vertical angles between
11693      * the bottom and top frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11694      * <p>
11695      * In order to apply the perspective projection transformation to an existing transformation,
11696      * use {@link #perspectiveOffCenterFov(double, double, double, double, double, double) perspectiveOffCenterFov()}.
11697      * 
11698      * @see #perspectiveOffCenterFov(double, double, double, double, double, double)
11699      * 
11700      * @param angleLeft
11701      *            the horizontal angle between left frustum plane and a line perpendicular to the near/far frustum planes.
11702      *            For a symmetric frustum, this value is negative.
11703      * @param angleRight
11704      *            the horizontal angle between right frustum plane and a line perpendicular to the near/far frustum planes
11705      * @param angleDown
11706      *            the vertical angle between bottom frustum plane and a line perpendicular to the near/far frustum planes.
11707      *            For a symmetric frustum, this value is negative.
11708      * @param angleUp
11709      *            the vertical angle between top frustum plane and a line perpendicular to the near/far frustum planes
11710      * @param zNear
11711      *            near clipping plane distance. This value must be greater than zero.
11712      *            If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11713      *            In that case, <code>zFar</code> may not also be {@link Float#POSITIVE_INFINITY}.
11714      * @param zFar
11715      *            far clipping plane distance. This value must be greater than zero.
11716      *            If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11717      *            In that case, <code>zNear</code> may not also be {@link Float#POSITIVE_INFINITY}.
11718      * @return this
11719      */
11720     ref public Matrix4d setPerspectiveOffCenterFov(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar) return {
11721         return setPerspectiveOffCenterFov(angleLeft, angleRight, angleDown, angleUp, zNear, zFar, false);
11722     }
11723     /**
11724      * Set this matrix to be an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system
11725      * using the given NDC z range.
11726      * <p>
11727      * The given angles <code>angleLeft</code> and <code>angleRight</code> are the horizontal angles between
11728      * the left and right frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11729      * The angles <code>angleDown</code> and <code>angleUp</code> are the vertical angles between
11730      * the bottom and top frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11731      * <p>
11732      * In order to apply the perspective projection transformation to an existing transformation,
11733      * use {@link #perspectiveOffCenterFov(double, double, double, double, double, double, bool) perspectiveOffCenterFov()}.
11734      * 
11735      * @see #perspectiveOffCenterFov(double, double, double, double, double, double, bool)
11736      * 
11737      * @param angleLeft
11738      *            the horizontal angle between left frustum plane and a line perpendicular to the near/far frustum planes.
11739      *            For a symmetric frustum, this value is negative.
11740      * @param angleRight
11741      *            the horizontal angle between right frustum plane and a line perpendicular to the near/far frustum planes
11742      * @param angleDown
11743      *            the vertical angle between bottom frustum plane and a line perpendicular to the near/far frustum planes.
11744      *            For a symmetric frustum, this value is negative.
11745      * @param angleUp
11746      *            the vertical angle between top frustum plane and a line perpendicular to the near/far frustum planes
11747      * @param zNear
11748      *            near clipping plane distance. This value must be greater than zero.
11749      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11750      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11751      * @param zFar
11752      *            far clipping plane distance. This value must be greater than zero.
11753      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11754      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11755      * @param zZeroToOne
11756      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11757      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11758      * @return this
11759      */
11760     ref public Matrix4d setPerspectiveOffCenterFov(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar, bool zZeroToOne) return {
11761         return setFrustum(Math.tan(angleLeft)*zNear, Math.tan(angleRight)*zNear, Math.tan(angleDown)*zNear, Math.tan(angleUp)*zNear, zNear, zFar, zZeroToOne);
11762     }
11763 
11764     /**
11765      * Set this matrix to be an asymmetric off-center perspective projection frustum transformation for a left-handed coordinate
11766      * system using OpenGL's NDC z range of <code>[-1..+1]</code>.
11767      * <p>
11768      * The given angles <code>angleLeft</code> and <code>angleRight</code> are the horizontal angles between
11769      * the left and right frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11770      * The angles <code>angleDown</code> and <code>angleUp</code> are the vertical angles between
11771      * the bottom and top frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11772      * <p>
11773      * In order to apply the perspective projection transformation to an existing transformation,
11774      * use {@link #perspectiveOffCenterFovLH(double, double, double, double, double, double) perspectiveOffCenterFovLH()}.
11775      * 
11776      * @see #perspectiveOffCenterFovLH(double, double, double, double, double, double)
11777      * 
11778      * @param angleLeft
11779      *            the horizontal angle between left frustum plane and a line perpendicular to the near/far frustum planes.
11780      *            For a symmetric frustum, this value is negative.
11781      * @param angleRight
11782      *            the horizontal angle between right frustum plane and a line perpendicular to the near/far frustum planes
11783      * @param angleDown
11784      *            the vertical angle between bottom frustum plane and a line perpendicular to the near/far frustum planes.
11785      *            For a symmetric frustum, this value is negative.
11786      * @param angleUp
11787      *            the vertical angle between top frustum plane and a line perpendicular to the near/far frustum planes
11788      * @param zNear
11789      *            near clipping plane distance. This value must be greater than zero.
11790      *            If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11791      *            In that case, <code>zFar</code> may not also be {@link Float#POSITIVE_INFINITY}.
11792      * @param zFar
11793      *            far clipping plane distance. This value must be greater than zero.
11794      *            If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11795      *            In that case, <code>zNear</code> may not also be {@link Float#POSITIVE_INFINITY}.
11796      * @return this
11797      */
11798     ref public Matrix4d setPerspectiveOffCenterFovLH(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar) return {
11799         return setPerspectiveOffCenterFovLH(angleLeft, angleRight, angleDown, angleUp, zNear, zFar, false);
11800     }
11801     /**
11802      * Set this matrix to be an asymmetric off-center perspective projection frustum transformation for a left-handed coordinate system
11803      * using the given NDC z range.
11804      * <p>
11805      * The given angles <code>angleLeft</code> and <code>angleRight</code> are the horizontal angles between
11806      * the left and right frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11807      * The angles <code>angleDown</code> and <code>angleUp</code> are the vertical angles between
11808      * the bottom and top frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11809      * <p>
11810      * In order to apply the perspective projection transformation to an existing transformation,
11811      * use {@link #perspectiveOffCenterFovLH(double, double, double, double, double, double, bool) perspectiveOffCenterFovLH()}.
11812      * 
11813      * @see #perspectiveOffCenterFovLH(double, double, double, double, double, double, bool)
11814      * 
11815      * @param angleLeft
11816      *            the horizontal angle between left frustum plane and a line perpendicular to the near/far frustum planes.
11817      *            For a symmetric frustum, this value is negative.
11818      * @param angleRight
11819      *            the horizontal angle between right frustum plane and a line perpendicular to the near/far frustum planes
11820      * @param angleDown
11821      *            the vertical angle between bottom frustum plane and a line perpendicular to the near/far frustum planes.
11822      *            For a symmetric frustum, this value is negative.
11823      * @param angleUp
11824      *            the vertical angle between top frustum plane and a line perpendicular to the near/far frustum planes
11825      * @param zNear
11826      *            near clipping plane distance. This value must be greater than zero.
11827      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11828      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11829      * @param zFar
11830      *            far clipping plane distance. This value must be greater than zero.
11831      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11832      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11833      * @param zZeroToOne
11834      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11835      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11836      * @return this
11837      */
11838     ref public Matrix4d setPerspectiveOffCenterFovLH(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar, bool zZeroToOne) return {
11839         return setFrustumLH(Math.tan(angleLeft)*zNear, Math.tan(angleRight)*zNear, Math.tan(angleDown)*zNear, Math.tan(angleUp)*zNear, zNear, zFar, zZeroToOne);
11840     }
11841 
11842     /**
11843      * Apply a symmetric perspective projection frustum transformation for a left-handed coordinate system
11844      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
11845      * <p>
11846      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11847      * then the new matrix will be <code>M * P</code>. So when transforming a
11848      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11849      * the perspective projection will be applied first!
11850      * <p>
11851      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11852      * use {@link #setPerspectiveLH(double, double, double, double, bool) setPerspectiveLH}.
11853      * 
11854      * @see #setPerspectiveLH(double, double, double, double, bool)
11855      * 
11856      * @param fovy
11857      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11858      * @param aspect
11859      *            the aspect ratio (i.e. width / height; must be greater than zero)
11860      * @param zNear
11861      *            near clipping plane distance. This value must be greater than zero.
11862      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11863      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11864      * @param zFar
11865      *            far clipping plane distance. This value must be greater than zero.
11866      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11867      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11868      * @param zZeroToOne
11869      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11870      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11871      * @param dest
11872      *            will hold the result
11873      * @return dest
11874      */
11875     public Matrix4d perspectiveLH(double fovy, double aspect, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
11876         if ((properties & PROPERTY_IDENTITY) != 0)
11877             return dest.setPerspectiveLH(fovy, aspect, zNear, zFar, zZeroToOne);
11878         return perspectiveLHGeneric(fovy, aspect, zNear, zFar, zZeroToOne, dest);
11879     }
11880     private Matrix4d perspectiveLHGeneric(double fovy, double aspect, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
11881         double h = Math.tan(fovy * 0.5);
11882         // calculate right matrix elements
11883         double rm00 = 1.0 / (h * aspect);
11884         double rm11 = 1.0 / h;
11885         double rm22;
11886         double rm32;
11887         bool farInf = zFar > 0 && Math.isInfinite(zFar);
11888         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
11889         if (farInf) {
11890             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
11891             double e = 1E-6;
11892             rm22 = 1.0 - e;
11893             rm32 = (e - (zZeroToOne ? 1.0 : 2.0)) * zNear;
11894         } else if (nearInf) {
11895             double e = 1E-6;
11896             rm22 = (zZeroToOne ? 0.0 : 1.0) - e;
11897             rm32 = ((zZeroToOne ? 1.0 : 2.0) - e) * zFar;
11898         } else {
11899             rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zFar - zNear);
11900             rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar);
11901         }
11902         // perform optimized matrix multiplication
11903         double nm20 = m20 * rm22 + m30;
11904         double nm21 = m21 * rm22 + m31;
11905         double nm22 = m22 * rm22 + m32;
11906         double nm23 = m23 * rm22 + m33;
11907         dest._m00(m00 * rm00)
11908         ._m01(m01 * rm00)
11909         ._m02(m02 * rm00)
11910         ._m03(m03 * rm00)
11911         ._m10(m10 * rm11)
11912         ._m11(m11 * rm11)
11913         ._m12(m12 * rm11)
11914         ._m13(m13 * rm11)
11915         ._m30(m20 * rm32)
11916         ._m31(m21 * rm32)
11917         ._m32(m22 * rm32)
11918         ._m33(m23 * rm32)
11919         ._m20(nm20)
11920         ._m21(nm21)
11921         ._m22(nm22)
11922         ._m23(nm23)
11923         ._properties(properties & ~(PROPERTY_AFFINE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
11924         return dest;
11925     }
11926 
11927     /**
11928      * Apply a symmetric perspective projection frustum transformation for a left-handed coordinate system
11929      * using the given NDC z range to this matrix.
11930      * <p>
11931      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11932      * then the new matrix will be <code>M * P</code>. So when transforming a
11933      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11934      * the perspective projection will be applied first!
11935      * <p>
11936      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11937      * use {@link #setPerspectiveLH(double, double, double, double, bool) setPerspectiveLH}.
11938      * 
11939      * @see #setPerspectiveLH(double, double, double, double, bool)
11940      * 
11941      * @param fovy
11942      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11943      * @param aspect
11944      *            the aspect ratio (i.e. width / height; must be greater than zero)
11945      * @param zNear
11946      *            near clipping plane distance. This value must be greater than zero.
11947      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11948      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11949      * @param zFar
11950      *            far clipping plane distance. This value must be greater than zero.
11951      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11952      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11953      * @param zZeroToOne
11954      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11955      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11956      * @return this
11957      */
11958     ref public Matrix4d perspectiveLH(double fovy, double aspect, double zNear, double zFar, bool zZeroToOne) return {
11959         perspectiveLH(fovy, aspect, zNear, zFar, zZeroToOne, this);
11960         return this;
11961     }
11962 
11963     /**
11964      * Apply a symmetric perspective projection frustum transformation for a left-handed coordinate system
11965      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
11966      * <p>
11967      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11968      * then the new matrix will be <code>M * P</code>. So when transforming a
11969      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11970      * the perspective projection will be applied first!
11971      * <p>
11972      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11973      * use {@link #setPerspectiveLH(double, double, double, double) setPerspectiveLH}.
11974      * 
11975      * @see #setPerspectiveLH(double, double, double, double)
11976      * 
11977      * @param fovy
11978      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11979      * @param aspect
11980      *            the aspect ratio (i.e. width / height; must be greater than zero)
11981      * @param zNear
11982      *            near clipping plane distance. This value must be greater than zero.
11983      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11984      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11985      * @param zFar
11986      *            far clipping plane distance. This value must be greater than zero.
11987      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11988      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11989      * @param dest
11990      *            will hold the result
11991      * @return dest
11992      */
11993     public Matrix4d perspectiveLH(double fovy, double aspect, double zNear, double zFar, ref Matrix4d dest) {
11994         return perspectiveLH(fovy, aspect, zNear, zFar, false, dest);
11995     }
11996 
11997     /**
11998      * Apply a symmetric perspective projection frustum transformation for a left-handed coordinate system
11999      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
12000      * <p>
12001      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
12002      * then the new matrix will be <code>M * P</code>. So when transforming a
12003      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
12004      * the perspective projection will be applied first!
12005      * <p>
12006      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12007      * use {@link #setPerspectiveLH(double, double, double, double) setPerspectiveLH}.
12008      * 
12009      * @see #setPerspectiveLH(double, double, double, double)
12010      * 
12011      * @param fovy
12012      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
12013      * @param aspect
12014      *            the aspect ratio (i.e. width / height; must be greater than zero)
12015      * @param zNear
12016      *            near clipping plane distance. This value must be greater than zero.
12017      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12018      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12019      * @param zFar
12020      *            far clipping plane distance. This value must be greater than zero.
12021      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12022      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12023      * @return this
12024      */
12025     ref public Matrix4d perspectiveLH(double fovy, double aspect, double zNear, double zFar) return {
12026         perspectiveLH(fovy, aspect, zNear, zFar, this);
12027         return this;
12028     }
12029 
12030     /**
12031      * Set this matrix to be a symmetric perspective projection frustum transformation for a left-handed coordinate system
12032      * using the given NDC z range of <code>[-1..+1]</code>.
12033      * <p>
12034      * In order to apply the perspective projection transformation to an existing transformation,
12035      * use {@link #perspectiveLH(double, double, double, double, bool) perspectiveLH()}.
12036      * 
12037      * @see #perspectiveLH(double, double, double, double, bool)
12038      * 
12039      * @param fovy
12040      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
12041      * @param aspect
12042      *            the aspect ratio (i.e. width / height; must be greater than zero)
12043      * @param zNear
12044      *            near clipping plane distance. This value must be greater than zero.
12045      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12046      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12047      * @param zFar
12048      *            far clipping plane distance. This value must be greater than zero.
12049      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12050      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12051      * @param zZeroToOne
12052      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
12053      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
12054      * @return this
12055      */
12056     ref public Matrix4d setPerspectiveLH(double fovy, double aspect, double zNear, double zFar, bool zZeroToOne) return {
12057         double h = Math.tan(fovy * 0.5);
12058         _m00(1.0 / (h * aspect)).
12059         _m01(0.0).
12060         _m02(0.0).
12061         _m03(0.0).
12062         _m10(0.0).
12063         _m11(1.0 / h).
12064         _m12(0.0).
12065         _m13(0.0).
12066         _m20(0.0).
12067         _m21(0.0);
12068         bool farInf = zFar > 0 && Math.isInfinite(zFar);
12069         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
12070         if (farInf) {
12071             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
12072             double e = 1E-6;
12073             _m22(1.0 - e).
12074             _m32((e - (zZeroToOne ? 1.0 : 2.0)) * zNear);
12075         } else if (nearInf) {
12076             double e = 1E-6;
12077             _m22((zZeroToOne ? 0.0 : 1.0) - e).
12078             _m32(((zZeroToOne ? 1.0 : 2.0) - e) * zFar);
12079         } else {
12080             _m22((zZeroToOne ? zFar : zFar + zNear) / (zFar - zNear)).
12081             _m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar));
12082         }
12083         _m23(1.0).
12084         _m30(0.0).
12085         _m31(0.0).
12086         _m33(0.0).
12087         properties = PROPERTY_PERSPECTIVE;
12088         return this;
12089     }
12090 
12091     /**
12092      * Set this matrix to be a symmetric perspective projection frustum transformation for a left-handed coordinate system
12093      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
12094      * <p>
12095      * In order to apply the perspective projection transformation to an existing transformation,
12096      * use {@link #perspectiveLH(double, double, double, double) perspectiveLH()}.
12097      * 
12098      * @see #perspectiveLH(double, double, double, double)
12099      * 
12100      * @param fovy
12101      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
12102      * @param aspect
12103      *            the aspect ratio (i.e. width / height; must be greater than zero)
12104      * @param zNear
12105      *            near clipping plane distance. This value must be greater than zero.
12106      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12107      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12108      * @param zFar
12109      *            far clipping plane distance. This value must be greater than zero.
12110      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12111      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12112      * @return this
12113      */
12114     ref public Matrix4d setPerspectiveLH(double fovy, double aspect, double zNear, double zFar) return {
12115         return setPerspectiveLH(fovy, aspect, zNear, zFar, false);
12116     }
12117 
12118     /**
12119      * Apply an arbitrary perspective projection frustum transformation for a right-handed coordinate system
12120      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
12121      * <p>
12122      * If <code>M</code> is <code>this</code> matrix and <code>F</code> the frustum matrix,
12123      * then the new matrix will be <code>M * F</code>. So when transforming a
12124      * vector <code>v</code> with the new matrix by using <code>M * F * v</code>,
12125      * the frustum transformation will be applied first!
12126      * <p>
12127      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12128      * use {@link #setFrustum(double, double, double, double, double, double, bool) setFrustum()}.
12129      * <p>
12130      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12131      * 
12132      * @see #setFrustum(double, double, double, double, double, double, bool)
12133      * 
12134      * @param left
12135      *            the distance along the x-axis to the left frustum edge
12136      * @param right
12137      *            the distance along the x-axis to the right frustum edge
12138      * @param bottom
12139      *            the distance along the y-axis to the bottom frustum edge
12140      * @param top
12141      *            the distance along the y-axis to the top frustum edge
12142      * @param zNear
12143      *            near clipping plane distance. This value must be greater than zero.
12144      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12145      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12146      * @param zFar
12147      *            far clipping plane distance. This value must be greater than zero.
12148      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12149      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12150      * @param zZeroToOne
12151      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
12152      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
12153      * @param dest
12154      *            will hold the result
12155      * @return dest
12156      */
12157     public Matrix4d frustum(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
12158         if ((properties & PROPERTY_IDENTITY) != 0)
12159             return dest.setFrustum(left, right, bottom, top, zNear, zFar, zZeroToOne);
12160         return frustumGeneric(left, right, bottom, top, zNear, zFar, zZeroToOne, dest);
12161     }
12162     private Matrix4d frustumGeneric(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
12163         // calculate right matrix elements
12164         double rm00 = (zNear + zNear) / (right - left);
12165         double rm11 = (zNear + zNear) / (top - bottom);
12166         double rm20 = (right + left) / (right - left);
12167         double rm21 = (top + bottom) / (top - bottom);
12168         double rm22;
12169         double rm32;
12170         bool farInf = zFar > 0 && Math.isInfinite(zFar);
12171         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
12172         if (farInf) {
12173             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
12174             double e = 1E-6;
12175             rm22 = e - 1.0;
12176             rm32 = (e - (zZeroToOne ? 1.0 : 2.0)) * zNear;
12177         } else if (nearInf) {
12178             double e = 1E-6;
12179             rm22 = (zZeroToOne ? 0.0 : 1.0) - e;
12180             rm32 = ((zZeroToOne ? 1.0 : 2.0) - e) * zFar;
12181         } else {
12182             rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar);
12183             rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar);
12184         }
12185         // perform optimized matrix multiplication
12186         double nm20 = m00 * rm20 + m10 * rm21 + m20 * rm22 - m30;
12187         double nm21 = m01 * rm20 + m11 * rm21 + m21 * rm22 - m31;
12188         double nm22 = m02 * rm20 + m12 * rm21 + m22 * rm22 - m32;
12189         double nm23 = m03 * rm20 + m13 * rm21 + m23 * rm22 - m33;
12190         dest._m00(m00 * rm00)
12191         ._m01(m01 * rm00)
12192         ._m02(m02 * rm00)
12193         ._m03(m03 * rm00)
12194         ._m10(m10 * rm11)
12195         ._m11(m11 * rm11)
12196         ._m12(m12 * rm11)
12197         ._m13(m13 * rm11)
12198         ._m30(m20 * rm32)
12199         ._m31(m21 * rm32)
12200         ._m32(m22 * rm32)
12201         ._m33(m23 * rm32)
12202         ._m20(nm20)
12203         ._m21(nm21)
12204         ._m22(nm22)
12205         ._m23(nm23)
12206         ._properties(0);
12207         return dest;
12208     }
12209 
12210     /**
12211      * Apply an arbitrary perspective projection frustum transformation for a right-handed coordinate system
12212      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
12213      * <p>
12214      * If <code>M</code> is <code>this</code> matrix and <code>F</code> the frustum matrix,
12215      * then the new matrix will be <code>M * F</code>. So when transforming a
12216      * vector <code>v</code> with the new matrix by using <code>M * F * v</code>,
12217      * the frustum transformation will be applied first!
12218      * <p>
12219      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12220      * use {@link #setFrustum(double, double, double, double, double, double) setFrustum()}.
12221      * <p>
12222      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12223      * 
12224      * @see #setFrustum(double, double, double, double, double, double)
12225      * 
12226      * @param left
12227      *            the distance along the x-axis to the left frustum edge
12228      * @param right
12229      *            the distance along the x-axis to the right frustum edge
12230      * @param bottom
12231      *            the distance along the y-axis to the bottom frustum edge
12232      * @param top
12233      *            the distance along the y-axis to the top frustum edge
12234      * @param zNear
12235      *            near clipping plane distance. This value must be greater than zero.
12236      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12237      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12238      * @param zFar
12239      *            far clipping plane distance. This value must be greater than zero.
12240      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12241      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12242      * @param dest
12243      *            will hold the result
12244      * @return dest
12245      */
12246     public Matrix4d frustum(double left, double right, double bottom, double top, double zNear, double zFar, ref Matrix4d dest) {
12247         return frustum(left, right, bottom, top, zNear, zFar, false, dest);
12248     }
12249 
12250     /**
12251      * Apply an arbitrary perspective projection frustum transformation for a right-handed coordinate system
12252      * using the given NDC z range to this matrix.
12253      * <p>
12254      * If <code>M</code> is <code>this</code> matrix and <code>F</code> the frustum matrix,
12255      * then the new matrix will be <code>M * F</code>. So when transforming a
12256      * vector <code>v</code> with the new matrix by using <code>M * F * v</code>,
12257      * the frustum transformation will be applied first!
12258      * <p>
12259      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12260      * use {@link #setFrustum(double, double, double, double, double, double, bool) setFrustum()}.
12261      * <p>
12262      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12263      * 
12264      * @see #setFrustum(double, double, double, double, double, double, bool)
12265      * 
12266      * @param left
12267      *            the distance along the x-axis to the left frustum edge
12268      * @param right
12269      *            the distance along the x-axis to the right frustum edge
12270      * @param bottom
12271      *            the distance along the y-axis to the bottom frustum edge
12272      * @param top
12273      *            the distance along the y-axis to the top frustum edge
12274      * @param zNear
12275      *            near clipping plane distance. This value must be greater than zero.
12276      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12277      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12278      * @param zFar
12279      *            far clipping plane distance. This value must be greater than zero.
12280      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12281      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12282      * @param zZeroToOne
12283      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
12284      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
12285      * @return this
12286      */
12287     ref public Matrix4d frustum(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne) return {
12288         frustum(left, right, bottom, top, zNear, zFar, zZeroToOne, this);
12289         return this;
12290     }
12291 
12292     /**
12293      * Apply an arbitrary perspective projection frustum transformation for a right-handed coordinate system
12294      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
12295      * <p>
12296      * If <code>M</code> is <code>this</code> matrix and <code>F</code> the frustum matrix,
12297      * then the new matrix will be <code>M * F</code>. So when transforming a
12298      * vector <code>v</code> with the new matrix by using <code>M * F * v</code>,
12299      * the frustum transformation will be applied first!
12300      * <p>
12301      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12302      * use {@link #setFrustum(double, double, double, double, double, double) setFrustum()}.
12303      * <p>
12304      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12305      * 
12306      * @see #setFrustum(double, double, double, double, double, double)
12307      * 
12308      * @param left
12309      *            the distance along the x-axis to the left frustum edge
12310      * @param right
12311      *            the distance along the x-axis to the right frustum edge
12312      * @param bottom
12313      *            the distance along the y-axis to the bottom frustum edge
12314      * @param top
12315      *            the distance along the y-axis to the top frustum edge
12316      * @param zNear
12317      *            near clipping plane distance. This value must be greater than zero.
12318      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12319      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12320      * @param zFar
12321      *            far clipping plane distance. This value must be greater than zero.
12322      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12323      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12324      * @return this
12325      */
12326     ref public Matrix4d frustum(double left, double right, double bottom, double top, double zNear, double zFar) return {
12327         frustum(left, right, bottom, top, zNear, zFar, this);
12328         return this;
12329     }
12330 
12331     /**
12332      * Set this matrix to be an arbitrary perspective projection frustum transformation for a right-handed coordinate system
12333      * using the given NDC z range.
12334      * <p>
12335      * In order to apply the perspective frustum transformation to an existing transformation,
12336      * use {@link #frustum(double, double, double, double, double, double, bool) frustum()}.
12337      * <p>
12338      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12339      * 
12340      * @see #frustum(double, double, double, double, double, double, bool)
12341      * 
12342      * @param left
12343      *            the distance along the x-axis to the left frustum edge
12344      * @param right
12345      *            the distance along the x-axis to the right frustum edge
12346      * @param bottom
12347      *            the distance along the y-axis to the bottom frustum edge
12348      * @param top
12349      *            the distance along the y-axis to the top frustum edge
12350      * @param zNear
12351      *            near clipping plane distance. This value must be greater than zero.
12352      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12353      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12354      * @param zFar
12355      *            far clipping plane distance. This value must be greater than zero.
12356      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12357      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12358      * @param zZeroToOne
12359      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
12360      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
12361      * @return this
12362      */
12363     ref public Matrix4d setFrustum(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne) return {
12364         if ((properties & PROPERTY_IDENTITY) == 0)
12365             _identity();
12366         _m00((zNear + zNear) / (right - left)).
12367         _m11((zNear + zNear) / (top - bottom)).
12368         _m20((right + left) / (right - left)).
12369         _m21((top + bottom) / (top - bottom));
12370         bool farInf = zFar > 0 && Math.isInfinite(zFar);
12371         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
12372         if (farInf) {
12373             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
12374             double e = 1E-6;
12375             _m22(e - 1.0).
12376             _m32((e - (zZeroToOne ? 1.0 : 2.0)) * zNear);
12377         } else if (nearInf) {
12378             double e = 1E-6;
12379             _m22((zZeroToOne ? 0.0 : 1.0) - e).
12380             _m32(((zZeroToOne ? 1.0 : 2.0) - e) * zFar);
12381         } else {
12382             _m22((zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar)).
12383             _m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar));
12384         }
12385         _m23(-1.0).
12386         _m33(0.0).
12387         properties = this.m20 == 0.0 && this.m21 == 0.0 ? PROPERTY_PERSPECTIVE : 0;
12388         return this;
12389     }
12390 
12391     /**
12392      * Set this matrix to be an arbitrary perspective projection frustum transformation for a right-handed coordinate system
12393      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
12394      * <p>
12395      * In order to apply the perspective frustum transformation to an existing transformation,
12396      * use {@link #frustum(double, double, double, double, double, double) frustum()}.
12397      * <p>
12398      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12399      * 
12400      * @see #frustum(double, double, double, double, double, double)
12401      * 
12402      * @param left
12403      *            the distance along the x-axis to the left frustum edge
12404      * @param right
12405      *            the distance along the x-axis to the right frustum edge
12406      * @param bottom
12407      *            the distance along the y-axis to the bottom frustum edge
12408      * @param top
12409      *            the distance along the y-axis to the top frustum edge
12410      * @param zNear
12411      *            near clipping plane distance. This value must be greater than zero.
12412      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12413      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12414      * @param zFar
12415      *            far clipping plane distance. This value must be greater than zero.
12416      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12417      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12418      * @return this
12419      */
12420     ref public Matrix4d setFrustum(double left, double right, double bottom, double top, double zNear, double zFar) return {
12421         return setFrustum(left, right, bottom, top, zNear, zFar, false);
12422     }
12423 
12424     /**
12425      * Apply an arbitrary perspective projection frustum transformation for a left-handed coordinate system
12426      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
12427      * <p>
12428      * If <code>M</code> is <code>this</code> matrix and <code>F</code> the frustum matrix,
12429      * then the new matrix will be <code>M * F</code>. So when transforming a
12430      * vector <code>v</code> with the new matrix by using <code>M * F * v</code>,
12431      * the frustum transformation will be applied first!
12432      * <p>
12433      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12434      * use {@link #setFrustumLH(double, double, double, double, double, double, bool) setFrustumLH()}.
12435      * <p>
12436      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12437      * 
12438      * @see #setFrustumLH(double, double, double, double, double, double, bool)
12439      * 
12440      * @param left
12441      *            the distance along the x-axis to the left frustum edge
12442      * @param right
12443      *            the distance along the x-axis to the right frustum edge
12444      * @param bottom
12445      *            the distance along the y-axis to the bottom frustum edge
12446      * @param top
12447      *            the distance along the y-axis to the top frustum edge
12448      * @param zNear
12449      *            near clipping plane distance. This value must be greater than zero.
12450      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12451      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12452      * @param zFar
12453      *            far clipping plane distance. This value must be greater than zero.
12454      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12455      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12456      * @param zZeroToOne
12457      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
12458      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
12459      * @param dest
12460      *            will hold the result
12461      * @return dest
12462      */
12463     public Matrix4d frustumLH(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
12464         if ((properties & PROPERTY_IDENTITY) != 0)
12465             return dest.setFrustumLH(left, right, bottom, top, zNear, zFar, zZeroToOne);
12466         return frustumLHGeneric(left, right, bottom, top, zNear, zFar, zZeroToOne, dest);
12467     }
12468     private Matrix4d frustumLHGeneric(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
12469         // calculate right matrix elements
12470         double rm00 = (zNear + zNear) / (right - left);
12471         double rm11 = (zNear + zNear) / (top - bottom);
12472         double rm20 = (right + left) / (right - left);
12473         double rm21 = (top + bottom) / (top - bottom);
12474         double rm22;
12475         double rm32;
12476         bool farInf = zFar > 0 && Math.isInfinite(zFar);
12477         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
12478         if (farInf) {
12479             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
12480             double e = 1E-6;
12481             rm22 = 1.0 - e;
12482             rm32 = (e - (zZeroToOne ? 1.0 : 2.0)) * zNear;
12483         } else if (nearInf) {
12484             double e = 1E-6;
12485             rm22 = (zZeroToOne ? 0.0 : 1.0) - e;
12486             rm32 = ((zZeroToOne ? 1.0 : 2.0) - e) * zFar;
12487         } else {
12488             rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zFar - zNear);
12489             rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar);
12490         }
12491         // perform optimized matrix multiplication
12492         double nm20 = m00 * rm20 + m10 * rm21 + m20 * rm22 + m30;
12493         double nm21 = m01 * rm20 + m11 * rm21 + m21 * rm22 + m31;
12494         double nm22 = m02 * rm20 + m12 * rm21 + m22 * rm22 + m32;
12495         double nm23 = m03 * rm20 + m13 * rm21 + m23 * rm22 + m33;
12496         dest._m00(m00 * rm00)
12497         ._m01(m01 * rm00)
12498         ._m02(m02 * rm00)
12499         ._m03(m03 * rm00)
12500         ._m10(m10 * rm11)
12501         ._m11(m11 * rm11)
12502         ._m12(m12 * rm11)
12503         ._m13(m13 * rm11)
12504         ._m30(m20 * rm32)
12505         ._m31(m21 * rm32)
12506         ._m32(m22 * rm32)
12507         ._m33(m23 * rm32)
12508         ._m20(nm20)
12509         ._m21(nm21)
12510         ._m22(nm22)
12511         ._m23(nm23)
12512         ._properties(0);
12513         return dest;
12514     }
12515 
12516     /**
12517      * Apply an arbitrary perspective projection frustum transformation for a left-handed coordinate system
12518      * using the given NDC z range to this matrix.
12519      * <p>
12520      * If <code>M</code> is <code>this</code> matrix and <code>F</code> the frustum matrix,
12521      * then the new matrix will be <code>M * F</code>. So when transforming a
12522      * vector <code>v</code> with the new matrix by using <code>M * F * v</code>,
12523      * the frustum transformation will be applied first!
12524      * <p>
12525      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12526      * use {@link #setFrustumLH(double, double, double, double, double, double, bool) setFrustumLH()}.
12527      * <p>
12528      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12529      * 
12530      * @see #setFrustumLH(double, double, double, double, double, double, bool)
12531      * 
12532      * @param left
12533      *            the distance along the x-axis to the left frustum edge
12534      * @param right
12535      *            the distance along the x-axis to the right frustum edge
12536      * @param bottom
12537      *            the distance along the y-axis to the bottom frustum edge
12538      * @param top
12539      *            the distance along the y-axis to the top frustum edge
12540      * @param zNear
12541      *            near clipping plane distance. This value must be greater than zero.
12542      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12543      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12544      * @param zFar
12545      *            far clipping plane distance. This value must be greater than zero.
12546      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12547      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12548      * @param zZeroToOne
12549      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
12550      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
12551      * @return this
12552      */
12553     ref public Matrix4d frustumLH(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne) return {
12554         frustumLH(left, right, bottom, top, zNear, zFar, zZeroToOne, this);
12555         return this;
12556     }
12557 
12558     /**
12559      * Apply an arbitrary perspective projection frustum transformation for a left-handed coordinate system
12560      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
12561      * <p>
12562      * If <code>M</code> is <code>this</code> matrix and <code>F</code> the frustum matrix,
12563      * then the new matrix will be <code>M * F</code>. So when transforming a
12564      * vector <code>v</code> with the new matrix by using <code>M * F * v</code>,
12565      * the frustum transformation will be applied first!
12566      * <p>
12567      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12568      * use {@link #setFrustumLH(double, double, double, double, double, double) setFrustumLH()}.
12569      * <p>
12570      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12571      * 
12572      * @see #setFrustumLH(double, double, double, double, double, double)
12573      * 
12574      * @param left
12575      *            the distance along the x-axis to the left frustum edge
12576      * @param right
12577      *            the distance along the x-axis to the right frustum edge
12578      * @param bottom
12579      *            the distance along the y-axis to the bottom frustum edge
12580      * @param top
12581      *            the distance along the y-axis to the top frustum edge
12582      * @param zNear
12583      *            near clipping plane distance. This value must be greater than zero.
12584      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12585      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12586      * @param zFar
12587      *            far clipping plane distance. This value must be greater than zero.
12588      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12589      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12590      * @param dest
12591      *            will hold the result
12592      * @return dest
12593      */
12594     public Matrix4d frustumLH(double left, double right, double bottom, double top, double zNear, double zFar, ref Matrix4d dest) {
12595         return frustumLH(left, right, bottom, top, zNear, zFar, false, dest);
12596     }
12597 
12598     /**
12599      * Apply an arbitrary perspective projection frustum transformation for a left-handed coordinate system
12600      * using the given NDC z range to this matrix.
12601      * <p>
12602      * If <code>M</code> is <code>this</code> matrix and <code>F</code> the frustum matrix,
12603      * then the new matrix will be <code>M * F</code>. So when transforming a
12604      * vector <code>v</code> with the new matrix by using <code>M * F * v</code>,
12605      * the frustum transformation will be applied first!
12606      * <p>
12607      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12608      * use {@link #setFrustumLH(double, double, double, double, double, double) setFrustumLH()}.
12609      * <p>
12610      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12611      * 
12612      * @see #setFrustumLH(double, double, double, double, double, double)
12613      * 
12614      * @param left
12615      *            the distance along the x-axis to the left frustum edge
12616      * @param right
12617      *            the distance along the x-axis to the right frustum edge
12618      * @param bottom
12619      *            the distance along the y-axis to the bottom frustum edge
12620      * @param top
12621      *            the distance along the y-axis to the top frustum edge
12622      * @param zNear
12623      *            near clipping plane distance. This value must be greater than zero.
12624      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12625      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12626      * @param zFar
12627      *            far clipping plane distance. This value must be greater than zero.
12628      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12629      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12630      * @return this
12631      */
12632     ref public Matrix4d frustumLH(double left, double right, double bottom, double top, double zNear, double zFar) return {
12633         frustumLH(left, right, bottom, top, zNear, zFar, this);
12634         return this;
12635     }
12636 
12637     /**
12638      * Set this matrix to be an arbitrary perspective projection frustum transformation for a left-handed coordinate system
12639      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
12640      * <p>
12641      * In order to apply the perspective frustum transformation to an existing transformation,
12642      * use {@link #frustumLH(double, double, double, double, double, double, bool) frustumLH()}.
12643      * <p>
12644      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12645      * 
12646      * @see #frustumLH(double, double, double, double, double, double, bool)
12647      * 
12648      * @param left
12649      *            the distance along the x-axis to the left frustum edge
12650      * @param right
12651      *            the distance along the x-axis to the right frustum edge
12652      * @param bottom
12653      *            the distance along the y-axis to the bottom frustum edge
12654      * @param top
12655      *            the distance along the y-axis to the top frustum edge
12656      * @param zNear
12657      *            near clipping plane distance. This value must be greater than zero.
12658      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12659      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12660      * @param zFar
12661      *            far clipping plane distance. This value must be greater than zero.
12662      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12663      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12664      * @param zZeroToOne
12665      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
12666      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
12667      * @return this
12668      */
12669     ref public Matrix4d setFrustumLH(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne) return {
12670         if ((properties & PROPERTY_IDENTITY) == 0)
12671             _identity();
12672         _m00((zNear + zNear) / (right - left)).
12673         _m11((zNear + zNear) / (top - bottom)).
12674         _m20((right + left) / (right - left)).
12675         _m21((top + bottom) / (top - bottom));
12676         bool farInf = zFar > 0 && Math.isInfinite(zFar);
12677         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
12678         if (farInf) {
12679             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
12680             double e = 1E-6;
12681             _m22(1.0 - e).
12682             _m32((e - (zZeroToOne ? 1.0 : 2.0)) * zNear);
12683         } else if (nearInf) {
12684             double e = 1E-6;
12685             _m22((zZeroToOne ? 0.0 : 1.0) - e).
12686             _m32(((zZeroToOne ? 1.0 : 2.0) - e) * zFar);
12687         } else {
12688             _m22((zZeroToOne ? zFar : zFar + zNear) / (zFar - zNear)).
12689             _m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar));
12690         }
12691         _m23(1.0).
12692         _m33(0.0).
12693         properties = this.m20 == 0.0 && this.m21 == 0.0 ? PROPERTY_PERSPECTIVE : 0;
12694         return this;
12695     }
12696 
12697     /**
12698      * Set this matrix to be an arbitrary perspective projection frustum transformation for a left-handed coordinate system
12699      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
12700      * <p>
12701      * In order to apply the perspective frustum transformation to an existing transformation,
12702      * use {@link #frustumLH(double, double, double, double, double, double) frustumLH()}.
12703      * <p>
12704      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12705      * 
12706      * @see #frustumLH(double, double, double, double, double, double)
12707      * 
12708      * @param left
12709      *            the distance along the x-axis to the left frustum edge
12710      * @param right
12711      *            the distance along the x-axis to the right frustum edge
12712      * @param bottom
12713      *            the distance along the y-axis to the bottom frustum edge
12714      * @param top
12715      *            the distance along the y-axis to the top frustum edge
12716      * @param zNear
12717      *            near clipping plane distance. This value must be greater than zero.
12718      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12719      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12720      * @param zFar
12721      *            far clipping plane distance. This value must be greater than zero.
12722      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12723      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12724      * @return this
12725      */
12726     ref public Matrix4d setFrustumLH(double left, double right, double bottom, double top, double zNear, double zFar) return {
12727         return setFrustumLH(left, right, bottom, top, zNear, zFar, false);
12728     }
12729 
12730     /**
12731      * Set this matrix to represent a perspective projection equivalent to the given intrinsic camera calibration parameters.
12732      * The resulting matrix will be suited for a right-handed coordinate system using OpenGL's NDC z range of <code>[-1..+1]</code>.
12733      * <p>
12734      * See: <a href="https://en.wikipedia.org/wiki/Camera_resectioning#Intrinsic_parameters">https://en.wikipedia.org/</a>
12735      * <p>
12736      * Reference: <a href="http://ksimek.github.io/2013/06/03/calibrated_cameras_in_opengl/">http://ksimek.github.io/</a>
12737      * 
12738      * @param alphaX
12739      *          specifies the focal length and scale along the X axis
12740      * @param alphaY
12741      *          specifies the focal length and scale along the Y axis
12742      * @param gamma
12743      *          the skew coefficient between the X and Y axis (may be <code>0</code>)
12744      * @param u0
12745      *          the X coordinate of the principal point in image/sensor units
12746      * @param v0
12747      *          the Y coordinate of the principal point in image/sensor units
12748      * @param imgWidth
12749      *          the width of the sensor/image image/sensor units
12750      * @param imgHeight
12751      *          the height of the sensor/image image/sensor units
12752      * @param near
12753      *          the distance to the near plane
12754      * @param far
12755      *          the distance to the far plane
12756      * @return this
12757      */
12758     ref public Matrix4d setFromIntrinsic(double alphaX, double alphaY, double gamma, double u0, double v0, int imgWidth, int imgHeight, double near, double far) return {
12759         double l00 = 2.0 / imgWidth;
12760         double l11 = 2.0 / imgHeight;
12761         double l22 = 2.0 / (near - far);
12762         setm00(l00 * alphaX);
12763         setm01(0.0);
12764         setm02(0.0);
12765         setm03(0.0);
12766         setm10(l00 * gamma);
12767         setm11(l11 * alphaY);
12768         setm12(0.0);
12769         setm13(0.0);
12770         setm20(l00 * u0 - 1.0);
12771         setm21(l11 * v0 - 1.0);
12772         setm22(l22 * -(near + far) + (far + near) / (near - far));
12773         setm23(-1.0);
12774         setm30(0.0);
12775         setm31(0.0);
12776         setm32(l22 * -near * far);
12777         setm33(0.0);
12778         this.properties = PROPERTY_PERSPECTIVE;
12779         return this;
12780     }
12781 
12782     public Vector4d frustumPlane(int plane, ref Vector4d dest) {
12783         switch (plane) {
12784         case PLANE_NX:
12785             dest.set(m03 + m00, m13 + m10, m23 + m20, m33 + m30).normalize3();
12786             break;
12787         case PLANE_PX:
12788             dest.set(m03 - m00, m13 - m10, m23 - m20, m33 - m30).normalize3();
12789             break;
12790         case PLANE_NY:
12791             dest.set(m03 + m01, m13 + m11, m23 + m21, m33 + m31).normalize3();
12792             break;
12793         case PLANE_PY:
12794             dest.set(m03 - m01, m13 - m11, m23 - m21, m33 - m31).normalize3();
12795             break;
12796         case PLANE_NZ:
12797             dest.set(m03 + m02, m13 + m12, m23 + m22, m33 + m32).normalize3();
12798             break;
12799         case PLANE_PZ:
12800             dest.set(m03 - m02, m13 - m12, m23 - m22, m33 - m32).normalize3();
12801             break;
12802         default:
12803             // do nothing
12804         }
12805         return dest;
12806     }
12807 
12808     public Vector3d frustumCorner(int corner, ref Vector3d dest) {
12809         double d1, d2, d3;
12810         double n1x, n1y, n1z, n2x, n2y, n2z, n3x, n3y, n3z;
12811         switch (corner) {
12812         case CORNER_NXNYNZ: // left, bottom, near
12813             n1x = m03 + m00; n1y = m13 + m10; n1z = m23 + m20; d1 = m33 + m30; // left
12814             n2x = m03 + m01; n2y = m13 + m11; n2z = m23 + m21; d2 = m33 + m31; // bottom
12815             n3x = m03 + m02; n3y = m13 + m12; n3z = m23 + m22; d3 = m33 + m32; // near
12816             break;
12817         case CORNER_PXNYNZ: // right, bottom, near
12818             n1x = m03 - m00; n1y = m13 - m10; n1z = m23 - m20; d1 = m33 - m30; // right
12819             n2x = m03 + m01; n2y = m13 + m11; n2z = m23 + m21; d2 = m33 + m31; // bottom
12820             n3x = m03 + m02; n3y = m13 + m12; n3z = m23 + m22; d3 = m33 + m32; // near
12821             break;
12822         case CORNER_PXPYNZ: // right, top, near
12823             n1x = m03 - m00; n1y = m13 - m10; n1z = m23 - m20; d1 = m33 - m30; // right
12824             n2x = m03 - m01; n2y = m13 - m11; n2z = m23 - m21; d2 = m33 - m31; // top
12825             n3x = m03 + m02; n3y = m13 + m12; n3z = m23 + m22; d3 = m33 + m32; // near
12826             break;
12827         case CORNER_NXPYNZ: // left, top, near
12828             n1x = m03 + m00; n1y = m13 + m10; n1z = m23 + m20; d1 = m33 + m30; // left
12829             n2x = m03 - m01; n2y = m13 - m11; n2z = m23 - m21; d2 = m33 - m31; // top
12830             n3x = m03 + m02; n3y = m13 + m12; n3z = m23 + m22; d3 = m33 + m32; // near
12831             break;
12832         case CORNER_PXNYPZ: // right, bottom, far
12833             n1x = m03 - m00; n1y = m13 - m10; n1z = m23 - m20; d1 = m33 - m30; // right
12834             n2x = m03 + m01; n2y = m13 + m11; n2z = m23 + m21; d2 = m33 + m31; // bottom
12835             n3x = m03 - m02; n3y = m13 - m12; n3z = m23 - m22; d3 = m33 - m32; // far
12836             break;
12837         case CORNER_NXNYPZ: // left, bottom, far
12838             n1x = m03 + m00; n1y = m13 + m10; n1z = m23 + m20; d1 = m33 + m30; // left
12839             n2x = m03 + m01; n2y = m13 + m11; n2z = m23 + m21; d2 = m33 + m31; // bottom
12840             n3x = m03 - m02; n3y = m13 - m12; n3z = m23 - m22; d3 = m33 - m32; // far
12841             break;
12842         case CORNER_NXPYPZ: // left, top, far
12843             n1x = m03 + m00; n1y = m13 + m10; n1z = m23 + m20; d1 = m33 + m30; // left
12844             n2x = m03 - m01; n2y = m13 - m11; n2z = m23 - m21; d2 = m33 - m31; // top
12845             n3x = m03 - m02; n3y = m13 - m12; n3z = m23 - m22; d3 = m33 - m32; // far
12846             break;
12847         case CORNER_PXPYPZ: // right, top, far
12848             n1x = m03 - m00; n1y = m13 - m10; n1z = m23 - m20; d1 = m33 - m30; // right
12849             n2x = m03 - m01; n2y = m13 - m11; n2z = m23 - m21; d2 = m33 - m31; // top
12850             n3x = m03 - m02; n3y = m13 - m12; n3z = m23 - m22; d3 = m33 - m32; // far
12851             break;
12852         default:
12853             // do nothing
12854         }
12855         double c23x, c23y, c23z;
12856         c23x = n2y * n3z - n2z * n3y;
12857         c23y = n2z * n3x - n2x * n3z;
12858         c23z = n2x * n3y - n2y * n3x;
12859         double c31x, c31y, c31z;
12860         c31x = n3y * n1z - n3z * n1y;
12861         c31y = n3z * n1x - n3x * n1z;
12862         c31z = n3x * n1y - n3y * n1x;
12863         double c12x, c12y, c12z;
12864         c12x = n1y * n2z - n1z * n2y;
12865         c12y = n1z * n2x - n1x * n2z;
12866         c12z = n1x * n2y - n1y * n2x;
12867         double invDot = 1.0 / (n1x * c23x + n1y * c23y + n1z * c23z);
12868         dest.x = (-c23x * d1 - c31x * d2 - c12x * d3) * invDot;
12869         dest.y = (-c23y * d1 - c31y * d2 - c12y * d3) * invDot;
12870         dest.z = (-c23z * d1 - c31z * d2 - c12z * d3) * invDot;
12871         return dest;
12872     }
12873 
12874     public Vector3d perspectiveOrigin(ref Vector3d dest) {
12875         /*
12876          * Simply compute the intersection point of the left, right and top frustum plane.
12877          */
12878         double d1, d2, d3;
12879         double n1x, n1y, n1z, n2x, n2y, n2z, n3x, n3y, n3z;
12880         n1x = m03 + m00; n1y = m13 + m10; n1z = m23 + m20; d1 = m33 + m30; // left
12881         n2x = m03 - m00; n2y = m13 - m10; n2z = m23 - m20; d2 = m33 - m30; // right
12882         n3x = m03 - m01; n3y = m13 - m11; n3z = m23 - m21; d3 = m33 - m31; // top
12883         double c23x, c23y, c23z;
12884         c23x = n2y * n3z - n2z * n3y;
12885         c23y = n2z * n3x - n2x * n3z;
12886         c23z = n2x * n3y - n2y * n3x;
12887         double c31x, c31y, c31z;
12888         c31x = n3y * n1z - n3z * n1y;
12889         c31y = n3z * n1x - n3x * n1z;
12890         c31z = n3x * n1y - n3y * n1x;
12891         double c12x, c12y, c12z;
12892         c12x = n1y * n2z - n1z * n2y;
12893         c12y = n1z * n2x - n1x * n2z;
12894         c12z = n1x * n2y - n1y * n2x;
12895         double invDot = 1.0 / (n1x * c23x + n1y * c23y + n1z * c23z);
12896         dest.x = (-c23x * d1 - c31x * d2 - c12x * d3) * invDot;
12897         dest.y = (-c23y * d1 - c31y * d2 - c12y * d3) * invDot;
12898         dest.z = (-c23z * d1 - c31z * d2 - c12z * d3) * invDot;
12899         return dest;
12900     }
12901 
12902     public Vector3d perspectiveInvOrigin(ref Vector3d dest) {
12903         double invW = 1.0 / m23;
12904         dest.x = m20 * invW;
12905         dest.y = m21 * invW;
12906         dest.z = m22 * invW;
12907         return dest;
12908     }
12909 
12910     public double perspectiveFov() {
12911         /*
12912          * Compute the angle between the bottom and top frustum plane normals.
12913          */
12914         double n1x, n1y, n1z, n2x, n2y, n2z;
12915         n1x = m03 + m01; n1y = m13 + m11; n1z = m23 + m21; // bottom
12916         n2x = m01 - m03; n2y = m11 - m13; n2z = m21 - m23; // top
12917         double n1len = Math.sqrt(n1x * n1x + n1y * n1y + n1z * n1z);
12918         double n2len = Math.sqrt(n2x * n2x + n2y * n2y + n2z * n2z);
12919         return Math.acos((n1x * n2x + n1y * n2y + n1z * n2z) / (n1len * n2len));
12920     }
12921 
12922     public double perspectiveNear() {
12923         return m32 / (m23 + m22);
12924     }
12925 
12926     public double perspectiveFar() {
12927         return m32 / (m22 - m23);
12928     }
12929 
12930     public Vector3d frustumRayDir(double x, double y, ref Vector3d dest) {
12931         /*
12932          * This method works by first obtaining the frustum plane normals,
12933          * then building the cross product to obtain the corner rays,
12934          * and finally bilinearly interpolating to obtain the desired direction.
12935          * The code below uses a condense form of doing all this making use 
12936          * of some mathematical identities to simplify the overall expression.
12937          */
12938         double a = m10 * m23, b = m13 * m21, c = m10 * m21, d = m11 * m23, e = m13 * m20, f = m11 * m20;
12939         double g = m03 * m20, h = m01 * m23, i = m01 * m20, j = m03 * m21, k = m00 * m23, l = m00 * m21;
12940         double m = m00 * m13, n = m03 * m11, o = m00 * m11, p = m01 * m13, q = m03 * m10, r = m01 * m10;
12941         double m1x, m1y, m1z;
12942         m1x = (d + e + f - a - b - c) * (1.0 - y) + (a - b - c + d - e + f) * y;
12943         m1y = (j + k + l - g - h - i) * (1.0 - y) + (g - h - i + j - k + l) * y;
12944         m1z = (p + q + r - m - n - o) * (1.0 - y) + (m - n - o + p - q + r) * y;
12945         double m2x, m2y, m2z;
12946         m2x = (b - c - d + e + f - a) * (1.0 - y) + (a + b - c - d - e + f) * y;
12947         m2y = (h - i - j + k + l - g) * (1.0 - y) + (g + h - i - j - k + l) * y;
12948         m2z = (n - o - p + q + r - m) * (1.0 - y) + (m + n - o - p - q + r) * y;
12949         dest.x = m1x * (1.0 - x) + m2x * x;
12950         dest.y = m1y * (1.0 - x) + m2y * x;
12951         dest.z = m1z * (1.0 - x) + m2z * x;
12952         return dest.normalize(dest);
12953     }
12954 
12955     public Vector3d positiveZ(ref Vector3d dir) {
12956         if ((properties & PROPERTY_ORTHONORMAL) != 0)
12957             return normalizedPositiveZ(dir);
12958         return positiveZGeneric(dir);
12959     }
12960     private Vector3d positiveZGeneric(ref Vector3d dir) {
12961         return dir.set(m10 * m21 - m11 * m20, m20 * m01 - m21 * m00, m00 * m11 - m01 * m10).normalize();
12962     }
12963 
12964     public Vector3d normalizedPositiveZ(ref Vector3d dir) {
12965         return dir.set(m02, m12, m22);
12966     }
12967 
12968     public Vector3d positiveX(ref Vector3d dir) {
12969         if ((properties & PROPERTY_ORTHONORMAL) != 0)
12970             return normalizedPositiveX(dir);
12971         return positiveXGeneric(dir);
12972     }
12973     private Vector3d positiveXGeneric(ref Vector3d dir) {
12974         return dir.set(m11 * m22 - m12 * m21, m02 * m21 - m01 * m22, m01 * m12 - m02 * m11).normalize();
12975     }
12976 
12977     public Vector3d normalizedPositiveX(ref Vector3d dir) {
12978         return dir.set(m00, m10, m20);
12979     }
12980 
12981     public Vector3d positiveY(ref Vector3d dir) {
12982         if ((properties & PROPERTY_ORTHONORMAL) != 0)
12983             return normalizedPositiveY(dir);
12984         return positiveYGeneric(dir);
12985     }
12986     private Vector3d positiveYGeneric(ref Vector3d dir) {
12987         return dir.set(m12 * m20 - m10 * m22, m00 * m22 - m02 * m20, m02 * m10 - m00 * m12).normalize();
12988     }
12989 
12990     public Vector3d normalizedPositiveY(ref Vector3d dir) {
12991         return dir.set(m01, m11, m21);
12992     }
12993 
12994     public Vector3d originAffine(ref Vector3d dest) {
12995         double a = m00 * m11 - m01 * m10;
12996         double b = m00 * m12 - m02 * m10;
12997         double d = m01 * m12 - m02 * m11;
12998         double g = m20 * m31 - m21 * m30;
12999         double h = m20 * m32 - m22 * m30;
13000         double j = m21 * m32 - m22 * m31;
13001         dest.x = -m10 * j + m11 * h - m12 * g;
13002         dest.y =  m00 * j - m01 * h + m02 * g;
13003         dest.z = -m30 * d + m31 * b - m32 * a;
13004         return dest;
13005     }
13006 
13007     public Vector3d origin(ref Vector3d dest) {
13008         if ((properties & PROPERTY_AFFINE) != 0)
13009             return originAffine(dest);
13010         return originGeneric(dest);
13011     }
13012     private Vector3d originGeneric(ref Vector3d dest) {
13013         double a = m00 * m11 - m01 * m10;
13014         double b = m00 * m12 - m02 * m10;
13015         double c = m00 * m13 - m03 * m10;
13016         double d = m01 * m12 - m02 * m11;
13017         double e = m01 * m13 - m03 * m11;
13018         double f = m02 * m13 - m03 * m12;
13019         double g = m20 * m31 - m21 * m30;
13020         double h = m20 * m32 - m22 * m30;
13021         double i = m20 * m33 - m23 * m30;
13022         double j = m21 * m32 - m22 * m31;
13023         double k = m21 * m33 - m23 * m31;
13024         double l = m22 * m33 - m23 * m32;
13025         double det = a * l - b * k + c * j + d * i - e * h + f * g;
13026         double invDet = 1.0 / det;
13027         double nm30 = (-m10 * j + m11 * h - m12 * g) * invDet;
13028         double nm31 = ( m00 * j - m01 * h + m02 * g) * invDet;
13029         double nm32 = (-m30 * d + m31 * b - m32 * a) * invDet;
13030         double nm33 = det / ( m20 * d - m21 * b + m22 * a);
13031         double x = nm30 * nm33;
13032         double y = nm31 * nm33;
13033         double z = nm32 * nm33;
13034         return dest.set(x, y, z);
13035     }
13036 
13037     /**
13038      * Apply a projection transformation to this matrix that projects onto the plane specified via the general plane equation
13039      * <code>x*a + y*b + z*c + d = 0</code> as if casting a shadow from a given light position/direction <code>light</code>.
13040      * <p>
13041      * If <code>light.w</code> is <code>0.0</code> the light is being treated as a directional light; if it is <code>1.0</code> it is a point light.
13042      * <p>
13043      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the shadow matrix,
13044      * then the new matrix will be <code>M * S</code>. So when transforming a
13045      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>, the
13046      * shadow projection will be applied first!
13047      * <p>
13048      * Reference: <a href="ftp://ftp.sgi.com/opengl/contrib/blythe/advanced99/notes/node192.html">ftp.sgi.com</a>
13049      * 
13050      * @param light
13051      *          the light's vector
13052      * @param a
13053      *          the x factor in the plane equation
13054      * @param b
13055      *          the y factor in the plane equation
13056      * @param c
13057      *          the z factor in the plane equation
13058      * @param d
13059      *          the constant in the plane equation
13060      * @return this
13061      */
13062     ref public Matrix4d shadow(ref Vector4d light, double a, double b, double c, double d) return {
13063         shadow(light.x, light.y, light.z, light.w, a, b, c, d, this);
13064         return this;
13065     }
13066 
13067     public Matrix4d shadow(ref Vector4d light, double a, double b, double c, double d, ref Matrix4d dest) {
13068         return shadow(light.x, light.y, light.z, light.w, a, b, c, d, dest);
13069     }
13070 
13071     /**
13072      * Apply a projection transformation to this matrix that projects onto the plane specified via the general plane equation
13073      * <code>x*a + y*b + z*c + d = 0</code> as if casting a shadow from a given light position/direction <code>(lightX, lightY, lightZ, lightW)</code>.
13074      * <p>
13075      * If <code>lightW</code> is <code>0.0</code> the light is being treated as a directional light; if it is <code>1.0</code> it is a point light.
13076      * <p>
13077      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the shadow matrix,
13078      * then the new matrix will be <code>M * S</code>. So when transforming a
13079      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>, the
13080      * shadow projection will be applied first!
13081      * <p>
13082      * Reference: <a href="ftp://ftp.sgi.com/opengl/contrib/blythe/advanced99/notes/node192.html">ftp.sgi.com</a>
13083      * 
13084      * @param lightX
13085      *          the x-component of the light's vector
13086      * @param lightY
13087      *          the y-component of the light's vector
13088      * @param lightZ
13089      *          the z-component of the light's vector
13090      * @param lightW
13091      *          the w-component of the light's vector
13092      * @param a
13093      *          the x factor in the plane equation
13094      * @param b
13095      *          the y factor in the plane equation
13096      * @param c
13097      *          the z factor in the plane equation
13098      * @param d
13099      *          the constant in the plane equation
13100      * @return this
13101      */
13102     ref public Matrix4d shadow(double lightX, double lightY, double lightZ, double lightW, double a, double b, double c, double d) return {
13103         shadow(lightX, lightY, lightZ, lightW, a, b, c, d, this);
13104         return this;
13105     }
13106 
13107     public Matrix4d shadow(double lightX, double lightY, double lightZ, double lightW, double a, double b, double c, double d, ref Matrix4d dest) {
13108         // normalize plane
13109         double invPlaneLen = Math.invsqrt(a*a + b*b + c*c);
13110         double an = a * invPlaneLen;
13111         double bn = b * invPlaneLen;
13112         double cn = c * invPlaneLen;
13113         double dn = d * invPlaneLen;
13114 
13115         double dot = an * lightX + bn * lightY + cn * lightZ + dn * lightW;
13116 
13117         // compute right matrix elements
13118         double rm00 = dot - an * lightX;
13119         double rm01 = -an * lightY;
13120         double rm02 = -an * lightZ;
13121         double rm03 = -an * lightW;
13122         double rm10 = -bn * lightX;
13123         double rm11 = dot - bn * lightY;
13124         double rm12 = -bn * lightZ;
13125         double rm13 = -bn * lightW;
13126         double rm20 = -cn * lightX;
13127         double rm21 = -cn * lightY;
13128         double rm22 = dot - cn * lightZ;
13129         double rm23 = -cn * lightW;
13130         double rm30 = -dn * lightX;
13131         double rm31 = -dn * lightY;
13132         double rm32 = -dn * lightZ;
13133         double rm33 = dot - dn * lightW;
13134 
13135         // matrix multiplication
13136         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02 + m30 * rm03;
13137         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02 + m31 * rm03;
13138         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02 + m32 * rm03;
13139         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02 + m33 * rm03;
13140         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12 + m30 * rm13;
13141         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12 + m31 * rm13;
13142         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12 + m32 * rm13;
13143         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12 + m33 * rm13;
13144         double nm20 = m00 * rm20 + m10 * rm21 + m20 * rm22 + m30 * rm23;
13145         double nm21 = m01 * rm20 + m11 * rm21 + m21 * rm22 + m31 * rm23;
13146         double nm22 = m02 * rm20 + m12 * rm21 + m22 * rm22 + m32 * rm23;
13147         double nm23 = m03 * rm20 + m13 * rm21 + m23 * rm22 + m33 * rm23;
13148         dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30 * rm33)
13149         ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31 * rm33)
13150         ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32 * rm33)
13151         ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33 * rm33)
13152         ._m00(nm00)
13153         ._m01(nm01)
13154         ._m02(nm02)
13155         ._m03(nm03)
13156         ._m10(nm10)
13157         ._m11(nm11)
13158         ._m12(nm12)
13159         ._m13(nm13)
13160         ._m20(nm20)
13161         ._m21(nm21)
13162         ._m22(nm22)
13163         ._m23(nm23)
13164         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
13165         return dest;
13166     }
13167 
13168     public Matrix4d shadow(ref Vector4d light, Matrix4d planeTransform, ref Matrix4d dest) {
13169         // compute plane equation by transforming (y = 0)
13170         double a = planeTransform.m10;
13171         double b = planeTransform.m11;
13172         double c = planeTransform.m12;
13173         double d = -a * planeTransform.m30 - b * planeTransform.m31 - c * planeTransform.m32;
13174         return shadow(light.x, light.y, light.z, light.w, a, b, c, d, dest);
13175     }
13176 
13177     /**
13178      * Apply a projection transformation to this matrix that projects onto the plane with the general plane equation
13179      * <code>y = 0</code> as if casting a shadow from a given light position/direction <code>light</code>.
13180      * <p>
13181      * Before the shadow projection is applied, the plane is transformed via the specified <code>planeTransformation</code>.
13182      * <p>
13183      * If <code>light.w</code> is <code>0.0</code> the light is being treated as a directional light; if it is <code>1.0</code> it is a point light.
13184      * <p>
13185      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the shadow matrix,
13186      * then the new matrix will be <code>M * S</code>. So when transforming a
13187      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>, the
13188      * shadow projection will be applied first!
13189      * 
13190      * @param light
13191      *          the light's vector
13192      * @param planeTransform
13193      *          the transformation to transform the implied plane <code>y = 0</code> before applying the projection
13194      * @return this
13195      */
13196     ref public Matrix4d shadow(ref Vector4d light, Matrix4d planeTransform) return {
13197         shadow(light, planeTransform, this);
13198         return this;
13199     }
13200 
13201     public Matrix4d shadow(double lightX, double lightY, double lightZ, double lightW, Matrix4d planeTransform, ref Matrix4d dest) {
13202         // compute plane equation by transforming (y = 0)
13203         double a = planeTransform.m10;
13204         double b = planeTransform.m11;
13205         double c = planeTransform.m12;
13206         double d = -a * planeTransform.m30 - b * planeTransform.m31 - c * planeTransform.m32;
13207         return shadow(lightX, lightY, lightZ, lightW, a, b, c, d, dest);
13208     }
13209 
13210     /**
13211      * Apply a projection transformation to this matrix that projects onto the plane with the general plane equation
13212      * <code>y = 0</code> as if casting a shadow from a given light position/direction <code>(lightX, lightY, lightZ, lightW)</code>.
13213      * <p>
13214      * Before the shadow projection is applied, the plane is transformed via the specified <code>planeTransformation</code>.
13215      * <p>
13216      * If <code>lightW</code> is <code>0.0</code> the light is being treated as a directional light; if it is <code>1.0</code> it is a point light.
13217      * <p>
13218      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the shadow matrix,
13219      * then the new matrix will be <code>M * S</code>. So when transforming a
13220      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>, the
13221      * shadow projection will be applied first!
13222      * 
13223      * @param lightX
13224      *          the x-component of the light vector
13225      * @param lightY
13226      *          the y-component of the light vector
13227      * @param lightZ
13228      *          the z-component of the light vector
13229      * @param lightW
13230      *          the w-component of the light vector
13231      * @param planeTransform
13232      *          the transformation to transform the implied plane <code>y = 0</code> before applying the projection
13233      * @return this
13234      */
13235     ref public Matrix4d shadow(double lightX, double lightY, double lightZ, double lightW, Matrix4d planeTransform) return {
13236         shadow(lightX, lightY, lightZ, lightW, planeTransform, this);
13237         return this;
13238     }
13239 
13240     /**
13241      * Set this matrix to a cylindrical billboard transformation that rotates the local +Z axis of a given object with position <code>objPos</code> towards
13242      * a target position at <code>targetPos</code> while constraining a cylindrical rotation around the given <code>up</code> vector.
13243      * <p>
13244      * This method can be used to create the complete model transformation for a given object, including the translation of the object to
13245      * its position <code>objPos</code>.
13246      * 
13247      * @param objPos
13248      *          the position of the object to rotate towards <code>targetPos</code>
13249      * @param targetPos
13250      *          the position of the target (for example the camera) towards which to rotate the object
13251      * @param up
13252      *          the rotation axis (must be {@link Vector3d#normalize() normalized})
13253      * @return this
13254      */
13255     ref public Matrix4d billboardCylindrical(ref Vector3d objPos, Vector3d targetPos, Vector3d up) return {
13256         double dirX = targetPos.x - objPos.x;
13257         double dirY = targetPos.y - objPos.y;
13258         double dirZ = targetPos.z - objPos.z;
13259         // left = up x dir
13260         double leftX = up.y * dirZ - up.z * dirY;
13261         double leftY = up.z * dirX - up.x * dirZ;
13262         double leftZ = up.x * dirY - up.y * dirX;
13263         // normalize left
13264         double invLeftLen = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
13265         leftX *= invLeftLen;
13266         leftY *= invLeftLen;
13267         leftZ *= invLeftLen;
13268         // recompute dir by constraining rotation around 'up'
13269         // dir = left x up
13270         dirX = leftY * up.z - leftZ * up.y;
13271         dirY = leftZ * up.x - leftX * up.z;
13272         dirZ = leftX * up.y - leftY * up.x;
13273         // normalize dir
13274         double invDirLen = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
13275         dirX *= invDirLen;
13276         dirY *= invDirLen;
13277         dirZ *= invDirLen;
13278         // set matrix elements
13279         _m00(leftX).
13280         _m01(leftY).
13281         _m02(leftZ).
13282         _m03(0.0).
13283         _m10(up.x).
13284         _m11(up.y).
13285         _m12(up.z).
13286         _m13(0.0).
13287         _m20(dirX).
13288         _m21(dirY).
13289         _m22(dirZ).
13290         _m23(0.0).
13291         _m30(objPos.x).
13292         _m31(objPos.y).
13293         _m32(objPos.z).
13294         _m33(1.0).
13295         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
13296         return this;
13297     }
13298 
13299     /**
13300      * Set this matrix to a spherical billboard transformation that rotates the local +Z axis of a given object with position <code>objPos</code> towards
13301      * a target position at <code>targetPos</code>.
13302      * <p>
13303      * This method can be used to create the complete model transformation for a given object, including the translation of the object to
13304      * its position <code>objPos</code>.
13305      * <p>
13306      * If preserving an <i>up</i> vector is not necessary when rotating the +Z axis, then a shortest arc rotation can be obtained 
13307      * using {@link #billboardSpherical(ref Vector3d, Vector3d)}.
13308      * 
13309      * @see #billboardSpherical(ref Vector3d, Vector3d)
13310      * 
13311      * @param objPos
13312      *          the position of the object to rotate towards <code>targetPos</code>
13313      * @param targetPos
13314      *          the position of the target (for example the camera) towards which to rotate the object
13315      * @param up
13316      *          the up axis used to orient the object
13317      * @return this
13318      */
13319     ref public Matrix4d billboardSpherical(ref Vector3d objPos, Vector3d targetPos, Vector3d up) return {
13320         double dirX = targetPos.x - objPos.x;
13321         double dirY = targetPos.y - objPos.y;
13322         double dirZ = targetPos.z - objPos.z;
13323         // normalize dir
13324         double invDirLen = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
13325         dirX *= invDirLen;
13326         dirY *= invDirLen;
13327         dirZ *= invDirLen;
13328         // left = up x dir
13329         double leftX = up.y * dirZ - up.z * dirY;
13330         double leftY = up.z * dirX - up.x * dirZ;
13331         double leftZ = up.x * dirY - up.y * dirX;
13332         // normalize left
13333         double invLeftLen = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
13334         leftX *= invLeftLen;
13335         leftY *= invLeftLen;
13336         leftZ *= invLeftLen;
13337         // up = dir x left
13338         double upX = dirY * leftZ - dirZ * leftY;
13339         double upY = dirZ * leftX - dirX * leftZ;
13340         double upZ = dirX * leftY - dirY * leftX;
13341         // set matrix elements
13342         _m00(leftX).
13343         _m01(leftY).
13344         _m02(leftZ).
13345         _m03(0.0).
13346         _m10(upX).
13347         _m11(upY).
13348         _m12(upZ).
13349         _m13(0.0).
13350         _m20(dirX).
13351         _m21(dirY).
13352         _m22(dirZ).
13353         _m23(0.0).
13354         _m30(objPos.x).
13355         _m31(objPos.y).
13356         _m32(objPos.z).
13357         _m33(1.0).
13358         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
13359         return this;
13360     }
13361 
13362     /**
13363      * Set this matrix to a spherical billboard transformation that rotates the local +Z axis of a given object with position <code>objPos</code> towards
13364      * a target position at <code>targetPos</code> using a shortest arc rotation by not preserving any <i>up</i> vector of the object.
13365      * <p>
13366      * This method can be used to create the complete model transformation for a given object, including the translation of the object to
13367      * its position <code>objPos</code>.
13368      * <p>
13369      * In order to specify an <i>up</i> vector which needs to be maintained when rotating the +Z axis of the object,
13370      * use {@link #billboardSpherical(ref Vector3d, Vector3d, Vector3d)}.
13371      * 
13372      * @see #billboardSpherical(ref Vector3d, Vector3d, Vector3d)
13373      * 
13374      * @param objPos
13375      *          the position of the object to rotate towards <code>targetPos</code>
13376      * @param targetPos
13377      *          the position of the target (for example the camera) towards which to rotate the object
13378      * @return this
13379      */
13380     ref public Matrix4d billboardSpherical(ref Vector3d objPos, Vector3d targetPos) return {
13381         double toDirX = targetPos.x - objPos.x;
13382         double toDirY = targetPos.y - objPos.y;
13383         double toDirZ = targetPos.z - objPos.z;
13384         double x = -toDirY;
13385         double y = toDirX;
13386         double w = Math.sqrt(toDirX * toDirX + toDirY * toDirY + toDirZ * toDirZ) + toDirZ;
13387         double invNorm = Math.invsqrt(x * x + y * y + w * w);
13388         x *= invNorm;
13389         y *= invNorm;
13390         w *= invNorm;
13391         double q00 = (x + x) * x;
13392         double q11 = (y + y) * y;
13393         double q01 = (x + x) * y;
13394         double q03 = (x + x) * w;
13395         double q13 = (y + y) * w;
13396         _m00(1.0 - q11).
13397         _m01(q01).
13398         _m02(-q13).
13399         _m03(0.0).
13400         _m10(q01).
13401         _m11(1.0 - q00).
13402         _m12(q03).
13403         _m13(0.0).
13404         _m20(q13).
13405         _m21(-q03).
13406         _m22(1.0 - q11 - q00).
13407         _m23(0.0).
13408         _m30(objPos.x).
13409         _m31(objPos.y).
13410         _m32(objPos.z).
13411         _m33(1.0).
13412         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
13413         return this;
13414     }
13415 
13416     public int hashCode() {
13417         immutable int prime = 31;
13418         int result = 1;
13419         long temp;
13420         temp = Math.doubleToLongBits(m00);
13421         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13422         temp = Math.doubleToLongBits(m01);
13423         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13424         temp = Math.doubleToLongBits(m02);
13425         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13426         temp = Math.doubleToLongBits(m03);
13427         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13428         temp = Math.doubleToLongBits(m10);
13429         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13430         temp = Math.doubleToLongBits(m11);
13431         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13432         temp = Math.doubleToLongBits(m12);
13433         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13434         temp = Math.doubleToLongBits(m13);
13435         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13436         temp = Math.doubleToLongBits(m20);
13437         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13438         temp = Math.doubleToLongBits(m21);
13439         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13440         temp = Math.doubleToLongBits(m22);
13441         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13442         temp = Math.doubleToLongBits(m23);
13443         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13444         temp = Math.doubleToLongBits(m30);
13445         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13446         temp = Math.doubleToLongBits(m31);
13447         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13448         temp = Math.doubleToLongBits(m32);
13449         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13450         temp = Math.doubleToLongBits(m33);
13451         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13452         return result;
13453     }
13454 
13455     public bool equals(Matrix4d m, double delta) {
13456         if (this == m)
13457             return true;
13458         if (!Math.equals(m00, m.m00, delta))
13459             return false;
13460         if (!Math.equals(m01, m.m01, delta))
13461             return false;
13462         if (!Math.equals(m02, m.m02, delta))
13463             return false;
13464         if (!Math.equals(m03, m.m03, delta))
13465             return false;
13466         if (!Math.equals(m10, m.m10, delta))
13467             return false;
13468         if (!Math.equals(m11, m.m11, delta))
13469             return false;
13470         if (!Math.equals(m12, m.m12, delta))
13471             return false;
13472         if (!Math.equals(m13, m.m13, delta))
13473             return false;
13474         if (!Math.equals(m20, m.m20, delta))
13475             return false;
13476         if (!Math.equals(m21, m.m21, delta))
13477             return false;
13478         if (!Math.equals(m22, m.m22, delta))
13479             return false;
13480         if (!Math.equals(m23, m.m23, delta))
13481             return false;
13482         if (!Math.equals(m30, m.m30, delta))
13483             return false;
13484         if (!Math.equals(m31, m.m31, delta))
13485             return false;
13486         if (!Math.equals(m32, m.m32, delta))
13487             return false;
13488         if (!Math.equals(m33, m.m33, delta))
13489             return false;
13490         return true;
13491     }
13492 
13493     public Matrix4d pick(double x, double y, double width, double height, int[] viewport, ref Matrix4d dest) {
13494         double sx = viewport[2] / width;
13495         double sy = viewport[3] / height;
13496         double tx = (viewport[2] + 2.0 * (viewport[0] - x)) / width;
13497         double ty = (viewport[3] + 2.0 * (viewport[1] - y)) / height;
13498         dest._m30(m00 * tx + m10 * ty + m30)
13499         ._m31(m01 * tx + m11 * ty + m31)
13500         ._m32(m02 * tx + m12 * ty + m32)
13501         ._m33(m03 * tx + m13 * ty + m33)
13502         ._m00(m00 * sx)
13503         ._m01(m01 * sx)
13504         ._m02(m02 * sx)
13505         ._m03(m03 * sx)
13506         ._m10(m10 * sy)
13507         ._m11(m11 * sy)
13508         ._m12(m12 * sy)
13509         ._m13(m13 * sy)
13510         ._properties(0);
13511         return dest;
13512     }
13513 
13514     /**
13515      * Apply a picking transformation to this matrix using the given window coordinates <code>(x, y)</code> as the pick center
13516      * and the given <code>(width, height)</code> as the size of the picking region in window coordinates.
13517      * 
13518      * @param x
13519      *          the x coordinate of the picking region center in window coordinates
13520      * @param y
13521      *          the y coordinate of the picking region center in window coordinates
13522      * @param width
13523      *          the width of the picking region in window coordinates
13524      * @param height
13525      *          the height of the picking region in window coordinates
13526      * @param viewport
13527      *          the viewport described by <code>[x, y, width, height]</code>
13528      * @return this
13529      */
13530     ref public Matrix4d pick(double x, double y, double width, double height, int[] viewport) return {
13531         pick(x, y, width, height, viewport, this);
13532         return this;
13533     }
13534 
13535     public bool isAffine() {
13536         return m03 == 0.0 && m13 == 0.0 && m23 == 0.0 && m33 == 1.0;
13537     }
13538 
13539     /**
13540      * Exchange the values of <code>this</code> matrix with the given <code>other</code> matrix.
13541      * 
13542      * @param other
13543      *          the other matrix to exchange the values with
13544      * @return this
13545      */
13546     ref public Matrix4d swap(ref Matrix4d other) return {
13547         double tmp;
13548         tmp = m00; m00 = other.m00; other.m00 = tmp;
13549         tmp = m01; m01 = other.m01; other.m01 = tmp;
13550         tmp = m02; m02 = other.m02; other.m02 = tmp;
13551         tmp = m03; m03 = other.m03; other.m03 = tmp;
13552         tmp = m10; m10 = other.m10; other.m10 = tmp;
13553         tmp = m11; m11 = other.m11; other.m11 = tmp;
13554         tmp = m12; m12 = other.m12; other.m12 = tmp;
13555         tmp = m13; m13 = other.m13; other.m13 = tmp;
13556         tmp = m20; m20 = other.m20; other.m20 = tmp;
13557         tmp = m21; m21 = other.m21; other.m21 = tmp;
13558         tmp = m22; m22 = other.m22; other.m22 = tmp;
13559         tmp = m23; m23 = other.m23; other.m23 = tmp;
13560         tmp = m30; m30 = other.m30; other.m30 = tmp;
13561         tmp = m31; m31 = other.m31; other.m31 = tmp;
13562         tmp = m32; m32 = other.m32; other.m32 = tmp;
13563         tmp = m33; m33 = other.m33; other.m33 = tmp;
13564         int props = properties;
13565         this.properties = other.properties;
13566         other.properties = props;
13567         return this;
13568     }
13569 
13570     public Matrix4d arcball(double radius, double centerX, double centerY, double centerZ, double angleX, double angleY, ref Matrix4d dest) {
13571         double m30 = m20 * -radius + this.m30;
13572         double m31 = m21 * -radius + this.m31;
13573         double m32 = m22 * -radius + this.m32;
13574         double m33 = m23 * -radius + this.m33;
13575         double sin = Math.sin(angleX);
13576         double cos = Math.cosFromSin(sin, angleX);
13577         double nm10 = m10 * cos + m20 * sin;
13578         double nm11 = m11 * cos + m21 * sin;
13579         double nm12 = m12 * cos + m22 * sin;
13580         double nm13 = m13 * cos + m23 * sin;
13581         double m20 = this.m20 * cos - m10 * sin;
13582         double m21 = this.m21 * cos - m11 * sin;
13583         double m22 = this.m22 * cos - m12 * sin;
13584         double m23 = this.m23 * cos - m13 * sin;
13585         sin = Math.sin(angleY);
13586         cos = Math.cosFromSin(sin, angleY);
13587         double nm00 = m00 * cos - m20 * sin;
13588         double nm01 = m01 * cos - m21 * sin;
13589         double nm02 = m02 * cos - m22 * sin;
13590         double nm03 = m03 * cos - m23 * sin;
13591         double nm20 = m00 * sin + m20 * cos;
13592         double nm21 = m01 * sin + m21 * cos;
13593         double nm22 = m02 * sin + m22 * cos;
13594         double nm23 = m03 * sin + m23 * cos;
13595         dest._m30(-nm00 * centerX - nm10 * centerY - nm20 * centerZ + m30)
13596         ._m31(-nm01 * centerX - nm11 * centerY - nm21 * centerZ + m31)
13597         ._m32(-nm02 * centerX - nm12 * centerY - nm22 * centerZ + m32)
13598         ._m33(-nm03 * centerX - nm13 * centerY - nm23 * centerZ + m33)
13599         ._m20(nm20)
13600         ._m21(nm21)
13601         ._m22(nm22)
13602         ._m23(nm23)
13603         ._m10(nm10)
13604         ._m11(nm11)
13605         ._m12(nm12)
13606         ._m13(nm13)
13607         ._m00(nm00)
13608         ._m01(nm01)
13609         ._m02(nm02)
13610         ._m03(nm03)
13611         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
13612         return dest;
13613     }
13614 
13615     public Matrix4d arcball(double radius, Vector3d center, double angleX, double angleY, ref Matrix4d dest) {
13616         return arcball(radius, center.x, center.y, center.z, angleX, angleY, dest);
13617     }
13618 
13619     /**
13620      * Apply an arcball view transformation to this matrix with the given <code>radius</code> and center <code>(centerX, centerY, centerZ)</code>
13621      * position of the arcball and the specified X and Y rotation angles.
13622      * <p>
13623      * This method is equivalent to calling: <code>translate(0, 0, -radius).rotateX(angleX).rotateY(angleY).translate(-centerX, -centerY, -centerZ)</code>
13624      * 
13625      * @param radius
13626      *          the arcball radius
13627      * @param centerX
13628      *          the x coordinate of the center position of the arcball
13629      * @param centerY
13630      *          the y coordinate of the center position of the arcball
13631      * @param centerZ
13632      *          the z coordinate of the center position of the arcball
13633      * @param angleX
13634      *          the rotation angle around the X axis in radians
13635      * @param angleY
13636      *          the rotation angle around the Y axis in radians
13637      * @return this
13638      */
13639     ref public Matrix4d arcball(double radius, double centerX, double centerY, double centerZ, double angleX, double angleY) return {
13640         arcball(radius, centerX, centerY, centerZ, angleX, angleY, this);
13641         return this;
13642     }
13643 
13644     /**
13645      * Apply an arcball view transformation to this matrix with the given <code>radius</code> and <code>center</code>
13646      * position of the arcball and the specified X and Y rotation angles.
13647      * <p>
13648      * This method is equivalent to calling: <code>translate(0, 0, -radius).rotateX(angleX).rotateY(angleY).translate(-center.x, -center.y, -center.z)</code>
13649      * 
13650      * @param radius
13651      *          the arcball radius
13652      * @param center
13653      *          the center position of the arcball
13654      * @param angleX
13655      *          the rotation angle around the X axis in radians
13656      * @param angleY
13657      *          the rotation angle around the Y axis in radians
13658      * @return this
13659      */
13660     ref public Matrix4d arcball(double radius, Vector3d center, double angleX, double angleY) return {
13661         arcball(radius, center.x, center.y, center.z, angleX, angleY, this);
13662         return this;
13663     }
13664 
13665     /**
13666      * Compute the axis-aligned bounding box of the frustum described by <code>this</code> matrix and store the minimum corner
13667      * coordinates in the given <code>min</code> and the maximum corner coordinates in the given <code>max</code> vector.
13668      * <p>
13669      * The matrix <code>this</code> is assumed to be the {@link #invert() inverse} of the origial view-projection matrix
13670      * for which to compute the axis-aligned bounding box in world-space.
13671      * <p>
13672      * The axis-aligned bounding box of the unit frustum is <code>(-1, -1, -1)</code>, <code>(1, 1, 1)</code>.
13673      * 
13674      * @param min
13675      *          will hold the minimum corner coordinates of the axis-aligned bounding box
13676      * @param max
13677      *          will hold the maximum corner coordinates of the axis-aligned bounding box
13678      * @return this
13679      */
13680     ref public Matrix4d frustumAabb(ref Vector3d min, Vector3d max) return {
13681         double minX = double.infinity;
13682         double minY = double.infinity;
13683         double minZ = double.infinity;
13684         double maxX = -double.infinity;
13685         double maxY = -double.infinity;
13686         double maxZ = -double.infinity;
13687         for (int t = 0; t < 8; t++) {
13688             double x = ((t & 1) << 1) - 1.0;
13689             double y = (((t >>> 1) & 1) << 1) - 1.0;
13690             double z = (((t >>> 2) & 1) << 1) - 1.0;
13691             double invW = 1.0 / (m03 * x + m13 * y + m23 * z + m33);
13692             double nx = (m00 * x + m10 * y + m20 * z + m30) * invW;
13693             double ny = (m01 * x + m11 * y + m21 * z + m31) * invW;
13694             double nz = (m02 * x + m12 * y + m22 * z + m32) * invW;
13695             minX = minX < nx ? minX : nx;
13696             minY = minY < ny ? minY : ny;
13697             minZ = minZ < nz ? minZ : nz;
13698             maxX = maxX > nx ? maxX : nx;
13699             maxY = maxY > ny ? maxY : ny;
13700             maxZ = maxZ > nz ? maxZ : nz;
13701         }
13702         min.x = minX;
13703         min.y = minY;
13704         min.z = minZ;
13705         max.x = maxX;
13706         max.y = maxY;
13707         max.z = maxZ;
13708         return this;
13709     }
13710 
13711     public Matrix4d projectedGridRange(Matrix4d projector, double sLower, double sUpper, ref Matrix4d dest) {
13712         // Compute intersection with frustum edges and plane
13713         double minX = double.infinity, minY = double.infinity;
13714         double maxX = -double.infinity, maxY = -double.infinity;
13715         bool intersection = false;
13716         for (int t = 0; t < 3 * 4; t++) {
13717             double c0X, c0Y, c0Z;
13718             double c1X, c1Y, c1Z;
13719             if (t < 4) {
13720                 // all x edges
13721                 c0X = -1; c1X = +1;
13722                 c0Y = c1Y = ((t & 1) << 1) - 1.0;
13723                 c0Z = c1Z = (((t >>> 1) & 1) << 1) - 1.0;
13724             } else if (t < 8) {
13725                 // all y edges
13726                 c0Y = -1; c1Y = +1;
13727                 c0X = c1X = ((t & 1) << 1) - 1.0;
13728                 c0Z = c1Z = (((t >>> 1) & 1) << 1) - 1.0;
13729             } else {
13730                 // all z edges
13731                 c0Z = -1; c1Z = +1;
13732                 c0X = c1X = ((t & 1) << 1) - 1.0;
13733                 c0Y = c1Y = (((t >>> 1) & 1) << 1) - 1.0;
13734             }
13735             // unproject corners
13736             double invW = 1.0 / (m03 * c0X + m13 * c0Y + m23 * c0Z + m33);
13737             double p0x = (m00 * c0X + m10 * c0Y + m20 * c0Z + m30) * invW;
13738             double p0y = (m01 * c0X + m11 * c0Y + m21 * c0Z + m31) * invW;
13739             double p0z = (m02 * c0X + m12 * c0Y + m22 * c0Z + m32) * invW;
13740             invW = 1.0 / (m03 * c1X + m13 * c1Y + m23 * c1Z + m33);
13741             double p1x = (m00 * c1X + m10 * c1Y + m20 * c1Z + m30) * invW;
13742             double p1y = (m01 * c1X + m11 * c1Y + m21 * c1Z + m31) * invW;
13743             double p1z = (m02 * c1X + m12 * c1Y + m22 * c1Z + m32) * invW;
13744             double dirX = p1x - p0x;
13745             double dirY = p1y - p0y;
13746             double dirZ = p1z - p0z;
13747             double invDenom = 1.0 / dirY;
13748             // test for intersection
13749             for (int s = 0; s < 2; s++) {
13750                 double isectT = -(p0y + (s == 0 ? sLower : sUpper)) * invDenom;
13751                 if (isectT >= 0.0 && isectT <= 1.0) {
13752                     intersection = true;
13753                     // project with projector matrix
13754                     double ix = p0x + isectT * dirX;
13755                     double iz = p0z + isectT * dirZ;
13756                     invW = 1.0 / (projector.m03 * ix + projector.m23 * iz + projector.m33);
13757                     double px = (projector.m00 * ix + projector.m20 * iz + projector.m30) * invW;
13758                     double py = (projector.m01 * ix + projector.m21 * iz + projector.m31) * invW;
13759                     minX = minX < px ? minX : px;
13760                     minY = minY < py ? minY : py;
13761                     maxX = maxX > px ? maxX : px;
13762                     maxY = maxY > py ? maxY : py;
13763                 }
13764             }
13765         }
13766         if (!intersection)
13767             return dest; // <- projected grid is not visible
13768         dest.set(maxX - minX, 0, 0, 0, 0, maxY - minY, 0, 0, 0, 0, 1, 0, minX, minY, 0, 1)
13769         ._properties(PROPERTY_AFFINE);
13770         return dest;
13771     }
13772 
13773     public Matrix4d perspectiveFrustumSlice(double near, double far, ref Matrix4d dest) {
13774         double invOldNear = (m23 + m22) / m32;
13775         double invNearFar = 1.0 / (near - far);
13776         dest._m00(m00 * invOldNear * near)
13777         ._m01(m01)
13778         ._m02(m02)
13779         ._m03(m03)
13780         ._m10(m10)
13781         ._m11(m11 * invOldNear * near)
13782         ._m12(m12)
13783         ._m13(m13)
13784         ._m20(m20)
13785         ._m21(m21)
13786         ._m22((far + near) * invNearFar)
13787         ._m23(m23)
13788         ._m30(m30)
13789         ._m31(m31)
13790         ._m32((far + far) * near * invNearFar)
13791         ._m33(m33)
13792         ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
13793         return dest;
13794     }
13795 
13796     public Matrix4d orthoCrop(Matrix4d view, ref Matrix4d dest) {
13797         // determine min/max world z and min/max orthographically view-projected x/y
13798         double minX = double.infinity, maxX = -double.infinity;
13799         double minY = double.infinity, maxY = -double.infinity;
13800         double minZ = double.infinity, maxZ = -double.infinity;
13801         for (int t = 0; t < 8; t++) {
13802             double x = ((t & 1) << 1) - 1.0;
13803             double y = (((t >>> 1) & 1) << 1) - 1.0;
13804             double z = (((t >>> 2) & 1) << 1) - 1.0;
13805             double invW = 1.0 / (m03 * x + m13 * y + m23 * z + m33);
13806             double wx = (m00 * x + m10 * y + m20 * z + m30) * invW;
13807             double wy = (m01 * x + m11 * y + m21 * z + m31) * invW;
13808             double wz = (m02 * x + m12 * y + m22 * z + m32) * invW;
13809             invW = 1.0 / (view.m03 * wx + view.m13 * wy + view.m23 * wz + view.m33);
13810             double vx = view.m00 * wx + view.m10 * wy + view.m20 * wz + view.m30;
13811             double vy = view.m01 * wx + view.m11 * wy + view.m21 * wz + view.m31;
13812             double vz = (view.m02 * wx + view.m12 * wy + view.m22 * wz + view.m32) * invW;
13813             minX = minX < vx ? minX : vx;
13814             maxX = maxX > vx ? maxX : vx;
13815             minY = minY < vy ? minY : vy;
13816             maxY = maxY > vy ? maxY : vy;
13817             minZ = minZ < vz ? minZ : vz;
13818             maxZ = maxZ > vz ? maxZ : vz;
13819         }
13820         // build crop projection matrix to fit 'this' frustum into view
13821         return dest.setOrtho(minX, maxX, minY, maxY, -maxZ, -minZ);
13822     }
13823 
13824     /**
13825      * Set <code>this</code> matrix to a perspective transformation that maps the trapezoid spanned by the four corner coordinates
13826      * <code>(p0x, p0y)</code>, <code>(p1x, p1y)</code>, <code>(p2x, p2y)</code> and <code>(p3x, p3y)</code> to the unit square <code>[(-1, -1)..(+1, +1)]</code>.
13827      * <p>
13828      * The corner coordinates are given in counter-clockwise order starting from the <i>left</i> corner on the smaller parallel side of the trapezoid
13829      * seen when looking at the trapezoid oriented with its shorter parallel edge at the bottom and its longer parallel edge at the top.
13830      * <p>
13831      * Reference: <a href="http://www.comp.nus.edu.sg/~tants/tsm/TSM_recipe.html">Trapezoidal Shadow Maps (TSM) - Recipe</a>
13832      * 
13833      * @param p0x
13834      *          the x coordinate of the left corner at the shorter edge of the trapezoid
13835      * @param p0y
13836      *          the y coordinate of the left corner at the shorter edge of the trapezoid
13837      * @param p1x
13838      *          the x coordinate of the right corner at the shorter edge of the trapezoid
13839      * @param p1y
13840      *          the y coordinate of the right corner at the shorter edge of the trapezoid
13841      * @param p2x
13842      *          the x coordinate of the right corner at the longer edge of the trapezoid
13843      * @param p2y
13844      *          the y coordinate of the right corner at the longer edge of the trapezoid
13845      * @param p3x
13846      *          the x coordinate of the left corner at the longer edge of the trapezoid
13847      * @param p3y
13848      *          the y coordinate of the left corner at the longer edge of the trapezoid
13849      * @return this
13850      */
13851     ref public Matrix4d trapezoidCrop(double p0x, double p0y, double p1x, double p1y, double p2x, double p2y, double p3x, double p3y) return {
13852         double aX = p1y - p0y, aY = p0x - p1x;
13853         double nm00 = aY;
13854         double nm10 = -aX;
13855         double nm30 = aX * p0y - aY * p0x;
13856         double nm01 = aX;
13857         double nm11 = aY;
13858         double nm31 = -(aX * p0x + aY * p0y);
13859         double c3x = nm00 * p3x + nm10 * p3y + nm30;
13860         double c3y = nm01 * p3x + nm11 * p3y + nm31;
13861         double s = -c3x / c3y;
13862         nm00 += s * nm01;
13863         nm10 += s * nm11;
13864         nm30 += s * nm31;
13865         double d1x = nm00 * p1x + nm10 * p1y + nm30;
13866         double d2x = nm00 * p2x + nm10 * p2y + nm30;
13867         double d = d1x * c3y / (d2x - d1x);
13868         nm31 += d;
13869         double sx = 2.0 / d2x;
13870         double sy = 1.0 / (c3y + d);
13871         double u = (sy + sy) * d / (1.0 - sy * d);
13872         double m03 = nm01 * sy;
13873         double m13 = nm11 * sy;
13874         double m33 = nm31 * sy;
13875         nm01 = (u + 1.0) * m03;
13876         nm11 = (u + 1.0) * m13;
13877         nm31 = (u + 1.0) * m33 - u;
13878         nm00 = sx * nm00 - m03;
13879         nm10 = sx * nm10 - m13;
13880         nm30 = sx * nm30 - m33;
13881         set(nm00, nm01, 0, m03,
13882             nm10, nm11, 0, m13,
13883               0,   0, 1,   0,
13884             nm30, nm31, 0, m33);
13885         properties = 0;
13886         return this;
13887     }
13888 
13889     ref public Matrix4d transformAab(double minX, double minY, double minZ, double maxX, double maxY, double maxZ, ref Vector3d outMin, ref Vector3d outMax) return {
13890         double xax = m00 * minX, xay = m01 * minX, xaz = m02 * minX;
13891         double xbx = m00 * maxX, xby = m01 * maxX, xbz = m02 * maxX;
13892         double yax = m10 * minY, yay = m11 * minY, yaz = m12 * minY;
13893         double ybx = m10 * maxY, yby = m11 * maxY, ybz = m12 * maxY;
13894         double zax = m20 * minZ, zay = m21 * minZ, zaz = m22 * minZ;
13895         double zbx = m20 * maxZ, zby = m21 * maxZ, zbz = m22 * maxZ;
13896         double xminx, xminy, xminz, yminx, yminy, yminz, zminx, zminy, zminz;
13897         double xmaxx, xmaxy, xmaxz, ymaxx, ymaxy, ymaxz, zmaxx, zmaxy, zmaxz;
13898         if (xax < xbx) {
13899             xminx = xax;
13900             xmaxx = xbx;
13901         } else {
13902             xminx = xbx;
13903             xmaxx = xax;
13904         }
13905         if (xay < xby) {
13906             xminy = xay;
13907             xmaxy = xby;
13908         } else {
13909             xminy = xby;
13910             xmaxy = xay;
13911         }
13912         if (xaz < xbz) {
13913             xminz = xaz;
13914             xmaxz = xbz;
13915         } else {
13916             xminz = xbz;
13917             xmaxz = xaz;
13918         }
13919         if (yax < ybx) {
13920             yminx = yax;
13921             ymaxx = ybx;
13922         } else {
13923             yminx = ybx;
13924             ymaxx = yax;
13925         }
13926         if (yay < yby) {
13927             yminy = yay;
13928             ymaxy = yby;
13929         } else {
13930             yminy = yby;
13931             ymaxy = yay;
13932         }
13933         if (yaz < ybz) {
13934             yminz = yaz;
13935             ymaxz = ybz;
13936         } else {
13937             yminz = ybz;
13938             ymaxz = yaz;
13939         }
13940         if (zax < zbx) {
13941             zminx = zax;
13942             zmaxx = zbx;
13943         } else {
13944             zminx = zbx;
13945             zmaxx = zax;
13946         }
13947         if (zay < zby) {
13948             zminy = zay;
13949             zmaxy = zby;
13950         } else {
13951             zminy = zby;
13952             zmaxy = zay;
13953         }
13954         if (zaz < zbz) {
13955             zminz = zaz;
13956             zmaxz = zbz;
13957         } else {
13958             zminz = zbz;
13959             zmaxz = zaz;
13960         }
13961         outMin.x = xminx + yminx + zminx + m30;
13962         outMin.y = xminy + yminy + zminy + m31;
13963         outMin.z = xminz + yminz + zminz + m32;
13964         outMax.x = xmaxx + ymaxx + zmaxx + m30;
13965         outMax.y = xmaxy + ymaxy + zmaxy + m31;
13966         outMax.z = xmaxz + ymaxz + zmaxz + m32;
13967         return this;
13968     }
13969 
13970     ref public Matrix4d transformAab(ref Vector3d min, ref Vector3d max, ref Vector3d outMin, ref Vector3d outMax) return {
13971         return transformAab(min.x, min.y, min.z, max.x, max.y, max.z, outMin, outMax);
13972     }
13973 
13974     /**
13975      * Linearly interpolate <code>this</code> and <code>other</code> using the given interpolation factor <code>t</code>
13976      * and store the result in <code>this</code>.
13977      * <p>
13978      * If <code>t</code> is <code>0.0</code> then the result is <code>this</code>. If the interpolation factor is <code>1.0</code>
13979      * then the result is <code>other</code>.
13980      *
13981      * @param other
13982      *          the other matrix
13983      * @param t
13984      *          the interpolation factor between 0.0 and 1.0
13985      * @return this
13986      */
13987     ref public Matrix4d lerp(Matrix4d other, double t) return {
13988         lerp(other, t, this);
13989         return this;
13990     }
13991 
13992     public Matrix4d lerp(Matrix4d other, double t, ref Matrix4d dest) {
13993         dest._m00(Math.fma(other.m00 - m00, t, m00))
13994         ._m01(Math.fma(other.m01 - m01, t, m01))
13995         ._m02(Math.fma(other.m02 - m02, t, m02))
13996         ._m03(Math.fma(other.m03 - m03, t, m03))
13997         ._m10(Math.fma(other.m10 - m10, t, m10))
13998         ._m11(Math.fma(other.m11 - m11, t, m11))
13999         ._m12(Math.fma(other.m12 - m12, t, m12))
14000         ._m13(Math.fma(other.m13 - m13, t, m13))
14001         ._m20(Math.fma(other.m20 - m20, t, m20))
14002         ._m21(Math.fma(other.m21 - m21, t, m21))
14003         ._m22(Math.fma(other.m22 - m22, t, m22))
14004         ._m23(Math.fma(other.m23 - m23, t, m23))
14005         ._m30(Math.fma(other.m30 - m30, t, m30))
14006         ._m31(Math.fma(other.m31 - m31, t, m31))
14007         ._m32(Math.fma(other.m32 - m32, t, m32))
14008         ._m33(Math.fma(other.m33 - m33, t, m33))
14009         ._properties(properties & other.properties);
14010         return dest;
14011     }
14012 
14013     /**
14014      * Apply a model transformation to this matrix for a right-handed coordinate system, 
14015      * that aligns the local <code>+Z</code> axis with <code>direction</code>
14016      * and store the result in <code>dest</code>.
14017      * <p>
14018      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
14019      * then the new matrix will be <code>M * L</code>. So when transforming a
14020      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
14021      * the lookat transformation will be applied first!
14022      * <p>
14023      * In order to set the matrix to a rotation transformation without post-multiplying it,
14024      * use {@link #rotationTowards(ref Vector3d, Vector3d) rotationTowards()}.
14025      * <p>
14026      * This method is equivalent to calling: <code>mulAffine(new Matrix4d().lookAt(new Vector3d(), new Vector3d(dir).negate(), up).invertAffine(), dest)</code>
14027      * 
14028      * @see #rotateTowards(double, double, double, double, double, double, Matrix4d)
14029      * @see #rotationTowards(ref Vector3d, Vector3d)
14030      * 
14031      * @param direction
14032      *              the direction to rotate towards
14033      * @param up
14034      *              the up vector
14035      * @param dest
14036      *              will hold the result
14037      * @return dest
14038      */
14039     public Matrix4d rotateTowards(ref Vector3d direction, Vector3d up, ref Matrix4d dest) {
14040         return rotateTowards(direction.x, direction.y, direction.z, up.x, up.y, up.z, dest);
14041     }
14042 
14043     /**
14044      * Apply a model transformation to this matrix for a right-handed coordinate system, 
14045      * that aligns the local <code>+Z</code> axis with <code>direction</code>.
14046      * <p>
14047      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
14048      * then the new matrix will be <code>M * L</code>. So when transforming a
14049      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
14050      * the lookat transformation will be applied first!
14051      * <p>
14052      * In order to set the matrix to a rotation transformation without post-multiplying it,
14053      * use {@link #rotationTowards(ref Vector3d, Vector3d) rotationTowards()}.
14054      * <p>
14055      * This method is equivalent to calling: <code>mulAffine(new Matrix4d().lookAt(new Vector3d(), new Vector3d(dir).negate(), up).invertAffine())</code>
14056      * 
14057      * @see #rotateTowards(double, double, double, double, double, double)
14058      * @see #rotationTowards(ref Vector3d, Vector3d)
14059      * 
14060      * @param direction
14061      *              the direction to orient towards
14062      * @param up
14063      *              the up vector
14064      * @return this
14065      */
14066     ref public Matrix4d rotateTowards(ref Vector3d direction, Vector3d up) return {
14067         rotateTowards(direction.x, direction.y, direction.z, up.x, up.y, up.z, this);
14068         return this;
14069     }
14070 
14071     /**
14072      * Apply a model transformation to this matrix for a right-handed coordinate system, 
14073      * that aligns the local <code>+Z</code> axis with <code>(dirX, dirY, dirZ)</code>.
14074      * <p>
14075      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
14076      * then the new matrix will be <code>M * L</code>. So when transforming a
14077      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
14078      * the lookat transformation will be applied first!
14079      * <p>
14080      * In order to set the matrix to a rotation transformation without post-multiplying it,
14081      * use {@link #rotationTowards(double, double, double, double, double, double) rotationTowards()}.
14082      * <p>
14083      * This method is equivalent to calling: <code>mulAffine(new Matrix4d().lookAt(0, 0, 0, -dirX, -dirY, -dirZ, upX, upY, upZ).invertAffine())</code>
14084      * 
14085      * @see #rotateTowards(ref Vector3d, Vector3d)
14086      * @see #rotationTowards(double, double, double, double, double, double)
14087      * 
14088      * @param dirX
14089      *              the x-coordinate of the direction to rotate towards
14090      * @param dirY
14091      *              the y-coordinate of the direction to rotate towards
14092      * @param dirZ
14093      *              the z-coordinate of the direction to rotate towards
14094      * @param upX
14095      *              the x-coordinate of the up vector
14096      * @param upY
14097      *              the y-coordinate of the up vector
14098      * @param upZ
14099      *              the z-coordinate of the up vector
14100      * @return this
14101      */
14102     ref public Matrix4d rotateTowards(double dirX, double dirY, double dirZ, double upX, double upY, double upZ) return {
14103         rotateTowards(dirX, dirY, dirZ, upX, upY, upZ, this);
14104         return this;
14105     }
14106 
14107     /**
14108      * Apply a model transformation to this matrix for a right-handed coordinate system, 
14109      * that aligns the local <code>+Z</code> axis with <code>dir</code>
14110      * and store the result in <code>dest</code>.
14111      * <p>
14112      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
14113      * then the new matrix will be <code>M * L</code>. So when transforming a
14114      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
14115      * the lookat transformation will be applied first!
14116      * <p>
14117      * In order to set the matrix to a rotation transformation without post-multiplying it,
14118      * use {@link #rotationTowards(double, double, double, double, double, double) rotationTowards()}.
14119      * <p>
14120      * This method is equivalent to calling: <code>mulAffine(new Matrix4d().lookAt(0, 0, 0, -dirX, -dirY, -dirZ, upX, upY, upZ).invertAffine(), dest)</code>
14121      * 
14122      * @see #rotateTowards(ref Vector3d, Vector3d)
14123      * @see #rotationTowards(double, double, double, double, double, double)
14124      * 
14125      * @param dirX
14126      *              the x-coordinate of the direction to rotate towards
14127      * @param dirY
14128      *              the y-coordinate of the direction to rotate towards
14129      * @param dirZ
14130      *              the z-coordinate of the direction to rotate towards
14131      * @param upX
14132      *              the x-coordinate of the up vector
14133      * @param upY
14134      *              the y-coordinate of the up vector
14135      * @param upZ
14136      *              the z-coordinate of the up vector
14137      * @param dest
14138      *              will hold the result
14139      * @return dest
14140      */
14141     public Matrix4d rotateTowards(double dirX, double dirY, double dirZ, double upX, double upY, double upZ, ref Matrix4d dest) {
14142         // Normalize direction
14143         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
14144         double ndirX = dirX * invDirLength;
14145         double ndirY = dirY * invDirLength;
14146         double ndirZ = dirZ * invDirLength;
14147         // left = up x direction
14148         double leftX, leftY, leftZ;
14149         leftX = upY * ndirZ - upZ * ndirY;
14150         leftY = upZ * ndirX - upX * ndirZ;
14151         leftZ = upX * ndirY - upY * ndirX;
14152         // normalize left
14153         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
14154         leftX *= invLeftLength;
14155         leftY *= invLeftLength;
14156         leftZ *= invLeftLength;
14157         // up = direction x left
14158         double upnX = ndirY * leftZ - ndirZ * leftY;
14159         double upnY = ndirZ * leftX - ndirX * leftZ;
14160         double upnZ = ndirX * leftY - ndirY * leftX;
14161         double rm00 = leftX;
14162         double rm01 = leftY;
14163         double rm02 = leftZ;
14164         double rm10 = upnX;
14165         double rm11 = upnY;
14166         double rm12 = upnZ;
14167         double rm20 = ndirX;
14168         double rm21 = ndirY;
14169         double rm22 = ndirZ;
14170         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
14171         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
14172         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
14173         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02;
14174         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
14175         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
14176         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
14177         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12;
14178         dest._m30(m30)
14179         ._m31(m31)
14180         ._m32(m32)
14181         ._m33(m33)
14182         ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
14183         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
14184         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
14185         ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22)
14186         ._m00(nm00)
14187         ._m01(nm01)
14188         ._m02(nm02)
14189         ._m03(nm03)
14190         ._m10(nm10)
14191         ._m11(nm11)
14192         ._m12(nm12)
14193         ._m13(nm13)
14194         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
14195         return dest;
14196     }
14197 
14198     /**
14199      * Set this matrix to a model transformation for a right-handed coordinate system, 
14200      * that aligns the local <code>-z</code> axis with <code>dir</code>.
14201      * <p>
14202      * In order to apply the rotation transformation to a previous existing transformation,
14203      * use {@link #rotateTowards(double, double, double, double, double, double) rotateTowards}.
14204      * <p>
14205      * This method is equivalent to calling: <code>setLookAt(new Vector3d(), new Vector3d(dir).negate(), up).invertAffine()</code>
14206      * 
14207      * @see #rotationTowards(ref Vector3d, Vector3d)
14208      * @see #rotateTowards(double, double, double, double, double, double)
14209      * 
14210      * @param dir
14211      *              the direction to orient the local -z axis towards
14212      * @param up
14213      *              the up vector
14214      * @return this
14215      */
14216     ref public Matrix4d rotationTowards(ref Vector3d dir, Vector3d up) return {
14217         return rotationTowards(dir.x, dir.y, dir.z, up.x, up.y, up.z);
14218     }
14219 
14220     /**
14221      * Set this matrix to a model transformation for a right-handed coordinate system, 
14222      * that aligns the local <code>-z</code> axis with <code>dir</code>.
14223      * <p>
14224      * In order to apply the rotation transformation to a previous existing transformation,
14225      * use {@link #rotateTowards(double, double, double, double, double, double) rotateTowards}.
14226      * <p>
14227      * This method is equivalent to calling: <code>setLookAt(0, 0, 0, -dirX, -dirY, -dirZ, upX, upY, upZ).invertAffine()</code>
14228      * 
14229      * @see #rotateTowards(ref Vector3d, Vector3d)
14230      * @see #rotationTowards(double, double, double, double, double, double)
14231      * 
14232      * @param dirX
14233      *              the x-coordinate of the direction to rotate towards
14234      * @param dirY
14235      *              the y-coordinate of the direction to rotate towards
14236      * @param dirZ
14237      *              the z-coordinate of the direction to rotate towards
14238      * @param upX
14239      *              the x-coordinate of the up vector
14240      * @param upY
14241      *              the y-coordinate of the up vector
14242      * @param upZ
14243      *              the z-coordinate of the up vector
14244      * @return this
14245      */
14246     ref public Matrix4d rotationTowards(double dirX, double dirY, double dirZ, double upX, double upY, double upZ) return {
14247         // Normalize direction
14248         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
14249         double ndirX = dirX * invDirLength;
14250         double ndirY = dirY * invDirLength;
14251         double ndirZ = dirZ * invDirLength;
14252         // left = up x direction
14253         double leftX, leftY, leftZ;
14254         leftX = upY * ndirZ - upZ * ndirY;
14255         leftY = upZ * ndirX - upX * ndirZ;
14256         leftZ = upX * ndirY - upY * ndirX;
14257         // normalize left
14258         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
14259         leftX *= invLeftLength;
14260         leftY *= invLeftLength;
14261         leftZ *= invLeftLength;
14262         // up = direction x left
14263         double upnX = ndirY * leftZ - ndirZ * leftY;
14264         double upnY = ndirZ * leftX - ndirX * leftZ;
14265         double upnZ = ndirX * leftY - ndirY * leftX;
14266         if ((properties & PROPERTY_IDENTITY) == 0)
14267             this._identity();
14268         setm00(leftX);
14269         setm01(leftY);
14270         setm02(leftZ);
14271         setm10(upnX);
14272         setm11(upnY);
14273         setm12(upnZ);
14274         setm20(ndirX);
14275         setm21(ndirY);
14276         setm22(ndirZ);
14277         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
14278         return this;
14279     }
14280 
14281     /**
14282      * Set this matrix to a model transformation for a right-handed coordinate system, 
14283      * that translates to the given <code>pos</code> and aligns the local <code>-z</code>
14284      * axis with <code>dir</code>.
14285      * <p>
14286      * This method is equivalent to calling: <code>translation(pos).rotateTowards(dir, up)</code>
14287      * 
14288      * @see #translation(ref Vector3d)
14289      * @see #rotateTowards(ref Vector3d, Vector3d)
14290      *
14291      * @param pos
14292      *              the position to translate to
14293      * @param dir
14294      *              the direction to rotate towards
14295      * @param up
14296      *              the up vector
14297      * @return this
14298      */
14299     ref public Matrix4d translationRotateTowards(ref Vector3d pos, Vector3d dir, Vector3d up) return {
14300         return translationRotateTowards(pos.x, pos.y, pos.z, dir.x, dir.y, dir.z, up.x, up.y, up.z);
14301     }
14302 
14303     /**
14304      * Set this matrix to a model transformation for a right-handed coordinate system, 
14305      * that translates to the given <code>(posX, posY, posZ)</code> and aligns the local <code>-z</code>
14306      * axis with <code>(dirX, dirY, dirZ)</code>.
14307      * <p>
14308      * This method is equivalent to calling: <code>translation(posX, posY, posZ).rotateTowards(dirX, dirY, dirZ, upX, upY, upZ)</code>
14309      * 
14310      * @see #translation(double, double, double)
14311      * @see #rotateTowards(double, double, double, double, double, double)
14312      * 
14313      * @param posX
14314      *              the x-coordinate of the position to translate to
14315      * @param posY
14316      *              the y-coordinate of the position to translate to
14317      * @param posZ
14318      *              the z-coordinate of the position to translate to
14319      * @param dirX
14320      *              the x-coordinate of the direction to rotate towards
14321      * @param dirY
14322      *              the y-coordinate of the direction to rotate towards
14323      * @param dirZ
14324      *              the z-coordinate of the direction to rotate towards
14325      * @param upX
14326      *              the x-coordinate of the up vector
14327      * @param upY
14328      *              the y-coordinate of the up vector
14329      * @param upZ
14330      *              the z-coordinate of the up vector
14331      * @return this
14332      */
14333     ref public Matrix4d translationRotateTowards(double posX, double posY, double posZ, double dirX, double dirY, double dirZ, double upX, double upY, double upZ) return {
14334         // Normalize direction
14335         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
14336         double ndirX = dirX * invDirLength;
14337         double ndirY = dirY * invDirLength;
14338         double ndirZ = dirZ * invDirLength;
14339         // left = up x direction
14340         double leftX, leftY, leftZ;
14341         leftX = upY * ndirZ - upZ * ndirY;
14342         leftY = upZ * ndirX - upX * ndirZ;
14343         leftZ = upX * ndirY - upY * ndirX;
14344         // normalize left
14345         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
14346         leftX *= invLeftLength;
14347         leftY *= invLeftLength;
14348         leftZ *= invLeftLength;
14349         // up = direction x left
14350         double upnX = ndirY * leftZ - ndirZ * leftY;
14351         double upnY = ndirZ * leftX - ndirX * leftZ;
14352         double upnZ = ndirX * leftY - ndirY * leftX;
14353         setm00(leftX);
14354         setm01(leftY);
14355         setm02(leftZ);
14356         setm03(0.0);
14357         setm10(upnX);
14358         setm11(upnY);
14359         setm12(upnZ);
14360         setm13(0.0);
14361         setm20(ndirX);
14362         setm21(ndirY);
14363         setm22(ndirZ);
14364         setm23(0.0);
14365         setm30(posX);
14366         setm31(posY);
14367         setm32(posZ);
14368         setm33(1.0);
14369         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
14370         return this;
14371     }
14372 
14373     public Vector3d getEulerAnglesZYX(ref Vector3d dest) {
14374         dest.x = Math.atan2(m12, m22);
14375         dest.y = Math.atan2(-m02, Math.sqrt(1.0 - m02 * m02));
14376         dest.z = Math.atan2(m01, m00);
14377         return dest;
14378     }
14379 
14380     public Vector3d getEulerAnglesXYZ(ref Vector3d dest) {
14381         dest.x = Math.atan2(-m21, m22);
14382         dest.y = Math.atan2(m20, Math.sqrt(1.0 - m20 * m20));
14383         dest.z = Math.atan2(-m10, m00);
14384         return dest;
14385     }
14386 
14387     /**
14388      * Compute the extents of the coordinate system before this {@link #isAffine() affine} transformation was applied
14389      * and store the resulting corner coordinates in <code>corner</code> and the span vectors in
14390      * <code>xDir</code>, <code>yDir</code> and <code>zDir</code>.
14391      * <p>
14392      * That means, given the maximum extents of the coordinate system between <code>[-1..+1]</code> in all dimensions,
14393      * this method returns one corner and the length and direction of the three base axis vectors in the coordinate
14394      * system before this transformation is applied, which transforms into the corner coordinates <code>[-1, +1]</code>.
14395      * <p>
14396      * This method is equivalent to computing at least three adjacent corners using {@link #frustumCorner(int, Vector3d)}
14397      * and subtracting them to obtain the length and direction of the span vectors.
14398      * 
14399      * @param corner
14400      *          will hold one corner of the span (usually the corner {@link Matrix4d#CORNER_NXNYNZ})
14401      * @param xDir
14402      *          will hold the direction and length of the span along the positive X axis
14403      * @param yDir
14404      *          will hold the direction and length of the span along the positive Y axis
14405      * @param zDir
14406      *          will hold the direction and length of the span along the positive z axis
14407      * @return this
14408      */
14409     ref public Matrix4d affineSpan(ref Vector3d corner, ref Vector3d xDir, ref Vector3d yDir, ref Vector3d zDir) return {
14410         double a = m10 * m22, b = m10 * m21, c = m10 * m02, d = m10 * m01;
14411         double e = m11 * m22, f = m11 * m20, g = m11 * m02, h = m11 * m00;
14412         double i = m12 * m21, j = m12 * m20, k = m12 * m01, l = m12 * m00;
14413         double m = m20 * m02, n = m20 * m01, o = m21 * m02, p = m21 * m00;
14414         double q = m22 * m01, r = m22 * m00;
14415         double s = 1.0 / (m00 * m11 - m01 * m10) * m22 + (m02 * m10 - m00 * m12) * m21 + (m01 * m12 - m02 * m11) * m20;
14416         double nm00 = (e - i) * s, nm01 = (o - q) * s, nm02 = (k - g) * s;
14417         double nm10 = (j - a) * s, nm11 = (r - m) * s, nm12 = (c - l) * s;
14418         double nm20 = (b - f) * s, nm21 = (n - p) * s, nm22 = (h - d) * s;
14419         corner.x = -nm00 - nm10 - nm20 + (a * m31 - b * m32 + f * m32 - e * m30 + i * m30 - j * m31) * s;
14420         corner.y = -nm01 - nm11 - nm21 + (m * m31 - n * m32 + p * m32 - o * m30 + q * m30 - r * m31) * s;
14421         corner.z = -nm02 - nm12 - nm22 + (g * m30 - k * m30 + l * m31 - c * m31 + d * m32 - h * m32) * s;
14422         xDir.x = 2.0 * nm00; xDir.y = 2.0 * nm01; xDir.z = 2.0 * nm02;
14423         yDir.x = 2.0 * nm10; yDir.y = 2.0 * nm11; yDir.z = 2.0 * nm12;
14424         zDir.x = 2.0 * nm20; zDir.y = 2.0 * nm21; zDir.z = 2.0 * nm22;
14425         return this;
14426     }
14427 
14428     public bool testPoint(double x, double y, double z) {
14429         double nxX = m03 + m00, nxY = m13 + m10, nxZ = m23 + m20, nxW = m33 + m30;
14430         double pxX = m03 - m00, pxY = m13 - m10, pxZ = m23 - m20, pxW = m33 - m30;
14431         double nyX = m03 + m01, nyY = m13 + m11, nyZ = m23 + m21, nyW = m33 + m31;
14432         double pyX = m03 - m01, pyY = m13 - m11, pyZ = m23 - m21, pyW = m33 - m31;
14433         double nzX = m03 + m02, nzY = m13 + m12, nzZ = m23 + m22, nzW = m33 + m32;
14434         double pzX = m03 - m02, pzY = m13 - m12, pzZ = m23 - m22, pzW = m33 - m32;
14435         return nxX * x + nxY * y + nxZ * z + nxW >= 0 && pxX * x + pxY * y + pxZ * z + pxW >= 0 &&
14436                nyX * x + nyY * y + nyZ * z + nyW >= 0 && pyX * x + pyY * y + pyZ * z + pyW >= 0 &&
14437                nzX * x + nzY * y + nzZ * z + nzW >= 0 && pzX * x + pzY * y + pzZ * z + pzW >= 0;
14438     }
14439 
14440     public bool testSphere(double x, double y, double z, double r) {
14441         double invl;
14442         double nxX = m03 + m00, nxY = m13 + m10, nxZ = m23 + m20, nxW = m33 + m30;
14443         invl = Math.invsqrt(nxX * nxX + nxY * nxY + nxZ * nxZ);
14444         nxX *= invl; nxY *= invl; nxZ *= invl; nxW *= invl;
14445         double pxX = m03 - m00, pxY = m13 - m10, pxZ = m23 - m20, pxW = m33 - m30;
14446         invl = Math.invsqrt(pxX * pxX + pxY * pxY + pxZ * pxZ);
14447         pxX *= invl; pxY *= invl; pxZ *= invl; pxW *= invl;
14448         double nyX = m03 + m01, nyY = m13 + m11, nyZ = m23 + m21, nyW = m33 + m31;
14449         invl = Math.invsqrt(nyX * nyX + nyY * nyY + nyZ * nyZ);
14450         nyX *= invl; nyY *= invl; nyZ *= invl; nyW *= invl;
14451         double pyX = m03 - m01, pyY = m13 - m11, pyZ = m23 - m21, pyW = m33 - m31;
14452         invl = Math.invsqrt(pyX * pyX + pyY * pyY + pyZ * pyZ);
14453         pyX *= invl; pyY *= invl; pyZ *= invl; pyW *= invl;
14454         double nzX = m03 + m02, nzY = m13 + m12, nzZ = m23 + m22, nzW = m33 + m32;
14455         invl = Math.invsqrt(nzX * nzX + nzY * nzY + nzZ * nzZ);
14456         nzX *= invl; nzY *= invl; nzZ *= invl; nzW *= invl;
14457         double pzX = m03 - m02, pzY = m13 - m12, pzZ = m23 - m22, pzW = m33 - m32;
14458         invl = Math.invsqrt(pzX * pzX + pzY * pzY + pzZ * pzZ);
14459         pzX *= invl; pzY *= invl; pzZ *= invl; pzW *= invl;
14460         return nxX * x + nxY * y + nxZ * z + nxW >= -r && pxX * x + pxY * y + pxZ * z + pxW >= -r &&
14461                nyX * x + nyY * y + nyZ * z + nyW >= -r && pyX * x + pyY * y + pyZ * z + pyW >= -r &&
14462                nzX * x + nzY * y + nzZ * z + nzW >= -r && pzX * x + pzY * y + pzZ * z + pzW >= -r;
14463     }
14464 
14465     public bool testAab(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
14466         double nxX = m03 + m00, nxY = m13 + m10, nxZ = m23 + m20, nxW = m33 + m30;
14467         double pxX = m03 - m00, pxY = m13 - m10, pxZ = m23 - m20, pxW = m33 - m30;
14468         double nyX = m03 + m01, nyY = m13 + m11, nyZ = m23 + m21, nyW = m33 + m31;
14469         double pyX = m03 - m01, pyY = m13 - m11, pyZ = m23 - m21, pyW = m33 - m31;
14470         double nzX = m03 + m02, nzY = m13 + m12, nzZ = m23 + m22, nzW = m33 + m32;
14471         double pzX = m03 - m02, pzY = m13 - m12, pzZ = m23 - m22, pzW = m33 - m32;
14472         /*
14473          * This is an implementation of the "2.4 Basic intersection test" of the mentioned site.
14474          * It does not distinguish between partially inside and fully inside, though, so the test with the 'p' vertex is omitted.
14475          */
14476         return nxX * (nxX < 0 ? minX : maxX) + nxY * (nxY < 0 ? minY : maxY) + nxZ * (nxZ < 0 ? minZ : maxZ) >= -nxW &&
14477                pxX * (pxX < 0 ? minX : maxX) + pxY * (pxY < 0 ? minY : maxY) + pxZ * (pxZ < 0 ? minZ : maxZ) >= -pxW &&
14478                nyX * (nyX < 0 ? minX : maxX) + nyY * (nyY < 0 ? minY : maxY) + nyZ * (nyZ < 0 ? minZ : maxZ) >= -nyW &&
14479                pyX * (pyX < 0 ? minX : maxX) + pyY * (pyY < 0 ? minY : maxY) + pyZ * (pyZ < 0 ? minZ : maxZ) >= -pyW &&
14480                nzX * (nzX < 0 ? minX : maxX) + nzY * (nzY < 0 ? minY : maxY) + nzZ * (nzZ < 0 ? minZ : maxZ) >= -nzW &&
14481                pzX * (pzX < 0 ? minX : maxX) + pzY * (pzY < 0 ? minY : maxY) + pzZ * (pzZ < 0 ? minZ : maxZ) >= -pzW;
14482     }
14483 
14484     /**
14485      * Apply an oblique projection transformation to this matrix with the given values for <code>a</code> and
14486      * <code>b</code>.
14487      * <p>
14488      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the oblique transformation matrix,
14489      * then the new matrix will be <code>M * O</code>. So when transforming a
14490      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
14491      * oblique transformation will be applied first!
14492      * <p>
14493      * The oblique transformation is defined as:
14494      * <pre>
14495      * x' = x + a*z
14496      * y' = y + a*z
14497      * z' = z
14498      * </pre>
14499      * or in matrix form:
14500      * <pre>
14501      * 1 0 a 0
14502      * 0 1 b 0
14503      * 0 0 1 0
14504      * 0 0 0 1
14505      * </pre>
14506      * 
14507      * @param a
14508      *            the value for the z factor that applies to x
14509      * @param b
14510      *            the value for the z factor that applies to y
14511      * @return this
14512      */
14513     ref public Matrix4d obliqueZ(double a, double b) return {
14514         setm20(m00 * a + m10 * b + m20);
14515         setm21(m01 * a + m11 * b + m21);
14516         setm22(m02 * a + m12 * b + m22);
14517         this.properties &= PROPERTY_AFFINE;
14518         return this;
14519     }
14520 
14521     /**
14522      * Apply an oblique projection transformation to this matrix with the given values for <code>a</code> and
14523      * <code>b</code> and store the result in <code>dest</code>.
14524      * <p>
14525      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the oblique transformation matrix,
14526      * then the new matrix will be <code>M * O</code>. So when transforming a
14527      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
14528      * oblique transformation will be applied first!
14529      * <p>
14530      * The oblique transformation is defined as:
14531      * <pre>
14532      * x' = x + a*z
14533      * y' = y + a*z
14534      * z' = z
14535      * </pre>
14536      * or in matrix form:
14537      * <pre>
14538      * 1 0 a 0
14539      * 0 1 b 0
14540      * 0 0 1 0
14541      * 0 0 0 1
14542      * </pre>
14543      * 
14544      * @param a
14545      *            the value for the z factor that applies to x
14546      * @param b
14547      *            the value for the z factor that applies to y
14548      * @param dest
14549      *            will hold the result
14550      * @return dest
14551      */
14552     public Matrix4d obliqueZ(double a, double b, ref Matrix4d dest) {
14553         dest._m00(m00)
14554         ._m01(m01)
14555         ._m02(m02)
14556         ._m03(m03)
14557         ._m10(m10)
14558         ._m11(m11)
14559         ._m12(m12)
14560         ._m13(m13)
14561         ._m20(m00 * a + m10 * b + m20)
14562         ._m21(m01 * a + m11 * b + m21)
14563         ._m22(m02 * a + m12 * b + m22)
14564         ._m23(m23)
14565         ._m30(m30)
14566         ._m31(m31)
14567         ._m32(m32)
14568         ._m33(m33)
14569         ._properties(properties & PROPERTY_AFFINE);
14570         return dest;
14571     }
14572 
14573     /**
14574      * Create a view and projection matrix from a given <code>eye</code> position, a given bottom left corner position <code>p</code> of the near plane rectangle
14575      * and the extents of the near plane rectangle along its local <code>x</code> and <code>y</code> axes, and store the resulting matrices
14576      * in <code>projDest</code> and <code>viewDest</code>.
14577      * <p>
14578      * This method creates a view and perspective projection matrix assuming that there is a pinhole camera at position <code>eye</code>
14579      * projecting the scene onto the near plane defined by the rectangle.
14580      * <p>
14581      * All positions and lengths are in the same (world) unit.
14582      * 
14583      * @param eye
14584      *          the position of the camera
14585      * @param p
14586      *          the bottom left corner of the near plane rectangle (will map to the bottom left corner in window coordinates)
14587      * @param x
14588      *          the direction and length of the local "bottom/top" X axis/side of the near plane rectangle
14589      * @param y
14590      *          the direction and length of the local "left/right" Y axis/side of the near plane rectangle
14591      * @param nearFarDist
14592      *          the distance between the far and near plane (the near plane will be calculated by this method).
14593      *          If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
14594      *          If the special value {@link Double#NEGATIVE_INFINITY} is used, the near and far planes will be swapped and 
14595      *          the near clipping plane will be at positive infinity.
14596      *          If a negative value is used (except for {@link Double#NEGATIVE_INFINITY}) the near and far planes will be swapped
14597      * @param zeroToOne
14598      *          whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
14599      *          or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
14600      * @param projDest
14601      *          will hold the resulting projection matrix
14602      * @param viewDest
14603      *          will hold the resulting view matrix
14604      */
14605     public static void projViewFromRectangle(
14606             Vector3d eye, Vector3d p, Vector3d x, Vector3d y, double nearFarDist, bool zeroToOne,
14607             ref Matrix4d projDest, ref Matrix4d viewDest) {
14608         double zx = y.y * x.z - y.z * x.y, zy = y.z * x.x - y.x * x.z, zz = y.x * x.y - y.y * x.x;
14609         double zd = zx * (p.x - eye.x) + zy * (p.y - eye.y) + zz * (p.z - eye.z);
14610         double zs = zd >= 0 ? 1 : -1; zx *= zs; zy *= zs; zz *= zs; zd *= zs; 
14611         viewDest.setLookAt(eye.x, eye.y, eye.z, eye.x + zx, eye.y + zy, eye.z + zz, y.x, y.y, y.z);
14612         double px = viewDest.m00 * p.x + viewDest.m10 * p.y + viewDest.m20 * p.z + viewDest.m30;
14613         double py = viewDest.m01 * p.x + viewDest.m11 * p.y + viewDest.m21 * p.z + viewDest.m31;
14614         double tx = viewDest.m00 * x.x + viewDest.m10 * x.y + viewDest.m20 * x.z;
14615         double ty = viewDest.m01 * y.x + viewDest.m11 * y.y + viewDest.m21 * y.z;
14616         double len = Math.sqrt(zx * zx + zy * zy + zz * zz);
14617         double near = zd / len, far;
14618         if (Math.isInfinite(nearFarDist) && nearFarDist < 0.0) {
14619             far = near;
14620             near = double.infinity;
14621         } else if (Math.isInfinite(nearFarDist) && nearFarDist > 0.0) {
14622             far = double.infinity;
14623         } else if (nearFarDist < 0.0) {
14624             far = near;
14625             near = near + nearFarDist;
14626         } else {
14627             far = near + nearFarDist;
14628         }
14629         projDest.setFrustum(px, px + tx, py, py + ty, near, far, zeroToOne);
14630     }
14631 
14632     /**
14633      * Apply a transformation to this matrix to ensure that the local Y axis (as obtained by {@link #positiveY(ref Vector3d)})
14634      * will be coplanar to the plane spanned by the local Z axis (as obtained by {@link #positiveZ(ref Vector3d)}) and the
14635      * given vector <code>up</code>.
14636      * <p>
14637      * This effectively ensures that the resulting matrix will be equal to the one obtained from 
14638      * {@link #setLookAt(ref Vector3d, Vector3d, Vector3d)} called with the current 
14639      * local origin of this matrix (as obtained by {@link #originAffine(ref Vector3d)}), the sum of this position and the 
14640      * negated local Z axis as well as the given vector <code>up</code>.
14641      * <p>
14642      * This method must only be called on {@link #isAffine()} matrices.
14643      * 
14644      * @param up
14645      *            the up vector
14646      * @return this
14647      */
14648     ref public Matrix4d withLookAtUp(ref Vector3d up) return {
14649         withLookAtUp(up.x, up.y, up.z, this);
14650         return this;
14651     }
14652 
14653     public Matrix4d withLookAtUp(ref Vector3d up, ref Matrix4d dest) {
14654         return dest.withLookAtUp(up.x, up.y, up.z);
14655     }
14656 
14657     /**
14658      * Apply a transformation to this matrix to ensure that the local Y axis (as obtained by {@link #positiveY(ref Vector3d)})
14659      * will be coplanar to the plane spanned by the local Z axis (as obtained by {@link #positiveZ(ref Vector3d)}) and the
14660      * given vector <code>(upX, upY, upZ)</code>.
14661      * <p>
14662      * This effectively ensures that the resulting matrix will be equal to the one obtained from 
14663      * {@link #setLookAt(double, double, double, double, double, double, double, double, double)} called with the current 
14664      * local origin of this matrix (as obtained by {@link #originAffine(ref Vector3d)}), the sum of this position and the 
14665      * negated local Z axis as well as the given vector <code>(upX, upY, upZ)</code>.
14666      * <p>
14667      * This method must only be called on {@link #isAffine()} matrices.
14668      * 
14669      * @param upX
14670      *            the x coordinate of the up vector
14671      * @param upY
14672      *            the y coordinate of the up vector
14673      * @param upZ
14674      *            the z coordinate of the up vector
14675      * @return this
14676      */
14677     ref public Matrix4d withLookAtUp(double upX, double upY, double upZ) return {
14678         withLookAtUp(upX, upY, upZ, this);
14679         return this;
14680     }
14681 
14682     public Matrix4d withLookAtUp(double upX, double upY, double upZ, ref Matrix4d dest) {
14683         double y = (upY * m21 - upZ * m11) * m02 +
14684                    (upZ * m01 - upX * m21) * m12 +
14685                    (upX * m11 - upY * m01) * m22;
14686         double x = upX * m01 + upY * m11 + upZ * m21;
14687         if ((properties & PROPERTY_ORTHONORMAL) == 0)
14688             x *= Math.sqrt(m01 * m01 + m11 * m11 + m21 * m21);
14689         double invsqrt = Math.invsqrt(y * y + x * x);
14690         double c = x * invsqrt, s = y * invsqrt;
14691         double nm00 = c * m00 - s * m01, nm10 = c * m10 - s * m11, nm20 = c * m20 - s * m21, nm31 = s * m30 + c * m31;
14692         double nm01 = s * m00 + c * m01, nm11 = s * m10 + c * m11, nm21 = s * m20 + c * m21, nm30 = c * m30 - s * m31;
14693         dest._m00(nm00)._m10(nm10)._m20(nm20)._m30(nm30)
14694         ._m01(nm01)._m11(nm11)._m21(nm21)._m31(nm31);
14695         if (dest != this) {
14696             dest
14697             ._m02(m02)._m12(m12)._m22(m22)._m32(m32)
14698             ._m03(m03)._m13(m13)._m23(m23)._m33(m33);
14699         }
14700         dest._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
14701         return dest;
14702     }
14703 
14704     /**
14705      * Multiply <code>this</code> by the matrix
14706      * <pre>
14707      * 1 0 0 0
14708      * 0 0 1 0
14709      * 0 1 0 0
14710      * 0 0 0 1
14711      * </pre>
14712      * 
14713      * @return this
14714      */
14715     ref public Matrix4d mapXZY() return {
14716         mapXZY(this);
14717         return this;
14718     }
14719     public Matrix4d mapXZY(ref Matrix4d dest) {
14720         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
14721         return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14722     }
14723     /**
14724      * Multiply <code>this</code> by the matrix
14725      * <pre>
14726      * 1 0  0 0
14727      * 0 0 -1 0
14728      * 0 1  0 0
14729      * 0 0  0 1
14730      * </pre>
14731      * 
14732      * @return this
14733      */
14734     ref public Matrix4d mapXZnY() return {
14735         mapXZnY(this);
14736         return this;
14737     }
14738     public Matrix4d mapXZnY(ref Matrix4d dest) {
14739         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
14740         return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14741     }
14742     /**
14743      * Multiply <code>this</code> by the matrix
14744      * <pre>
14745      * 1  0  0 0
14746      * 0 -1  0 0
14747      * 0  0 -1 0
14748      * 0  0  0 1
14749      * </pre>
14750      * 
14751      * @return this
14752      */
14753     ref public Matrix4d mapXnYnZ() return {
14754         mapXnYnZ(this);
14755         return this;
14756     }
14757     public Matrix4d mapXnYnZ(ref Matrix4d dest) {
14758         return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14759     }
14760     /**
14761      * Multiply <code>this</code> by the matrix
14762      * <pre>
14763      * 1  0 0 0
14764      * 0  0 1 0
14765      * 0 -1 0 0
14766      * 0  0 0 1
14767      * </pre>
14768      * 
14769      * @return this
14770      */
14771     ref public Matrix4d mapXnZY() return {
14772         mapXnZY(this);
14773         return this;
14774     }
14775     public Matrix4d mapXnZY(ref Matrix4d dest) {
14776         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
14777         return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14778     }
14779     /**
14780      * Multiply <code>this</code> by the matrix
14781      * <pre>
14782      * 1  0  0 0
14783      * 0  0 -1 0
14784      * 0 -1  0 0
14785      * 0  0  0 1
14786      * </pre>
14787      * 
14788      * @return this
14789      */
14790     ref public Matrix4d mapXnZnY() return {
14791         mapXnZnY(this);
14792         return this;
14793     }
14794     public Matrix4d mapXnZnY(ref Matrix4d dest) {
14795         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
14796         return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14797     }
14798     /**
14799      * Multiply <code>this</code> by the matrix
14800      * <pre>
14801      * 0 1 0 0
14802      * 1 0 0 0
14803      * 0 0 1 0
14804      * 0 0 0 1
14805      * </pre>
14806      * 
14807      * @return this
14808      */
14809     ref public Matrix4d mapYXZ() return {
14810         mapYXZ(this);
14811         return this;
14812     }
14813     public Matrix4d mapYXZ(ref Matrix4d dest) {
14814         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14815         return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14816     }
14817     /**
14818      * Multiply <code>this</code> by the matrix
14819      * <pre>
14820      * 0 1  0 0
14821      * 1 0  0 0
14822      * 0 0 -1 0
14823      * 0 0  0 1
14824      * </pre>
14825      * 
14826      * @return this
14827      */
14828     ref public Matrix4d mapYXnZ() return {
14829         mapYXnZ(this);
14830         return this;
14831     }
14832     public Matrix4d mapYXnZ(ref Matrix4d dest) {
14833         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14834         return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14835     }
14836     /**
14837      * Multiply <code>this</code> by the matrix
14838      * <pre>
14839      * 0 0 1 0
14840      * 1 0 0 0
14841      * 0 1 0 0
14842      * 0 0 0 1
14843      * </pre>
14844      * 
14845      * @return this
14846      */
14847     ref public Matrix4d mapYZX() return {
14848         mapYZX(this);
14849         return this;
14850     }
14851     public Matrix4d mapYZX(ref Matrix4d dest) {
14852         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14853         return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14854     }
14855     /**
14856      * Multiply <code>this</code> by the matrix
14857      * <pre>
14858      * 0 0 -1 0
14859      * 1 0  0 0
14860      * 0 1  0 0
14861      * 0 0  0 1
14862      * </pre>
14863      * 
14864      * @return this
14865      */
14866     ref public Matrix4d mapYZnX() return {
14867         mapYZnX(this);
14868         return this;
14869     }
14870     public Matrix4d mapYZnX(ref Matrix4d dest) {
14871         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14872         return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14873     }
14874     /**
14875      * Multiply <code>this</code> by the matrix
14876      * <pre>
14877      * 0 -1 0 0
14878      * 1  0 0 0
14879      * 0  0 1 0
14880      * 0  0 0 1
14881      * </pre>
14882      * 
14883      * @return this
14884      */
14885     ref public Matrix4d mapYnXZ() return {
14886         mapYnXZ(this);
14887         return this;
14888     }
14889     public Matrix4d mapYnXZ(ref Matrix4d dest) {
14890         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14891         return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14892     }
14893     /**
14894      * Multiply <code>this</code> by the matrix
14895      * <pre>
14896      * 0 -1  0 0
14897      * 1  0  0 0
14898      * 0  0 -1 0
14899      * 0  0  0 1
14900      * </pre>
14901      * 
14902      * @return this
14903      */
14904     ref public Matrix4d mapYnXnZ() return {
14905         mapYnXnZ(this);
14906         return this;
14907     }
14908     public Matrix4d mapYnXnZ(ref Matrix4d dest) {
14909         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14910         return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14911     }
14912     /**
14913      * Multiply <code>this</code> by the matrix
14914      * <pre>
14915      * 0  0 1 0
14916      * 1  0 0 0
14917      * 0 -1 0 0
14918      * 0  0 0 1
14919      * </pre>
14920      * 
14921      * @return this
14922      */
14923     ref public Matrix4d mapYnZX() return {
14924         mapYnZX(this);
14925         return this;
14926     }
14927     public Matrix4d mapYnZX(ref Matrix4d dest) {
14928         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14929         return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14930     }
14931     /**
14932      * Multiply <code>this</code> by the matrix
14933      * <pre>
14934      * 0  0 -1 0
14935      * 1  0  0 0
14936      * 0 -1  0 0
14937      * 0  0  0 1
14938      * </pre>
14939      * 
14940      * @return this
14941      */
14942     ref public Matrix4d mapYnZnX() return {
14943         mapYnZnX(this);
14944         return this;
14945     }
14946     public Matrix4d mapYnZnX(ref Matrix4d dest) {
14947         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14948         return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14949     }
14950     /**
14951      * Multiply <code>this</code> by the matrix
14952      * <pre>
14953      * 0 1 0 0
14954      * 0 0 1 0
14955      * 1 0 0 0
14956      * 0 0 0 1
14957      * </pre>
14958      * 
14959      * @return this
14960      */
14961     ref public Matrix4d mapZXY() return {
14962         mapZXY(this);
14963         return this;
14964     }
14965     public Matrix4d mapZXY(ref Matrix4d dest) {
14966         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14967         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
14968         return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14969     }
14970     /**
14971      * Multiply <code>this</code> by the matrix
14972      * <pre>
14973      * 0 1  0 0
14974      * 0 0 -1 0
14975      * 1 0  0 0
14976      * 0 0  0 1
14977      * </pre>
14978      * 
14979      * @return this
14980      */
14981     ref public Matrix4d mapZXnY() return {
14982         mapZXnY(this);
14983         return this;
14984     }
14985     public Matrix4d mapZXnY(ref Matrix4d dest) {
14986         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14987         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
14988         return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14989     }
14990     /**
14991      * Multiply <code>this</code> by the matrix
14992      * <pre>
14993      * 0 0 1 0
14994      * 0 1 0 0
14995      * 1 0 0 0
14996      * 0 0 0 1
14997      * </pre>
14998      * 
14999      * @return this
15000      */
15001     ref public Matrix4d mapZYX() return {
15002         mapZYX(this);
15003         return this;
15004     }
15005     public Matrix4d mapZYX(ref Matrix4d dest) {
15006         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15007         return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15008     }
15009     /**
15010      * Multiply <code>this</code> by the matrix
15011      * <pre>
15012      * 0 0 -1 0
15013      * 0 1  0 0
15014      * 1 0  0 0
15015      * 0 0  0 1
15016      * </pre>
15017      * 
15018      * @return this
15019      */
15020     ref public Matrix4d mapZYnX() return {
15021         mapZYnX(this);
15022         return this;
15023     }
15024     public Matrix4d mapZYnX(ref Matrix4d dest) {
15025         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15026         return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15027     }
15028     /**
15029      * Multiply <code>this</code> by the matrix
15030      * <pre>
15031      * 0 -1 0 0
15032      * 0  0 1 0
15033      * 1  0 0 0
15034      * 0  0 0 1
15035      * </pre>
15036      * 
15037      * @return this
15038      */
15039     ref public Matrix4d mapZnXY() return {
15040         mapZnXY(this);
15041         return this;
15042     }
15043     public Matrix4d mapZnXY(ref Matrix4d dest) {
15044         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15045         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15046         return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15047     }
15048     /**
15049      * Multiply <code>this</code> by the matrix
15050      * <pre>
15051      * 0 -1  0 0
15052      * 0  0 -1 0
15053      * 1  0  0 0
15054      * 0  0  0 1
15055      * </pre>
15056      * 
15057      * @return this
15058      */
15059     ref public Matrix4d mapZnXnY() return {
15060         mapZnXnY(this);
15061         return this;
15062     }
15063     public Matrix4d mapZnXnY(ref Matrix4d dest) {
15064         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15065         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15066         return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15067     }
15068     /**
15069      * Multiply <code>this</code> by the matrix
15070      * <pre>
15071      * 0  0 1 0
15072      * 0 -1 0 0
15073      * 1  0 0 0
15074      * 0  0 0 1
15075      * </pre>
15076      * 
15077      * @return this
15078      */
15079     ref public Matrix4d mapZnYX() return {
15080         mapZnYX(this);
15081         return this;
15082     }
15083     public Matrix4d mapZnYX(ref Matrix4d dest) {
15084         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15085         return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15086     }
15087     /**
15088      * Multiply <code>this</code> by the matrix
15089      * <pre>
15090      * 0  0 -1 0
15091      * 0 -1  0 0
15092      * 1  0  0 0
15093      * 0  0  0 1
15094      * </pre>
15095      * 
15096      * @return this
15097      */
15098     ref public Matrix4d mapZnYnX() return {
15099         mapZnYnX(this);
15100         return this;
15101     }
15102     public Matrix4d mapZnYnX(ref Matrix4d dest) {
15103         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15104         return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15105     }
15106     /**
15107      * Multiply <code>this</code> by the matrix
15108      * <pre>
15109      * -1 0  0 0
15110      *  0 1  0 0
15111      *  0 0 -1 0
15112      *  0 0  0 1
15113      * </pre>
15114      * 
15115      * @return this
15116      */
15117     ref public Matrix4d mapnXYnZ() return {
15118         mapnXYnZ(this);
15119         return this;
15120     }
15121     public Matrix4d mapnXYnZ(ref Matrix4d dest) {
15122         return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15123     }
15124     /**
15125      * Multiply <code>this</code> by the matrix
15126      * <pre>
15127      * -1 0 0 0
15128      *  0 0 1 0
15129      *  0 1 0 0
15130      *  0 0 0 1
15131      * </pre>
15132      * 
15133      * @return this
15134      */
15135     ref public Matrix4d mapnXZY() return {
15136         mapnXZY(this);
15137         return this;
15138     }
15139     public Matrix4d mapnXZY(ref Matrix4d dest) {
15140         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15141         return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15142     }
15143     /**
15144      * Multiply <code>this</code> by the matrix
15145      * <pre>
15146      * -1 0  0 0
15147      *  0 0 -1 0
15148      *  0 1  0 0
15149      *  0 0  0 1
15150      * </pre>
15151      * 
15152      * @return this
15153      */
15154     ref public Matrix4d mapnXZnY() return {
15155         mapnXZnY(this);
15156         return this;
15157     }
15158     public Matrix4d mapnXZnY(ref Matrix4d dest) {
15159         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15160         return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15161     }
15162     /**
15163      * Multiply <code>this</code> by the matrix
15164      * <pre>
15165      * -1  0 0 0
15166      *  0 -1 0 0
15167      *  0  0 1 0
15168      *  0  0 0 1
15169      * </pre>
15170      * 
15171      * @return this
15172      */
15173     ref public Matrix4d mapnXnYZ() return {
15174         mapnXnYZ(this);
15175         return this;
15176     }
15177     public Matrix4d mapnXnYZ(ref Matrix4d dest) {
15178         return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15179     }
15180     /**
15181      * Multiply <code>this</code> by the matrix
15182      * <pre>
15183      * -1  0  0 0
15184      *  0 -1  0 0
15185      *  0  0 -1 0
15186      *  0  0  0 1
15187      * </pre>
15188      * 
15189      * @return this
15190      */
15191     ref public Matrix4d mapnXnYnZ() return {
15192         mapnXnYnZ(this);
15193         return this;
15194     }
15195     public Matrix4d mapnXnYnZ(ref Matrix4d dest) {
15196         return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15197     }
15198     /**
15199      * Multiply <code>this</code> by the matrix
15200      * <pre>
15201      * -1  0 0 0
15202      *  0  0 1 0
15203      *  0 -1 0 0
15204      *  0  0 0 1
15205      * </pre>
15206      * 
15207      * @return this
15208      */
15209     ref public Matrix4d mapnXnZY() return {
15210         mapnXnZY(this);
15211         return this;
15212     }
15213     public Matrix4d mapnXnZY(ref Matrix4d dest) {
15214         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15215         return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15216     }
15217     /**
15218      * Multiply <code>this</code> by the matrix
15219      * <pre>
15220      * -1  0  0 0
15221      *  0  0 -1 0
15222      *  0 -1  0 0
15223      *  0  0  0 1
15224      * </pre>
15225      * 
15226      * @return this
15227      */
15228     ref public Matrix4d mapnXnZnY() return {
15229         mapnXnZnY(this);
15230         return this;
15231     }
15232     public Matrix4d mapnXnZnY(ref Matrix4d dest) {
15233         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15234         return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15235     }
15236     /**
15237      * Multiply <code>this</code> by the matrix
15238      * <pre>
15239      *  0 1 0 0
15240      * -1 0 0 0
15241      *  0 0 1 0
15242      *  0 0 0 1
15243      * </pre>
15244      * 
15245      * @return this
15246      */
15247     ref public Matrix4d mapnYXZ() return {
15248         mapnYXZ(this);
15249         return this;
15250     }
15251     public Matrix4d mapnYXZ(ref Matrix4d dest) {
15252         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15253         return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15254     }
15255     /**
15256      * Multiply <code>this</code> by the matrix
15257      * <pre>
15258      *  0 1  0 0
15259      * -1 0  0 0
15260      *  0 0 -1 0
15261      *  0 0  0 1
15262      * </pre>
15263      * 
15264      * @return this
15265      */
15266     ref public Matrix4d mapnYXnZ() return {
15267         mapnYXnZ(this);
15268         return this;
15269     }
15270     public Matrix4d mapnYXnZ(ref Matrix4d dest) {
15271         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15272         return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15273     }
15274     /**
15275      * Multiply <code>this</code> by the matrix
15276      * <pre>
15277      *  0 0 1 0
15278      * -1 0 0 0
15279      *  0 1 0 0
15280      *  0 0 0 1
15281      * </pre>
15282      * 
15283      * @return this
15284      */
15285     ref public Matrix4d mapnYZX() return {
15286         mapnYZX(this);
15287         return this;
15288     }
15289     public Matrix4d mapnYZX(ref Matrix4d dest) {
15290         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15291         return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15292     }
15293     /**
15294      * Multiply <code>this</code> by the matrix
15295      * <pre>
15296      *  0 0 -1 0
15297      * -1 0  0 0
15298      *  0 1  0 0
15299      *  0 0  0 1
15300      * </pre>
15301      * 
15302      * @return this
15303      */
15304     ref public Matrix4d mapnYZnX() return {
15305         mapnYZnX(this);
15306         return this;
15307     }
15308     public Matrix4d mapnYZnX(ref Matrix4d dest) {
15309         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15310         return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15311     }
15312     /**
15313      * Multiply <code>this</code> by the matrix
15314      * <pre>
15315      *  0 -1 0 0
15316      * -1  0 0 0
15317      *  0  0 1 0
15318      *  0  0 0 1
15319      * </pre>
15320      * 
15321      * @return this
15322      */
15323     ref public Matrix4d mapnYnXZ() return {
15324         mapnYnXZ(this);
15325         return this;
15326     }
15327     public Matrix4d mapnYnXZ(ref Matrix4d dest) {
15328         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15329         return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15330     }
15331     /**
15332      * Multiply <code>this</code> by the matrix
15333      * <pre>
15334      *  0 -1  0 0
15335      * -1  0  0 0
15336      *  0  0 -1 0
15337      *  0  0  0 1
15338      * </pre>
15339      * 
15340      * @return this
15341      */
15342     ref public Matrix4d mapnYnXnZ() return {
15343         mapnYnXnZ(this);
15344         return this;
15345     }
15346     public Matrix4d mapnYnXnZ(ref Matrix4d dest) {
15347         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15348         return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15349     }
15350     /**
15351      * Multiply <code>this</code> by the matrix
15352      * <pre>
15353      *  0  0 1 0
15354      * -1  0 0 0
15355      *  0 -1 0 0
15356      *  0  0 0 1
15357      * </pre>
15358      * 
15359      * @return this
15360      */
15361     ref public Matrix4d mapnYnZX() return {
15362         mapnYnZX(this);
15363         return this;
15364     }
15365     public Matrix4d mapnYnZX(ref Matrix4d dest) {
15366         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15367         return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15368     }
15369     /**
15370      * Multiply <code>this</code> by the matrix
15371      * <pre>
15372      *  0  0 -1 0
15373      * -1  0  0 0
15374      *  0 -1  0 0
15375      *  0  0  0 1
15376      * </pre>
15377      * 
15378      * @return this
15379      */
15380     ref public Matrix4d mapnYnZnX() return {
15381         mapnYnZnX(this);
15382         return this;
15383     }
15384     public Matrix4d mapnYnZnX(ref Matrix4d dest) {
15385         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15386         return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15387     }
15388     /**
15389      * Multiply <code>this</code> by the matrix
15390      * <pre>
15391      *  0 1 0 0
15392      *  0 0 1 0
15393      * -1 0 0 0
15394      *  0 0 0 1
15395      * </pre>
15396      * 
15397      * @return this
15398      */
15399     ref public Matrix4d mapnZXY() return {
15400         mapnZXY(this);
15401         return this;
15402     }
15403     public Matrix4d mapnZXY(ref Matrix4d dest) {
15404         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15405         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15406         return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15407     }
15408     /**
15409      * Multiply <code>this</code> by the matrix
15410      * <pre>
15411      *  0 1  0 0
15412      *  0 0 -1 0
15413      * -1 0  0 0
15414      *  0 0  0 1
15415      * </pre>
15416      * 
15417      * @return this
15418      */
15419     ref public Matrix4d mapnZXnY() return {
15420         mapnZXnY(this);
15421         return this;
15422     }
15423     public Matrix4d mapnZXnY(ref Matrix4d dest) {
15424         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15425         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15426         return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15427     }
15428     /**
15429      * Multiply <code>this</code> by the matrix
15430      * <pre>
15431      *  0 0 1 0
15432      *  0 1 0 0
15433      * -1 0 0 0
15434      *  0 0 0 1
15435      * </pre>
15436      * 
15437      * @return this
15438      */
15439     ref public Matrix4d mapnZYX() return {
15440         mapnZYX(this);
15441         return this;
15442     }
15443     public Matrix4d mapnZYX(ref Matrix4d dest) {
15444         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15445         return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15446     }
15447     /**
15448      * Multiply <code>this</code> by the matrix
15449      * <pre>
15450      *  0 0 -1 0
15451      *  0 1  0 0
15452      * -1 0  0 0
15453      *  0 0  0 1
15454      * </pre>
15455      * 
15456      * @return this
15457      */
15458     ref public Matrix4d mapnZYnX() return {
15459         mapnZYnX(this);
15460         return this;
15461     }
15462     public Matrix4d mapnZYnX(ref Matrix4d dest) {
15463         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15464         return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15465     }
15466     /**
15467      * Multiply <code>this</code> by the matrix
15468      * <pre>
15469      *  0 -1 0 0
15470      *  0  0 1 0
15471      * -1  0 0 0
15472      *  0  0 0 1
15473      * </pre>
15474      * 
15475      * @return this
15476      */
15477     ref public Matrix4d mapnZnXY() return {
15478         mapnZnXY(this);
15479         return this;
15480     }
15481     public Matrix4d mapnZnXY(ref Matrix4d dest) {
15482         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15483         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15484         return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15485     }
15486     /**
15487      * Multiply <code>this</code> by the matrix
15488      * <pre>
15489      *  0 -1  0 0
15490      *  0  0 -1 0
15491      * -1  0  0 0
15492      *  0  0  0 1
15493      * </pre>
15494      * 
15495      * @return this
15496      */
15497     ref public Matrix4d mapnZnXnY() return {
15498         mapnZnXnY(this);
15499         return this;
15500     }
15501     public Matrix4d mapnZnXnY(ref Matrix4d dest) {
15502         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15503         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15504         return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15505     }
15506     /**
15507      * Multiply <code>this</code> by the matrix
15508      * <pre>
15509      *  0  0 1 0
15510      *  0 -1 0 0
15511      * -1  0 0 0
15512      *  0  0 0 1
15513      * </pre>
15514      * 
15515      * @return this
15516      */
15517     ref public Matrix4d mapnZnYX() return {
15518         mapnZnYX(this);
15519         return this;
15520     }
15521     public Matrix4d mapnZnYX(ref Matrix4d dest) {
15522         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15523         return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15524     }
15525     /**
15526      * Multiply <code>this</code> by the matrix
15527      * <pre>
15528      *  0  0 -1 0
15529      *  0 -1  0 0
15530      * -1  0  0 0
15531      *  0  0  0 1
15532      * </pre>
15533      * 
15534      * @return this
15535      */
15536     ref public Matrix4d mapnZnYnX() return {
15537         mapnZnYnX(this);
15538         return this;
15539     }
15540     public Matrix4d mapnZnYnX(ref Matrix4d dest) {
15541         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15542         return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15543     }
15544 
15545     /**
15546      * Multiply <code>this</code> by the matrix
15547      * <pre>
15548      * -1 0 0 0
15549      *  0 1 0 0
15550      *  0 0 1 0
15551      *  0 0 0 1
15552      * </pre>
15553      * 
15554      * @return this
15555      */
15556     ref public Matrix4d negateX() return {
15557         return _m00(-m00)._m01(-m01)._m02(-m02)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15558     }
15559     public Matrix4d negateX(ref Matrix4d dest) {
15560         return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15561     }
15562 
15563     /**
15564      * Multiply <code>this</code> by the matrix
15565      * <pre>
15566      * 1  0 0 0
15567      * 0 -1 0 0
15568      * 0  0 1 0
15569      * 0  0 0 1
15570      * </pre>
15571      * 
15572      * @return this
15573      */
15574     ref public Matrix4d negateY() return {
15575         return _m10(-m10)._m11(-m11)._m12(-m12)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15576     }
15577     public Matrix4d negateY(ref Matrix4d dest) {
15578         return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15579     }
15580 
15581     /**
15582      * Multiply <code>this</code> by the matrix
15583      * <pre>
15584      * 1 0  0 0
15585      * 0 1  0 0
15586      * 0 0 -1 0
15587      * 0 0  0 1
15588      * </pre>
15589      * 
15590      * @return this
15591      */
15592     ref public Matrix4d negateZ() return {
15593         return _m20(-m20)._m21(-m21)._m22(-m22)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15594     }
15595     public Matrix4d negateZ(ref Matrix4d dest) {
15596         return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15597     }
15598 
15599     public bool isFinite() {
15600         return Math.isFinite(m00) && Math.isFinite(m01) && Math.isFinite(m02) && Math.isFinite(m03) &&
15601                Math.isFinite(m10) && Math.isFinite(m11) && Math.isFinite(m12) && Math.isFinite(m13) &&
15602                Math.isFinite(m20) && Math.isFinite(m21) && Math.isFinite(m22) && Math.isFinite(m23) &&
15603                Math.isFinite(m30) && Math.isFinite(m31) && Math.isFinite(m32) && Math.isFinite(m33);
15604     }
15605 
15606 }