2 Copyright (c) 2006 - 2008 The Open Toolkit library.
4 Permission is hereby granted, free of charge, to any person obtaining a copy of
5 this software and associated documentation files (the "Software"), to deal in
6 the Software without restriction, including without limitation the rights to
7 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 of the Software, and to permit persons to whom the Software is furnished to do
9 so, subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 using System.Runtime.InteropServices;
25 using System.Xml.Serialization;
30 /// Represents a Quaternion.
33 [StructLayout(LayoutKind.Sequential)]
34 public struct Quaternion : IEquatable<Quaternion>
37 /// The X, Y and Z components of this instance.
42 /// The W component of this instance.
47 /// Construct a new Quaternion from vector and w components
49 /// <param name="v">The vector part</param>
50 /// <param name="w">The w part</param>
51 public Quaternion(Vector3 v, float w)
58 /// Construct a new Quaternion
60 /// <param name="x">The x component</param>
61 /// <param name="y">The y component</param>
62 /// <param name="z">The z component</param>
63 /// <param name="w">The w component</param>
64 public Quaternion(float x, float y, float z, float w)
65 : this(new Vector3(x, y, z), w)
69 /// Construct a new Quaternion from given Euler angles. The rotations will get applied in following order:
70 /// 1. Around X, 2. Around Y, 3. Around Z
72 /// <param name="rotationX">Counterclockwise rotation around X axis in radian</param>
73 /// <param name="rotationY">Counterclockwise rotation around Y axis in radian</param>
74 /// <param name="rotationZ">Counterclockwise rotation around Z axis in radian</param>
75 public Quaternion(float rotationX, float rotationY, float rotationZ)
81 float c1 = (float)Math.Cos(rotationX);
82 float c2 = (float)Math.Cos(rotationY);
83 float c3 = (float)Math.Cos(rotationZ);
84 float s1 = (float)Math.Sin(rotationX);
85 float s2 = (float)Math.Sin(rotationY);
86 float s3 = (float)Math.Sin(rotationZ);
88 W = c1 * c2 * c3 - s1 * s2 * s3;
89 Xyz.X = s1 * c2 * c3 + c1 * s2 * s3;
90 Xyz.Y = c1 * s2 * c3 - s1 * c2 * s3;
91 Xyz.Z = c1 * c2 * s3 + s1 * s2 * c3;
95 /// Construct a new Quaternion from given Euler angles. The rotations will get applied in following order:
96 /// 1. Around X, 2. Around Y, 3. Around Z
98 /// <param name="eulerAngles">The counterclockwise euler angles as a Vector3</param>
99 public Quaternion(Vector3 eulerAngles)
100 : this(eulerAngles.X, eulerAngles.Y, eulerAngles.Z)
104 /// Gets or sets the X component of this instance.
107 public float X { get { return Xyz.X; } set { Xyz.X = value; } }
110 /// Gets or sets the Y component of this instance.
113 public float Y { get { return Xyz.Y; } set { Xyz.Y = value; } }
116 /// Gets or sets the Z component of this instance.
119 public float Z { get { return Xyz.Z; } set { Xyz.Z = value; } }
122 /// Convert the current quaternion to axis angle representation
124 /// <param name="axis">The resultant axis</param>
125 /// <param name="angle">The resultant angle</param>
126 public void ToAxisAngle(out Vector3 axis, out float angle)
128 Vector4 result = ToAxisAngle();
134 /// Convert this instance to an axis-angle representation.
136 /// <returns>A Vector4 that is the axis-angle representation of this quaternion.</returns>
137 public Vector4 ToAxisAngle()
140 if (Math.Abs(q.W) > 1.0f)
145 Vector4 result = new Vector4();
147 result.W = 2.0f * (float)System.Math.Acos(q.W); // angle
148 float den = (float)System.Math.Sqrt(1.0 - q.W * q.W);
151 result.Xyz = q.Xyz / den;
155 // This occurs when the angle is zero.
156 // Not a problem: just set an arbitrary normalized axis.
157 result.Xyz = Vector3.UnitX;
164 /// Gets the length (magnitude) of the quaternion.
166 /// <seealso cref="LengthSquared"/>
171 return (float)System.Math.Sqrt(W * W + Xyz.LengthSquared);
176 /// Gets the square of the quaternion length (magnitude).
178 public float LengthSquared
182 return W * W + Xyz.LengthSquared;
187 /// Returns a copy of the Quaternion scaled to unit length.
189 public Quaternion Normalized()
197 /// Reverses the rotation angle of this Quaterniond.
205 /// Returns a copy of this Quaterniond with its rotation angle reversed.
207 public Quaternion Inverted()
215 /// Scales the Quaternion to unit length.
217 public void Normalize()
219 float scale = 1.0f / this.Length;
225 /// Inverts the Vector3 component of this Quaternion.
227 public void Conjugate()
233 /// Defines the identity quaternion.
235 public static readonly Quaternion Identity = new Quaternion(0, 0, 0, 1);
238 /// Add two quaternions
240 /// <param name="left">The first operand</param>
241 /// <param name="right">The second operand</param>
242 /// <returns>The result of the addition</returns>
243 public static Quaternion Add(Quaternion left, Quaternion right)
245 return new Quaternion(
246 left.Xyz + right.Xyz,
251 /// Add two quaternions
253 /// <param name="left">The first operand</param>
254 /// <param name="right">The second operand</param>
255 /// <param name="result">The result of the addition</param>
256 public static void Add(ref Quaternion left, ref Quaternion right, out Quaternion result)
258 result = new Quaternion(
259 left.Xyz + right.Xyz,
264 /// Subtracts two instances.
266 /// <param name="left">The left instance.</param>
267 /// <param name="right">The right instance.</param>
268 /// <returns>The result of the operation.</returns>
269 public static Quaternion Sub(Quaternion left, Quaternion right)
271 return new Quaternion(
272 left.Xyz - right.Xyz,
277 /// Subtracts two instances.
279 /// <param name="left">The left instance.</param>
280 /// <param name="right">The right instance.</param>
281 /// <param name="result">The result of the operation.</param>
282 public static void Sub(ref Quaternion left, ref Quaternion right, out Quaternion result)
284 result = new Quaternion(
285 left.Xyz - right.Xyz,
290 /// Multiplies two instances.
292 /// <param name="left">The first instance.</param>
293 /// <param name="right">The second instance.</param>
294 /// <returns>A new instance containing the result of the calculation.</returns>
295 public static Quaternion Multiply(Quaternion left, Quaternion right)
298 Multiply(ref left, ref right, out result);
303 /// Multiplies two instances.
305 /// <param name="left">The first instance.</param>
306 /// <param name="right">The second instance.</param>
307 /// <param name="result">A new instance containing the result of the calculation.</param>
308 public static void Multiply(ref Quaternion left, ref Quaternion right, out Quaternion result)
310 result = new Quaternion(
311 right.W * left.Xyz + left.W * right.Xyz + Vector3.Cross(left.Xyz, right.Xyz),
312 left.W * right.W - Vector3.Dot(left.Xyz, right.Xyz));
316 /// Multiplies an instance by a scalar.
318 /// <param name="quaternion">The instance.</param>
319 /// <param name="scale">The scalar.</param>
320 /// <param name="result">A new instance containing the result of the calculation.</param>
321 public static void Multiply(ref Quaternion quaternion, float scale, out Quaternion result)
323 result = new Quaternion(quaternion.X * scale, quaternion.Y * scale, quaternion.Z * scale, quaternion.W * scale);
327 /// Multiplies an instance by a scalar.
329 /// <param name="quaternion">The instance.</param>
330 /// <param name="scale">The scalar.</param>
331 /// <returns>A new instance containing the result of the calculation.</returns>
332 public static Quaternion Multiply(Quaternion quaternion, float scale)
334 return new Quaternion(quaternion.X * scale, quaternion.Y * scale, quaternion.Z * scale, quaternion.W * scale);
338 /// Get the conjugate of the given quaternion
340 /// <param name="q">The quaternion</param>
341 /// <returns>The conjugate of the given quaternion</returns>
342 public static Quaternion Conjugate(Quaternion q)
344 return new Quaternion(-q.Xyz, q.W);
348 /// Get the conjugate of the given quaternion
350 /// <param name="q">The quaternion</param>
351 /// <param name="result">The conjugate of the given quaternion</param>
352 public static void Conjugate(ref Quaternion q, out Quaternion result)
354 result = new Quaternion(-q.Xyz, q.W);
358 /// Get the inverse of the given quaternion
360 /// <param name="q">The quaternion to invert</param>
361 /// <returns>The inverse of the given quaternion</returns>
362 public static Quaternion Invert(Quaternion q)
365 Invert(ref q, out result);
370 /// Get the inverse of the given quaternion
372 /// <param name="q">The quaternion to invert</param>
373 /// <param name="result">The inverse of the given quaternion</param>
374 public static void Invert(ref Quaternion q, out Quaternion result)
376 float lengthSq = q.LengthSquared;
379 float i = 1.0f / lengthSq;
380 result = new Quaternion(q.Xyz * -i, q.W * i);
389 /// Scale the given quaternion to unit length
391 /// <param name="q">The quaternion to normalize</param>
392 /// <returns>The normalized quaternion</returns>
393 public static Quaternion Normalize(Quaternion q)
396 Normalize(ref q, out result);
401 /// Scale the given quaternion to unit length
403 /// <param name="q">The quaternion to normalize</param>
404 /// <param name="result">The normalized quaternion</param>
405 public static void Normalize(ref Quaternion q, out Quaternion result)
407 float scale = 1.0f / q.Length;
408 result = new Quaternion(q.Xyz * scale, q.W * scale);
412 /// Build a quaternion from the given axis and angle
414 /// <param name="axis">The axis to rotate about</param>
415 /// <param name="angle">The rotation angle in radians</param>
416 /// <returns>The equivalent quaternion</returns>
417 public static Quaternion FromAxisAngle(Vector3 axis, float angle)
419 if (axis.LengthSquared == 0.0f)
424 Quaternion result = Identity;
428 result.Xyz = axis * (float)System.Math.Sin(angle);
429 result.W = (float)System.Math.Cos(angle);
431 return Normalize(result);
435 /// Builds a Quaternion from the given euler angles
436 /// The rotations will get applied in following order:
437 /// 1. pitch, 2. yaw, 3. roll
439 /// <param name="pitch">The pitch (attitude), counterclockwise rotation around X axis</param>
440 /// <param name="yaw">The yaw (heading), counterclockwise rotation around Y axis</param>
441 /// <param name="roll">The roll (bank), counterclockwise rotation around Z axis</param>
442 /// <returns></returns>
443 public static Quaternion FromEulerAngles(float pitch, float yaw, float roll)
445 return new Quaternion(pitch, yaw, roll);
449 /// Builds a Quaternion from the given euler angles
450 /// The rotations will get applied in following order:
451 /// 1. Around X, 2. Around Y, 3. Around Z
453 /// <param name="eulerAngles">The counterclockwise euler angles as a vector</param>
454 /// <returns>The equivalent Quaternion</returns>
455 public static Quaternion FromEulerAngles(Vector3 eulerAngles)
457 return new Quaternion(eulerAngles);
461 /// Builds a Quaternion from the given euler angles in radians.
462 /// The rotations will get applied in following order:
463 /// 1. Around X, 2. Around Y, 3. Around Z
465 /// <param name="eulerAngles">The counterclockwise euler angles a vector</param>
466 /// <param name="result">The equivalent Quaternion</param>
467 public static void FromEulerAngles(ref Vector3 eulerAngles, out Quaternion result)
470 float c1 = (float)Math.Cos(eulerAngles.X * 0.5f);
471 float c2 = (float)Math.Cos(eulerAngles.Y * 0.5f);
472 float c3 = (float)Math.Cos(eulerAngles.Z * 0.5f);
473 float s1 = (float)Math.Sin(eulerAngles.X * 0.5f);
474 float s2 = (float)Math.Sin(eulerAngles.Y * 0.5f);
475 float s3 = (float)Math.Sin(eulerAngles.Z * 0.5f);
477 result.W = c1 * c2 * c3 - s1 * s2 * s3;
478 result.Xyz.X = s1 * c2 * c3 + c1 * s2 * s3;
479 result.Xyz.Y = c1 * s2 * c3 - s1 * c2 * s3;
480 result.Xyz.Z = c1 * c2 * s3 + s1 * s2 * c3;
484 /// Builds a quaternion from the given rotation matrix
486 /// <param name="matrix">A rotation matrix</param>
487 /// <returns>The equivalent quaternion</returns>
488 public static Quaternion FromMatrix(Matrix3 matrix)
491 FromMatrix(ref matrix, out result);
496 /// Builds a quaternion from the given rotation matrix
498 /// <param name="matrix">A rotation matrix</param>
499 /// <param name="result">The equivalent quaternion</param>
500 public static void FromMatrix(ref Matrix3 matrix, out Quaternion result)
502 float trace = matrix.Trace;
506 float s = (float)Math.Sqrt(trace + 1) * 2;
509 result.W = s * 0.25f;
510 result.Xyz.X = (matrix.Row2.Y - matrix.Row1.Z) * invS;
511 result.Xyz.Y = (matrix.Row0.Z - matrix.Row2.X) * invS;
512 result.Xyz.Z = (matrix.Row1.X - matrix.Row0.Y) * invS;
516 float m00 = matrix.Row0.X, m11 = matrix.Row1.Y, m22 = matrix.Row2.Z;
518 if (m00 > m11 && m00 > m22)
520 float s = (float)Math.Sqrt(1 + m00 - m11 - m22) * 2;
523 result.W = (matrix.Row2.Y - matrix.Row1.Z) * invS;
524 result.Xyz.X = s * 0.25f;
525 result.Xyz.Y = (matrix.Row0.Y + matrix.Row1.X) * invS;
526 result.Xyz.Z = (matrix.Row0.Z + matrix.Row2.X) * invS;
530 float s = (float)Math.Sqrt(1 + m11 - m00 - m22) * 2;
533 result.W = (matrix.Row0.Z - matrix.Row2.X) * invS;
534 result.Xyz.X = (matrix.Row0.Y + matrix.Row1.X) * invS;
535 result.Xyz.Y = s * 0.25f;
536 result.Xyz.Z = (matrix.Row1.Z + matrix.Row2.Y) * invS;
540 float s = (float)Math.Sqrt(1 + m22 - m00 - m11) * 2;
543 result.W = (matrix.Row1.X - matrix.Row0.Y) * invS;
544 result.Xyz.X = (matrix.Row0.Z + matrix.Row2.X) * invS;
545 result.Xyz.Y = (matrix.Row1.Z + matrix.Row2.Y) * invS;
546 result.Xyz.Z = s * 0.25f;
552 /// Do Spherical linear interpolation between two quaternions
554 /// <param name="q1">The first quaternion</param>
555 /// <param name="q2">The second quaternion</param>
556 /// <param name="blend">The blend factor</param>
557 /// <returns>A smooth blend between the given quaternions</returns>
558 public static Quaternion Slerp(Quaternion q1, Quaternion q2, float blend)
560 // if either input is zero, return the other.
561 if (q1.LengthSquared == 0.0f)
563 if (q2.LengthSquared == 0.0f)
569 else if (q2.LengthSquared == 0.0f)
575 float cosHalfAngle = q1.W * q2.W + Vector3.Dot(q1.Xyz, q2.Xyz);
577 if (cosHalfAngle >= 1.0f || cosHalfAngle <= -1.0f)
579 // angle = 0.0f, so just return one input.
582 else if (cosHalfAngle < 0.0f)
586 cosHalfAngle = -cosHalfAngle;
591 if (cosHalfAngle < 0.99f)
593 // do proper slerp for big angles
594 float halfAngle = (float)System.Math.Acos(cosHalfAngle);
595 float sinHalfAngle = (float)System.Math.Sin(halfAngle);
596 float oneOverSinHalfAngle = 1.0f / sinHalfAngle;
597 blendA = (float)System.Math.Sin(halfAngle * (1.0f - blend)) * oneOverSinHalfAngle;
598 blendB = (float)System.Math.Sin(halfAngle * blend) * oneOverSinHalfAngle;
602 // do lerp if angle is really small.
603 blendA = 1.0f - blend;
607 Quaternion result = new Quaternion(blendA * q1.Xyz + blendB * q2.Xyz, blendA * q1.W + blendB * q2.W);
608 if (result.LengthSquared > 0.0f)
610 return Normalize(result);
619 /// Adds two instances.
621 /// <param name="left">The first instance.</param>
622 /// <param name="right">The second instance.</param>
623 /// <returns>The result of the calculation.</returns>
624 public static Quaternion operator +(Quaternion left, Quaternion right)
626 left.Xyz += right.Xyz;
632 /// Subtracts two instances.
634 /// <param name="left">The first instance.</param>
635 /// <param name="right">The second instance.</param>
636 /// <returns>The result of the calculation.</returns>
637 public static Quaternion operator -(Quaternion left, Quaternion right)
639 left.Xyz -= right.Xyz;
645 /// Multiplies two instances.
647 /// <param name="left">The first instance.</param>
648 /// <param name="right">The second instance.</param>
649 /// <returns>The result of the calculation.</returns>
650 public static Quaternion operator *(Quaternion left, Quaternion right)
652 Multiply(ref left, ref right, out left);
657 /// Multiplies an instance by a scalar.
659 /// <param name="quaternion">The instance.</param>
660 /// <param name="scale">The scalar.</param>
661 /// <returns>A new instance containing the result of the calculation.</returns>
662 public static Quaternion operator *(Quaternion quaternion, float scale)
664 Multiply(ref quaternion, scale, out quaternion);
669 /// Multiplies an instance by a scalar.
671 /// <param name="quaternion">The instance.</param>
672 /// <param name="scale">The scalar.</param>
673 /// <returns>A new instance containing the result of the calculation.</returns>
674 public static Quaternion operator *(float scale, Quaternion quaternion)
676 return new Quaternion(quaternion.X * scale, quaternion.Y * scale, quaternion.Z * scale, quaternion.W * scale);
680 /// Compares two instances for equality.
682 /// <param name="left">The first instance.</param>
683 /// <param name="right">The second instance.</param>
684 /// <returns>True, if left equals right; false otherwise.</returns>
685 public static bool operator ==(Quaternion left, Quaternion right)
687 return left.Equals(right);
691 /// Compares two instances for inequality.
693 /// <param name="left">The first instance.</param>
694 /// <param name="right">The second instance.</param>
695 /// <returns>True, if left does not equal right; false otherwise.</returns>
696 public static bool operator !=(Quaternion left, Quaternion right)
698 return !left.Equals(right);
702 /// Returns a System.String that represents the current Quaternion.
704 /// <returns></returns>
705 public override string ToString()
707 return String.Format("V: {0}, W: {1}", Xyz, W);
711 /// Compares this object instance to another object for equality.
713 /// <param name="other">The other object to be used in the comparison.</param>
714 /// <returns>True if both objects are Quaternions of equal value. Otherwise it returns false.</returns>
715 public override bool Equals(object other)
717 if (other is Quaternion == false)
721 return this == (Quaternion)other;
725 /// Provides the hash code for this object.
727 /// <returns>A hash code formed from the bitwise XOR of this objects members.</returns>
728 public override int GetHashCode()
732 return (this.Xyz.GetHashCode() * 397) ^ this.W.GetHashCode();
737 /// Compares this Quaternion instance to another Quaternion for equality.
739 /// <param name="other">The other Quaternion to be used in the comparison.</param>
740 /// <returns>True if both instances are equal; false otherwise.</returns>
741 public bool Equals(Quaternion other)
743 return Xyz == other.Xyz && W == other.W;