1 /**
2  * Quaternion of 4 double-precision doubles which can represent rotation and uniform scaling.
3  *
4  * @author Richard Greenlees
5  * @author Kai Burjack
6  */
7 module doml.quaternion_d;
8 
9 import Math = doml.math;
10 
11 import doml.axis_angle_4d;
12 import doml.matrix_3d;
13 import doml.matrix_4d;
14 import doml.matrix_4x3d;
15 import doml.vector_3d;
16 import doml.vector_4d;
17 
18 /*
19  * The MIT License
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  * Quaternion of 4 double-precision doubles which can represent rotation and uniform scaling.
44  *
45  * @author Richard Greenlees
46  * @author Kai Burjack
47  */
48 struct Quaterniond {
49 
50     /**
51      * The first component of the vector part.
52      */
53     public double x = 0.0;
54     /**
55      * The second component of the vector part.
56      */
57     public double y = 0.0;
58     /**
59      * The third component of the vector part.
60      */
61     public double z = 0.0;
62     /**
63      * The real/scalar part of the quaternion.
64      */
65     public double w = 1.0;
66 
67     /**
68      * Create a new {@link Quaterniond} and initialize its components to the given values.
69      * 
70      * @param x
71      *          the first component of the imaginary part
72      * @param y
73      *          the second component of the imaginary part
74      * @param z
75      *          the third component of the imaginary part
76      * @param w
77      *          the real part
78      */
79     this(double x, double y, double z, double w) {
80         this.x = x;
81         this.y = y;
82         this.z = z;
83         this.w = w;
84     }
85 
86     /**
87      * Create a new {@link Quaterniond} and initialize its components to the same values as the given {@link Quaterniond}.
88      * 
89      * @param source
90      *          the {@link Quaterniond} to take the component values from
91      */
92     this(Quaterniond source) {
93         x = source.x;
94         y = source.y;
95         z = source.z;
96         w = source.w;
97     }
98 
99     /**
100      * Create a new {@link Quaterniond} and initialize it to represent the same rotation as the given {@link AxisAngle4d}.
101      * 
102      * @param axisAngle
103      *          the axis-angle to initialize this quaternion with
104      */
105     this(AxisAngle4d axisAngle) {
106         double s = Math.sin(axisAngle.angle * 0.5);
107         x = axisAngle.x * s;
108         y = axisAngle.y * s;
109         z = axisAngle.z * s;
110         w = Math.cosFromSin(s, axisAngle.angle * 0.5);
111     }
112 
113     /**
114      * Normalize this quaternion.
115      * 
116      * @return this
117      */
118     ref public Quaterniond normalize() return {
119         double invNorm = Math.invsqrt(lengthSquared());
120         x *= invNorm;
121         y *= invNorm;
122         z *= invNorm;
123         w *= invNorm;
124         return this;
125     }
126 
127     public Quaterniond normalize(ref Quaterniond dest) {
128         double invNorm = Math.invsqrt(lengthSquared());
129         dest.x = x * invNorm;
130         dest.y = y * invNorm;
131         dest.z = z * invNorm;
132         dest.w = w * invNorm;
133         return dest;
134     }
135 
136     /**
137      * Add the quaternion <code>(x, y, z, w)</code> to this quaternion.
138      * 
139      * @param x
140      *          the x component of the vector part
141      * @param y
142      *          the y component of the vector part
143      * @param z
144      *          the z component of the vector part
145      * @param w
146      *          the real/scalar component
147      * @return this
148      */
149     ref public Quaterniond add(double x, double y, double z, double w) return {
150         add(x, y, z, w, this);
151         return this;
152     }
153 
154     public Quaterniond add(double x, double y, double z, double w, ref Quaterniond dest) {
155         dest.x = this.x + x;
156         dest.y = this.y + y;
157         dest.z = this.z + z;
158         dest.w = this.w + w;
159         return dest;
160     }
161 
162     /**
163      * Add <code>q2</code> to this quaternion.
164      * 
165      * @param q2
166      *          the quaternion to add to this
167      * @return this
168      */
169     ref public Quaterniond add(Quaterniond q2) return {
170         x += q2.x;
171         y += q2.y;
172         z += q2.z;
173         w += q2.w;
174         return this;
175     }
176 
177     public Quaterniond add(Quaterniond q2, ref Quaterniond dest) {
178         dest.x = x + q2.x;
179         dest.y = y + q2.y;
180         dest.z = z + q2.z;
181         dest.w = w + q2.w;
182         return dest;
183     }
184 
185     public double dot(Quaterniond otherQuat) {
186         return this.x * otherQuat.x + this.y * otherQuat.y + this.z * otherQuat.z + this.w * otherQuat.w;
187     }
188 
189     public double angle() {
190         return 2.0 * Math.safeAcos(w);
191     }
192 
193     public Matrix3d get(ref Matrix3d dest) {
194         return dest.set(this);
195     }
196 
197 
198     public Matrix4d get(ref Matrix4d dest) {
199         return dest.set(this);
200     }
201 
202     public AxisAngle4d get(AxisAngle4d dest) {
203         double x = this.x;
204         double y = this.y;
205         double z = this.z;
206         double w = this.w;
207         if (w > 1.0) {
208             double invNorm = Math.invsqrt(lengthSquared());
209             x *= invNorm;
210             y *= invNorm;
211             z *= invNorm;
212             w *= invNorm;
213         }
214         dest.angle = 2.0 * Math.acos(w);
215         double s = Math.sqrt(1.0 - w * w);
216         if (s < 0.001) {
217             dest.x = x;
218             dest.y = y;
219             dest.z = z;
220         } else {
221             s = 1.0 / s;
222             dest.x = x * s;
223             dest.y = y * s;
224             dest.z = z * s;
225         }
226         return dest;
227     }
228 
229     /**
230      * Set the given {@link Quaterniond} to the values of <code>this</code>.
231      * 
232      * @see #set(Quaterniond)
233      * 
234      * @param dest
235      *          the {@link Quaterniond} to set
236      * @return the passed in destination
237      */
238     public Quaterniond get(ref Quaterniond dest) {
239         return dest.set(this);
240     }
241 
242 
243     /**
244      * Set this quaternion to the new values.
245      * 
246      * @param x
247      *          the new value of x
248      * @param y
249      *          the new value of y
250      * @param z
251      *          the new value of z
252      * @param w
253      *          the new value of w
254      * @return this
255      */
256     ref public Quaterniond set(double x, double y, double z, double w) return {
257         this.x = x;
258         this.y = y;
259         this.z = z;
260         this.w = w;
261         return this;
262     }
263 
264     /**
265      * Set this quaternion to be a copy of q.
266      * 
267      * @param q
268      *          the {@link Quaterniond} to copy
269      * @return this
270      */
271     ref public Quaterniond set(Quaterniond q) return {
272         x = q.x;
273         y = q.y;
274         z = q.z;
275         w = q.w;
276         return this;
277     }
278 
279     /**
280      * Set this {@link Quaterniond} to be equivalent to the given
281      * {@link AxisAngle4d}.
282      * 
283      * @param axisAngle
284      *            the {@link AxisAngle4d}
285      * @return this
286      */
287     ref public Quaterniond set(AxisAngle4d axisAngle) return {
288         return setAngleAxis(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z);
289     }
290 
291     /**
292      * Set this quaternion to a rotation equivalent to the supplied axis and
293      * angle (in radians).
294      * <p>
295      * This method assumes that the given rotation axis <code>(x, y, z)</code> is already normalized
296      * 
297      * @param angle
298      *          the angle in radians
299      * @param x
300      *          the x-component of the normalized rotation axis
301      * @param y
302      *          the y-component of the normalized rotation axis
303      * @param z
304      *          the z-component of the normalized rotation axis
305      * @return this
306      */
307     ref public Quaterniond setAngleAxis(double angle, double x, double y, double z) return {
308         double s = Math.sin(angle * 0.5);
309         this.x = x * s;
310         this.y = y * s;
311         this.z = z * s;
312         this.w = Math.cosFromSin(s, angle * 0.5);
313         return this;
314     }
315 
316     /**
317      * Set this quaternion to be a representation of the supplied axis and
318      * angle (in radians).
319      * 
320      * @param angle
321      *          the angle in radians
322      * @param axis
323      *          the rotation axis
324      * @return this
325      */
326     ref public Quaterniond setAngleAxis(double angle, Vector3d axis) return {
327         return setAngleAxis(angle, axis.x, axis.y, axis.z);
328     }
329 
330     private void setFromUnnormalized(double m00, double m01, double m02, double m10, double m11, double m12, double m20, double m21, double m22) {
331         double nm00 = m00, nm01 = m01, nm02 = m02;
332         double nm10 = m10, nm11 = m11, nm12 = m12;
333         double nm20 = m20, nm21 = m21, nm22 = m22;
334         double lenX = Math.invsqrt(m00 * m00 + m01 * m01 + m02 * m02);
335         double lenY = Math.invsqrt(m10 * m10 + m11 * m11 + m12 * m12);
336         double lenZ = Math.invsqrt(m20 * m20 + m21 * m21 + m22 * m22);
337         nm00 *= lenX; nm01 *= lenX; nm02 *= lenX;
338         nm10 *= lenY; nm11 *= lenY; nm12 *= lenY;
339         nm20 *= lenZ; nm21 *= lenZ; nm22 *= lenZ;
340         setFromNormalized(nm00, nm01, nm02, nm10, nm11, nm12, nm20, nm21, nm22);
341     }
342 
343     private void setFromNormalized(double m00, double m01, double m02, double m10, double m11, double m12, double m20, double m21, double m22) {
344         double t;
345         double tr = m00 + m11 + m22;
346         if (tr >= 0.0) {
347             t = Math.sqrt(tr + 1.0);
348             w = t * 0.5;
349             t = 0.5 / t;
350             x = (m12 - m21) * t;
351             y = (m20 - m02) * t;
352             z = (m01 - m10) * t;
353         } else {
354             if (m00 >= m11 && m00 >= m22) {
355                 t = Math.sqrt(m00 - (m11 + m22) + 1.0);
356                 x = t * 0.5;
357                 t = 0.5 / t;
358                 y = (m10 + m01) * t;
359                 z = (m02 + m20) * t;
360                 w = (m12 - m21) * t;
361             } else if (m11 > m22) {
362                 t = Math.sqrt(m11 - (m22 + m00) + 1.0);
363                 y = t * 0.5;
364                 t = 0.5 / t;
365                 z = (m21 + m12) * t;
366                 x = (m10 + m01) * t;
367                 w = (m20 - m02) * t;
368             } else {
369                 t = Math.sqrt(m22 - (m00 + m11) + 1.0);
370                 z = t * 0.5;
371                 t = 0.5 / t;
372                 x = (m02 + m20) * t;
373                 y = (m21 + m12) * t;
374                 w = (m01 - m10) * t;
375             }
376         }
377     }
378 
379 
380     /**
381      * Set this quaternion to be a representation of the rotational component of the given matrix.
382      * <p>
383      * This method assumes that the first three columns of the upper left 3x3 submatrix are no unit vectors.
384      * 
385      * @param mat
386      *          the matrix whose rotational component is used to set this quaternion
387      * @return this
388      */
389     ref public Quaterniond setFromUnnormalized(Matrix4x3d mat) return {
390         setFromUnnormalized(mat.m00, mat.m01, mat.m02, mat.m10, mat.m11, mat.m12, mat.m20, mat.m21, mat.m22);
391         return this;
392     }
393 
394 
395     /**
396      * Set this quaternion to be a representation of the rotational component of the given matrix.
397      * <p>
398      * This method assumes that the first three columns of the upper left 3x3 submatrix are unit vectors.
399      * 
400      * @param mat
401      *          the matrix whose rotational component is used to set this quaternion
402      * @return this
403      */
404     ref public Quaterniond setFromNormalized(Matrix4x3d mat) return {
405         setFromNormalized(mat.m00, mat.m01, mat.m02, mat.m10, mat.m11, mat.m12, mat.m20, mat.m21, mat.m22);
406         return this;
407     }
408 
409     /**
410      * Set this quaternion to be a representation of the rotational component of the given matrix.
411      * <p>
412      * This method assumes that the first three columns of the upper left 3x3 submatrix are no unit vectors.
413      * 
414      * @param mat
415      *          the matrix whose rotational component is used to set this quaternion
416      * @return this
417      */
418     ref public Quaterniond setFromUnnormalized(Matrix4d mat) return {
419         setFromUnnormalized(mat.m00, mat.m01, mat.m02, mat.m10, mat.m11, mat.m12, mat.m20, mat.m21, mat.m22);
420         return this;
421     }
422 
423     /**
424      * Set this quaternion to be a representation of the rotational component of the given matrix.
425      * <p>
426      * This method assumes that the first three columns of the upper left 3x3 submatrix are unit vectors.
427      * 
428      * @param mat
429      *          the matrix whose rotational component is used to set this quaternion
430      * @return this
431      */
432     ref public Quaterniond setFromNormalized(Matrix4d mat) return {
433         setFromNormalized(mat.m00, mat.m01, mat.m02, mat.m10, mat.m11, mat.m12, mat.m20, mat.m21, mat.m22);
434         return this;
435     }
436 
437 
438 
439     /**
440      * Set this quaternion to be a representation of the rotational component of the given matrix.
441      * <p>
442      * This method assumes that the first three columns of the upper left 3x3 submatrix are no unit vectors.
443      * 
444      * @param mat
445      *          the matrix whose rotational component is used to set this quaternion
446      * @return this
447      */
448     ref public Quaterniond setFromUnnormalized(Matrix3d mat) return {
449         setFromUnnormalized(mat.m00, mat.m01, mat.m02, mat.m10, mat.m11, mat.m12, mat.m20, mat.m21, mat.m22);
450         return this;
451     }
452 
453     /**
454      * Set this quaternion to be a representation of the rotational component of the given matrix.
455      * 
456      * @param mat
457      *          the matrix whose rotational component is used to set this quaternion
458      * @return this
459      */
460     ref public Quaterniond setFromNormalized(Matrix3d mat) return {
461         setFromNormalized(mat.m00, mat.m01, mat.m02, mat.m10, mat.m11, mat.m12, mat.m20, mat.m21, mat.m22);
462         return this;
463     }
464 
465     /**
466      * Set this quaternion to be a representation of the supplied axis and
467      * angle (in radians).
468      * 
469      * @param axis
470      *          the rotation axis
471      * @param angle
472      *          the angle in radians
473      * @return this
474      */
475     ref public Quaterniond fromAxisAngleRad(Vector3d axis, double angle) return {
476         return fromAxisAngleRad(axis.x, axis.y, axis.z, angle);
477     }
478 
479     /**
480      * Set this quaternion to be a representation of the supplied axis and
481      * angle (in radians).
482      * 
483      * @param axisX
484      *          the x component of the rotation axis
485      * @param axisY
486      *          the y component of the rotation axis
487      * @param axisZ
488      *          the z component of the rotation axis         
489      * @param angle
490      *          the angle in radians
491      * @return this
492      */
493     ref public Quaterniond fromAxisAngleRad(double axisX, double axisY, double axisZ, double angle) return {
494         double hangle = angle / 2.0;
495         double sinAngle = Math.sin(hangle);
496         double vLength = Math.sqrt(axisX * axisX + axisY * axisY + axisZ * axisZ);
497         x = axisX / vLength * sinAngle;
498         y = axisY / vLength * sinAngle;
499         z = axisZ / vLength * sinAngle;
500         w = Math.cosFromSin(sinAngle, hangle);
501         return this;
502     }
503 
504     /**
505      * Set this quaternion to be a representation of the supplied axis and
506      * angle (in degrees).
507      * 
508      * @param axis
509      *          the rotation axis
510      * @param angle
511      *          the angle in degrees
512      * @return this
513      */
514     ref public Quaterniond fromAxisAngleDeg(Vector3d axis, double angle) return {
515         return fromAxisAngleRad(axis.x, axis.y, axis.z, Math.toRadians(angle));
516     }
517 
518     /**
519      * Set this quaternion to be a representation of the supplied axis and
520      * angle (in degrees).
521      * 
522      * @param axisX
523      *          the x component of the rotation axis
524      * @param axisY
525      *          the y component of the rotation axis
526      * @param axisZ
527      *          the z component of the rotation axis         
528      * @param angle
529      *          the angle in radians
530      * @return this
531      */
532     ref public Quaterniond fromAxisAngleDeg(double axisX, double axisY, double axisZ, double angle) return {
533         return fromAxisAngleRad(axisX, axisY, axisZ, Math.toRadians(angle));
534     }
535 
536     /**
537      * Multiply this quaternion by <code>q</code>.
538      * <p>
539      * If <code>T</code> is <code>this</code> and <code>Q</code> is the given
540      * quaternion, then the resulting quaternion <code>R</code> is:
541      * <p>
542      * <code>R = T * Q</code>
543      * <p>
544      * So, this method uses post-multiplication like the matrix classes, resulting in a
545      * vector to be transformed by <code>Q</code> first, and then by <code>T</code>.
546      * 
547      * @param q
548      *          the quaternion to multiply <code>this</code> by
549      * @return this
550      */
551     ref public Quaterniond mul(Quaterniond q) return {
552         mul(q, this);
553         return this;
554     }
555 
556     public Quaterniond mul(Quaterniond q, ref Quaterniond dest) {
557         return mul(q.x, q.y, q.z, q.w, dest);
558     }
559 
560     /**
561      * Multiply this quaternion by the quaternion represented via <code>(qx, qy, qz, qw)</code>.
562      * <p>
563      * If <code>T</code> is <code>this</code> and <code>Q</code> is the given
564      * quaternion, then the resulting quaternion <code>R</code> is:
565      * <p>
566      * <code>R = T * Q</code>
567      * <p>
568      * So, this method uses post-multiplication like the matrix classes, resulting in a
569      * vector to be transformed by <code>Q</code> first, and then by <code>T</code>.
570      * 
571      * @param qx
572      *          the x component of the quaternion to multiply <code>this</code> by
573      * @param qy
574      *          the y component of the quaternion to multiply <code>this</code> by
575      * @param qz
576      *          the z component of the quaternion to multiply <code>this</code> by
577      * @param qw
578      *          the w component of the quaternion to multiply <code>this</code> by
579      * @return this
580      */
581     ref public Quaterniond mul(double qx, double qy, double qz, double qw) return {
582         mul(qx, qy, qz, qw, this);
583         return this;
584     }
585 
586     public Quaterniond mul(double qx, double qy, double qz, double qw, ref Quaterniond dest) {
587         return dest.set(Math.fma(w, qx, Math.fma(x, qw, Math.fma(y, qz, -z * qy))),
588                         Math.fma(w, qy, Math.fma(-x, qz, Math.fma(y, qw, z * qx))),
589                         Math.fma(w, qz, Math.fma(x, qy, Math.fma(-y, qx, z * qw))),
590                         Math.fma(w, qw, Math.fma(-x, qx, Math.fma(-y, qy, -z * qz))));
591     }
592 
593     /**
594      * Pre-multiply this quaternion by <code>q</code>.
595      * <p>
596      * If <code>T</code> is <code>this</code> and <code>Q</code> is the given quaternion, then the resulting quaternion <code>R</code> is:
597      * <p>
598      * <code>R = Q * T</code>
599      * <p>
600      * So, this method uses pre-multiplication, resulting in a vector to be transformed by <code>T</code> first, and then by <code>Q</code>.
601      * 
602      * @param q
603      *            the quaternion to pre-multiply <code>this</code> by
604      * @return this
605      */
606     ref public Quaterniond premul(Quaterniond q) return {
607         premul(q, this);
608         return this;
609     }
610 
611     public Quaterniond premul(Quaterniond q, ref Quaterniond dest) {
612         return premul(q.x, q.y, q.z, q.w, dest);
613     }
614 
615     /**
616      * Pre-multiply this quaternion by the quaternion represented via <code>(qx, qy, qz, qw)</code>.
617      * <p>
618      * If <code>T</code> is <code>this</code> and <code>Q</code> is the given quaternion, then the resulting quaternion <code>R</code> is:
619      * <p>
620      * <code>R = Q * T</code>
621      * <p>
622      * So, this method uses pre-multiplication, resulting in a vector to be transformed by <code>T</code> first, and then by <code>Q</code>.
623      * 
624      * @param qx
625      *          the x component of the quaternion to multiply <code>this</code> by
626      * @param qy
627      *          the y component of the quaternion to multiply <code>this</code> by
628      * @param qz
629      *          the z component of the quaternion to multiply <code>this</code> by
630      * @param qw
631      *          the w component of the quaternion to multiply <code>this</code> by
632      * @return this
633      */
634     ref public Quaterniond premul(double qx, double qy, double qz, double qw) return {
635         premul(qx, qy, qz, qw, this);
636         return this;
637     }
638 
639     public Quaterniond premul(double qx, double qy, double qz, double qw, ref Quaterniond dest) {
640         return dest.set(Math.fma(qw, x, Math.fma(qx, w, Math.fma(qy, z, -qz * y))),
641                         Math.fma(qw, y, Math.fma(-qx, z, Math.fma(qy, w, qz * x))),
642                         Math.fma(qw, z, Math.fma(qx, y, Math.fma(-qy, x, qz * w))),
643                         Math.fma(qw, w, Math.fma(-qx, x, Math.fma(-qy, y, -qz * z))));
644     }
645 
646     public Vector3d transform(Vector3d vec){
647         return transform(vec.x, vec.y, vec.z, vec);
648     }
649 
650     public Vector3d transformInverse(Vector3d vec){
651         return transformInverse(vec.x, vec.y, vec.z, vec);
652     }
653 
654     public Vector3d transformUnit(Vector3d vec){
655         return transformUnit(vec.x, vec.y, vec.z, vec);
656     }
657 
658     public Vector3d transformInverseUnit(Vector3d vec){
659         return transformInverseUnit(vec.x, vec.y, vec.z, vec);
660     }
661 
662     public Vector3d transformPositiveX(ref Vector3d dest) {
663         double ww = w * w;
664         double xx = x * x;
665         double yy = y * y;
666         double zz = z * z;
667         double zw = z * w;
668         double xy = x * y;
669         double xz = x * z;
670         double yw = y * w;
671         dest.x = ww + xx - zz - yy;
672         dest.y = xy + zw + zw + xy;
673         dest.z = xz - yw + xz - yw;
674         return dest;
675     }
676 
677     public Vector4d transformPositiveX(ref Vector4d dest) {
678         double ww = w * w;
679         double xx = x * x;
680         double yy = y * y;
681         double zz = z * z;
682         double zw = z * w;
683         double xy = x * y;
684         double xz = x * z;
685         double yw = y * w;
686         dest.x = ww + xx - zz - yy;
687         dest.y = xy + zw + zw + xy;
688         dest.z = xz - yw + xz - yw;
689         return dest;
690     }
691 
692     public Vector3d transformUnitPositiveX(ref Vector3d dest) {
693         double yy = y * y;
694         double zz = z * z;
695         double xy = x * y;
696         double xz = x * z;
697         double yw = y * w;
698         double zw = z * w;
699         dest.x = 1.0 - yy - yy - zz - zz;
700         dest.y = xy + zw + xy + zw;
701         dest.z = xz - yw + xz - yw;
702         return dest;
703     }
704 
705     public Vector4d transformUnitPositiveX(ref Vector4d dest) {
706         double yy = y * y;
707         double zz = z * z;
708         double xy = x * y;
709         double xz = x * z;
710         double yw = y * w;
711         double zw = z * w;
712         dest.x = 1.0 - yy - yy - zz - zz;
713         dest.y = xy + zw + xy + zw;
714         dest.z = xz - yw + xz - yw;
715         return dest;
716     }
717 
718     public Vector3d transformPositiveY(ref Vector3d dest) {
719         double ww = w * w;
720         double xx = x * x;
721         double yy = y * y;
722         double zz = z * z;
723         double zw = z * w;
724         double xy = x * y;
725         double yz = y * z;
726         double xw = x * w;
727         dest.x = -zw + xy - zw + xy;
728         dest.y = yy - zz + ww - xx;
729         dest.z = yz + yz + xw + xw;
730         return dest;
731     }
732 
733     public Vector4d transformPositiveY(ref Vector4d dest) {
734         double ww = w * w;
735         double xx = x * x;
736         double yy = y * y;
737         double zz = z * z;
738         double zw = z * w;
739         double xy = x * y;
740         double yz = y * z;
741         double xw = x * w;
742         dest.x = -zw + xy - zw + xy;
743         dest.y = yy - zz + ww - xx;
744         dest.z = yz + yz + xw + xw;
745         return dest;
746     }
747 
748     public Vector4d transformUnitPositiveY(ref Vector4d dest) {
749         double xx = x * x;
750         double zz = z * z;
751         double xy = x * y;
752         double yz = y * z;
753         double xw = x * w;
754         double zw = z * w;
755         dest.x = xy - zw + xy - zw;
756         dest.y = 1.0 - xx - xx - zz - zz;
757         dest.z = yz + yz + xw + xw;
758         return dest;
759     }
760 
761     public Vector3d transformUnitPositiveY(ref Vector3d dest) {
762         double xx = x * x;
763         double zz = z * z;
764         double xy = x * y;
765         double yz = y * z;
766         double xw = x * w;
767         double zw = z * w;
768         dest.x = xy - zw + xy - zw;
769         dest.y = 1.0 - xx - xx - zz - zz;
770         dest.z = yz + yz + xw + xw;
771         return dest;
772     }
773 
774     public Vector3d transformPositiveZ(ref Vector3d dest) {
775         double ww = w * w;
776         double xx = x * x;
777         double yy = y * y;
778         double zz = z * z;
779         double xz = x * z;
780         double yw = y * w;
781         double yz = y * z;
782         double xw = x * w;
783         dest.x = yw + xz + xz + yw;
784         dest.y = yz + yz - xw - xw;
785         dest.z = zz - yy - xx + ww;
786         return dest;
787     }
788 
789     public Vector4d transformPositiveZ(ref Vector4d dest) {
790         double ww = w * w;
791         double xx = x * x;
792         double yy = y * y;
793         double zz = z * z;
794         double xz = x * z;
795         double yw = y * w;
796         double yz = y * z;
797         double xw = x * w;
798         dest.x = yw + xz + xz + yw;
799         dest.y = yz + yz - xw - xw;
800         dest.z = zz - yy - xx + ww;
801         return dest;
802     }
803 
804     public Vector4d transformUnitPositiveZ(ref Vector4d dest) {
805         double xx = x * x;
806         double yy = y * y;
807         double xz = x * z;
808         double yz = y * z;
809         double xw = x * w;
810         double yw = y * w;
811         dest.x = xz + yw + xz + yw;
812         dest.y = yz + yz - xw - xw;
813         dest.z = 1.0 - xx - xx - yy - yy;
814         return dest;
815     }
816 
817     public Vector3d transformUnitPositiveZ(ref Vector3d dest) {
818         double xx = x * x;
819         double yy = y * y;
820         double xz = x * z;
821         double yz = y * z;
822         double xw = x * w;
823         double yw = y * w;
824         dest.x = xz + yw + xz + yw;
825         dest.y = yz + yz - xw - xw;
826         dest.z = 1.0 - xx - xx - yy - yy;
827         return dest;
828     }
829 
830     public Vector4d transform(Vector4d vec){
831         return transform(vec, vec);
832     }
833 
834     public Vector4d transformInverse(Vector4d vec){
835         return transformInverse(vec, vec);
836     }
837 
838     public Vector3d transform(Vector3d vec, ref Vector3d dest) {
839         return transform(vec.x, vec.y, vec.z, dest);
840     }
841 
842     public Vector3d transformInverse(Vector3d vec, ref Vector3d dest) {
843         return transformInverse(vec.x, vec.y, vec.z, dest);
844     }
845 
846     public Vector3d transform(double x, double y, double z, ref Vector3d dest) {
847         double xx = this.x * this.x, yy = this.y * this.y, zz = this.z * this.z, ww = this.w * this.w;
848         double xy = this.x * this.y, xz = this.x * this.z, yz = this.y * this.z, xw = this.x * this.w;
849         double zw = this.z * this.w, yw = this.y * this.w, k = 1 / (xx + yy + zz + ww);
850         return dest.set(Math.fma((xx - yy - zz + ww) * k, x, Math.fma(2 * (xy - zw) * k, y, (2 * (xz + yw) * k) * z)),
851                         Math.fma(2 * (xy + zw) * k, x, Math.fma((yy - xx - zz + ww) * k, y, (2 * (yz - xw) * k) * z)),
852                         Math.fma(2 * (xz - yw) * k, x, Math.fma(2 * (yz + xw) * k, y, ((zz - xx - yy + ww) * k) * z)));
853     }
854 
855     public Vector3d transformInverse(double x, double y, double z, ref Vector3d dest) {
856         double n = 1.0 / Math.fma(this.x, this.x, Math.fma(this.y, this.y, Math.fma(this.z, this.z, this.w * this.w)));
857         double qx = this.x * n, qy = this.y * n, qz = this.z * n, qw = this.w * n;
858         double xx = qx * qx, yy = qy * qy, zz = qz * qz, ww = qw * qw;
859         double xy = qx * qy, xz = qx * qz, yz = qy * qz, xw = qx * qw;
860         double zw = qz * qw, yw = qy * qw, k = 1 / (xx + yy + zz + ww);
861         return dest.set(Math.fma((xx - yy - zz + ww) * k, x, Math.fma(2 * (xy + zw) * k, y, (2 * (xz - yw) * k) * z)),
862                         Math.fma(2 * (xy - zw) * k, x, Math.fma((yy - xx - zz + ww) * k, y, (2 * (yz + xw) * k) * z)),
863                         Math.fma(2 * (xz + yw) * k, x, Math.fma(2 * (yz - xw) * k, y, ((zz - xx - yy + ww) * k) * z)));
864     }
865 
866     public Vector4d transform(Vector4d vec, ref Vector4d dest) {
867         return transform(vec.x, vec.y, vec.z, dest);
868     }
869 
870     public Vector4d transformInverse(Vector4d vec, ref Vector4d dest) {
871         return transformInverse(vec.x, vec.y, vec.z, dest);
872     }
873 
874     public Vector4d transform(double x, double y, double z, ref Vector4d dest) {
875         double xx = this.x * this.x, yy = this.y * this.y, zz = this.z * this.z, ww = this.w * this.w;
876         double xy = this.x * this.y, xz = this.x * this.z, yz = this.y * this.z, xw = this.x * this.w;
877         double zw = this.z * this.w, yw = this.y * this.w, k = 1 / (xx + yy + zz + ww);
878         return dest.set(Math.fma((xx - yy - zz + ww) * k, x, Math.fma(2 * (xy - zw) * k, y, (2 * (xz + yw) * k) * z)),
879                         Math.fma(2 * (xy + zw) * k, x, Math.fma((yy - xx - zz + ww) * k, y, (2 * (yz - xw) * k) * z)),
880                         Math.fma(2 * (xz - yw) * k, x, Math.fma(2 * (yz + xw) * k, y, ((zz - xx - yy + ww) * k) * z)), dest.w);
881     }
882 
883     public Vector4d transformInverse(double x, double y, double z, ref Vector4d dest) {
884         double n = 1.0 / Math.fma(this.x, this.x, Math.fma(this.y, this.y, Math.fma(this.z, this.z, this.w * this.w)));
885         double qx = this.x * n, qy = this.y * n, qz = this.z * n, qw = this.w * n;
886         double xx = qx * qx, yy = qy * qy, zz = qz * qz, ww = qw * qw;
887         double xy = qx * qy, xz = qx * qz, yz = qy * qz, xw = qx * qw;
888         double zw = qz * qw, yw = qy * qw, k = 1 / (xx + yy + zz + ww);
889         return dest.set(Math.fma((xx - yy - zz + ww) * k, x, Math.fma(2 * (xy + zw) * k, y, (2 * (xz - yw) * k) * z)),
890                         Math.fma(2 * (xy - zw) * k, x, Math.fma((yy - xx - zz + ww) * k, y, (2 * (yz + xw) * k) * z)),
891                         Math.fma(2 * (xz + yw) * k, x, Math.fma(2 * (yz - xw) * k, y, ((zz - xx - yy + ww) * k) * z)));
892     }
893 
894     public Vector4d transformUnit(Vector4d vec){
895         return transformUnit(vec, vec);
896     }
897 
898     public Vector4d transformInverseUnit(Vector4d vec){
899         return transformInverseUnit(vec, vec);
900     }
901 
902     public Vector3d transformUnit(Vector3d vec, ref Vector3d dest) {
903         return transformUnit(vec.x, vec.y, vec.z, dest);
904     }
905 
906     public Vector3d transformInverseUnit(Vector3d vec, ref Vector3d dest) {
907         return transformInverseUnit(vec.x, vec.y, vec.z, dest);
908     }
909 
910     public Vector3d transformUnit(double x, double y, double z, ref Vector3d dest) {
911         double xx = this.x * this.x, xy = this.x * this.y, xz = this.x * this.z;
912         double xw = this.x * this.w, yy = this.y * this.y, yz = this.y * this.z;
913         double yw = this.y * this.w, zz = this.z * this.z, zw = this.z * this.w;
914         return dest.set(Math.fma(Math.fma(-2, yy + zz, 1), x, Math.fma(2 * (xy - zw), y, (2 * (xz + yw)) * z)),
915                         Math.fma(2 * (xy + zw), x, Math.fma(Math.fma(-2, xx + zz, 1), y, (2 * (yz - xw)) * z)),
916                         Math.fma(2 * (xz - yw), x, Math.fma(2 * (yz + xw), y, Math.fma(-2, xx + yy, 1) * z)));
917     }
918 
919     public Vector3d transformInverseUnit(double x, double y, double z, ref Vector3d dest) {
920         double xx = this.x * this.x, xy = this.x * this.y, xz = this.x * this.z;
921         double xw = this.x * this.w, yy = this.y * this.y, yz = this.y * this.z;
922         double yw = this.y * this.w, zz = this.z * this.z, zw = this.z * this.w;
923         return dest.set(Math.fma(Math.fma(-2, yy + zz, 1), x, Math.fma(2 * (xy + zw), y, (2 * (xz - yw)) * z)),
924                         Math.fma(2 * (xy - zw), x, Math.fma(Math.fma(-2, xx + zz, 1), y, (2 * (yz + xw)) * z)),
925                         Math.fma(2 * (xz + yw), x, Math.fma(2 * (yz - xw), y, Math.fma(-2, xx + yy, 1) * z)));
926     }
927 
928     public Vector4d transformUnit(Vector4d vec, ref Vector4d dest) {
929         return transformUnit(vec.x, vec.y, vec.z, dest);
930     }
931 
932     public Vector4d transformInverseUnit(Vector4d vec, ref Vector4d dest) {
933         return transformInverseUnit(vec.x, vec.y, vec.z, dest);
934     }
935 
936     public Vector4d transformUnit(double x, double y, double z, ref Vector4d dest) {
937         double xx = this.x * this.x, xy = this.x * this.y, xz = this.x * this.z;
938         double xw = this.x * this.w, yy = this.y * this.y, yz = this.y * this.z;
939         double yw = this.y * this.w, zz = this.z * this.z, zw = this.z * this.w;
940         return dest.set(Math.fma(Math.fma(-2, yy + zz, 1), x, Math.fma(2 * (xy - zw), y, (2 * (xz + yw)) * z)),
941                         Math.fma(2 * (xy + zw), x, Math.fma(Math.fma(-2, xx + zz, 1), y, (2 * (yz - xw)) * z)),
942                         Math.fma(2 * (xz - yw), x, Math.fma(2 * (yz + xw), y, Math.fma(-2, xx + yy, 1) * z)),
943                         dest.w);
944     }
945 
946     public Vector4d transformInverseUnit(double x, double y, double z, ref Vector4d dest) {
947         double xx = this.x * this.x, xy = this.x * this.y, xz = this.x * this.z;
948         double xw = this.x * this.w, yy = this.y * this.y, yz = this.y * this.z;
949         double yw = this.y * this.w, zz = this.z * this.z, zw = this.z * this.w;
950         return dest.set(Math.fma(Math.fma(-2, yy + zz, 1), x, Math.fma(2 * (xy + zw), y, (2 * (xz - yw)) * z)),
951                         Math.fma(2 * (xy - zw), x, Math.fma(Math.fma(-2, xx + zz, 1), y, (2 * (yz + xw)) * z)),
952                         Math.fma(2 * (xz + yw), x, Math.fma(2 * (yz - xw), y, Math.fma(-2, xx + yy, 1) * z)),
953                         dest.w);
954     }
955 
956 
957     public Quaterniond invert(ref Quaterniond dest) {
958         double invNorm = 1.0 / lengthSquared();
959         dest.x = -x * invNorm;
960         dest.y = -y * invNorm;
961         dest.z = -z * invNorm;
962         dest.w = w * invNorm;
963         return dest;
964     }
965 
966     /**
967      * Invert this quaternion and {@link #normalize() normalize} it.
968      * <p>
969      * If this quaternion is already normalized, then {@link #conjugate()} should be used instead.
970      * 
971      * @see #conjugate()
972      * 
973      * @return this
974      */
975     ref public Quaterniond invert() return {
976         invert(this);
977         return this;
978     }
979 
980     public Quaterniond div(Quaterniond b, ref Quaterniond dest) {
981         double invNorm = 1.0 / Math.fma(b.x, b.x, Math.fma(b.y, b.y, Math.fma(b.z, b.z, b.w * b.w)));
982         double x = -b.x * invNorm;
983         double y = -b.y * invNorm;
984         double z = -b.z * invNorm;
985         double w = b.w * invNorm;
986         return dest.set(Math.fma(this.w, x, Math.fma(this.x, w, Math.fma(this.y, z, -this.z * y))),
987                         Math.fma(this.w, y, Math.fma(-this.x, z, Math.fma(this.y, w, this.z * x))),
988                         Math.fma(this.w, z, Math.fma(this.x, y, Math.fma(-this.y, x, this.z * w))),
989                         Math.fma(this.w, w, Math.fma(-this.x, x, Math.fma(-this.y, y, -this.z * z))));
990     }
991 
992     /**
993      * Divide <code>this</code> quaternion by <code>b</code>.
994      * <p>
995      * The division expressed using the inverse is performed in the following way:
996      * <p>
997      * <code>this = this * b^-1</code>, where <code>b^-1</code> is the inverse of <code>b</code>.
998      * 
999      * @param b
1000      *          the {@link Quaterniond} to divide this by
1001      * @return this
1002      */
1003     ref public Quaterniond div(Quaterniond b) return {
1004         div(b, this);
1005         return this;
1006     }
1007 
1008     /**
1009      * Conjugate this quaternion.
1010      * 
1011      * @return this
1012      */
1013     ref public Quaterniond conjugate() return {
1014         x = -x;
1015         y = -y;
1016         z = -z;
1017         return this;
1018     }
1019 
1020     public Quaterniond conjugate(ref Quaterniond dest) {
1021         dest.x = -x;
1022         dest.y = -y;
1023         dest.z = -z;
1024         dest.w = w;
1025         return dest;
1026     }
1027 
1028     /**
1029      * Set this quaternion to the identity.
1030      * 
1031      * @return this
1032      */
1033     ref public Quaterniond identity() return {
1034         x = 0.0;
1035         y = 0.0;
1036         z = 0.0;
1037         w = 1.0;
1038         return this;
1039     }
1040 
1041     public double lengthSquared() {
1042         return Math.fma(x, x, Math.fma(y, y, Math.fma(z, z, w * w)));
1043     }
1044 
1045     /**
1046      * Set this quaternion from the supplied euler angles (in radians) with rotation order XYZ.
1047      * <p>
1048      * This method is equivalent to calling: <code>rotationX(angleX).rotateY(angleY).rotateZ(angleZ)</code>
1049      * <p>
1050      * Reference: <a href="http://gamedev.stackexchange.com/questions/13436/glm-euler-angles-to-quaternion#answer-13446">this stackexchange answer</a>
1051      * 
1052      * @param angleX
1053      *          the angle in radians to rotate about x
1054      * @param angleY
1055      *          the angle in radians to rotate about y
1056      * @param angleZ
1057      *          the angle in radians to rotate about z
1058      * @return this
1059      */
1060     ref public Quaterniond rotationXYZ(double angleX, double angleY, double angleZ) return {
1061         double sx = Math.sin(angleX * 0.5);
1062         double cx = Math.cosFromSin(sx, angleX * 0.5);
1063         double sy = Math.sin(angleY * 0.5);
1064         double cy = Math.cosFromSin(sy, angleY * 0.5);
1065         double sz = Math.sin(angleZ * 0.5);
1066         double cz = Math.cosFromSin(sz, angleZ * 0.5);
1067 
1068         double cycz = cy * cz;
1069         double sysz = sy * sz;
1070         double sycz = sy * cz;
1071         double cysz = cy * sz;
1072         w = cx*cycz - sx*sysz;
1073         x = sx*cycz + cx*sysz;
1074         y = cx*sycz - sx*cysz;
1075         z = cx*cysz + sx*sycz;
1076 
1077         return this;
1078     }
1079 
1080     /**
1081      * Set this quaternion from the supplied euler angles (in radians) with rotation order ZYX.
1082      * <p>
1083      * This method is equivalent to calling: <code>rotationZ(angleZ).rotateY(angleY).rotateX(angleX)</code>
1084      * <p>
1085      * Reference: <a href="http://gamedev.stackexchange.com/questions/13436/glm-euler-angles-to-quaternion#answer-13446">this stackexchange answer</a>
1086      * 
1087      * @param angleX
1088      *          the angle in radians to rotate about x
1089      * @param angleY
1090      *          the angle in radians to rotate about y
1091      * @param angleZ
1092      *          the angle in radians to rotate about z
1093      * @return this
1094      */
1095     ref public Quaterniond rotationZYX(double angleZ, double angleY, double angleX) return {
1096         double sx = Math.sin(angleX * 0.5);
1097         double cx = Math.cosFromSin(sx, angleX * 0.5);
1098         double sy = Math.sin(angleY * 0.5);
1099         double cy = Math.cosFromSin(sy, angleY * 0.5);
1100         double sz = Math.sin(angleZ * 0.5);
1101         double cz = Math.cosFromSin(sz, angleZ * 0.5);
1102 
1103         double cycz = cy * cz;
1104         double sysz = sy * sz;
1105         double sycz = sy * cz;
1106         double cysz = cy * sz;
1107         w = cx*cycz + sx*sysz;
1108         x = sx*cycz - cx*sysz;
1109         y = cx*sycz + sx*cysz;
1110         z = cx*cysz - sx*sycz;
1111 
1112         return this;
1113     }
1114 
1115     /**
1116      * Set this quaternion from the supplied euler angles (in radians) with rotation order YXZ.
1117      * <p>
1118      * This method is equivalent to calling: <code>rotationY(angleY).rotateX(angleX).rotateZ(angleZ)</code>
1119      * <p>
1120      * Reference: <a href="https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles">https://en.wikipedia.org</a>
1121      * 
1122      * @param angleY
1123      *          the angle in radians to rotate about y
1124      * @param angleX
1125      *          the angle in radians to rotate about x
1126      * @param angleZ
1127      *          the angle in radians to rotate about z
1128      * @return this
1129      */
1130     ref public Quaterniond rotationYXZ(double angleY, double angleX, double angleZ) return {
1131         double sx = Math.sin(angleX * 0.5);
1132         double cx = Math.cosFromSin(sx, angleX * 0.5);
1133         double sy = Math.sin(angleY * 0.5);
1134         double cy = Math.cosFromSin(sy, angleY * 0.5);
1135         double sz = Math.sin(angleZ * 0.5);
1136         double cz = Math.cosFromSin(sz, angleZ * 0.5);
1137 
1138         double x = cy * sx;
1139         double y = sy * cx;
1140         double z = sy * sx;
1141         double w = cy * cx;
1142         this.x = x * cz + y * sz;
1143         this.y = y * cz - x * sz;
1144         this.z = w * sz - z * cz;
1145         this.w = w * cz + z * sz;
1146 
1147         return this;
1148     }
1149 
1150     /**
1151      * Interpolate between <code>this</code> {@link #normalize() unit} quaternion and the specified
1152      * <code>target</code> {@link #normalize() unit} quaternion using spherical linear interpolation using the specified interpolation factor <code>alpha</code>.
1153      * <p>
1154      * This method resorts to non-spherical linear interpolation when the absolute dot product between <code>this</code> and <code>target</code> is
1155      * below <code>1E-6</code>.
1156      * 
1157      * @param target
1158      *          the target of the interpolation, which should be reached with <code>alpha = 1.0</code>
1159      * @param alpha
1160      *          the interpolation factor, within <code>[0..1]</code>
1161      * @return this
1162      */
1163     ref public Quaterniond slerp(Quaterniond target, double alpha) return {
1164         slerp(target, alpha, this);
1165         return this;
1166     }
1167 
1168     public Quaterniond slerp(Quaterniond target, double alpha, ref Quaterniond dest) {
1169         double cosom = Math.fma(x, target.x, Math.fma(y, target.y, Math.fma(z, target.z, w * target.w)));
1170         double absCosom = Math.abs(cosom);
1171         double scale0, scale1;
1172         if (1.0 - absCosom > 1E-6) {
1173             double sinSqr = 1.0 - absCosom * absCosom;
1174             double sinom = Math.invsqrt(sinSqr);
1175             double omega = Math.atan2(sinSqr * sinom, absCosom);
1176             scale0 = Math.sin((1.0 - alpha) * omega) * sinom;
1177             scale1 = Math.sin(alpha * omega) * sinom;
1178         } else {
1179             scale0 = 1.0 - alpha;
1180             scale1 = alpha;
1181         }
1182         scale1 = cosom >= 0.0 ? scale1 : -scale1;
1183         dest.x = Math.fma(scale0, x, scale1 * target.x);
1184         dest.y = Math.fma(scale0, y, scale1 * target.y);
1185         dest.z = Math.fma(scale0, z, scale1 * target.z);
1186         dest.w = Math.fma(scale0, w, scale1 * target.w);
1187         return dest;
1188     }
1189 
1190     /**
1191      * Interpolate between all of the quaternions given in <code>qs</code> via spherical linear interpolation using the specified interpolation factors <code>weights</code>,
1192      * and store the result in <code>dest</code>.
1193      * <p>
1194      * This method will interpolate between each two successive quaternions via {@link #slerp(Quaterniond, double)} using their relative interpolation weights.
1195      * <p>
1196      * This method resorts to non-spherical linear interpolation when the absolute dot product of any two interpolated quaternions is below <code>1E-6f</code>.
1197      * <p>
1198      * Reference: <a href="http://gamedev.stackexchange.com/questions/62354/method-for-interpolation-between-3-quaternions#answer-62356">http://gamedev.stackexchange.com/</a>
1199      * 
1200      * @param qs
1201      *          the quaternions to interpolate over
1202      * @param weights
1203      *          the weights of each individual quaternion in <code>qs</code>
1204      * @param dest
1205      *          will hold the result
1206      * @return dest
1207      */
1208     public static Quaterniond slerp(Quaterniond[] qs, double[] weights, ref Quaterniond dest) {
1209         dest.set(qs[0]);
1210         double w = weights[0];
1211         for (int i = 1; i < qs.length; i++) {
1212             double w0 = w;
1213             double w1 = weights[i];
1214             double rw1 = w1 / (w0 + w1);
1215             w += w1;
1216             dest.slerp(qs[i], rw1);
1217         }
1218         return dest;
1219     }
1220 
1221     /**
1222      * Apply scaling to this quaternion, which results in any vector transformed by this quaternion to change
1223      * its length by the given <code>factor</code>.
1224      * 
1225      * @param factor
1226      *          the scaling factor
1227      * @return this
1228      */
1229     ref public Quaterniond scale(double factor) return {
1230         scale(factor, this);
1231         return this;
1232     }
1233 
1234     public Quaterniond scale(double factor, ref Quaterniond dest) {
1235         double sqrt = Math.sqrt(factor);
1236         dest.x = sqrt * x;
1237         dest.y = sqrt * y;
1238         dest.z = sqrt * z;
1239         dest.w = sqrt * w;
1240         return dest;
1241     }
1242 
1243     /**
1244      * Set this quaternion to represent scaling, which results in a transformed vector to change
1245      * its length by the given <code>factor</code>.
1246      * 
1247      * @param factor
1248      *          the scaling factor
1249      * @return this
1250      */
1251     ref public Quaterniond scaling(double factor) return {
1252         double sqrt = Math.sqrt(factor);
1253         this.x = 0.0;
1254         this.y = 0.0;
1255         this.z = 0.0;
1256         this.w = sqrt;
1257         return this;
1258     }
1259 
1260     /**
1261      * Integrate the rotation given by the angular velocity <code>(vx, vy, vz)</code> around the x, y and z axis, respectively,
1262      * with respect to the given elapsed time delta <code>dt</code> and add the differentiate rotation to the rotation represented by this quaternion.
1263      * <p>
1264      * This method pre-multiplies the rotation given by <code>dt</code> and <code>(vx, vy, vz)</code> by <code>this</code>, so
1265      * the angular velocities are always relative to the local coordinate system of the rotation represented by <code>this</code> quaternion.
1266      * <p>
1267      * This method is equivalent to calling: <code>rotateLocal(dt * vx, dt * vy, dt * vz)</code>
1268      * <p>
1269      * Reference: <a href="http://physicsforgames.blogspot.de/2010/02/quaternions.html">http://physicsforgames.blogspot.de/</a>
1270      * 
1271      * @param dt
1272      *          the delta time
1273      * @param vx
1274      *          the angular velocity around the x axis
1275      * @param vy
1276      *          the angular velocity around the y axis
1277      * @param vz
1278      *          the angular velocity around the z axis
1279      * @return this
1280      */
1281     ref public Quaterniond integrate(double dt, double vx, double vy, double vz) return {
1282         integrate(dt, vx, vy, vz, this);
1283         return this;
1284     }
1285 
1286     public Quaterniond integrate(double dt, double vx, double vy, double vz, ref Quaterniond dest) {
1287         double thetaX = dt * vx * 0.5;
1288         double thetaY = dt * vy * 0.5;
1289         double thetaZ = dt * vz * 0.5;
1290         double thetaMagSq = thetaX * thetaX + thetaY * thetaY + thetaZ * thetaZ;
1291         double s;
1292         double dqX, dqY, dqZ, dqW;
1293         if (thetaMagSq * thetaMagSq / 24.0 < 1E-8) {
1294             dqW = 1.0 - thetaMagSq * 0.5;
1295             s = 1.0 - thetaMagSq / 6.0;
1296         } else {
1297             double thetaMag = Math.sqrt(thetaMagSq);
1298             double sin = Math.sin(thetaMag);
1299             s = sin / thetaMag;
1300             dqW = Math.cosFromSin(sin, thetaMag);
1301         }
1302         dqX = thetaX * s;
1303         dqY = thetaY * s;
1304         dqZ = thetaZ * s;
1305         /* Pre-multiplication */
1306         return dest.set(Math.fma(dqW, x, Math.fma(dqX, w, Math.fma(dqY, z, -dqZ * y))),
1307                         Math.fma(dqW, y, Math.fma(-dqX, z, Math.fma(dqY, w, dqZ * x))),
1308                         Math.fma(dqW, z, Math.fma(dqX, y, Math.fma(-dqY, x, dqZ * w))),
1309                         Math.fma(dqW, w, Math.fma(-dqX, x, Math.fma(-dqY, y, -dqZ * z))));
1310     }
1311 
1312     /**
1313      * Compute a linear (non-spherical) interpolation of <code>this</code> and the given quaternion <code>q</code>
1314      * and store the result in <code>this</code>.
1315      * 
1316      * @param q
1317      *          the other quaternion
1318      * @param factor
1319      *          the interpolation factor. It is between 0.0 and 1.0
1320      * @return this
1321      */
1322     ref public Quaterniond nlerp(Quaterniond q, double factor) return {
1323         nlerp(q, factor, this);
1324         return this;
1325     }
1326 
1327     public Quaterniond nlerp(Quaterniond q, double factor, ref Quaterniond dest) {
1328         double cosom = Math.fma(x, q.x, Math.fma(y, q.y, Math.fma(z, q.z, w * q.w)));
1329         double scale0 = 1.0 - factor;
1330         double scale1 = (cosom >= 0.0) ? factor : -factor;
1331         dest.x = Math.fma(scale0, x, scale1 * q.x);
1332         dest.y = Math.fma(scale0, y, scale1 * q.y);
1333         dest.z = Math.fma(scale0, z, scale1 * q.z);
1334         dest.w = Math.fma(scale0, w, scale1 * q.w);
1335         double s = Math.invsqrt(Math.fma(dest.x, dest.x, Math.fma(dest.y, dest.y, Math.fma(dest.z, dest.z, dest.w * dest.w))));
1336         dest.x *= s;
1337         dest.y *= s;
1338         dest.z *= s;
1339         dest.w *= s;
1340         return dest;
1341     }
1342 
1343     /**
1344      * Interpolate between all of the quaternions given in <code>qs</code> via non-spherical linear interpolation using the
1345      * specified interpolation factors <code>weights</code>, and store the result in <code>dest</code>.
1346      * <p>
1347      * This method will interpolate between each two successive quaternions via {@link #nlerp(Quaterniond, double)}
1348      * using their relative interpolation weights.
1349      * <p>
1350      * Reference: <a href="http://gamedev.stackexchange.com/questions/62354/method-for-interpolation-between-3-quaternions#answer-62356">http://gamedev.stackexchange.com/</a>
1351      * 
1352      * @param qs
1353      *          the quaternions to interpolate over
1354      * @param weights
1355      *          the weights of each individual quaternion in <code>qs</code>
1356      * @param dest
1357      *          will hold the result
1358      * @return dest
1359      */
1360     public static Quaterniond nlerp(Quaterniond[] qs, double[] weights, ref Quaterniond dest) {
1361         dest.set(qs[0]);
1362         double w = weights[0];
1363         for (int i = 1; i < qs.length; i++) {
1364             double w0 = w;
1365             double w1 = weights[i];
1366             double rw1 = w1 / (w0 + w1);
1367             w += w1;
1368             dest.nlerp(qs[i], rw1);
1369         }
1370         return dest;
1371     }
1372 
1373     public Quaterniond nlerpIterative(Quaterniond q, double alpha, double dotThreshold, ref Quaterniond dest) {
1374         double q1x = x, q1y = y, q1z = z, q1w = w;
1375         double q2x = q.x, q2y = q.y, q2z = q.z, q2w = q.w;
1376         double dot = Math.fma(q1x, q2x, Math.fma(q1y, q2y, Math.fma(q1z, q2z, q1w * q2w)));
1377         double absDot = Math.abs(dot);
1378         if (1.0 - 1E-6 < absDot) {
1379             return dest.set(this);
1380         }
1381         double alphaN = alpha;
1382         while (absDot < dotThreshold) {
1383             double scale0 = 0.5;
1384             double scale1 = dot >= 0.0 ? 0.5 : -0.5;
1385             if (alphaN < 0.5) {
1386                 q2x = Math.fma(scale0, q2x, scale1 * q1x);
1387                 q2y = Math.fma(scale0, q2y, scale1 * q1y);
1388                 q2z = Math.fma(scale0, q2z, scale1 * q1z);
1389                 q2w = Math.fma(scale0, q2w, scale1 * q1w);
1390                 double s = cast(double) Math.invsqrt(Math.fma(q2x, q2x, Math.fma(q2y, q2y, Math.fma(q2z, q2z, q2w * q2w))));
1391                 q2x *= s;
1392                 q2y *= s;
1393                 q2z *= s;
1394                 q2w *= s;
1395                 alphaN = alphaN + alphaN;
1396             } else {
1397                 q1x = Math.fma(scale0, q1x, scale1 * q2x);
1398                 q1y = Math.fma(scale0, q1y, scale1 * q2y);
1399                 q1z = Math.fma(scale0, q1z, scale1 * q2z);
1400                 q1w = Math.fma(scale0, q1w, scale1 * q2w);
1401                 double s = cast(double) Math.invsqrt(Math.fma(q1x, q1x, Math.fma(q1y, q1y, Math.fma(q1z, q1z, q1w * q1w))));
1402                 q1x *= s;
1403                 q1y *= s;
1404                 q1z *= s;
1405                 q1w *= s;
1406                 alphaN = alphaN + alphaN - 1.0;
1407             }
1408             dot = Math.fma(q1x, q2x, Math.fma(q1y, q2y, Math.fma(q1z, q2z, q1w * q2w)));
1409             absDot = Math.abs(dot);
1410         }
1411         double scale0 = 1.0 - alphaN;
1412         double scale1 = dot >= 0.0 ? alphaN : -alphaN;
1413         double resX = Math.fma(scale0, q1x, scale1 * q2x);
1414         double resY = Math.fma(scale0, q1y, scale1 * q2y);
1415         double resZ = Math.fma(scale0, q1z, scale1 * q2z);
1416         double resW = Math.fma(scale0, q1w, scale1 * q2w);
1417         double s = Math.invsqrt(Math.fma(resX, resX, Math.fma(resY, resY, Math.fma(resZ, resZ, resW * resW))));
1418         dest.x = resX * s;
1419         dest.y = resY * s;
1420         dest.z = resZ * s;
1421         dest.w = resW * s;
1422         return dest;
1423     }
1424 
1425     /**
1426      * Compute linear (non-spherical) interpolations of <code>this</code> and the given quaternion <code>q</code>
1427      * iteratively and store the result in <code>this</code>.
1428      * <p>
1429      * This method performs a series of small-step nlerp interpolations to avoid doing a costly spherical linear interpolation, like
1430      * {@link #slerp(Quaterniond, double, Quaterniond) slerp},
1431      * by subdividing the rotation arc between <code>this</code> and <code>q</code> via non-spherical linear interpolations as long as
1432      * the absolute dot product of <code>this</code> and <code>q</code> is greater than the given <code>dotThreshold</code> parameter.
1433      * <p>
1434      * Thanks to <code>@theagentd</code> at <a href="http://www.java-gaming.org/">http://www.java-gaming.org/</a> for providing the code.
1435      * 
1436      * @param q
1437      *          the other quaternion
1438      * @param alpha
1439      *          the interpolation factor, between 0.0 and 1.0
1440      * @param dotThreshold
1441      *          the threshold for the dot product of <code>this</code> and <code>q</code> above which this method performs another iteration
1442      *          of a small-step linear interpolation
1443      * @return this
1444      */
1445     ref public Quaterniond nlerpIterative(Quaterniond q, double alpha, double dotThreshold) return {
1446         nlerpIterative(q, alpha, dotThreshold, this);
1447         return this;
1448     }
1449 
1450     /**
1451      * Interpolate between all of the quaternions given in <code>qs</code> via iterative non-spherical linear interpolation using the
1452      * specified interpolation factors <code>weights</code>, and store the result in <code>dest</code>.
1453      * <p>
1454      * This method will interpolate between each two successive quaternions via {@link #nlerpIterative(Quaterniond, double, double)}
1455      * using their relative interpolation weights.
1456      * <p>
1457      * Reference: <a href="http://gamedev.stackexchange.com/questions/62354/method-for-interpolation-between-3-quaternions#answer-62356">http://gamedev.stackexchange.com/</a>
1458      * 
1459      * @param qs
1460      *          the quaternions to interpolate over
1461      * @param weights
1462      *          the weights of each individual quaternion in <code>qs</code>
1463      * @param dotThreshold
1464      *          the threshold for the dot product of each two interpolated quaternions above which {@link #nlerpIterative(Quaterniond, double, double)} performs another iteration
1465      *          of a small-step linear interpolation
1466      * @param dest
1467      *          will hold the result
1468      * @return dest
1469      */
1470     public static Quaterniond nlerpIterative(Quaterniond[] qs, double[] weights, double dotThreshold, ref Quaterniond dest) {
1471         dest.set(qs[0]);
1472         double w = weights[0];
1473         for (int i = 1; i < qs.length; i++) {
1474             double w0 = w;
1475             double w1 = weights[i];
1476             double rw1 = w1 / (w0 + w1);
1477             w += w1;
1478             dest.nlerpIterative(qs[i], rw1, dotThreshold);
1479         }
1480         return dest;
1481     }
1482 
1483     /**
1484      * Apply a rotation to this quaternion that maps the given direction to the positive Z axis.
1485      * <p>
1486      * Because there are multiple possibilities for such a rotation, this method will choose the one that ensures the given up direction to remain
1487      * parallel to the plane spanned by the <code>up</code> and <code>dir</code> vectors. 
1488      * <p>
1489      * If <code>Q</code> is <code>this</code> quaternion and <code>R</code> the quaternion representing the 
1490      * specified rotation, then the new quaternion will be <code>Q * R</code>. So when transforming a
1491      * vector <code>v</code> with the new quaternion by using <code>Q * R * v</code>, the
1492      * rotation added by this method will be applied first!
1493      * <p>
1494      * Reference: <a href="http://answers.unity3d.com/questions/467614/what-is-the-source-code-of-quaternionlookrotation.html">http://answers.unity3d.com</a>
1495      * 
1496      * @see #lookAlong(double, double, double, double, double, double, Quaterniond)
1497      * 
1498      * @param dir
1499      *              the direction to map to the positive Z axis
1500      * @param up
1501      *              the vector which will be mapped to a vector parallel to the plane
1502      *              spanned by the given <code>dir</code> and <code>up</code>
1503      * @return this
1504      */
1505     ref public Quaterniond lookAlong(Vector3d dir, Vector3d up) return {
1506         lookAlong(dir.x, dir.y, dir.z, up.x, up.y, up.z, this);
1507         return this;
1508     }
1509 
1510     public Quaterniond lookAlong(Vector3d dir, Vector3d up, ref Quaterniond dest) {
1511         return lookAlong(dir.x, dir.y, dir.z, up.x, up.y, up.z, dest);
1512     }
1513 
1514     /**
1515      * Apply a rotation to this quaternion that maps the given direction to the positive Z axis.
1516      * <p>
1517      * Because there are multiple possibilities for such a rotation, this method will choose the one that ensures the given up direction to remain
1518      * parallel to the plane spanned by the <code>up</code> and <code>dir</code> vectors. 
1519      * <p>
1520      * If <code>Q</code> is <code>this</code> quaternion and <code>R</code> the quaternion representing the 
1521      * specified rotation, then the new quaternion will be <code>Q * R</code>. So when transforming a
1522      * vector <code>v</code> with the new quaternion by using <code>Q * R * v</code>, the
1523      * rotation added by this method will be applied first!
1524      * <p>
1525      * Reference: <a href="http://answers.unity3d.com/questions/467614/what-is-the-source-code-of-quaternionlookrotation.html">http://answers.unity3d.com</a>
1526      * 
1527      * @see #lookAlong(double, double, double, double, double, double, Quaterniond)
1528      * 
1529      * @param dirX
1530      *              the x-coordinate of the direction to look along
1531      * @param dirY
1532      *              the y-coordinate of the direction to look along
1533      * @param dirZ
1534      *              the z-coordinate of the direction to look along
1535      * @param upX
1536      *              the x-coordinate of the up vector
1537      * @param upY
1538      *              the y-coordinate of the up vector
1539      * @param upZ
1540      *              the z-coordinate of the up vector
1541      * @return this
1542      */
1543     ref public Quaterniond lookAlong(double dirX, double dirY, double dirZ, double upX, double upY, double upZ) return {
1544         lookAlong(dirX, dirY, dirZ, upX, upY, upZ, this);
1545         return this;
1546     }
1547 
1548     public Quaterniond lookAlong(double dirX, double dirY, double dirZ, double upX, double upY, double upZ, ref Quaterniond dest) {
1549         // Normalize direction
1550         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
1551         double dirnX = -dirX * invDirLength;
1552         double dirnY = -dirY * invDirLength;
1553         double dirnZ = -dirZ * invDirLength;
1554         // left = up x dir
1555         double leftX, leftY, leftZ;
1556         leftX = upY * dirnZ - upZ * dirnY;
1557         leftY = upZ * dirnX - upX * dirnZ;
1558         leftZ = upX * dirnY - upY * dirnX;
1559         // normalize left
1560         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
1561         leftX *= invLeftLength;
1562         leftY *= invLeftLength;
1563         leftZ *= invLeftLength;
1564         // up = direction x left
1565         double upnX = dirnY * leftZ - dirnZ * leftY;
1566         double upnY = dirnZ * leftX - dirnX * leftZ;
1567         double upnZ = dirnX * leftY - dirnY * leftX;
1568 
1569         /* Convert orthonormal basis vectors to quaternion */
1570         double x, y, z, w;
1571         double t;
1572         double tr = leftX + upnY + dirnZ;
1573         if (tr >= 0.0) {
1574             t = Math.sqrt(tr + 1.0);
1575             w = t * 0.5;
1576             t = 0.5 / t;
1577             x = (dirnY - upnZ) * t;
1578             y = (leftZ - dirnX) * t;
1579             z = (upnX - leftY) * t;
1580         } else {
1581             if (leftX > upnY && leftX > dirnZ) {
1582                 t = Math.sqrt(1.0 + leftX - upnY - dirnZ);
1583                 x = t * 0.5;
1584                 t = 0.5 / t;
1585                 y = (leftY + upnX) * t;
1586                 z = (dirnX + leftZ) * t;
1587                 w = (dirnY - upnZ) * t;
1588             } else if (upnY > dirnZ) {
1589                 t = Math.sqrt(1.0 + upnY - leftX - dirnZ);
1590                 y = t * 0.5;
1591                 t = 0.5 / t;
1592                 x = (leftY + upnX) * t;
1593                 z = (upnZ + dirnY) * t;
1594                 w = (leftZ - dirnX) * t;
1595             } else {
1596                 t = Math.sqrt(1.0 + dirnZ - leftX - upnY);
1597                 z = t * 0.5;
1598                 t = 0.5 / t;
1599                 x = (dirnX + leftZ) * t;
1600                 y = (upnZ + dirnY) * t;
1601                 w = (upnX - leftY) * t;
1602             }
1603         }
1604         /* Multiply */
1605         return dest.set(Math.fma(this.w, x, Math.fma(this.x, w, Math.fma(this.y, z, -this.z * y))),
1606                         Math.fma(this.w, y, Math.fma(-this.x, z, Math.fma(this.y, w, this.z * x))),
1607                         Math.fma(this.w, z, Math.fma(this.x, y, Math.fma(-this.y, x, this.z * w))),
1608                         Math.fma(this.w, w, Math.fma(-this.x, x, Math.fma(-this.y, y, -this.z * z))));
1609     }
1610 
1611     public int hashCode() {
1612         immutable int prime = 31;
1613         int result = 1;
1614         long temp;
1615         temp = Math.doubleToLongBits(w);
1616         result = prime * result + cast(int) (temp ^ (temp >>> 32));
1617         temp = Math.doubleToLongBits(x);
1618         result = prime * result + cast(int) (temp ^ (temp >>> 32));
1619         temp = Math.doubleToLongBits(y);
1620         result = prime * result + cast(int) (temp ^ (temp >>> 32));
1621         temp = Math.doubleToLongBits(z);
1622         result = prime * result + cast(int) (temp ^ (temp >>> 32));
1623         return result;
1624     }
1625 
1626     public bool equals(Quaterniond other) {
1627         if (this == other)
1628             return true;
1629         if (Math.doubleToLongBits(w) != Math.doubleToLongBits(other.w))
1630             return false;
1631         if (Math.doubleToLongBits(x) != Math.doubleToLongBits(other.x))
1632             return false;
1633         if (Math.doubleToLongBits(y) != Math.doubleToLongBits(other.y))
1634             return false;
1635         if (Math.doubleToLongBits(z) != Math.doubleToLongBits(other.z))
1636             return false;
1637         return true;
1638     }
1639 
1640     /**
1641      * Compute the difference between <code>this</code> and the <code>other</code> quaternion
1642      * and store the result in <code>this</code>.
1643      * <p>
1644      * The difference is the rotation that has to be applied to get from
1645      * <code>this</code> rotation to <code>other</code>. If <code>T</code> is <code>this</code>, <code>Q</code>
1646      * is <code>other</code> and <code>D</code> is the computed difference, then the following equation holds:
1647      * <p>
1648      * <code>T * D = Q</code>
1649      * <p>
1650      * It is defined as: <code>D = T^-1 * Q</code>, where <code>T^-1</code> denotes the {@link #invert() inverse} of <code>T</code>.
1651      * 
1652      * @param other
1653      *          the other quaternion
1654      * @return this
1655      */
1656     ref public Quaterniond difference(Quaterniond other) return {
1657         difference(other, this);
1658         return this;
1659     }
1660 
1661     public Quaterniond difference(Quaterniond other, ref Quaterniond dest) {
1662         double invNorm = 1.0 / lengthSquared();
1663         double x = -this.x * invNorm;
1664         double y = -this.y * invNorm;
1665         double z = -this.z * invNorm;
1666         double w = this.w * invNorm;
1667         dest.set(Math.fma(w, other.x, Math.fma(x, other.w, Math.fma(y, other.z, -z * other.y))),
1668                  Math.fma(w, other.y, Math.fma(-x, other.z, Math.fma(y, other.w, z * other.x))),
1669                  Math.fma(w, other.z, Math.fma(x, other.y, Math.fma(-y, other.x, z * other.w))),
1670                  Math.fma(w, other.w, Math.fma(-x, other.x, Math.fma(-y, other.y, -z * other.z))));
1671         return dest;
1672     }
1673 
1674     /**
1675      * Set <code>this</code> quaternion to a rotation that rotates the <code>fromDir</code> vector to point along <code>toDir</code>.
1676      * <p>
1677      * Since there can be multiple possible rotations, this method chooses the one with the shortest arc.
1678      * <p>
1679      * Reference: <a href="http://stackoverflow.com/questions/1171849/finding-quaternion-representing-the-rotation-from-one-vector-to-another#answer-1171995">stackoverflow.com</a>
1680      * 
1681      * @param fromDirX
1682      *              the x-coordinate of the direction to rotate into the destination direction
1683      * @param fromDirY
1684      *              the y-coordinate of the direction to rotate into the destination direction
1685      * @param fromDirZ
1686      *              the z-coordinate of the direction to rotate into the destination direction
1687      * @param toDirX
1688      *              the x-coordinate of the direction to rotate to
1689      * @param toDirY
1690      *              the y-coordinate of the direction to rotate to
1691      * @param toDirZ
1692      *              the z-coordinate of the direction to rotate to
1693      * @return this
1694      */
1695     ref public Quaterniond rotationTo(double fromDirX, double fromDirY, double fromDirZ, double toDirX, double toDirY, double toDirZ) return {
1696         double fn = Math.invsqrt(Math.fma(fromDirX, fromDirX, Math.fma(fromDirY, fromDirY, fromDirZ * fromDirZ)));
1697         double tn = Math.invsqrt(Math.fma(toDirX, toDirX, Math.fma(toDirY, toDirY, toDirZ * toDirZ)));
1698         double fx = fromDirX * fn, fy = fromDirY * fn, fz = fromDirZ * fn;
1699         double tx = toDirX * tn, ty = toDirY * tn, tz = toDirZ * tn;
1700         double dot = fx * tx + fy * ty + fz * tz;
1701         double x, y, z, w;
1702         if (dot < -1.0 + 1E-6) {
1703             x = fy;
1704             y = -fx;
1705             z = 0.0;
1706             w = 0.0;
1707             if (x * x + y * y == 0.0) {
1708                 x = 0.0;
1709                 y = fz;
1710                 z = -fy;
1711                 w = 0.0;
1712             }
1713             this.x = x;
1714             this.y = y;
1715             this.z = z;
1716             this.w = 0;
1717         } else {
1718             double sd2 = Math.sqrt((1.0 + dot) * 2.0);
1719             double isd2 = 1.0 / sd2;
1720             double cx = fy * tz - fz * ty;
1721             double cy = fz * tx - fx * tz;
1722             double cz = fx * ty - fy * tx;
1723             x = cx * isd2;
1724             y = cy * isd2;
1725             z = cz * isd2;
1726             w = sd2 * 0.5;
1727             double n2 = Math.invsqrt(Math.fma(x, x, Math.fma(y, y, Math.fma(z, z, w * w))));
1728             this.x = x * n2;
1729             this.y = y * n2;
1730             this.z = z * n2;
1731             this.w = w * n2;
1732         }
1733         return this;
1734     }
1735 
1736     /**
1737      * Set <code>this</code> quaternion to a rotation that rotates the <code>fromDir</code> vector to point along <code>toDir</code>.
1738      * <p>
1739      * Because there can be multiple possible rotations, this method chooses the one with the shortest arc.
1740      * 
1741      * @see #rotationTo(double, double, double, double, double, double)
1742      * 
1743      * @param fromDir
1744      *          the starting direction
1745      * @param toDir
1746      *          the destination direction
1747      * @return this
1748      */
1749     ref public Quaterniond rotationTo(Vector3d fromDir, Vector3d toDir) return {
1750         return rotationTo(fromDir.x, fromDir.y, fromDir.z, toDir.x, toDir.y, toDir.z);
1751     }
1752 
1753     public Quaterniond rotateTo(double fromDirX, double fromDirY, double fromDirZ,
1754                                 double toDirX, double toDirY, double toDirZ, ref Quaterniond dest) {
1755         double fn = Math.invsqrt(Math.fma(fromDirX, fromDirX, Math.fma(fromDirY, fromDirY, fromDirZ * fromDirZ)));
1756         double tn = Math.invsqrt(Math.fma(toDirX, toDirX, Math.fma(toDirY, toDirY, toDirZ * toDirZ)));
1757         double fx = fromDirX * fn, fy = fromDirY * fn, fz = fromDirZ * fn;
1758         double tx = toDirX * tn, ty = toDirY * tn, tz = toDirZ * tn;
1759         double dot = fx * tx + fy * ty + fz * tz;
1760         double x, y, z, w;
1761         if (dot < -1.0 + 1E-6) {
1762             x = fy;
1763             y = -fx;
1764             z = 0.0;
1765             w = 0.0;
1766             if (x * x + y * y == 0.0) {
1767                 x = 0.0;
1768                 y = fz;
1769                 z = -fy;
1770                 w = 0.0;
1771             }
1772         } else {
1773             double sd2 = Math.sqrt((1.0 + dot) * 2.0);
1774             double isd2 = 1.0 / sd2;
1775             double cx = fy * tz - fz * ty;
1776             double cy = fz * tx - fx * tz;
1777             double cz = fx * ty - fy * tx;
1778             x = cx * isd2;
1779             y = cy * isd2;
1780             z = cz * isd2;
1781             w = sd2 * 0.5;
1782             double n2 = Math.invsqrt(Math.fma(x, x, Math.fma(y, y, Math.fma(z, z, w * w))));
1783             x *= n2;
1784             y *= n2;
1785             z *= n2;
1786             w *= n2;
1787         }
1788         /* Multiply */
1789         return dest.set(Math.fma(this.w, x, Math.fma(this.x, w, Math.fma(this.y, z, -this.z * y))),
1790                         Math.fma(this.w, y, Math.fma(-this.x, z, Math.fma(this.y, w, this.z * x))),
1791                         Math.fma(this.w, z, Math.fma(this.x, y, Math.fma(-this.y, x, this.z * w))),
1792                         Math.fma(this.w, w, Math.fma(-this.x, x, Math.fma(-this.y, y, -this.z * z))));
1793     }
1794 
1795     /**
1796      * Set this quaternion to a rotation of the given angle in radians about the supplied axis.
1797      * 
1798      * @param angle
1799      *          the rotation angle in radians
1800      * @param axisX
1801      *          the x-coordinate of the rotation axis
1802      * @param axisY
1803      *          the y-coordinate of the rotation axis
1804      * @param axisZ
1805      *          the z-coordinate of the rotation axis
1806      * @return this
1807      */
1808     ref public Quaterniond rotationAxis(double angle, double axisX, double axisY, double axisZ) return {
1809         double hangle = angle / 2.0;
1810         double sinAngle = Math.sin(hangle);
1811         double invVLength = Math.invsqrt(axisX * axisX + axisY * axisY + axisZ * axisZ);
1812         return set(axisX * invVLength * sinAngle,
1813                    axisY * invVLength * sinAngle,
1814                    axisZ * invVLength * sinAngle,
1815                    Math.cosFromSin(sinAngle, hangle));
1816     }
1817 
1818     /**
1819      * Set this quaternion to represent a rotation of the given radians about the x axis.
1820      * 
1821      * @param angle
1822      *              the angle in radians to rotate about the x axis
1823      * @return this
1824      */
1825     ref public Quaterniond rotationX(double angle) return {
1826         double sin = Math.sin(angle * 0.5);
1827         double cos = Math.cosFromSin(sin, angle * 0.5);
1828         return set(sin, 0, cos, 0);
1829     }
1830 
1831     /**
1832      * Set this quaternion to represent a rotation of the given radians about the y axis.
1833      * 
1834      * @param angle
1835      *              the angle in radians to rotate about the y axis
1836      * @return this
1837      */
1838     ref public Quaterniond rotationY(double angle) return {
1839         double sin = Math.sin(angle * 0.5);
1840         double cos = Math.cosFromSin(sin, angle * 0.5);
1841         return set(0, sin, 0, cos);
1842     }
1843 
1844     /**
1845      * Set this quaternion to represent a rotation of the given radians about the z axis.
1846      * 
1847      * @param angle
1848      *              the angle in radians to rotate about the z axis
1849      * @return this
1850      */
1851     ref public Quaterniond rotationZ(double angle) return {
1852         double sin = Math.sin(angle * 0.5);
1853         double cos = Math.cosFromSin(sin, angle * 0.5);
1854         return set(0, 0, sin, cos);
1855     }
1856 
1857     /**
1858      * Apply a rotation to <code>this</code> that rotates the <code>fromDir</code> vector to point along <code>toDir</code>.
1859      * <p>
1860      * Since there can be multiple possible rotations, this method chooses the one with the shortest arc.
1861      * <p>
1862      * If <code>Q</code> is <code>this</code> quaternion and <code>R</code> the quaternion representing the 
1863      * specified rotation, then the new quaternion will be <code>Q * R</code>. So when transforming a
1864      * vector <code>v</code> with the new quaternion by using <code>Q * R * v</code>, the
1865      * rotation added by this method will be applied first!
1866      * 
1867      * @see #rotateTo(double, double, double, double, double, double, Quaterniond)
1868      * 
1869      * @param fromDirX
1870      *              the x-coordinate of the direction to rotate into the destination direction
1871      * @param fromDirY
1872      *              the y-coordinate of the direction to rotate into the destination direction
1873      * @param fromDirZ
1874      *              the z-coordinate of the direction to rotate into the destination direction
1875      * @param toDirX
1876      *              the x-coordinate of the direction to rotate to
1877      * @param toDirY
1878      *              the y-coordinate of the direction to rotate to
1879      * @param toDirZ
1880      *              the z-coordinate of the direction to rotate to
1881      * @return this
1882      */
1883     ref public Quaterniond rotateTo(double fromDirX, double fromDirY, double fromDirZ, double toDirX, double toDirY, double toDirZ) return {
1884         rotateTo(fromDirX, fromDirY, fromDirZ, toDirX, toDirY, toDirZ, this);
1885         return this;
1886     }
1887 
1888     public Quaterniond rotateTo(Vector3d fromDir, Vector3d toDir, ref Quaterniond dest) {
1889         return rotateTo(fromDir.x, fromDir.y, fromDir.z, toDir.x, toDir.y, toDir.z, dest);
1890     }
1891 
1892     /**
1893      * Apply a rotation to <code>this</code> that rotates the <code>fromDir</code> vector to point along <code>toDir</code>.
1894      * <p>
1895      * Because there can be multiple possible rotations, this method chooses the one with the shortest arc.
1896      * <p>
1897      * If <code>Q</code> is <code>this</code> quaternion and <code>R</code> the quaternion representing the 
1898      * specified rotation, then the new quaternion will be <code>Q * R</code>. So when transforming a
1899      * vector <code>v</code> with the new quaternion by using <code>Q * R * v</code>, the
1900      * rotation added by this method will be applied first!
1901      * 
1902      * @see #rotateTo(double, double, double, double, double, double, Quaterniond)
1903      * 
1904      * @param fromDir
1905      *          the starting direction
1906      * @param toDir
1907      *          the destination direction
1908      * @return this
1909      */
1910     ref public Quaterniond rotateTo(Vector3d fromDir, Vector3d toDir) return {
1911         rotateTo(fromDir.x, fromDir.y, fromDir.z, toDir.x, toDir.y, toDir.z, this);
1912         return this;
1913     }
1914 
1915     /**
1916      * Apply a rotation to <code>this</code> quaternion rotating the given radians about the x axis.
1917      * <p>
1918      * If <code>Q</code> is <code>this</code> quaternion and <code>R</code> the quaternion representing the 
1919      * specified rotation, then the new quaternion will be <code>Q * R</code>. So when transforming a
1920      * vector <code>v</code> with the new quaternion by using <code>Q * R * v</code>, the
1921      * rotation added by this method will be applied first!
1922      * 
1923      * @param angle
1924      *              the angle in radians to rotate about the x axis
1925      * @return this
1926      */
1927     ref public Quaterniond rotateX(double angle) return {
1928         rotateX(angle, this);
1929         return this;
1930     }
1931 
1932     public Quaterniond rotateX(double angle, ref Quaterniond dest) {
1933         double sin = Math.sin(angle * 0.5);
1934         double cos = Math.cosFromSin(sin, angle * 0.5);
1935         return dest.set(w * sin + x * cos,
1936                         y * cos + z * sin,
1937                         z * cos - y * sin,
1938                         w * cos - x * sin);
1939     }
1940 
1941     /**
1942      * Apply a rotation to <code>this</code> quaternion rotating the given radians about the y axis.
1943      * <p>
1944      * If <code>Q</code> is <code>this</code> quaternion and <code>R</code> the quaternion representing the 
1945      * specified rotation, then the new quaternion will be <code>Q * R</code>. So when transforming a
1946      * vector <code>v</code> with the new quaternion by using <code>Q * R * v</code>, the
1947      * rotation added by this method will be applied first!
1948      * 
1949      * @param angle
1950      *              the angle in radians to rotate about the y axis
1951      * @return this
1952      */
1953     ref public Quaterniond rotateY(double angle) return {
1954         rotateY(angle, this);
1955         return this;
1956     }
1957 
1958     public Quaterniond rotateY(double angle, ref Quaterniond dest) {
1959         double sin = Math.sin(angle * 0.5);
1960         double cos = Math.cosFromSin(sin, angle * 0.5);
1961         return dest.set(x * cos - z * sin,
1962                         w * sin + y * cos,
1963                         x * sin + z * cos,
1964                         w * cos - y * sin);
1965     }
1966 
1967     /**
1968      * Apply a rotation to <code>this</code> quaternion rotating the given radians about the z axis.
1969      * <p>
1970      * If <code>Q</code> is <code>this</code> quaternion and <code>R</code> the quaternion representing the 
1971      * specified rotation, then the new quaternion will be <code>Q * R</code>. So when transforming a
1972      * vector <code>v</code> with the new quaternion by using <code>Q * R * v</code>, the
1973      * rotation added by this method will be applied first!
1974      * 
1975      * @param angle
1976      *              the angle in radians to rotate about the z axis
1977      * @return this
1978      */
1979     ref public Quaterniond rotateZ(double angle) return {
1980         rotateZ(angle, this);
1981         return this;
1982     }
1983 
1984     public Quaterniond rotateZ(double angle, ref Quaterniond dest) {
1985         double sin = Math.sin(angle * 0.5);
1986         double cos = Math.cosFromSin(sin, angle * 0.5);
1987         return dest.set(x * cos + y * sin,
1988                         y * cos - x * sin,
1989                         w * sin + z * cos,
1990                         w * cos - z * sin);
1991     }
1992 
1993     /**
1994      * Apply a rotation to <code>this</code> quaternion rotating the given radians about the local x axis.
1995      * <p>
1996      * If <code>Q</code> is <code>this</code> quaternion and <code>R</code> the quaternion representing the 
1997      * specified rotation, then the new quaternion will be <code>R * Q</code>. So when transforming a
1998      * vector <code>v</code> with the new quaternion by using <code>R * Q * v</code>, the
1999      * rotation represented by <code>this</code> will be applied first!
2000      * 
2001      * @param angle
2002      *              the angle in radians to rotate about the local x axis
2003      * @return this
2004      */
2005     ref public Quaterniond rotateLocalX(double angle) return {
2006         rotateLocalX(angle, this);
2007         return this;
2008     }
2009 
2010     public Quaterniond rotateLocalX(double angle, ref Quaterniond dest) {
2011         double hangle = angle * 0.5;
2012         double s = Math.sin(hangle);
2013         double c = Math.cosFromSin(s, hangle);
2014         dest.set(c * x + s * w,
2015                  c * y - s * z,
2016                  c * z + s * y,
2017                  c * w - s * x);
2018         return dest;
2019     }
2020 
2021     /**
2022      * Apply a rotation to <code>this</code> quaternion rotating the given radians about the local y axis.
2023      * <p>
2024      * If <code>Q</code> is <code>this</code> quaternion and <code>R</code> the quaternion representing the 
2025      * specified rotation, then the new quaternion will be <code>R * Q</code>. So when transforming a
2026      * vector <code>v</code> with the new quaternion by using <code>R * Q * v</code>, the
2027      * rotation represented by <code>this</code> will be applied first!
2028      * 
2029      * @param angle
2030      *              the angle in radians to rotate about the local y axis
2031      * @return this
2032      */
2033     ref public Quaterniond rotateLocalY(double angle) return {
2034         rotateLocalY(angle, this);
2035         return this;
2036     }
2037 
2038     public Quaterniond rotateLocalY(double angle, ref Quaterniond dest) {
2039         double hangle = angle * 0.5;
2040         double s = Math.sin(hangle);
2041         double c = Math.cosFromSin(s, hangle);
2042         dest.set(c * x + s * z,
2043                  c * y + s * w,
2044                  c * z - s * x,
2045                  c * w - s * y);
2046         return dest;
2047     }
2048 
2049     /**
2050      * Apply a rotation to <code>this</code> quaternion rotating the given radians about the local z axis.
2051      * <p>
2052      * If <code>Q</code> is <code>this</code> quaternion and <code>R</code> the quaternion representing the 
2053      * specified rotation, then the new quaternion will be <code>R * Q</code>. So when transforming a
2054      * vector <code>v</code> with the new quaternion by using <code>R * Q * v</code>, the
2055      * rotation represented by <code>this</code> will be applied first!
2056      * 
2057      * @param angle
2058      *              the angle in radians to rotate about the local z axis
2059      * @return this
2060      */
2061     ref public Quaterniond rotateLocalZ(double angle) return {
2062         rotateLocalZ(angle, this);
2063         return this;
2064     }
2065 
2066     public Quaterniond rotateLocalZ(double angle, ref Quaterniond dest) {
2067         double hangle = angle * 0.5;
2068         double s = Math.sin(hangle);
2069         double c = Math.cosFromSin(s, hangle);
2070         dest.set(c * x - s * y,
2071                  c * y + s * x,
2072                  c * z + s * w,
2073                  c * w - s * z);
2074         return dest;
2075     }
2076 
2077     /**
2078      * Apply a rotation to <code>this</code> quaternion rotating the given radians about the cartesian base unit axes,
2079      * called the euler angles using rotation sequence <code>XYZ</code>.
2080      * <p>
2081      * This method is equivalent to calling: <code>rotateX(angleX).rotateY(angleY).rotateZ(angleZ)</code>
2082      * <p>
2083      * If <code>Q</code> is <code>this</code> quaternion and <code>R</code> the quaternion representing the 
2084      * specified rotation, then the new quaternion will be <code>Q * R</code>. So when transforming a
2085      * vector <code>v</code> with the new quaternion by using <code>Q * R * v</code>, the
2086      * rotation added by this method will be applied first!
2087      * 
2088      * @param angleX
2089      *              the angle in radians to rotate about the x axis
2090      * @param angleY
2091      *              the angle in radians to rotate about the y axis
2092      * @param angleZ
2093      *              the angle in radians to rotate about the z axis
2094      * @return this
2095      */
2096     ref public Quaterniond rotateXYZ(double angleX, double angleY, double angleZ) return {
2097         rotateXYZ(angleX, angleY, angleZ, this);
2098         return this;
2099     }
2100 
2101     public Quaterniond rotateXYZ(double angleX, double angleY, double angleZ, ref Quaterniond dest) {
2102         double sx =  Math.sin(angleX * 0.5);
2103         double cx =  Math.cosFromSin(sx, angleX * 0.5);
2104         double sy =  Math.sin(angleY * 0.5);
2105         double cy =  Math.cosFromSin(sy, angleY * 0.5);
2106         double sz =  Math.sin(angleZ * 0.5);
2107         double cz =  Math.cosFromSin(sz, angleZ * 0.5);
2108 
2109         double cycz = cy * cz;
2110         double sysz = sy * sz;
2111         double sycz = sy * cz;
2112         double cysz = cy * sz;
2113         double w = cx*cycz - sx*sysz;
2114         double x = sx*cycz + cx*sysz;
2115         double y = cx*sycz - sx*cysz;
2116         double z = cx*cysz + sx*sycz;
2117         // right-multiply
2118         return dest.set(Math.fma(this.w, x, Math.fma(this.x, w, Math.fma(this.y, z, -this.z * y))),
2119                         Math.fma(this.w, y, Math.fma(-this.x, z, Math.fma(this.y, w, this.z * x))),
2120                         Math.fma(this.w, z, Math.fma(this.x, y, Math.fma(-this.y, x, this.z * w))),
2121                         Math.fma(this.w, w, Math.fma(-this.x, x, Math.fma(-this.y, y, -this.z * z))));
2122     }
2123 
2124     /**
2125      * Apply a rotation to <code>this</code> quaternion rotating the given radians about the cartesian base unit axes,
2126      * called the euler angles, using the rotation sequence <code>ZYX</code>.
2127      * <p>
2128      * This method is equivalent to calling: <code>rotateZ(angleZ).rotateY(angleY).rotateX(angleX)</code>
2129      * <p>
2130      * If <code>Q</code> is <code>this</code> quaternion and <code>R</code> the quaternion representing the 
2131      * specified rotation, then the new quaternion will be <code>Q * R</code>. So when transforming a
2132      * vector <code>v</code> with the new quaternion by using <code>Q * R * v</code>, the
2133      * rotation added by this method will be applied first!
2134      * 
2135      * @param angleZ
2136      *              the angle in radians to rotate about the z axis
2137      * @param angleY
2138      *              the angle in radians to rotate about the y axis
2139      * @param angleX
2140      *              the angle in radians to rotate about the x axis
2141      * @return this
2142      */
2143     ref public Quaterniond rotateZYX(double angleZ, double angleY, double angleX) return {
2144         rotateZYX(angleZ, angleY, angleX, this);
2145         return this;
2146     }
2147 
2148     public Quaterniond rotateZYX(double angleZ, double angleY, double angleX, ref Quaterniond dest) {
2149         double sx =  Math.sin(angleX * 0.5);
2150         double cx =  Math.cosFromSin(sx, angleX * 0.5);
2151         double sy =  Math.sin(angleY * 0.5);
2152         double cy =  Math.cosFromSin(sy, angleY * 0.5);
2153         double sz =  Math.sin(angleZ * 0.5);
2154         double cz =  Math.cosFromSin(sz, angleZ * 0.5);
2155 
2156         double cycz = cy * cz;
2157         double sysz = sy * sz;
2158         double sycz = sy * cz;
2159         double cysz = cy * sz;
2160         double w = cx*cycz + sx*sysz;
2161         double x = sx*cycz - cx*sysz;
2162         double y = cx*sycz + sx*cysz;
2163         double z = cx*cysz - sx*sycz;
2164         // right-multiply
2165         return dest.set(Math.fma(this.w, x, Math.fma(this.x, w, Math.fma(this.y, z, -this.z * y))),
2166                         Math.fma(this.w, y, Math.fma(-this.x, z, Math.fma(this.y, w, this.z * x))),
2167                         Math.fma(this.w, z, Math.fma(this.x, y, Math.fma(-this.y, x, this.z * w))),
2168                         Math.fma(this.w, w, Math.fma(-this.x, x, Math.fma(-this.y, y, -this.z * z))));
2169     }
2170 
2171     /**
2172      * Apply a rotation to <code>this</code> quaternion rotating the given radians about the cartesian base unit axes,
2173      * called the euler angles, using the rotation sequence <code>YXZ</code>.
2174      * <p>
2175      * This method is equivalent to calling: <code>rotateY(angleY).rotateX(angleX).rotateZ(angleZ)</code>
2176      * <p>
2177      * If <code>Q</code> is <code>this</code> quaternion and <code>R</code> the quaternion representing the 
2178      * specified rotation, then the new quaternion will be <code>Q * R</code>. So when transforming a
2179      * vector <code>v</code> with the new quaternion by using <code>Q * R * v</code>, the
2180      * rotation added by this method will be applied first!
2181      * 
2182      * @param angleY
2183      *              the angle in radians to rotate about the y axis
2184      * @param angleX
2185      *              the angle in radians to rotate about the x axis
2186      * @param angleZ
2187      *              the angle in radians to rotate about the z axis
2188      * @return this
2189      */
2190     ref public Quaterniond rotateYXZ(double angleY, double angleX, double angleZ) return {
2191         rotateYXZ(angleY, angleX, angleZ, this);
2192         return this;
2193     }
2194 
2195     public Quaterniond rotateYXZ(double angleY, double angleX, double angleZ, ref Quaterniond dest) {
2196         double sx =  Math.sin(angleX * 0.5);
2197         double cx =  Math.cosFromSin(sx, angleX * 0.5);
2198         double sy =  Math.sin(angleY * 0.5);
2199         double cy =  Math.cosFromSin(sy, angleY * 0.5);
2200         double sz =  Math.sin(angleZ * 0.5);
2201         double cz =  Math.cosFromSin(sz, angleZ * 0.5);
2202 
2203         double yx = cy * sx;
2204         double yy = sy * cx;
2205         double yz = sy * sx;
2206         double yw = cy * cx;
2207         double x = yx * cz + yy * sz;
2208         double y = yy * cz - yx * sz;
2209         double z = yw * sz - yz * cz;
2210         double w = yw * cz + yz * sz;
2211         // right-multiply
2212         return dest.set(Math.fma(this.w, x, Math.fma(this.x, w, Math.fma(this.y, z, -this.z * y))),
2213                         Math.fma(this.w, y, Math.fma(-this.x, z, Math.fma(this.y, w, this.z * x))),
2214                         Math.fma(this.w, z, Math.fma(this.x, y, Math.fma(-this.y, x, this.z * w))),
2215                         Math.fma(this.w, w, Math.fma(-this.x, x, Math.fma(-this.y, y, -this.z * z))));
2216     }
2217 
2218     public Vector3d getEulerAnglesXYZ(Vector3d eulerAngles) {
2219         eulerAngles.x = Math.atan2(x * w - y * z, 0.5 - x * x - y * y);
2220         eulerAngles.y = Math.safeAsin(2.0 * (x * z + y * w));
2221         eulerAngles.z = Math.atan2(z * w - x * y, 0.5 - y * y - z * z);
2222         return eulerAngles;
2223     }
2224 
2225     public Vector3d getEulerAnglesZYX(Vector3d eulerAngles) {
2226         eulerAngles.x = Math.atan2(y * z + w * x, 0.5 - x * x + y * y);
2227         eulerAngles.y = Math.safeAsin(-2.0 * (x * z - w * y));
2228         eulerAngles.z = Math.atan2(x * y + w * z, 0.5 - y * y - z * z);
2229         return eulerAngles;
2230     }
2231 
2232     public Vector3d getEulerAnglesZXY(Vector3d eulerAngles) {
2233         eulerAngles.x = Math.safeAsin(2.0 * (w * x + y * z));
2234         eulerAngles.y = Math.atan2(w * y - x * z, 0.5 - y * y - x * x);
2235         eulerAngles.z = Math.atan2(w * z - x * y, 0.5 - z * z - x * x);
2236         return eulerAngles;
2237     }
2238 
2239     public Vector3d getEulerAnglesYXZ(Vector3d eulerAngles) {
2240         eulerAngles.x = Math.safeAsin(-2.0 * (y * z - w * x));
2241         eulerAngles.y = Math.atan2(x * z + y * w, 0.5 - y * y - x * x);
2242         eulerAngles.z = Math.atan2(y * x + w * z, 0.5 - x * x - z * z);
2243         return eulerAngles;
2244     }
2245 
2246     public Quaterniond rotateAxis(double angle, double axisX, double axisY, double axisZ, ref Quaterniond dest) {
2247         double hangle = angle / 2.0;
2248         double sinAngle = Math.sin(hangle);
2249         double invVLength = Math.invsqrt(Math.fma(axisX, axisX, Math.fma(axisY, axisY, axisZ * axisZ)));
2250         double rx = axisX * invVLength * sinAngle;
2251         double ry = axisY * invVLength * sinAngle;
2252         double rz = axisZ * invVLength * sinAngle;
2253         double rw = Math.cosFromSin(sinAngle, hangle);
2254         return dest.set(Math.fma(this.w, rx, Math.fma(this.x, rw, Math.fma(this.y, rz, -this.z * ry))),
2255                         Math.fma(this.w, ry, Math.fma(-this.x, rz, Math.fma(this.y, rw, this.z * rx))),
2256                         Math.fma(this.w, rz, Math.fma(this.x, ry, Math.fma(-this.y, rx, this.z * rw))),
2257                         Math.fma(this.w, rw, Math.fma(-this.x, rx, Math.fma(-this.y, ry, -this.z * rz))));
2258     }
2259 
2260     public Quaterniond rotateAxis(double angle, Vector3d axis, ref Quaterniond dest) {
2261         return rotateAxis(angle, axis.x, axis.y, axis.z, dest);
2262     }
2263 
2264     /**
2265      * Apply a rotation to <code>this</code> quaternion rotating the given radians about the specified axis.
2266      * <p>
2267      * If <code>Q</code> is <code>this</code> quaternion and <code>R</code> the quaternion representing the 
2268      * specified rotation, then the new quaternion will be <code>Q * R</code>. So when transforming a
2269      * vector <code>v</code> with the new quaternion by using <code>Q * R * v</code>, the
2270      * rotation added by this method will be applied first!
2271      * 
2272      * @see #rotateAxis(double, double, double, double, Quaterniond)
2273      * 
2274      * @param angle
2275      *              the angle in radians to rotate about the specified axis
2276      * @param axis
2277      *              the rotation axis
2278      * @return this
2279      */
2280     ref public Quaterniond rotateAxis(double angle, Vector3d axis) return {
2281         rotateAxis(angle, axis.x, axis.y, axis.z, this);
2282         return this;
2283     }
2284 
2285     /**
2286      * Apply a rotation to <code>this</code> quaternion rotating the given radians about the specified axis.
2287      * <p>
2288      * If <code>Q</code> is <code>this</code> quaternion and <code>R</code> the quaternion representing the 
2289      * specified rotation, then the new quaternion will be <code>Q * R</code>. So when transforming a
2290      * vector <code>v</code> with the new quaternion by using <code>Q * R * v</code>, the
2291      * rotation added by this method will be applied first!
2292      * 
2293      * @see #rotateAxis(double, double, double, double, Quaterniond)
2294      * 
2295      * @param angle
2296      *              the angle in radians to rotate about the specified axis
2297      * @param axisX
2298      *              the x coordinate of the rotation axis
2299      * @param axisY
2300      *              the y coordinate of the rotation axis
2301      * @param axisZ
2302      *              the z coordinate of the rotation axis
2303      * @return this
2304      */
2305     ref public Quaterniond rotateAxis(double angle, double axisX, double axisY, double axisZ) return {
2306         rotateAxis(angle, axisX, axisY, axisZ, this);
2307         return this;
2308     }
2309 
2310     public Vector3d positiveX(Vector3d dir) {
2311         double invNorm = 1.0 / lengthSquared();
2312         double nx = -x * invNorm;
2313         double ny = -y * invNorm;
2314         double nz = -z * invNorm;
2315         double nw =  w * invNorm;
2316         double dy = ny + ny;
2317         double dz = nz + nz;
2318         dir.x = -ny * dy - nz * dz + 1.0;
2319         dir.y =  nx * dy + nw * dz;
2320         dir.z =  nx * dz - nw * dy;
2321         return dir;
2322     }
2323 
2324     public Vector3d normalizedPositiveX(Vector3d dir) {
2325         double dy = y + y;
2326         double dz = z + z;
2327         dir.x = -y * dy - z * dz + 1.0;
2328         dir.y =  x * dy - w * dz;
2329         dir.z =  x * dz + w * dy;
2330         return dir;
2331     }
2332 
2333     public Vector3d positiveY(Vector3d dir) {
2334         double invNorm = 1.0 / lengthSquared();
2335         double nx = -x * invNorm;
2336         double ny = -y * invNorm;
2337         double nz = -z * invNorm;
2338         double nw =  w * invNorm;
2339         double dx = nx + nx;
2340         double dy = ny + ny;
2341         double dz = nz + nz;
2342         dir.x =  nx * dy - nw * dz;
2343         dir.y = -nx * dx - nz * dz + 1.0;
2344         dir.z =  ny * dz + nw * dx;
2345         return dir;
2346     }
2347 
2348     public Vector3d normalizedPositiveY(Vector3d dir) {
2349         double dx = x + x;
2350         double dy = y + y;
2351         double dz = z + z;
2352         dir.x =  x * dy + w * dz;
2353         dir.y = -x * dx - z * dz + 1.0;
2354         dir.z =  y * dz - w * dx;
2355         return dir;
2356     }
2357 
2358     public Vector3d positiveZ(Vector3d dir) {
2359         double invNorm = 1.0 / lengthSquared();
2360         double nx = -x * invNorm;
2361         double ny = -y * invNorm;
2362         double nz = -z * invNorm;
2363         double nw =  w * invNorm;
2364         double dx = nx + nx;
2365         double dy = ny + ny;
2366         double dz = nz + nz;
2367         dir.x =  nx * dz + nw * dy;
2368         dir.y =  ny * dz - nw * dx;
2369         dir.z = -nx * dx - ny * dy + 1.0;
2370         return dir;
2371     }
2372 
2373     public Vector3d normalizedPositiveZ(Vector3d dir) {
2374         double dx = x + x;
2375         double dy = y + y;
2376         double dz = z + z;
2377         dir.x =  x * dz - w * dy;
2378         dir.y =  y * dz + w * dx;
2379         dir.z = -x * dx - y * dy + 1.0;
2380         return dir;
2381     }
2382 
2383     /**
2384      * Conjugate <code>this</code> by the given quaternion <code>q</code> by computing <code>q * this * q^-1</code>.
2385      * 
2386      * @param q
2387      *          the {@link Quaterniond} to conjugate <code>this</code> by
2388      * @return this
2389      */
2390     ref public Quaterniond conjugateBy(Quaterniond q) return {
2391         conjugateBy(q, this);
2392         return this;
2393     }
2394 
2395     /**
2396      * Conjugate <code>this</code> by the given quaternion <code>q</code> by computing <code>q * this * q^-1</code>
2397      * and store the result into <code>dest</code>.
2398      * 
2399      * @param q
2400      *          the {@link Quaterniond} to conjugate <code>this</code> by
2401      * @param dest
2402      *          will hold the result
2403      * @return dest
2404      */
2405     public Quaterniond conjugateBy(Quaterniond q, ref Quaterniond dest) {
2406         double invNorm = 1.0 / q.lengthSquared();
2407         double qix = -q.x * invNorm, qiy = -q.y * invNorm, qiz = -q.z * invNorm, qiw = q.w * invNorm;
2408         double qpx = Math.fma(q.w, x, Math.fma(q.x, w, Math.fma(q.y, z, -q.z * y)));
2409         double qpy = Math.fma(q.w, y, Math.fma(-q.x, z, Math.fma(q.y, w, q.z * x)));
2410         double qpz = Math.fma(q.w, z, Math.fma(q.x, y, Math.fma(-q.y, x, q.z * w)));
2411         double qpw = Math.fma(q.w, w, Math.fma(-q.x, x, Math.fma(-q.y, y, -q.z * z)));
2412         return dest.set(Math.fma(qpw, qix, Math.fma(qpx, qiw, Math.fma(qpy, qiz, -qpz * qiy))),
2413                         Math.fma(qpw, qiy, Math.fma(-qpx, qiz, Math.fma(qpy, qiw, qpz * qix))),
2414                         Math.fma(qpw, qiz, Math.fma(qpx, qiy, Math.fma(-qpy, qix, qpz * qiw))),
2415                         Math.fma(qpw, qiw, Math.fma(-qpx, qix, Math.fma(-qpy, qiy, -qpz * qiz))));
2416     }
2417 
2418     public bool isFinite() {
2419         return Math.isFinite(x) && Math.isFinite(y) && Math.isFinite(z) && Math.isFinite(w);
2420     }
2421 
2422     public bool equals(Quaterniond q, double delta) {
2423         if (this == q)
2424             return true;
2425         if (!Math.equals(x, q.x, delta))
2426             return false;
2427         if (!Math.equals(y, q.y, delta))
2428             return false;
2429         if (!Math.equals(z, q.z, delta))
2430             return false;
2431         if (!Math.equals(w, q.w, delta))
2432             return false;
2433         return true;
2434     }
2435 
2436     public bool equals(double x, double y, double z, double w) {
2437         if (Math.doubleToLongBits(this.x) != Math.doubleToLongBits(x))
2438             return false;
2439         if (Math.doubleToLongBits(this.y) != Math.doubleToLongBits(y))
2440             return false;
2441         if (Math.doubleToLongBits(this.z) != Math.doubleToLongBits(z))
2442             return false;
2443         if (Math.doubleToLongBits(this.w) != Math.doubleToLongBits(w))
2444             return false;
2445         return true;
2446     }
2447 
2448 }