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
;
10 * @author Chris Maxwell
11 * @version $Id: Quaternion.java 1749 2007-05-06 19:48:14Z tgaskins $
13 public class Quaternion
15 private final double x
;
16 private final double y
;
17 private final double z
;
18 private final double w
;
19 private final static double epsilon
= 0.0001;
21 public Quaternion(double x
, double y
, double z
, double w
)
30 public boolean equals(Object o
)
34 if (o
== null || getClass() != o
.getClass())
37 final gov
.nasa
.worldwind
.geom
.Quaternion quaternion
= (gov
.nasa
.worldwind
.geom
.Quaternion
) o
;
39 if (Double
.compare(quaternion
.w
, w
) != 0)
41 if (Double
.compare(quaternion
.x
, x
) != 0)
43 if (Double
.compare(quaternion
.y
, y
) != 0)
45 if (Double
.compare(quaternion
.z
, z
) != 0)
52 * Generates an integer that is always the same for identical objects, but usually different for different objects.
53 * This method overrides the one in <code>Object</code>.
55 * This method makes comparisons on private fields; overriding implementations should include a call to
56 * <code>super.hashCode()</code>.
58 * @return the hashCode for this <code>Point</code>.
65 temp
= x
!= +0.0d ? Double
.doubleToLongBits(x
) : 0L;
66 result
= (int) (temp ^
(temp
>>> 32));
67 temp
= y
!= +0.0d ? Double
.doubleToLongBits(y
) : 0L;
68 result
= 29 * result
+ (int) (temp ^
(temp
>>> 32));
69 temp
= z
!= +0.0d ? Double
.doubleToLongBits(z
) : 0L;
70 result
= 29 * result
+ (int) (temp ^
(temp
>>> 32));
71 temp
= w
!= +0.0d ? Double
.doubleToLongBits(w
) : 0L;
72 result
= 29 * result
+ (int) (temp ^
(temp
>>> 32));
77 public final String
toString()
79 return "(" + Double
.toString(this.x
) + ", " + Double
.toString(this.y
) + ", " + Double
.toString(
80 this.z
) + ", " + Double
.toString(this.w
) + ")";
83 public static Quaternion
EulerToQuaternion(double yaw
, double pitch
, double roll
)
85 double cy
= Math
.cos(yaw
* 0.5);
86 double cp
= Math
.cos(pitch
* 0.5);
87 double cr
= Math
.cos(roll
* 0.5);
88 double sy
= Math
.sin(yaw
* 0.5);
89 double sp
= Math
.sin(pitch
* 0.5);
90 double sr
= Math
.sin(roll
* 0.5);
92 double qw
= cy
* cp
* cr
+ sy
* sp
* sr
;
93 double qx
= sy
* cp
* cr
- cy
* sp
* sr
;
94 double qy
= cy
* sp
* cr
+ sy
* cp
* sr
;
95 double qz
= cy
* cp
* sr
- sy
* sp
* cr
;
97 return new Quaternion(qx
, qy
, qz
, qw
);
101 * Transforms a rotation in quaternion form to a set of Euler angles
104 * @return The rotation transformed to Euler angles, X=Yaw, Y=Pitch, Z=Roll (radians)
105 * @throws IllegalArgumentException if <code>q</code> is null
107 public static Point
QuaternionToEuler(Quaternion q
)
111 String msg
= gov
.nasa
.worldwind
.WorldWind
.retrieveErrMsg("nullValue.QuaternionIsNull");
112 gov
.nasa
.worldwind
.WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
113 throw new IllegalArgumentException(msg
);
121 double x
= Math
.atan2(2 * (q2
* q3
+ q0
* q1
), (q0
* q0
- q1
* q1
- q2
* q2
+ q3
* q3
));
122 double y
= Math
.asin(-2 * (q1
* q3
- q0
* q2
));
123 double z
= Math
.atan2(2 * (q1
* q2
+ q0
* q3
), (q0
* q0
+ q1
* q1
- q2
* q2
- q3
* q3
));
125 return new Point(x
, y
, z
);
128 public static Quaternion
AxisAngleToQuaternion(Angle angle
, double x
, double y
, double z
)
132 String msg
= gov
.nasa
.worldwind
.WorldWind
.retrieveErrMsg("nullValue.AngleIsNull");
133 gov
.nasa
.worldwind
.WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
134 throw new IllegalArgumentException(msg
);
136 double length
= Math
.sqrt(x
* x
+ y
* y
+ z
* z
);
137 if (length
> 0 && length
!= 1)
143 double sinAngle
= angle
.sinHalfAngle();
144 double cosAngle
= angle
.cosHalfAngle();
145 return new Quaternion(x
* sinAngle
, y
* sinAngle
, z
* sinAngle
, cosAngle
);
148 public static Point
QuaternionToAxisAngle(Quaternion q
)
152 String msg
= gov
.nasa
.worldwind
.WorldWind
.retrieveErrMsg("nullValue.QuaternionIsNull");
153 gov
.nasa
.worldwind
.WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
154 throw new IllegalArgumentException(msg
);
158 double s
= Math
.sqrt(q
.x
* q
.x
+ q
.y
* q
.y
+ q
.x
* q
.z
);
172 double angle
= 2 * Math
.acos(q
.w
);
173 return new Point(x
, y
, z
, angle
);
180 * @throws IllegalArgumentException if <code>a</code> or <code>b</code> is null
182 public static Quaternion
Add(Quaternion a
, Quaternion b
)
184 if (a
== null || b
== null)
186 String msg
= gov
.nasa
.worldwind
.WorldWind
.retrieveErrMsg("nullValue.QuaternionIsNull");
187 gov
.nasa
.worldwind
.WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
188 throw new IllegalArgumentException(msg
);
190 return new Quaternion(a
.x
+ b
.x
, a
.y
+ b
.y
, a
.z
+ b
.z
, a
.w
+ b
.w
);
197 * @throws IllegalArgumentException if <code>a</code> or <code>b</code> is null
199 public static Quaternion
Subtract(Quaternion a
, Quaternion b
)
201 if (a
== null || b
== null)
203 String msg
= gov
.nasa
.worldwind
.WorldWind
.retrieveErrMsg("nullValue.QuaternionIsNull");
204 gov
.nasa
.worldwind
.WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
205 throw new IllegalArgumentException(msg
);
207 return new Quaternion(a
.x
- b
.x
, a
.y
- b
.y
, a
.z
- b
.z
, a
.w
- b
.w
);
214 * @throws IllegalArgumentException if <code>a</code> or <code>b</code> is null
216 public static Quaternion
Multiply(Quaternion a
, Quaternion b
)
218 if (a
== null || b
== null)
220 String msg
= gov
.nasa
.worldwind
.WorldWind
.retrieveErrMsg("nullValue.QuaternionIsNull");
221 gov
.nasa
.worldwind
.WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
222 throw new IllegalArgumentException(msg
);
224 return new Quaternion(
225 a
.w
* b
.x
+ a
.x
* b
.w
+ a
.y
* b
.z
- a
.z
* b
.y
,
226 a
.w
* b
.y
+ a
.y
* b
.w
+ a
.z
* b
.x
- a
.x
* b
.z
,
227 a
.w
* b
.z
+ a
.z
* b
.w
+ a
.x
* b
.y
- a
.y
* b
.x
,
228 a
.w
* b
.w
- a
.x
* b
.x
- a
.y
* b
.y
- a
.z
* b
.z
);
234 * @throws IllegalArgumentException if <code>q</code> is null
236 public static Quaternion
Multiply(double s
, Quaternion q
)
240 String msg
= gov
.nasa
.worldwind
.WorldWind
.retrieveErrMsg("nullValue.QuaternionIsNull");
241 gov
.nasa
.worldwind
.WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
242 throw new IllegalArgumentException(msg
);
244 return new Quaternion(s
* q
.x
, s
* q
.y
, s
* q
.z
, s
* q
.w
);
251 * @throws IllegalArgumentException if <code>q</code> is null
253 public static Quaternion
Multiply(Quaternion q
, double s
)
257 String msg
= gov
.nasa
.worldwind
.WorldWind
.retrieveErrMsg("nullValue.QuaternionIsNull");
258 gov
.nasa
.worldwind
.WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
259 throw new IllegalArgumentException(msg
);
261 return new Quaternion(s
* q
.x
, s
* q
.y
, s
* q
.z
, s
* q
.w
);
265 * equivalent to multiplying by the quaternion (0, v)
270 * @throws IllegalArgumentException if <code>q</code> or <code>v</code> is null
272 public static Quaternion
Multiply(Point v
, Quaternion q
)
276 String msg
= gov
.nasa
.worldwind
.WorldWind
.retrieveErrMsg("nullValue.VectorIsNull");
277 gov
.nasa
.worldwind
.WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
278 throw new IllegalArgumentException(msg
);
282 String msg
= gov
.nasa
.worldwind
.WorldWind
.retrieveErrMsg("nullValue.QuaternionIsNull");
283 gov
.nasa
.worldwind
.WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
284 throw new IllegalArgumentException(msg
);
286 return new Quaternion(
287 v
.x() * q
.w
+ v
.y() * q
.z
- v
.z() * q
.y
,
288 v
.y() * q
.w
+ v
.z() * q
.x
- v
.x() * q
.z
,
289 v
.z() * q
.w
+ v
.x() * q
.y
- v
.y() * q
.x
,
290 -v
.x() * q
.x
- v
.y() * q
.y
- v
.z() * q
.z
);
297 * @throws IllegalArgumentException if <code>q</code> is null
299 public static Quaternion
Divide(Quaternion q
, double s
)
303 String msg
= gov
.nasa
.worldwind
.WorldWind
.retrieveErrMsg("nullValue.QuaternionIsNull");
304 gov
.nasa
.worldwind
.WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
305 throw new IllegalArgumentException(msg
);
307 return Quaternion
.Multiply(q
, (1 / s
));
310 // conjugate operator
311 public Quaternion
Conjugate()
313 return new Quaternion(-x
, -y
, -z
, w
);
319 * @throws IllegalArgumentException if <code>q</code> is null
321 public static double Norm2(Quaternion q
)
325 String msg
= gov
.nasa
.worldwind
.WorldWind
.retrieveErrMsg("nullValue.QuaternionIsNull");
326 gov
.nasa
.worldwind
.WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
327 throw new IllegalArgumentException(msg
);
329 return q
.x
* q
.x
+ q
.y
* q
.y
+ q
.z
* q
.z
+ q
.w
* q
.w
;
335 * @throws IllegalArgumentException if <code>q</code> is null
337 public static double Abs(Quaternion q
)
341 String msg
= gov
.nasa
.worldwind
.WorldWind
.retrieveErrMsg("nullValue.QuaternionIsNull");
342 gov
.nasa
.worldwind
.WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
343 throw new IllegalArgumentException(msg
);
345 return Math
.sqrt(Norm2(q
));
352 * @throws IllegalArgumentException if <code>a</code> or <code>b</code> is null
354 public static Quaternion
Divide(Quaternion a
, Quaternion b
)
356 if (a
== null || b
== null)
358 String msg
= gov
.nasa
.worldwind
.WorldWind
.retrieveErrMsg("nullValue.QuaternionIsNull");
359 gov
.nasa
.worldwind
.WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
360 throw new IllegalArgumentException(msg
);
362 return Quaternion
.Multiply(a
, Quaternion
.Divide(b
.Conjugate(), Abs(b
)));
369 * @throws IllegalArgumentException if <code>a</code> or <code>b</code> is null
371 public static double Dot(Quaternion a
, Quaternion b
)
373 if (a
== null || b
== null)
375 String msg
= gov
.nasa
.worldwind
.WorldWind
.retrieveErrMsg("nullValue.QuaternionIsNull");
376 gov
.nasa
.worldwind
.WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
377 throw new IllegalArgumentException(msg
);
379 return a
.x
* b
.x
+ a
.y
* b
.y
+ a
.z
* b
.z
+ a
.w
* b
.w
;
382 public Quaternion
Normalize()
384 double L
= this.Length();
386 return new Quaternion(
393 public double Length()
396 x
* x
+ y
* y
+ z
* z
+ w
* w
);
404 * @throws IllegalArgumentException if either supplied <code>Quaternion</code> is null
406 public static Quaternion
Slerp(Quaternion q0
, Quaternion q1
, double t
)
408 if (q0
== null || q1
== null)
410 String msg
= gov
.nasa
.worldwind
.WorldWind
.retrieveErrMsg("nullValue.QuaternionIsNull");
411 gov
.nasa
.worldwind
.WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
412 throw new IllegalArgumentException(msg
);
414 double cosom
= q0
.x
* q1
.x
+ q0
.y
* q1
.y
+ q0
.z
* q1
.z
+ q0
.w
* q1
.w
;
415 double tmp0
, tmp1
, tmp2
, tmp3
;
433 double scale0
, scale1
;
435 if ((1.0 - cosom
) > epsilon
)
437 // standard case (slerp)
438 double omega
= Math
.acos(cosom
);
439 double sinom
= Math
.sin(omega
);
440 scale0
= Math
.sin((1.0 - t
) * omega
) / sinom
;
441 scale1
= Math
.sin(t
* omega
) / sinom
;
450 return new Quaternion(
451 scale0
* q0
.x
+ scale1
* tmp0
,
452 scale0
* q0
.y
+ scale1
* tmp1
,
453 scale0
* q0
.z
+ scale1
* tmp2
,
454 scale0
* q0
.w
+ scale1
* tmp3
);
457 public Quaternion
Ln()
465 * @throws IllegalArgumentException if <code>q</code> is null
467 public static Quaternion
Ln(Quaternion q
)
471 String msg
= gov
.nasa
.worldwind
.WorldWind
.retrieveErrMsg("nullValue.QuaternionIsNull");
472 gov
.nasa
.worldwind
.WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
473 throw new IllegalArgumentException(msg
);
477 double s
= Math
.sqrt(q
.x
* q
.x
+ q
.y
* q
.y
+ q
.z
* q
.z
);
478 double om
= Math
.atan2(s
, q
.w
);
480 if (Math
.abs(s
) < epsilon
)
485 return new Quaternion(q
.x
* t
, q
.y
* t
, q
.z
* t
, 0.0f
);
488 /*****************the below functions have not been certified to work properly ******************/
493 * @throws IllegalArgumentException if <code>q</code> is null
495 public static Quaternion
Exp(Quaternion q
)
499 String msg
= gov
.nasa
.worldwind
.WorldWind
.retrieveErrMsg("nullValue.QuaternionIsNull");
500 gov
.nasa
.worldwind
.WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
501 throw new IllegalArgumentException(msg
);
504 double om
= Math
.sqrt(q
.x
* q
.x
+ q
.y
* q
.y
+ q
.z
* q
.z
);
506 if (Math
.abs(om
) < epsilon
)
509 sinom
= Math
.sin(om
) / om
;
511 return new Quaternion(q
.x
* sinom
, q
.y
* sinom
, q
.z
* sinom
, Math
.cos(om
));
514 public Quaternion
Exp()
526 * @throws IllegalArgumentException if any argument is null
528 public static Quaternion
Squad(
535 if (q1
== null || a
== null || b
== null || c
== null)
537 String msg
= gov
.nasa
.worldwind
.WorldWind
.retrieveErrMsg("nullValue.QuaternionIsNull");
538 gov
.nasa
.worldwind
.WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, msg
);
539 throw new IllegalArgumentException(msg
);
542 Slerp(q1
, c
, t
), Slerp(a
, b
, t
), 2 * t
* (1.0 - t
));
545 //TODO: this needs to be accounted for before Squad() is used
546 /*public static Quaternion[] SquadSetup(
552 if(q0 == null || q1 == null || q2 == null || q3 == null)
554 String msg = gov.nasa.worldwind.WorldWind.retrieveErrMsg("nullValue.QuaternionIsNull");
555 gov.nasa.worldwind.WorldWind.logger().log(java.util.logging.Level.FINE, msg);
556 throw new IllegalArgumentException(msg);
570 Quaternion[] ret = new Quaternion[3];
572 ret[0] = q1 * Exp(-0.25 * (Ln(Exp(q1) * q2) + Ln(Exp(q1) * q0))); // outA
573 ret[1] = q2 * Exp(-0.25 * (Ln(Exp(q2) * q3) + Ln(Exp(q2) * q1))); // outB