1 package net.sf.openrocket.util;
4 public class Quaternion implements Cloneable {
6 protected double w, x, y, z;
7 protected int modCount = 0;
13 public Quaternion(double w, double x, double y, double z) {
21 public static Quaternion rotation(Coordinate rotation) {
22 double length = rotation.length();
23 if (length < 0.000001) {
24 return new Quaternion(1,0,0,0);
26 double sin = Math.sin(length/2);
27 double cos = Math.cos(length/2);
28 return new Quaternion(cos,
29 sin*rotation.x/length, sin*rotation.y/length, sin*rotation.z/length);
32 public static Quaternion rotation(Coordinate axis, double angle) {
33 Coordinate a = axis.normalize();
34 double sin = Math.sin(angle);
35 double cos = Math.cos(angle);
36 return new Quaternion(cos, sin*a.x, sin*a.y, sin*a.z);
40 public double getW() {
44 public void setW(double w) {
49 public double getX() {
53 public void setX(double x) {
58 public double getY() {
62 public void setY(double y) {
67 public double getZ() {
71 public void setZ(double z) {
77 public void setAll(double w, double x, double y, double z) {
87 * Multiply this quaternion by the other quaternion from the right side. This
88 * calculates the product <code>this = this * other</code>.
90 * @param other the quaternion to multiply this quaternion by.
91 * @return this quaternion.
93 public Quaternion multiplyRight(Quaternion other) {
94 double w = (this.w*other.w - this.x*other.x - this.y*other.y - this.z*other.z);
95 double x = (this.w*other.x + this.x*other.w + this.y*other.z - this.z*other.y);
96 double y = (this.w*other.y + this.y*other.w + this.z*other.x - this.x*other.z);
97 double z = (this.w*other.z + this.z*other.w + this.x*other.y - this.y*other.x);
107 * Multiply this quaternion by the other quaternion from the left side. This
108 * calculates the product <code>this = other * this</code>.
110 * @param other the quaternion to multiply this quaternion by.
111 * @return this quaternion.
113 public Quaternion multiplyLeft(Quaternion other) {
114 /* other(abcd) * this(wxyz) */
116 double w = (other.w*this.w - other.x*this.x - other.y*this.y - other.z*this.z);
117 double x = (other.w*this.x + other.x*this.w + other.y*this.z - other.z*this.y);
118 double y = (other.w*this.y + other.y*this.w + other.z*this.x - other.x*this.z);
119 double z = (other.w*this.z + other.z*this.w + other.x*this.y - other.y*this.x);
134 public Quaternion clone() {
136 return (Quaternion) super.clone();
137 } catch (CloneNotSupportedException e) {
138 throw new BugException("CloneNotSupportedException encountered");
145 * Normalize this quaternion. After the call the norm of the quaternion is exactly
146 * one. If this quaternion is the zero quaternion, throws
147 * <code>IllegalStateException</code>. Returns this quaternion.
149 * @return this quaternion.
150 * @throws IllegalStateException if the norm of this quaternion is zero.
152 public Quaternion normalize() {
153 double norm = norm();
154 if (norm < 0.0000001) {
155 throw new IllegalStateException("attempting to normalize zero-quaternion");
166 * Normalize this quaternion if the norm is more than 1ppm from one.
168 * @return this quaternion.
169 * @throws IllegalStateException if the norm of this quaternion is zero.
171 public Quaternion normalizeIfNecessary() {
173 if (n2 < 0.999999 || n2 > 1.000001)
181 * Return the norm of this quaternion.
183 * @return the norm of this quaternion sqrt(w^2 + x^2 + y^2 + z^2).
185 public double norm() {
186 return Math.sqrt(x*x + y*y + z*z + w*w);
190 * Return the square of the norm of this quaternion.
192 * @return the square of the norm of this quaternion (w^2 + x^2 + y^2 + z^2).
194 public double norm2() {
195 return x*x + y*y + z*z + w*w;
199 public Coordinate rotate(Coordinate coord) {
202 assert(Math.abs(norm2()-1) < 0.00001) : "Quaternion not unit length: "+this;
205 // (a,b,c,d) = this * coord = (w,x,y,z) * (0,cx,cy,cz)
206 a = - x * coord.x - y * coord.y - z * coord.z; // w
207 b = w * coord.x + y * coord.z - z * coord.y; // x i
208 c = w * coord.y - x * coord.z + z * coord.x; // y j
209 d = w * coord.z + x * coord.y - y * coord.x; // z k
212 // return = (a,b,c,d) * (this)^-1 = (a,b,c,d) * (w,-x,-y,-z)
214 // Assert that the w-value is zero
215 assert(Math.abs(a*w + b*x + c*y + d*z) < coord.max() * MathUtil.EPSILON):
216 ("Should be zero: " + (a*w + b*x + c*y + d*z) + " in " + this + " c=" + coord);
218 return new Coordinate(
219 - a*x + b*w - c*z + d*y,
220 - a*y + b*z + c*w - d*x,
221 - a*z - b*y + c*x + d*w,
226 public Coordinate invRotate(Coordinate coord) {
229 assert(Math.abs(norm2()-1) < 0.00001) : "Quaternion not unit length: "+this;
231 // (a,b,c,d) = (this)^-1 * coord = (w,-x,-y,-z) * (0,cx,cy,cz)
232 a = + x * coord.x + y * coord.y + z * coord.z;
233 b = w * coord.x - y * coord.z + z * coord.y;
234 c = w * coord.y + x * coord.z - z * coord.x;
235 d = w * coord.z - x * coord.y + y * coord.x;
238 // return = (a,b,c,d) * this = (a,b,c,d) * (w,x,y,z)
239 assert(Math.abs(a*w - b*x - c*y - d*z) < Math.max(coord.max(), 1) * MathUtil.EPSILON):
240 ("Should be zero: " + (a*w - b*x - c*y - d*z) + " in " + this + " c=" + coord);
242 return new Coordinate(
243 a*x + b*w + c*z - d*y,
244 a*y - b*z + c*w + d*x,
245 a*z + b*y - c*x + d*w,
252 * Rotate the coordinate (0,0,1) using this quaternion. The result is returned
253 * as a Coordinate. This method is equivalent to calling
254 * <code>q.rotate(new Coordinate(0,0,1))</code> but requires only about half of the
257 * @return The coordinate (0,0,1) rotated using this quaternion.
259 public Coordinate rotateZ() {
260 return new Coordinate(
263 w*w - x*x - y*y + z*z
269 public String toString() {
270 return String.format("Quaternion[%f,%f,%f,%f,norm=%f]",w,x,y,z,this.norm());
273 public static void main(String[] arg) {
275 Quaternion q = new Quaternion(Math.random()-0.5,Math.random()-0.5,
276 Math.random()-0.5,Math.random()-0.5);
279 q = new Quaternion(-0.998717,0.000000,0.050649,-0.000000);
281 Coordinate coord = new Coordinate(10*(Math.random()-0.5),
282 10*(Math.random()-0.5), 10*(Math.random()-0.5));
284 System.out.println("Quaternion: "+q);
285 System.out.println("Coordinate: "+coord);
286 coord = q.invRotate(coord);
287 System.out.println("Rotated: "+ coord);
288 coord = q.rotate(coord);
289 System.out.println("Back: "+coord);
292 q = new Quaternion(0.237188,0.570190,-0.514542,0.594872);
294 Coordinate c = new Coordinate(148578428.914,8126778.954,-607.741);
296 System.out.println("Rotated: " + q.rotate(c));
298 // Coordinate c = new Coordinate(0,1,0);
299 // Coordinate rot = new Coordinate(Math.PI/4,0,0);
301 // System.out.println("Before: "+c);
302 // c = rotation(rot).invRotate(c);
303 // System.out.println("After: "+c);