Quaternion.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668
  1. using System;
  2. using System.Runtime.InteropServices;
  3. namespace Godot
  4. {
  5. /// <summary>
  6. /// A unit quaternion used for representing 3D rotations.
  7. /// Quaternions need to be normalized to be used for rotation.
  8. ///
  9. /// It is similar to <see cref="Basis"/>, which implements matrix
  10. /// representation of rotations, and can be parametrized using both
  11. /// an axis-angle pair or Euler angles. Basis stores rotation, scale,
  12. /// and shearing, while Quaternion only stores rotation.
  13. ///
  14. /// Due to its compactness and the way it is stored in memory, certain
  15. /// operations (obtaining axis-angle and performing SLERP, in particular)
  16. /// are more efficient and robust against floating-point errors.
  17. /// </summary>
  18. [Serializable]
  19. [StructLayout(LayoutKind.Sequential)]
  20. public struct Quaternion : IEquatable<Quaternion>
  21. {
  22. /// <summary>
  23. /// X component of the quaternion (imaginary <c>i</c> axis part).
  24. /// Quaternion components should usually not be manipulated directly.
  25. /// </summary>
  26. public real_t x;
  27. /// <summary>
  28. /// Y component of the quaternion (imaginary <c>j</c> axis part).
  29. /// Quaternion components should usually not be manipulated directly.
  30. /// </summary>
  31. public real_t y;
  32. /// <summary>
  33. /// Z component of the quaternion (imaginary <c>k</c> axis part).
  34. /// Quaternion components should usually not be manipulated directly.
  35. /// </summary>
  36. public real_t z;
  37. /// <summary>
  38. /// W component of the quaternion (real part).
  39. /// Quaternion components should usually not be manipulated directly.
  40. /// </summary>
  41. public real_t w;
  42. /// <summary>
  43. /// Access quaternion components using their index.
  44. /// </summary>
  45. /// <value>
  46. /// <c>[0]</c> is equivalent to <see cref="x"/>,
  47. /// <c>[1]</c> is equivalent to <see cref="y"/>,
  48. /// <c>[2]</c> is equivalent to <see cref="z"/>,
  49. /// <c>[3]</c> is equivalent to <see cref="w"/>.
  50. /// </value>
  51. public real_t this[int index]
  52. {
  53. get
  54. {
  55. switch (index)
  56. {
  57. case 0:
  58. return x;
  59. case 1:
  60. return y;
  61. case 2:
  62. return z;
  63. case 3:
  64. return w;
  65. default:
  66. throw new ArgumentOutOfRangeException(nameof(index));
  67. }
  68. }
  69. set
  70. {
  71. switch (index)
  72. {
  73. case 0:
  74. x = value;
  75. break;
  76. case 1:
  77. y = value;
  78. break;
  79. case 2:
  80. z = value;
  81. break;
  82. case 3:
  83. w = value;
  84. break;
  85. default:
  86. throw new ArgumentOutOfRangeException(nameof(index));
  87. }
  88. }
  89. }
  90. /// <summary>
  91. /// Returns the length (magnitude) of the quaternion.
  92. /// </summary>
  93. /// <seealso cref="LengthSquared"/>
  94. /// <value>Equivalent to <c>Mathf.Sqrt(LengthSquared)</c>.</value>
  95. public real_t Length
  96. {
  97. get { return Mathf.Sqrt(LengthSquared); }
  98. }
  99. /// <summary>
  100. /// Returns the squared length (squared magnitude) of the quaternion.
  101. /// This method runs faster than <see cref="Length"/>, so prefer it if
  102. /// you need to compare quaternions or need the squared length for some formula.
  103. /// </summary>
  104. /// <value>Equivalent to <c>Dot(this)</c>.</value>
  105. public real_t LengthSquared
  106. {
  107. get { return Dot(this); }
  108. }
  109. /// <summary>
  110. /// Returns the angle between this quaternion and <paramref name="to"/>.
  111. /// This is the magnitude of the angle you would need to rotate
  112. /// by to get from one to the other.
  113. ///
  114. /// Note: This method has an abnormally high amount
  115. /// of floating-point error, so methods such as
  116. /// <see cref="Mathf.IsZeroApprox"/> will not work reliably.
  117. /// </summary>
  118. /// <param name="to">The other quaternion.</param>
  119. /// <returns>The angle between the quaternions.</returns>
  120. public real_t AngleTo(Quaternion to)
  121. {
  122. real_t dot = Dot(to);
  123. return Mathf.Acos(Mathf.Clamp(dot * dot * 2 - 1, -1, 1));
  124. }
  125. /// <summary>
  126. /// Performs a cubic spherical interpolation between quaternions <paramref name="preA"/>, this quaternion,
  127. /// <paramref name="b"/>, and <paramref name="postB"/>, by the given amount <paramref name="weight"/>.
  128. /// </summary>
  129. /// <param name="b">The destination quaternion.</param>
  130. /// <param name="preA">A quaternion before this quaternion.</param>
  131. /// <param name="postB">A quaternion after <paramref name="b"/>.</param>
  132. /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
  133. /// <returns>The interpolated quaternion.</returns>
  134. public Quaternion CubicSlerp(Quaternion b, Quaternion preA, Quaternion postB, real_t weight)
  135. {
  136. real_t t2 = (1.0f - weight) * weight * 2f;
  137. Quaternion sp = Slerp(b, weight);
  138. Quaternion sq = preA.Slerpni(postB, weight);
  139. return sp.Slerpni(sq, t2);
  140. }
  141. /// <summary>
  142. /// Returns the dot product of two quaternions.
  143. /// </summary>
  144. /// <param name="b">The other quaternion.</param>
  145. /// <returns>The dot product.</returns>
  146. public real_t Dot(Quaternion b)
  147. {
  148. return (x * b.x) + (y * b.y) + (z * b.z) + (w * b.w);
  149. }
  150. /// <summary>
  151. /// Returns Euler angles (in the YXZ convention: when decomposing,
  152. /// first Z, then X, and Y last) corresponding to the rotation
  153. /// represented by the unit quaternion. Returned vector contains
  154. /// the rotation angles in the format (X angle, Y angle, Z angle).
  155. /// </summary>
  156. /// <returns>The Euler angle representation of this quaternion.</returns>
  157. public Vector3 GetEuler()
  158. {
  159. #if DEBUG
  160. if (!IsNormalized())
  161. {
  162. throw new InvalidOperationException("Quaternion is not normalized");
  163. }
  164. #endif
  165. var basis = new Basis(this);
  166. return basis.GetEuler();
  167. }
  168. /// <summary>
  169. /// Returns the inverse of the quaternion.
  170. /// </summary>
  171. /// <returns>The inverse quaternion.</returns>
  172. public Quaternion Inverse()
  173. {
  174. #if DEBUG
  175. if (!IsNormalized())
  176. {
  177. throw new InvalidOperationException("Quaternion is not normalized");
  178. }
  179. #endif
  180. return new Quaternion(-x, -y, -z, w);
  181. }
  182. /// <summary>
  183. /// Returns whether the quaternion is normalized or not.
  184. /// </summary>
  185. /// <returns>A <see langword="bool"/> for whether the quaternion is normalized or not.</returns>
  186. public bool IsNormalized()
  187. {
  188. return Mathf.Abs(LengthSquared - 1) <= Mathf.Epsilon;
  189. }
  190. /// <summary>
  191. /// Returns a copy of the quaternion, normalized to unit length.
  192. /// </summary>
  193. /// <returns>The normalized quaternion.</returns>
  194. public Quaternion Normalized()
  195. {
  196. return this / Length;
  197. }
  198. /// <summary>
  199. /// Returns the result of the spherical linear interpolation between
  200. /// this quaternion and <paramref name="to"/> by amount <paramref name="weight"/>.
  201. ///
  202. /// Note: Both quaternions must be normalized.
  203. /// </summary>
  204. /// <param name="to">The destination quaternion for interpolation. Must be normalized.</param>
  205. /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
  206. /// <returns>The resulting quaternion of the interpolation.</returns>
  207. public Quaternion Slerp(Quaternion to, real_t weight)
  208. {
  209. #if DEBUG
  210. if (!IsNormalized())
  211. {
  212. throw new InvalidOperationException("Quaternion is not normalized");
  213. }
  214. if (!to.IsNormalized())
  215. {
  216. throw new ArgumentException("Argument is not normalized", nameof(to));
  217. }
  218. #endif
  219. // Calculate cosine.
  220. real_t cosom = x * to.x + y * to.y + z * to.z + w * to.w;
  221. var to1 = new Quaternion();
  222. // Adjust signs if necessary.
  223. if (cosom < 0.0)
  224. {
  225. cosom = -cosom;
  226. to1.x = -to.x;
  227. to1.y = -to.y;
  228. to1.z = -to.z;
  229. to1.w = -to.w;
  230. }
  231. else
  232. {
  233. to1.x = to.x;
  234. to1.y = to.y;
  235. to1.z = to.z;
  236. to1.w = to.w;
  237. }
  238. real_t sinom, scale0, scale1;
  239. // Calculate coefficients.
  240. if (1.0 - cosom > Mathf.Epsilon)
  241. {
  242. // Standard case (Slerp).
  243. real_t omega = Mathf.Acos(cosom);
  244. sinom = Mathf.Sin(omega);
  245. scale0 = Mathf.Sin((1.0f - weight) * omega) / sinom;
  246. scale1 = Mathf.Sin(weight * omega) / sinom;
  247. }
  248. else
  249. {
  250. // Quaternions are very close so we can do a linear interpolation.
  251. scale0 = 1.0f - weight;
  252. scale1 = weight;
  253. }
  254. // Calculate final values.
  255. return new Quaternion
  256. (
  257. (scale0 * x) + (scale1 * to1.x),
  258. (scale0 * y) + (scale1 * to1.y),
  259. (scale0 * z) + (scale1 * to1.z),
  260. (scale0 * w) + (scale1 * to1.w)
  261. );
  262. }
  263. /// <summary>
  264. /// Returns the result of the spherical linear interpolation between
  265. /// this quaternion and <paramref name="to"/> by amount <paramref name="weight"/>, but without
  266. /// checking if the rotation path is not bigger than 90 degrees.
  267. /// </summary>
  268. /// <param name="to">The destination quaternion for interpolation. Must be normalized.</param>
  269. /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
  270. /// <returns>The resulting quaternion of the interpolation.</returns>
  271. public Quaternion Slerpni(Quaternion to, real_t weight)
  272. {
  273. real_t dot = Dot(to);
  274. if (Mathf.Abs(dot) > 0.9999f)
  275. {
  276. return this;
  277. }
  278. real_t theta = Mathf.Acos(dot);
  279. real_t sinT = 1.0f / Mathf.Sin(theta);
  280. real_t newFactor = Mathf.Sin(weight * theta) * sinT;
  281. real_t invFactor = Mathf.Sin((1.0f - weight) * theta) * sinT;
  282. return new Quaternion
  283. (
  284. (invFactor * x) + (newFactor * to.x),
  285. (invFactor * y) + (newFactor * to.y),
  286. (invFactor * z) + (newFactor * to.z),
  287. (invFactor * w) + (newFactor * to.w)
  288. );
  289. }
  290. /// <summary>
  291. /// Returns a vector transformed (multiplied) by this quaternion.
  292. /// </summary>
  293. /// <param name="v">A vector to transform.</param>
  294. /// <returns>The transformed vector.</returns>
  295. public Vector3 Xform(Vector3 v)
  296. {
  297. #if DEBUG
  298. if (!IsNormalized())
  299. {
  300. throw new InvalidOperationException("Quaternion is not normalized");
  301. }
  302. #endif
  303. var u = new Vector3(x, y, z);
  304. Vector3 uv = u.Cross(v);
  305. return v + (((uv * w) + u.Cross(uv)) * 2);
  306. }
  307. // Constants
  308. private static readonly Quaternion _identity = new Quaternion(0, 0, 0, 1);
  309. /// <summary>
  310. /// The identity quaternion, representing no rotation.
  311. /// Equivalent to an identity <see cref="Basis"/> matrix. If a vector is transformed by
  312. /// an identity quaternion, it will not change.
  313. /// </summary>
  314. /// <value>Equivalent to <c>new Quaternion(0, 0, 0, 1)</c>.</value>
  315. public static Quaternion Identity { get { return _identity; } }
  316. /// <summary>
  317. /// Constructs a <see cref="Quaternion"/> defined by the given values.
  318. /// </summary>
  319. /// <param name="x">X component of the quaternion (imaginary <c>i</c> axis part).</param>
  320. /// <param name="y">Y component of the quaternion (imaginary <c>j</c> axis part).</param>
  321. /// <param name="z">Z component of the quaternion (imaginary <c>k</c> axis part).</param>
  322. /// <param name="w">W component of the quaternion (real part).</param>
  323. public Quaternion(real_t x, real_t y, real_t z, real_t w)
  324. {
  325. this.x = x;
  326. this.y = y;
  327. this.z = z;
  328. this.w = w;
  329. }
  330. /// <summary>
  331. /// Constructs a <see cref="Quaternion"/> from the given <see cref="Quaternion"/>.
  332. /// </summary>
  333. /// <param name="q">The existing quaternion.</param>
  334. public Quaternion(Quaternion q)
  335. {
  336. this = q;
  337. }
  338. /// <summary>
  339. /// Constructs a <see cref="Quaternion"/> from the given <see cref="Basis"/>.
  340. /// </summary>
  341. /// <param name="basis">The <see cref="Basis"/> to construct from.</param>
  342. public Quaternion(Basis basis)
  343. {
  344. this = basis.GetQuaternion();
  345. }
  346. /// <summary>
  347. /// Constructs a <see cref="Quaternion"/> that will perform a rotation specified by
  348. /// Euler angles (in the YXZ convention: when decomposing, first Z, then X, and Y last),
  349. /// given in the vector format as (X angle, Y angle, Z angle).
  350. /// </summary>
  351. /// <param name="eulerYXZ">Euler angles that the quaternion will be rotated by.</param>
  352. public Quaternion(Vector3 eulerYXZ)
  353. {
  354. real_t halfA1 = eulerYXZ.y * 0.5f;
  355. real_t halfA2 = eulerYXZ.x * 0.5f;
  356. real_t halfA3 = eulerYXZ.z * 0.5f;
  357. // R = Y(a1).X(a2).Z(a3) convention for Euler angles.
  358. // Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-6)
  359. // a3 is the angle of the first rotation, following the notation in this reference.
  360. real_t cosA1 = Mathf.Cos(halfA1);
  361. real_t sinA1 = Mathf.Sin(halfA1);
  362. real_t cosA2 = Mathf.Cos(halfA2);
  363. real_t sinA2 = Mathf.Sin(halfA2);
  364. real_t cosA3 = Mathf.Cos(halfA3);
  365. real_t sinA3 = Mathf.Sin(halfA3);
  366. x = (sinA1 * cosA2 * sinA3) + (cosA1 * sinA2 * cosA3);
  367. y = (sinA1 * cosA2 * cosA3) - (cosA1 * sinA2 * sinA3);
  368. z = (cosA1 * cosA2 * sinA3) - (sinA1 * sinA2 * cosA3);
  369. w = (sinA1 * sinA2 * sinA3) + (cosA1 * cosA2 * cosA3);
  370. }
  371. /// <summary>
  372. /// Constructs a <see cref="Quaternion"/> that will rotate around the given axis
  373. /// by the specified angle. The axis must be a normalized vector.
  374. /// </summary>
  375. /// <param name="axis">The axis to rotate around. Must be normalized.</param>
  376. /// <param name="angle">The angle to rotate, in radians.</param>
  377. public Quaternion(Vector3 axis, real_t angle)
  378. {
  379. #if DEBUG
  380. if (!axis.IsNormalized())
  381. {
  382. throw new ArgumentException("Argument is not normalized", nameof(axis));
  383. }
  384. #endif
  385. real_t d = axis.Length();
  386. if (d == 0f)
  387. {
  388. x = 0f;
  389. y = 0f;
  390. z = 0f;
  391. w = 0f;
  392. }
  393. else
  394. {
  395. real_t sinAngle = Mathf.Sin(angle * 0.5f);
  396. real_t cosAngle = Mathf.Cos(angle * 0.5f);
  397. real_t s = sinAngle / d;
  398. x = axis.x * s;
  399. y = axis.y * s;
  400. z = axis.z * s;
  401. w = cosAngle;
  402. }
  403. }
  404. /// <summary>
  405. /// Composes these two quaternions by multiplying them together.
  406. /// This has the effect of rotating the second quaternion
  407. /// (the child) by the first quaternion (the parent).
  408. /// </summary>
  409. /// <param name="left">The parent quaternion.</param>
  410. /// <param name="right">The child quaternion.</param>
  411. /// <returns>The composed quaternion.</returns>
  412. public static Quaternion operator *(Quaternion left, Quaternion right)
  413. {
  414. return new Quaternion
  415. (
  416. (left.w * right.x) + (left.x * right.w) + (left.y * right.z) - (left.z * right.y),
  417. (left.w * right.y) + (left.y * right.w) + (left.z * right.x) - (left.x * right.z),
  418. (left.w * right.z) + (left.z * right.w) + (left.x * right.y) - (left.y * right.x),
  419. (left.w * right.w) - (left.x * right.x) - (left.y * right.y) - (left.z * right.z)
  420. );
  421. }
  422. /// <summary>
  423. /// Adds each component of the left <see cref="Quaternion"/>
  424. /// to the right <see cref="Quaternion"/>. This operation is not
  425. /// meaningful on its own, but it can be used as a part of a
  426. /// larger expression, such as approximating an intermediate
  427. /// rotation between two nearby rotations.
  428. /// </summary>
  429. /// <param name="left">The left quaternion to add.</param>
  430. /// <param name="right">The right quaternion to add.</param>
  431. /// <returns>The added quaternion.</returns>
  432. public static Quaternion operator +(Quaternion left, Quaternion right)
  433. {
  434. return new Quaternion(left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w);
  435. }
  436. /// <summary>
  437. /// Subtracts each component of the left <see cref="Quaternion"/>
  438. /// by the right <see cref="Quaternion"/>. This operation is not
  439. /// meaningful on its own, but it can be used as a part of a
  440. /// larger expression.
  441. /// </summary>
  442. /// <param name="left">The left quaternion to subtract.</param>
  443. /// <param name="right">The right quaternion to subtract.</param>
  444. /// <returns>The subtracted quaternion.</returns>
  445. public static Quaternion operator -(Quaternion left, Quaternion right)
  446. {
  447. return new Quaternion(left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w);
  448. }
  449. /// <summary>
  450. /// Returns the negative value of the <see cref="Quaternion"/>.
  451. /// This is the same as writing
  452. /// <c>new Quaternion(-q.x, -q.y, -q.z, -q.w)</c>. This operation
  453. /// results in a quaternion that represents the same rotation.
  454. /// </summary>
  455. /// <param name="quat">The quaternion to negate.</param>
  456. /// <returns>The negated quaternion.</returns>
  457. public static Quaternion operator -(Quaternion quat)
  458. {
  459. return new Quaternion(-quat.x, -quat.y, -quat.z, -quat.w);
  460. }
  461. /// <summary>
  462. /// Rotates (multiplies) the <see cref="Vector3"/>
  463. /// by the given <see cref="Quaternion"/>.
  464. /// </summary>
  465. /// <param name="quat">The quaternion to rotate by.</param>
  466. /// <param name="vec">The vector to rotate.</param>
  467. /// <returns>The rotated vector.</returns>
  468. public static Vector3 operator *(Quaternion quat, Vector3 vec)
  469. {
  470. #if DEBUG
  471. if (!quat.IsNormalized())
  472. {
  473. throw new InvalidOperationException("Quaternion is not normalized.");
  474. }
  475. #endif
  476. var u = new Vector3(quat.x, quat.y, quat.z);
  477. Vector3 uv = u.Cross(vec);
  478. return vec + (((uv * quat.w) + u.Cross(uv)) * 2);
  479. }
  480. /// <summary>
  481. /// Inversely rotates (multiplies) the <see cref="Vector3"/>
  482. /// by the given <see cref="Quaternion"/>.
  483. /// </summary>
  484. /// <param name="vec">The vector to rotate.</param>
  485. /// <param name="quat">The quaternion to rotate by.</param>
  486. /// <returns>The inversely rotated vector.</returns>
  487. public static Vector3 operator *(Vector3 vec, Quaternion quat)
  488. {
  489. return quat.Inverse() * vec;
  490. }
  491. /// <summary>
  492. /// Multiplies each component of the <see cref="Quaternion"/>
  493. /// by the given <see cref="real_t"/>. This operation is not
  494. /// meaningful on its own, but it can be used as a part of a
  495. /// larger expression.
  496. /// </summary>
  497. /// <param name="left">The quaternion to multiply.</param>
  498. /// <param name="right">The value to multiply by.</param>
  499. /// <returns>The multiplied quaternion.</returns>
  500. public static Quaternion operator *(Quaternion left, real_t right)
  501. {
  502. return new Quaternion(left.x * right, left.y * right, left.z * right, left.w * right);
  503. }
  504. /// <summary>
  505. /// Multiplies each component of the <see cref="Quaternion"/>
  506. /// by the given <see cref="real_t"/>. This operation is not
  507. /// meaningful on its own, but it can be used as a part of a
  508. /// larger expression.
  509. /// </summary>
  510. /// <param name="left">The value to multiply by.</param>
  511. /// <param name="right">The quaternion to multiply.</param>
  512. /// <returns>The multiplied quaternion.</returns>
  513. public static Quaternion operator *(real_t left, Quaternion right)
  514. {
  515. return new Quaternion(right.x * left, right.y * left, right.z * left, right.w * left);
  516. }
  517. /// <summary>
  518. /// Divides each component of the <see cref="Quaternion"/>
  519. /// by the given <see cref="real_t"/>. This operation is not
  520. /// meaningful on its own, but it can be used as a part of a
  521. /// larger expression.
  522. /// </summary>
  523. /// <param name="left">The quaternion to divide.</param>
  524. /// <param name="right">The value to divide by.</param>
  525. /// <returns>The divided quaternion.</returns>
  526. public static Quaternion operator /(Quaternion left, real_t right)
  527. {
  528. return left * (1.0f / right);
  529. }
  530. /// <summary>
  531. /// Returns <see langword="true"/> if the quaternions are exactly equal.
  532. /// Note: Due to floating-point precision errors, consider using
  533. /// <see cref="IsEqualApprox"/> instead, which is more reliable.
  534. /// </summary>
  535. /// <param name="left">The left quaternion.</param>
  536. /// <param name="right">The right quaternion.</param>
  537. /// <returns>Whether or not the quaternions are exactly equal.</returns>
  538. public static bool operator ==(Quaternion left, Quaternion right)
  539. {
  540. return left.Equals(right);
  541. }
  542. /// <summary>
  543. /// Returns <see langword="true"/> if the quaternions are not equal.
  544. /// Note: Due to floating-point precision errors, consider using
  545. /// <see cref="IsEqualApprox"/> instead, which is more reliable.
  546. /// </summary>
  547. /// <param name="left">The left quaternion.</param>
  548. /// <param name="right">The right quaternion.</param>
  549. /// <returns>Whether or not the quaternions are not equal.</returns>
  550. public static bool operator !=(Quaternion left, Quaternion right)
  551. {
  552. return !left.Equals(right);
  553. }
  554. /// <summary>
  555. /// Returns <see langword="true"/> if this quaternion and <paramref name="obj"/> are equal.
  556. /// </summary>
  557. /// <param name="obj">The other object to compare.</param>
  558. /// <returns>Whether or not the quaternion and the other object are exactly equal.</returns>
  559. public override bool Equals(object obj)
  560. {
  561. if (obj is Quaternion)
  562. {
  563. return Equals((Quaternion)obj);
  564. }
  565. return false;
  566. }
  567. /// <summary>
  568. /// Returns <see langword="true"/> if this quaternion and <paramref name="other"/> are equal.
  569. /// </summary>
  570. /// <param name="other">The other quaternion to compare.</param>
  571. /// <returns>Whether or not the quaternions are exactly equal.</returns>
  572. public bool Equals(Quaternion other)
  573. {
  574. return x == other.x && y == other.y && z == other.z && w == other.w;
  575. }
  576. /// <summary>
  577. /// Returns <see langword="true"/> if this quaternion and <paramref name="other"/> are approximately equal,
  578. /// by running <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component.
  579. /// </summary>
  580. /// <param name="other">The other quaternion to compare.</param>
  581. /// <returns>Whether or not the quaternions are approximately equal.</returns>
  582. public bool IsEqualApprox(Quaternion other)
  583. {
  584. return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z) && Mathf.IsEqualApprox(w, other.w);
  585. }
  586. /// <summary>
  587. /// Serves as the hash function for <see cref="Quaternion"/>.
  588. /// </summary>
  589. /// <returns>A hash code for this quaternion.</returns>
  590. public override int GetHashCode()
  591. {
  592. return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode();
  593. }
  594. /// <summary>
  595. /// Converts this <see cref="Quaternion"/> to a string.
  596. /// </summary>
  597. /// <returns>A string representation of this quaternion.</returns>
  598. public override string ToString()
  599. {
  600. return $"({x}, {y}, {z}, {w})";
  601. }
  602. /// <summary>
  603. /// Converts this <see cref="Quaternion"/> to a string with the given <paramref name="format"/>.
  604. /// </summary>
  605. /// <returns>A string representation of this quaternion.</returns>
  606. public string ToString(string format)
  607. {
  608. return $"({x.ToString(format)}, {y.ToString(format)}, {z.ToString(format)}, {w.ToString(format)})";
  609. }
  610. }
  611. }