/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

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