2 Copyright (C) 2001, 2006 United States Government
3 as represented by the Administrator of the
4 National Aeronautics and Space Administration.
7 package gov
.nasa
.worldwind
.geom
;
9 import gov
.nasa
.worldwind
.util
.Logging
;
12 * @author Chris Maxwell
13 * @version $Id: Quaternion.java 2471 2007-07-31 21:50:57Z tgaskins $
15 public class Quaternion
17 // Multiplicative identity quaternion.
18 public static final Quaternion IDENTITY
= new Quaternion(0, 0, 0, 1);
20 public final double x
;
21 public final double y
;
22 public final double z
;
23 public final double w
;
25 // 4 values in a quaternion.
26 private static final int NUM_ELEMENTS
= 4;
27 // Cached computations.
28 private int hashCode
= -1;
29 private double[] quatArray
= null;
30 private double length
= -1;
31 private double lengthSquared
= -1;
32 private Quaternion normalized
= null;
33 private Quaternion inverse
= null;
35 public Quaternion(double x
, double y
, double z
, double w
)
43 public final boolean equals(Object obj
)
47 if (obj
== null || obj
.getClass() != this.getClass())
50 Quaternion that
= (Quaternion
) obj
;
51 return (this.x
== that
.x
)
54 && (this.w
== that
.w
);
57 public final int hashCode()
59 if (this.hashCode
< 0)
63 tmp
= Double
.doubleToLongBits(this.x
);
64 result
= (int) (tmp ^
(tmp
>>> 32));
65 tmp
= Double
.doubleToLongBits(this.y
);
66 result
= 31 * result
+ (int) (tmp ^
(tmp
>>> 32));
67 tmp
= Double
.doubleToLongBits(this.z
);
68 result
= 31 * result
+ (int) (tmp ^
(tmp
>>> 32));
69 tmp
= Double
.doubleToLongBits(this.w
);
70 result
= 31 * result
+ (int) (tmp ^
(tmp
>>> 32));
71 this.hashCode
= result
;
76 public static Quaternion
fromArray(double[] compArray
, int offset
)
78 if (compArray
== null)
80 String msg
= Logging
.getMessage("nullValue.ArrayIsNull");
81 Logging
.logger().severe(msg
);
82 throw new IllegalArgumentException(msg
);
84 if ((compArray
.length
+ offset
) < NUM_ELEMENTS
)
86 String msg
= Logging
.getMessage("generic.ArrayInvalidLength", compArray
.length
+ offset
);
87 Logging
.logger().severe(msg
);
88 throw new IllegalArgumentException(msg
);
91 return new Quaternion(
92 compArray
[0 + offset
],
93 compArray
[1 + offset
],
94 compArray
[2 + offset
],
95 compArray
[3 + offset
]);
98 public final double[] toArray(double[] compArray
, int offset
)
100 this.ensureQuatArray();
101 if (compArray
== null)
102 compArray
= new double[NUM_ELEMENTS
+ offset
];
103 System
.arraycopy(this.quatArray
, 0, compArray
, offset
, NUM_ELEMENTS
);
107 private void ensureQuatArray()
109 if (this.quatArray
!= null)
112 this.quatArray
= new double[NUM_ELEMENTS
];
113 this.quatArray
[0] = this.x
;
114 this.quatArray
[1] = this.y
;
115 this.quatArray
[2] = this.z
;
116 this.quatArray
[3] = this.w
;
119 public final String
toString()
121 StringBuilder sb
= new StringBuilder();
123 sb
.append(this.x
).append(", ");
124 sb
.append(this.y
).append(", ");
125 sb
.append(this.z
).append(", ");
128 return sb
.toString();
131 public final double getX()
136 public final double getY()
141 public final double getZ()
146 public final double getW()
151 public final double x()
156 public final double y()
161 public final double z()
166 public final double w()
171 // ============== Factory Functions ======================= //
172 // ============== Factory Functions ======================= //
173 // ============== Factory Functions ======================= //
175 public static Quaternion
fromAxisAngle(Angle angle
, Vec4 axis
)
179 String msg
= Logging
.getMessage("nullValue.AngleIsNull");
180 Logging
.logger().severe(msg
);
181 throw new IllegalArgumentException(msg
);
185 String msg
= Logging
.getMessage("nullValue.Vec4IsNull");
186 Logging
.logger().severe(msg
);
187 throw new IllegalArgumentException(msg
);
190 return fromAxisAngle(angle
, axis
.x
, axis
.y
, axis
.z
, true);
193 public static Quaternion
fromAxisAngle(Angle angle
, double axisX
, double axisY
, double axisZ
)
197 String msg
= Logging
.getMessage("nullValue.AngleIsNull");
198 Logging
.logger().severe(msg
);
199 throw new IllegalArgumentException(msg
);
201 return fromAxisAngle(angle
, axisX
, axisY
, axisZ
, true);
204 private static Quaternion
fromAxisAngle(Angle angle
, double axisX
, double axisY
, double axisZ
, boolean normalize
)
208 String msg
= Logging
.getMessage("nullValue.AngleIsNull");
209 Logging
.logger().severe(msg
);
210 throw new IllegalArgumentException(msg
);
215 double length
= Math
.sqrt((axisX
* axisX
) + (axisY
* axisY
) + (axisZ
* axisZ
));
216 if (!isZero(length
) && (length
!= 1.0))
224 double s
= angle
.sinHalfAngle();
225 double c
= angle
.cosHalfAngle();
226 return new Quaternion(axisX
* s
, axisY
* s
, axisZ
* s
, c
);
229 public static Quaternion
fromMatrix(Matrix matrix
)
233 String msg
= Logging
.getMessage("nullValue.MatrixIsNull");
234 Logging
.logger().severe(msg
);
235 throw new IllegalArgumentException(msg
);
238 double t
= 1.0 + matrix
.m11
+ matrix
.m22
+ matrix
.m33
;
241 final double EPSILON
= 0.00000001;
243 s
= 2.0 * Math
.sqrt(t
);
244 x
= (matrix
.m32
- matrix
.m23
) / s
;
245 y
= (matrix
.m13
- matrix
.m31
) / s
;
246 z
= (matrix
.m21
- matrix
.m12
) / s
;
248 } else if ((matrix
.m11
> matrix
.m22
) && (matrix
.m11
> matrix
.m33
)) {
249 s
= 2.0 * Math
.sqrt(1.0 + matrix
.m11
- matrix
.m22
- matrix
.m33
);
251 y
= (matrix
.m21
+ matrix
.m12
) / s
;
252 z
= (matrix
.m13
+ matrix
.m31
) / s
;
253 w
= (matrix
.m32
- matrix
.m23
) / s
;
254 } else if (matrix
.m22
> matrix
.m33
) {
255 s
= 2.0 * Math
.sqrt(1.0 + matrix
.m22
- matrix
.m11
- matrix
.m33
);
256 x
= (matrix
.m21
+ matrix
.m12
) / s
;
258 z
= (matrix
.m32
+ matrix
.m23
) / s
;
259 w
= (matrix
.m13
- matrix
.m31
) / s
;
261 s
= 2.0 * Math
.sqrt(1.0 + matrix
.m33
- matrix
.m11
- matrix
.m22
);
262 x
= (matrix
.m13
+ matrix
.m31
) / s
;
263 y
= (matrix
.m32
+ matrix
.m23
) / s
;
265 w
= (matrix
.m21
- matrix
.m12
) / s
;
267 return new Quaternion(x
, y
, z
, w
);
270 public static Quaternion
fromRotationYPR(Angle yaw
, Angle pitch
, Angle roll
)
272 if ((yaw
== null) || (pitch
== null) || (roll
== null))
274 String msg
= Logging
.getMessage("nullValue.AngleIsNull");
275 Logging
.logger().severe(msg
);
276 throw new IllegalArgumentException(msg
);
279 double cy
= yaw
.cosHalfAngle();
280 double cp
= pitch
.cosHalfAngle();
281 double cr
= roll
.cosHalfAngle();
282 double sy
= yaw
.sinHalfAngle();
283 double sp
= pitch
.sinHalfAngle();
284 double sr
= roll
.sinHalfAngle();
286 double qw
= (cy
* cp
* cr
) + (sy
* sp
* sr
);
287 double qx
= (sy
* cp
* cr
) - (cy
* sp
* sr
);
288 double qy
= (cy
* sp
* cr
) + (sy
* cp
* sr
);
289 double qz
= (cy
* cp
* sr
) - (sy
* sp
* cr
);
291 return new Quaternion(qx
, qy
, qz
, qw
);
294 // ============== Arithmetic Functions ======================= //
295 // ============== Arithmetic Functions ======================= //
296 // ============== Arithmetic Functions ======================= //
298 public final Quaternion
add(Quaternion quaternion
)
300 if (quaternion
== null)
302 String msg
= Logging
.getMessage("nullValue.QuaternionIsNull");
303 Logging
.logger().severe(msg
);
304 throw new IllegalArgumentException(msg
);
307 return new Quaternion(
308 this.x
+ quaternion
.x
,
309 this.y
+ quaternion
.y
,
310 this.z
+ quaternion
.z
,
311 this.w
+ quaternion
.w
);
314 public final Quaternion
subtract(Quaternion quaternion
)
316 if (quaternion
== null)
318 String msg
= Logging
.getMessage("nullValue.QuaternionIsNull");
319 Logging
.logger().severe(msg
);
320 throw new IllegalArgumentException(msg
);
323 return new Quaternion(
324 this.x
- quaternion
.x
,
325 this.y
- quaternion
.y
,
326 this.z
- quaternion
.z
,
327 this.w
- quaternion
.w
);
330 public final Quaternion
multiplyComponents(double value
)
332 return new Quaternion(
339 public final Quaternion
multiply(Quaternion quaternion
)
341 if (quaternion
== null)
343 String msg
= Logging
.getMessage("nullValue.QuaternionIsNull");
344 Logging
.logger().severe(msg
);
345 throw new IllegalArgumentException(msg
);
348 return new Quaternion(
349 (this.w
* quaternion
.x
) + (this.x
* quaternion
.w
) + (this.y
* quaternion
.z
) - (this.z
* quaternion
.y
),
350 (this.w
* quaternion
.y
) + (this.y
* quaternion
.w
) + (this.z
* quaternion
.x
) - (this.x
* quaternion
.z
),
351 (this.w
* quaternion
.z
) + (this.z
* quaternion
.w
) + (this.x
* quaternion
.y
) - (this.y
* quaternion
.x
),
352 (this.w
* quaternion
.w
) - (this.x
* quaternion
.x
) - (this.y
* quaternion
.y
) - (this.z
* quaternion
.z
));
355 public final Quaternion
divideComponents(double value
)
359 String msg
= Logging
.getMessage("generic.ArgumentOutOfRange", value
);
360 Logging
.logger().severe(msg
);
361 throw new IllegalArgumentException(msg
);
364 return new Quaternion(
371 public final Quaternion
divideComponents(Quaternion quaternion
)
373 if (quaternion
== null)
375 String msg
= Logging
.getMessage("nullValue.QuaternionIsNull");
376 Logging
.logger().severe(msg
);
377 throw new IllegalArgumentException(msg
);
380 return new Quaternion(
381 this.x
/ quaternion
.x
,
382 this.y
/ quaternion
.y
,
383 this.z
/ quaternion
.z
,
384 this.w
/ quaternion
.w
);
387 public final Quaternion
getConjugate()
389 return new Quaternion(
396 public final Quaternion
getNegative()
398 return new Quaternion(
405 // ============== Geometric Functions ======================= //
406 // ============== Geometric Functions ======================= //
407 // ============== Geometric Functions ======================= //
409 public final double getLength()
415 private void ensureLength()
417 if (this.length
>= 0.0)
420 // Compute cached length.
421 this.length
= Math
.sqrt(this.getLengthSquared());
424 public final double getLengthSquared()
426 this.ensureLengthSquared();
427 return this.lengthSquared
;
430 private void ensureLengthSquared()
432 if (this.lengthSquared
>= 0.0)
435 // Compute cached length.
436 this.lengthSquared
= (this.x
* this.x
)
442 public final Quaternion
normalize()
444 this.ensureNormalized();
445 return this.normalized
;
448 private void ensureNormalized()
450 if (this.normalized
!= null)
453 // Compute cached normalization.
454 double length
= this.getLength();
455 // Vector has zero length.
458 this.normalized
= this;
462 this.normalized
= new Quaternion(
470 public final double dot(Quaternion quaternion
)
472 if (quaternion
== null)
474 String msg
= Logging
.getMessage("nullValue.QuaternionIsNull");
475 Logging
.logger().severe(msg
);
476 throw new IllegalArgumentException(msg
);
479 return (this.x
* quaternion
.x
) + (this.y
* quaternion
.y
) + (this.z
* quaternion
.z
) + (this.w
* quaternion
.w
);
482 public final Quaternion
getInverse()
484 this.ensureInverse();
488 private void ensureInverse()
490 if (this.inverse
!= null)
493 double length
= this.getLength();
494 this.inverse
= new Quaternion(
495 (0.0 - this.x
) / length
,
496 (0.0 - this.y
) / length
,
497 (0.0 - this.z
) / length
,
501 // ============== Mixing Functions ======================= //
502 // ============== Mixing Functions ======================= //
503 // ============== Mixing Functions ======================= //
505 public static Quaternion
mix(double amount
, Quaternion value1
, Quaternion value2
)
507 if ((value1
== null) || (value2
== null))
509 String msg
= Logging
.getMessage("nullValue.QuaternionIsNull");
510 Logging
.logger().severe(msg
);
511 throw new IllegalArgumentException(msg
);
516 else if (amount
> 1.0)
519 double t1
= 1.0 - amount
;
520 return new Quaternion(
521 (value1
.x
* t1
) + (value2
.x
* amount
),
522 (value1
.y
* t1
) + (value2
.y
* amount
),
523 (value1
.z
* t1
) + (value2
.z
* amount
),
524 (value1
.w
* t1
) + (value2
.w
* amount
));
527 public static Quaternion
slerp(double amount
, Quaternion value1
, Quaternion value2
)
529 if ((value1
== null) || (value2
== null))
531 String msg
= Logging
.getMessage("nullValue.QuaternionIsNull");
532 Logging
.logger().severe(msg
);
533 throw new IllegalArgumentException(msg
);
538 else if (amount
> 1.0)
541 double dot
= value1
.dot(value2
);
542 double x2
, y2
, z2
, w2
;
561 final double EPSILON
= 0.0001;
562 if ((1.0 - dot
) > EPSILON
) // standard case (slerp)
564 double angle
= Math
.acos(dot
);
565 double sinAngle
= Math
.sin(angle
);
566 t1
= Math
.sin((1.0 - amount
) * angle
) / sinAngle
;
567 t2
= Math
.sin(amount
* angle
) / sinAngle
;
575 return new Quaternion(
576 (value1
.x
* t1
) + (x2
* t2
),
577 (value1
.y
* t1
) + (y2
* t2
),
578 (value1
.z
* t1
) + (z2
* t2
),
579 (value1
.w
* t1
) + (w2
* t2
));
582 // ============== Accessor Functions ======================= //
583 // ============== Accessor Functions ======================= //
584 // ============== Accessor Functions ======================= //
586 public final Angle
getAngle()
590 double length
= this.getLength();
591 if (!isZero(length
) && (length
!= 1.0))
594 double radians
= 2.0 * Math
.acos(w
);
595 if (Double
.isNaN(radians
))
598 return Angle
.fromRadians(radians
);
601 public final Vec4
getAxis()
607 double length
= this.getLength();
608 if (!isZero(length
) && (length
!= 1.0))
615 double vecLength
= Math
.sqrt((x
* x
) + (y
* y
) + (z
* z
));
616 if (!isZero(vecLength
) && (vecLength
!= 1.0))
623 return new Vec4(x
, y
, z
);
626 public final Angle
getRotationX()
628 double radians
= Math
.atan2(
629 2.0 * (this.y
* this.z
+ this.w
* this.x
),
630 (this.w
* this.w
) - (this.x
* this.x
) - (this.y
* this.y
) + (this.z
* this.z
));
631 if (Double
.isNaN(radians
))
634 return Angle
.fromRadians(radians
);
637 public final Angle
getRotationY()
639 double radians
= Math
.asin(-2.0 * (this.x
* this.z
- this.w
* this.y
));
640 if (Double
.isNaN(radians
))
643 return Angle
.fromRadians(radians
);
646 public final Angle
getRotationZ()
648 double radians
= Math
.atan2(
649 2.0 * (this.x
* this.y
+ this.w
* this.z
),
650 (this.w
* this.w
) + (this.x
* this.x
) - (this.y
* this.y
) - (this.z
* this.z
));
651 if (Double
.isNaN(radians
))
654 return Angle
.fromRadians(radians
);
657 // ============== Helper Functions ======================= //
658 // ============== Helper Functions ======================= //
659 // ============== Helper Functions ======================= //
661 private static final Double PositiveZero
= +0.0d
;
663 private static final Double NegativeZero
= -0.0d
;
665 private static boolean isZero(double value
)
667 return (PositiveZero
.compareTo(value
) == 0)
668 || (NegativeZero
.compareTo(value
) == 0);
674 // * @throws IllegalArgumentException if <code>q</code> is null
676 // public static double Norm2(Quaternion q)
680 // String msg = gov.nasa.worldwind.WorldWind.retrieveErrMsg("nullValue.QuaternionIsNull");
681 // gov.nasa.worldwind.WorldWind.logger().logger(Level.SEVERE, msg);
682 // throw new IllegalArgumentException(msg);
684 // return q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w;
690 // * @throws IllegalArgumentException if <code>q</code> is null
692 // public static double Abs(Quaternion q)
696 // String msg = gov.nasa.worldwind.WorldWind.retrieveErrMsg("nullValue.QuaternionIsNull");
697 // gov.nasa.worldwind.WorldWind.logger().logger(Level.SEVERE, msg);
698 // throw new IllegalArgumentException(msg);
700 // return Math.sqrt(Norm2(q));
707 // * @throws IllegalArgumentException if <code>a</code> or <code>b</code> is null
709 // public static Quaternion Divide(Quaternion a, Quaternion b)
711 // if (a == null || b == null)
713 // String msg = gov.nasa.worldwind.WorldWind.retrieveErrMsg("nullValue.QuaternionIsNull");
714 // gov.nasa.worldwind.WorldWind.logger().logger(Level.SEVERE, msg);
715 // throw new IllegalArgumentException(msg);
717 // return Quaternion.Multiply(a, Quaternion.Divide(b.Conjugate(), Abs(b)));
721 // /*****************the below functions have not been certified to work properly ******************/
723 // public Quaternion Ln()
731 // * @throws IllegalArgumentException if <code>q</code> is null
733 // public static Quaternion Ln(Quaternion q)
737 // String msg = gov.nasa.worldwind.WorldWind.retrieveErrMsg("nullValue.QuaternionIsNull");
738 // gov.nasa.worldwind.WorldWind.logger().logger(Level.SEVERE, msg);
739 // throw new IllegalArgumentException(msg);
743 // double s = Math.sqrt(q.x * q.x + q.y * q.y + q.z * q.z);
744 // double om = Math.atan2(s, q.w);
746 // if (Math.abs(s) < epsilon)
751 // return new Quaternion(q.x * t, q.y * t, q.z * t, 0.0f);
757 // * @throws IllegalArgumentException if <code>q</code> is null
759 // public static Quaternion Exp(Quaternion q)
763 // String msg = gov.nasa.worldwind.WorldWind.retrieveErrMsg("nullValue.QuaternionIsNull");
764 // gov.nasa.worldwind.WorldWind.logger().logger(Level.SEVERE, msg);
765 // throw new IllegalArgumentException(msg);
768 // double om = Math.sqrt(q.x * q.x + q.y * q.y + q.z * q.z);
770 // if (Math.abs(om) < epsilon)
773 // sinom = Math.sin(om) / om;
775 // return new Quaternion(q.x * sinom, q.y * sinom, q.z * sinom, Math.cos(om));
778 // public Quaternion Exp()
790 // * @throws IllegalArgumentException if any argument is null
792 // public static Quaternion Squad(
799 // if (q1 == null || a == null || b == null || c == null)
801 // String msg = gov.nasa.worldwind.WorldWind.retrieveErrMsg("nullValue.QuaternionIsNull");
802 // gov.nasa.worldwind.WorldWind.logger().logger(Level.SEVERE, msg);
803 // throw new IllegalArgumentException(msg);
806 // Slerp(q1, c, t), Slerp(a, b, t), 2 * t * (1.0 - t));
809 // // This needs to be accounted for before Squad() is used
810 // public static Quaternion[] SquadSetup(
816 // if(q0 == null || q1 == null || q2 == null || q3 == null)
818 // String msg = gov.nasa.worldwind.WorldWind.retrieveErrMsg("nullValue.QuaternionIsNull");
819 // gov.nasa.worldwind.WorldWind.logger().logger(Level.SEVERE, msg);
820 // throw new IllegalArgumentException(msg);
834 // Quaternion[] ret = new Quaternion[3];
836 // ret[0] = q1 * Exp(-0.25 * (Ln(Exp(q1) * q2) + Ln(Exp(q1) * q0))); // outA
837 // ret[1] = q2 * Exp(-0.25 * (Ln(Exp(q2) * q3) + Ln(Exp(q2) * q1))); // outB
838 // ret[2] = q2; // outC