Worldwind public release 0.2
[worldwind-tracker.git] / gov / nasa / worldwind / geom / Quaternion.java
blob98f26eb92180f96236937163f8650d5dfd07cdd6
1 /*
2 Copyright (C) 2001, 2006 United States Government
3 as represented by the Administrator of the
4 National Aeronautics and Space Administration.
5 All Rights Reserved.
6 */
7 package gov.nasa.worldwind.geom;
9 /**
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)
23 this.x = x;
24 this.y = y;
25 this.z = z;
26 this.w = w;
29 @Override
30 public boolean equals(Object o)
32 if (this == o)
33 return true;
34 if (o == null || getClass() != o.getClass())
35 return false;
37 final gov.nasa.worldwind.geom.Quaternion quaternion = (gov.nasa.worldwind.geom.Quaternion) o;
39 if (Double.compare(quaternion.w, w) != 0)
40 return false;
41 if (Double.compare(quaternion.x, x) != 0)
42 return false;
43 if (Double.compare(quaternion.y, y) != 0)
44 return false;
45 if (Double.compare(quaternion.z, z) != 0)
46 return false;
48 return true;
51 /**
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>.
54 * <p/>
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>.
60 @Override
61 public int hashCode()
63 int result;
64 long temp;
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));
73 return result;
76 @Override
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
103 * @param q
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)
109 if (q == null)
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);
116 double q0 = q.w;
117 double q1 = q.x;
118 double q2 = q.y;
119 double q3 = q.z;
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)
130 if (angle == null)
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)
139 x = x / length;
140 y = y / length;
141 z = z / length;
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)
150 if (q == null)
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);
156 double x, y, z;
157 q = q.Normalize();
158 double s = Math.sqrt(q.x * q.x + q.y * q.y + q.x * q.z);
159 if (s > 0)
161 x = q.x / s;
162 y = q.y / s;
163 z = q.z / s;
166 else
168 x = q.x;
169 y = q.y;
170 z = q.z;
172 double angle = 2 * Math.acos(q.w);
173 return new Point(x, y, z, angle);
177 * @param a
178 * @param b
179 * @return
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);
194 * @param a
195 * @param b
196 * @return
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);
211 * @param a
212 * @param b
213 * @return
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);
232 * @param q
233 * @return
234 * @throws IllegalArgumentException if <code>q</code> is null
236 public static Quaternion Multiply(double s, Quaternion q)
238 if (q == null)
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);
248 * @param q
249 * @param s
250 * @return
251 * @throws IllegalArgumentException if <code>q</code> is null
253 public static Quaternion Multiply(Quaternion q, double s)
255 if (q == null)
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)
267 * @param v
268 * @param q
269 * @return
270 * @throws IllegalArgumentException if <code>q</code> or <code>v</code> is null
272 public static Quaternion Multiply(Point v, Quaternion q)
274 if (v == null)
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);
280 if (q == null)
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);
294 * @param q
295 * @param s
296 * @return
297 * @throws IllegalArgumentException if <code>q</code> is null
299 public static Quaternion Divide(Quaternion q, double s)
301 if (q == null)
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);
317 * @param q
318 * @return
319 * @throws IllegalArgumentException if <code>q</code> is null
321 public static double Norm2(Quaternion q)
323 if (q == null)
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;
333 * @param q
334 * @return
335 * @throws IllegalArgumentException if <code>q</code> is null
337 public static double Abs(Quaternion q)
339 if (q == null)
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));
349 * @param a
350 * @param b
351 * @return
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)));
366 * @param a
367 * @param b
368 * @return
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(
387 x / L,
388 y / L,
389 z / L,
390 w / L);
393 public double Length()
395 return Math.sqrt(
396 x * x + y * y + z * z + w * w);
400 * @param q0
401 * @param q1
402 * @param t
403 * @return
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;
416 if (cosom < 0.0)
418 cosom = -cosom;
419 tmp0 = -q1.x;
420 tmp1 = -q1.y;
421 tmp2 = -q1.z;
422 tmp3 = -q1.w;
424 else
426 tmp0 = q1.x;
427 tmp1 = q1.y;
428 tmp2 = q1.z;
429 tmp3 = q1.w;
432 /* calc coeffs */
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;
443 else
445 /* just lerp */
446 scale0 = 1.0 - t;
447 scale1 = t;
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()
459 return Ln(this);
463 * @param q
464 * @return
465 * @throws IllegalArgumentException if <code>q</code> is null
467 public static Quaternion Ln(Quaternion q)
469 if (q == null)
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);
475 double t;
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)
481 t = 0.0f;
482 else
483 t = om / s;
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 ******************/
491 * @param q
492 * @return
493 * @throws IllegalArgumentException if <code>q</code> is null
495 public static Quaternion Exp(Quaternion q)
497 if (q == null)
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);
503 double sinom;
504 double om = Math.sqrt(q.x * q.x + q.y * q.y + q.z * q.z);
506 if (Math.abs(om) < epsilon)
507 sinom = 1.0;
508 else
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()
516 return Ln(this);
520 * @param q1
521 * @param a
522 * @param b
523 * @param c
524 * @param t
525 * @return
526 * @throws IllegalArgumentException if any argument is null
528 public static Quaternion Squad(
529 Quaternion q1,
530 Quaternion a,
531 Quaternion b,
532 Quaternion c,
533 double t)
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);
541 return Slerp(
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(
547 Quaternion4d q0,
548 Quaternion4d q1,
549 Quaternion4d q2,
550 Quaternion4d q3)
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);
559 q0 = q0 + q1;
560 q0.Normalize();
562 q2 = q2 + q1;
563 q2.Normalize();
565 q3 = q3 + q1;
566 q3.Normalize();
568 q1.Normalize();
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
574 ret[2] = q2; // outC