quaternion.vala 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /*
  2. * Copyright (c) 2012-2026 Daniele Bartolini et al.
  3. * SPDX-License-Identifier: GPL-3.0-or-later
  4. */
  5. namespace Crown
  6. {
  7. [Compact]
  8. public struct Quaternion
  9. {
  10. public double x;
  11. public double y;
  12. public double z;
  13. public double w;
  14. public Quaternion(double x, double y, double z, double w)
  15. {
  16. this.x = x;
  17. this.y = y;
  18. this.z = z;
  19. this.w = w;
  20. }
  21. public Quaternion.from_array(Gee.ArrayList<Value?> arr)
  22. {
  23. this.x = (double)arr[0];
  24. this.y = (double)arr[1];
  25. this.z = (double)arr[2];
  26. this.w = (double)arr[3];
  27. }
  28. public Quaternion.from_axis_angle(Vector3 axis, float angle)
  29. {
  30. double ha = angle * 0.5;
  31. double sa = Math.sin(ha);
  32. double ca = Math.cos(ha);
  33. this.x = axis.x * sa;
  34. this.y = axis.y * sa;
  35. this.z = axis.z * sa;
  36. this.w = ca;
  37. }
  38. public Quaternion.from_euler(double rx, double ry, double rz)
  39. {
  40. // http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/
  41. double c1 = Math.cos(ry*0.5);
  42. double s1 = Math.sin(ry*0.5);
  43. double c2 = Math.cos(rz*0.5);
  44. double s2 = Math.sin(rz*0.5);
  45. double c3 = Math.cos(rx*0.5);
  46. double s3 = Math.sin(rx*0.5);
  47. double c1c2 = c1*c2;
  48. double s1s2 = s1*s2;
  49. double nw = c1c2*c3 - s1s2*s3;
  50. double nx = c1c2*s3 + s1s2*c3;
  51. double ny = s1*c2*c3 + c1*s2*s3;
  52. double nz = c1*s2*c3 - s1*c2*s3;
  53. this.x = nx;
  54. this.y = ny;
  55. this.z = nz;
  56. this.w = nw;
  57. }
  58. public Quaternion.from_matrix(Matrix4x4 m)
  59. {
  60. // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/
  61. double tr = m.x.x + m.y.y + m.z.z;
  62. if (tr > 0.0) {
  63. double sq = Math.sqrt(1.0 + tr) * 0.5;
  64. double inv = 0.25 / sq;
  65. this.w = sq;
  66. this.x = (m.y.z - m.z.y) * inv;
  67. this.y = (m.z.x - m.x.z) * inv;
  68. this.z = (m.x.y - m.y.x) * inv;
  69. } else if ((m.x.x > m.y.y) && (m.x.x > m.z.z)) {
  70. double sq = Math.sqrt(1.0 + m.x.x - m.y.y - m.z.z) * 0.5;
  71. double inv = 0.25 / sq;
  72. this.x = sq;
  73. this.w = (m.y.z - m.z.y) * inv;
  74. this.y = (m.x.y + m.y.x) * inv;
  75. this.z = (m.z.x + m.x.z) * inv;
  76. } else if (m.y.y > m.z.z) {
  77. double sq = Math.sqrt(1.0 + m.y.y - m.x.x - m.z.z) * 0.5;
  78. double inv = 0.25 / sq;
  79. this.y = sq;
  80. this.w = (m.z.x - m.x.z) * inv;
  81. this.x = (m.x.y + m.y.x) * inv;
  82. this.z = (m.y.z + m.z.y) * inv;
  83. } else {
  84. double sq = Math.sqrt(1.0 + m.z.z - m.x.x - m.y.y) * 0.5;
  85. double inv = 0.25 / sq;
  86. this.z = sq;
  87. this.w = (m.x.y - m.y.x) * inv;
  88. this.x = (m.z.x + m.x.z) * inv;
  89. this.y = (m.y.z + m.z.y) * inv;
  90. }
  91. }
  92. public Quaternion.look(Vector3 dir, Vector3 up)
  93. {
  94. Vector3 xaxis = dir.cross(up);
  95. Vector3 zaxis = xaxis.cross(dir);
  96. Matrix4x4 m = {};
  97. m.x.x = xaxis.x;
  98. m.x.y = xaxis.y;
  99. m.x.z = xaxis.z;
  100. m.x.w = 0.0;
  101. m.y.x = dir.x;
  102. m.y.y = dir.y;
  103. m.y.z = dir.z;
  104. m.y.w = 0.0;
  105. m.z.x = zaxis.x;
  106. m.z.y = zaxis.y;
  107. m.z.z = zaxis.z;
  108. m.z.w = 0.0;
  109. m.t.x = 0.0;
  110. m.t.y = 0.0;
  111. m.t.z = 0.0;
  112. m.t.w = 1.0;
  113. Quaternion q = Quaternion.from_matrix(m);
  114. q.normalize();
  115. this.x = q.x;
  116. this.y = q.y;
  117. this.z = q.z;
  118. this.w = q.w;
  119. }
  120. /// Returns the dot product between quaternions @a a and @a b.
  121. public double dot(Quaternion b)
  122. {
  123. return this.w * b.w + this.x * b.x + this.y * b.y + this.z * b.z;
  124. }
  125. /// Returns the length of @a q.
  126. public double length()
  127. {
  128. return Math.sqrt(dot(this));
  129. }
  130. public void normalize()
  131. {
  132. double len = length();
  133. double inv_len = 1.0f / len;
  134. this.x *= inv_len;
  135. this.y *= inv_len;
  136. this.z *= inv_len;
  137. this.w *= inv_len;
  138. }
  139. public Gee.ArrayList<Value?> to_array()
  140. {
  141. Gee.ArrayList<Value?> arr = new Gee.ArrayList<Value?>();
  142. arr.add(this.x);
  143. arr.add(this.y);
  144. arr.add(this.z);
  145. arr.add(this.w);
  146. return arr;
  147. }
  148. public Vector3 to_euler()
  149. {
  150. // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/
  151. double test = x*y + z*w;
  152. if (test > 0.499) { // singularity at north pole
  153. double rx = 0.0;
  154. double ry = 2.0 * Math.atan2(x, w);
  155. double rz = Math.PI*0.5;
  156. return Vector3(rx, ry, rz);
  157. }
  158. if (test < -0.499) { // singularity at south pole
  159. double rx = +0.0;
  160. double ry = -2.0 * Math.atan2(x, w);
  161. double rz = -Math.PI*0.5;
  162. return Vector3(rx, ry, rz);
  163. }
  164. double xx = x*x;
  165. double yy = y*y;
  166. double zz = z*z;
  167. double rrx = Math.atan2(2.0*x*w - 2.0*y*z, 1.0 - 2.0*xx - 2.0*zz);
  168. double rry = Math.atan2(2.0*y*w - 2.0*x*z, 1.0 - 2.0*yy - 2.0*zz);
  169. double rrz = Math.asin(2.0*test);
  170. return Vector3(rrx, rry, rrz);
  171. }
  172. public string to_string()
  173. {
  174. return "%f, %f, %f, %f".printf(x, y, z, w);
  175. }
  176. public static bool equal_func(Quaternion? a, Quaternion? b)
  177. {
  178. return a.x == b.x
  179. && a.y == b.y
  180. && a.z == b.z
  181. && a.w == b.w
  182. ;
  183. }
  184. }
  185. public const Quaternion QUATERNION_IDENTITY = { 0.0, 0.0, 0.0, 1.0 };
  186. } /* namespace Crown */