PageRenderTime 42ms CodeModel.GetById 15ms app.highlight 23ms RepoModel.GetById 1ms app.codeStats 0ms

/src/away3d/core/math/Quaternion.as

http://github.com/away3d/away3d-core-fp11
ActionScript | 365 lines | 220 code | 43 blank | 102 comment | 6 complexity | 68babf3787434729d1b343a70d70fb72 MD5 | raw file
  1package away3d.core.math
  2{
  3	import flash.geom.Matrix3D;
  4	import flash.geom.Orientation3D;
  5	import flash.geom.Vector3D;
  6	
  7	/**
  8	 * A Quaternion object which can be used to represent rotations.
  9	 */
 10	public final class Quaternion
 11	{
 12		/**
 13		 * The x value of the quaternion.
 14		 */
 15		public var x:Number = 0;
 16		
 17		/**
 18		 * The y value of the quaternion.
 19		 */
 20		public var y:Number = 0;
 21		
 22		/**
 23		 * The z value of the quaternion.
 24		 */
 25		public var z:Number = 0;
 26		
 27		/**
 28		 * The w value of the quaternion.
 29		 */
 30		public var w:Number = 1;
 31		
 32		/**
 33		 * Creates a new Quaternion object.
 34		 * @param x The x value of the quaternion.
 35		 * @param y The y value of the quaternion.
 36		 * @param z The z value of the quaternion.
 37		 * @param w The w value of the quaternion.
 38		 */
 39		public function Quaternion(x:Number = 0, y:Number = 0, z:Number = 0, w:Number = 1)
 40		{
 41			this.x = x;
 42			this.y = y;
 43			this.z = z;
 44			this.w = w;
 45		}
 46		
 47		/**
 48		 * Returns the magnitude of the quaternion object.
 49		 */
 50		public function get magnitude():Number
 51		{
 52			return Math.sqrt(w*w + x*x + y*y + z*z);
 53		}
 54		
 55		/**
 56		 * Fills the quaternion object with the result from a multiplication of two quaternion objects.
 57		 *
 58		 * @param    qa    The first quaternion in the multiplication.
 59		 * @param    qb    The second quaternion in the multiplication.
 60		 */
 61		public function multiply(qa:Quaternion, qb:Quaternion):void
 62		{
 63			var w1:Number = qa.w, x1:Number = qa.x, y1:Number = qa.y, z1:Number = qa.z;
 64			var w2:Number = qb.w, x2:Number = qb.x, y2:Number = qb.y, z2:Number = qb.z;
 65			
 66			w = w1*w2 - x1*x2 - y1*y2 - z1*z2;
 67			x = w1*x2 + x1*w2 + y1*z2 - z1*y2;
 68			y = w1*y2 - x1*z2 + y1*w2 + z1*x2;
 69			z = w1*z2 + x1*y2 - y1*x2 + z1*w2;
 70		}
 71		
 72		public function multiplyVector(vector:Vector3D, target:Quaternion = null):Quaternion
 73		{
 74			target ||= new Quaternion();
 75			
 76			var x2:Number = vector.x;
 77			var y2:Number = vector.y;
 78			var z2:Number = vector.z;
 79			
 80			target.w = -x*x2 - y*y2 - z*z2;
 81			target.x = w*x2 + y*z2 - z*y2;
 82			target.y = w*y2 - x*z2 + z*x2;
 83			target.z = w*z2 + x*y2 - y*x2;
 84			
 85			return target;
 86		}
 87		
 88		/**
 89		 * Fills the quaternion object with values representing the given rotation around a vector.
 90		 *
 91		 * @param    axis    The axis around which to rotate
 92		 * @param    angle    The angle in radians of the rotation.
 93		 */
 94		public function fromAxisAngle(axis:Vector3D, angle:Number):void
 95		{
 96			var sin_a:Number = Math.sin(angle/2);
 97			var cos_a:Number = Math.cos(angle/2);
 98			
 99			x = axis.x*sin_a;
100			y = axis.y*sin_a;
101			z = axis.z*sin_a;
102			w = cos_a;
103			normalize();
104		}
105		
106		/**
107		 * Spherically interpolates between two quaternions, providing an interpolation between rotations with constant angle change rate.
108		 * @param qa The first quaternion to interpolate.
109		 * @param qb The second quaternion to interpolate.
110		 * @param t The interpolation weight, a value between 0 and 1.
111		 */
112		public function slerp(qa:Quaternion, qb:Quaternion, t:Number):void
113		{
114			var w1:Number = qa.w, x1:Number = qa.x, y1:Number = qa.y, z1:Number = qa.z;
115			var w2:Number = qb.w, x2:Number = qb.x, y2:Number = qb.y, z2:Number = qb.z;
116			var dot:Number = w1*w2 + x1*x2 + y1*y2 + z1*z2;
117			
118			// shortest direction
119			if (dot < 0) {
120				dot = -dot;
121				w2 = -w2;
122				x2 = -x2;
123				y2 = -y2;
124				z2 = -z2;
125			}
126			
127			if (dot < 0.95) {
128				// interpolate angle linearly
129				var angle:Number = Math.acos(dot);
130				var s:Number = 1/Math.sin(angle);
131				var s1:Number = Math.sin(angle*(1 - t))*s;
132				var s2:Number = Math.sin(angle*t)*s;
133				w = w1*s1 + w2*s2;
134				x = x1*s1 + x2*s2;
135				y = y1*s1 + y2*s2;
136				z = z1*s1 + z2*s2;
137			} else {
138				// nearly identical angle, interpolate linearly
139				w = w1 + t*(w2 - w1);
140				x = x1 + t*(x2 - x1);
141				y = y1 + t*(y2 - y1);
142				z = z1 + t*(z2 - z1);
143				var len:Number = 1.0/Math.sqrt(w*w + x*x + y*y + z*z);
144				w *= len;
145				x *= len;
146				y *= len;
147				z *= len;
148			}
149		}
150		
151		/**
152		 * Linearly interpolates between two quaternions.
153		 * @param qa The first quaternion to interpolate.
154		 * @param qb The second quaternion to interpolate.
155		 * @param t The interpolation weight, a value between 0 and 1.
156		 */
157		public function lerp(qa:Quaternion, qb:Quaternion, t:Number):void
158		{
159			var w1:Number = qa.w, x1:Number = qa.x, y1:Number = qa.y, z1:Number = qa.z;
160			var w2:Number = qb.w, x2:Number = qb.x, y2:Number = qb.y, z2:Number = qb.z;
161			var len:Number;
162			
163			// shortest direction
164			if (w1*w2 + x1*x2 + y1*y2 + z1*z2 < 0) {
165				w2 = -w2;
166				x2 = -x2;
167				y2 = -y2;
168				z2 = -z2;
169			}
170			
171			w = w1 + t*(w2 - w1);
172			x = x1 + t*(x2 - x1);
173			y = y1 + t*(y2 - y1);
174			z = z1 + t*(z2 - z1);
175			
176			len = 1.0/Math.sqrt(w*w + x*x + y*y + z*z);
177			w *= len;
178			x *= len;
179			y *= len;
180			z *= len;
181		}
182		
183		/**
184		 * Fills the quaternion object with values representing the given euler rotation.
185		 *
186		 * @param    ax        The angle in radians of the rotation around the ax axis.
187		 * @param    ay        The angle in radians of the rotation around the ay axis.
188		 * @param    az        The angle in radians of the rotation around the az axis.
189		 */
190		public function fromEulerAngles(ax:Number, ay:Number, az:Number):void
191		{
192			var halfX:Number = ax*.5, halfY:Number = ay*.5, halfZ:Number = az*.5;
193			var cosX:Number = Math.cos(halfX), sinX:Number = Math.sin(halfX);
194			var cosY:Number = Math.cos(halfY), sinY:Number = Math.sin(halfY);
195			var cosZ:Number = Math.cos(halfZ), sinZ:Number = Math.sin(halfZ);
196			
197			w = cosX*cosY*cosZ + sinX*sinY*sinZ;
198			x = sinX*cosY*cosZ - cosX*sinY*sinZ;
199			y = cosX*sinY*cosZ + sinX*cosY*sinZ;
200			z = cosX*cosY*sinZ - sinX*sinY*cosZ;
201		}
202		
203		/**
204		 * Fills a target Vector3D object with the Euler angles that form the rotation represented by this quaternion.
205		 * @param target An optional Vector3D object to contain the Euler angles. If not provided, a new object is created.
206		 * @return The Vector3D containing the Euler angles.
207		 */
208		public function toEulerAngles(target:Vector3D = null):Vector3D
209		{
210			target ||= new Vector3D();
211			target.x = Math.atan2(2*(w*x + y*z), 1 - 2*(x*x + y*y));
212			target.y = Math.asin(2*(w*y - z*x));
213			target.z = Math.atan2(2*(w*z + x*y), 1 - 2*(y*y + z*z));
214			return target;
215		}
216		
217		/**
218		 * Normalises the quaternion object.
219		 */
220		public function normalize(val:Number = 1):void
221		{
222			var mag:Number = val/Math.sqrt(x*x + y*y + z*z + w*w);
223			
224			x *= mag;
225			y *= mag;
226			z *= mag;
227			w *= mag;
228		}
229		
230		/**
231		 * Used to trace the values of a quaternion.
232		 *
233		 * @return A string representation of the quaternion object.
234		 */
235		public function toString():String
236		{
237			return "{x:" + x + " y:" + y + " z:" + z + " w:" + w + "}";
238		}
239		
240		/**
241		 * Converts the quaternion to a Matrix3D object representing an equivalent rotation.
242		 * @param target An optional Matrix3D container to store the transformation in. If not provided, a new object is created.
243		 * @return A Matrix3D object representing an equivalent rotation.
244		 */
245		public function toMatrix3D(target:Matrix3D = null):Matrix3D
246		{
247			var rawData:Vector.<Number> = Matrix3DUtils.RAW_DATA_CONTAINER;
248			var xy2:Number = 2.0*x*y, xz2:Number = 2.0*x*z, xw2:Number = 2.0*x*w;
249			var yz2:Number = 2.0*y*z, yw2:Number = 2.0*y*w, zw2:Number = 2.0*z*w;
250			var xx:Number = x*x, yy:Number = y*y, zz:Number = z*z, ww:Number = w*w;
251			
252			rawData[0] = xx - yy - zz + ww;
253			rawData[4] = xy2 - zw2;
254			rawData[8] = xz2 + yw2;
255			rawData[12] = 0;
256			rawData[1] = xy2 + zw2;
257			rawData[5] = -xx + yy - zz + ww;
258			rawData[9] = yz2 - xw2;
259			rawData[13] = 0;
260			rawData[2] = xz2 - yw2;
261			rawData[6] = yz2 + xw2;
262			rawData[10] = -xx - yy + zz + ww;
263			rawData[14] = 0;
264			rawData[3] = 0.0;
265			rawData[7] = 0.0;
266			rawData[11] = 0;
267			rawData[15] = 1;
268			
269			if (!target)
270				return new Matrix3D(rawData);
271			
272			target.copyRawDataFrom(rawData);
273			
274			return target;
275		}
276		
277		/**
278		 * Extracts a quaternion rotation matrix out of a given Matrix3D object.
279		 * @param matrix The Matrix3D out of which the rotation will be extracted.
280		 */
281		public function fromMatrix(matrix:Matrix3D):void
282		{
283			var v:Vector3D = matrix.decompose(Orientation3D.QUATERNION)[1];
284			x = v.x;
285			y = v.y;
286			z = v.z;
287			w = v.w;
288		}
289		
290		/**
291		 * Converts the quaternion to a Vector.&lt;Number&gt; matrix representation of a rotation equivalent to this quaternion.
292		 * @param target The Vector.&lt;Number&gt; to contain the raw matrix data.
293		 * @param exclude4thRow If true, the last row will be omitted, and a 4x3 matrix will be generated instead of a 4x4.
294		 */
295		public function toRawData(target:Vector.<Number>, exclude4thRow:Boolean = false):void
296		{
297			var xy2:Number = 2.0*x*y, xz2:Number = 2.0*x*z, xw2:Number = 2.0*x*w;
298			var yz2:Number = 2.0*y*z, yw2:Number = 2.0*y*w, zw2:Number = 2.0*z*w;
299			var xx:Number = x*x, yy:Number = y*y, zz:Number = z*z, ww:Number = w*w;
300			
301			target[0] = xx - yy - zz + ww;
302			target[1] = xy2 - zw2;
303			target[2] = xz2 + yw2;
304			target[4] = xy2 + zw2;
305			target[5] = -xx + yy - zz + ww;
306			target[6] = yz2 - xw2;
307			target[8] = xz2 - yw2;
308			target[9] = yz2 + xw2;
309			target[10] = -xx - yy + zz + ww;
310			target[3] = target[7] = target[11] = 0;
311			
312			if (!exclude4thRow) {
313				target[12] = target[13] = target[14] = 0;
314				target[15] = 1;
315			}
316		}
317		
318		/**
319		 * Clones the quaternion.
320		 * @return An exact duplicate of the current Quaternion.
321		 */
322		public function clone():Quaternion
323		{
324			return new Quaternion(x, y, z, w);
325		}
326		
327		/**
328		 * Rotates a point.
329		 * @param vector The Vector3D object to be rotated.
330		 * @param target An optional Vector3D object that will contain the rotated coordinates. If not provided, a new object will be created.
331		 * @return A Vector3D object containing the rotated point.
332		 */
333		public function rotatePoint(vector:Vector3D, target:Vector3D = null):Vector3D
334		{
335			var x1:Number, y1:Number, z1:Number, w1:Number;
336			var x2:Number = vector.x, y2:Number = vector.y, z2:Number = vector.z;
337			
338			target ||= new Vector3D();
339			
340			// p*q'
341			w1 = -x*x2 - y*y2 - z*z2;
342			x1 = w*x2 + y*z2 - z*y2;
343			y1 = w*y2 - x*z2 + z*x2;
344			z1 = w*z2 + x*y2 - y*x2;
345			
346			target.x = -w1*x + x1*w - y1*z + z1*y;
347			target.y = -w1*y + x1*z + y1*w - z1*x;
348			target.z = -w1*z - x1*y + y1*x + z1*w;
349			
350			return target;
351		}
352		
353		/**
354		 * Copies the data from a quaternion into this instance.
355		 * @param q The quaternion to copy from.
356		 */
357		public function copyFrom(q:Quaternion):void
358		{
359			x = q.x;
360			y = q.y;
361			z = q.z;
362			w = q.w;
363		}
364	}
365}