Opentk/Source/OpenTK/Math/Quaternion.cs
the_fiddler 2af017c3ea Added serializable attribute.
Added IEquatable interface and operator== overloads to Matrix4 and Vector* structs.
2008-07-10 14:01:38 +00:00

460 lines
11 KiB
C#

#region --- License ---
/* Copyright (c) 2006, 2007 the OpenTK team
* See license.txt for license info
*
* Implemented by Andy Gill
*/
#endregion
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace OpenTK.Math
{
/// <summary>
/// Represents a Quaternion
/// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct Quaternion
{
#region Fields
/// <summary>
/// The vector part of the quaternion
/// </summary>
public Vector3 XYZ;
/// <summary>
/// The w component of the quaternion
/// </summary>
public float W;
public static Quaternion Identity = new Quaternion(0, 0, 0, 1);
#endregion
#region Constructors
/// <summary>
/// Construct a new Quaternion from vector and w components
/// </summary>
/// <param name="v">The vector part</param>
/// <param name="w">The w part</param>
public Quaternion(Vector3 v, float w)
{
XYZ = v;
W = w;
}
/// <summary>
/// Construct a new Quaternion
/// </summary>
/// <param name="x">The x component</param>
/// <param name="y">The y component</param>
/// <param name="z">The z component</param>
/// <param name="w">The w component</param>
public Quaternion(float x, float y, float z, float w)
{
XYZ = new Vector3(x, y, z);
W = w;
}
#endregion
#region Functions
#region pubilc void ToAxisAngle(out Vector3 axis, out float angle)
/// <summary>
/// Convert the current quaternion to axis angle representation
/// </summary>
/// <param name="axis">The resultant axis</param>
/// <param name="angle">The resultant angle</param>
public void ToAxisAngle(out Vector3 axis, out float angle)
{
Quaternion q = this;
if (q.W > 1.0f)
q.Normalize();
angle = 2.0f * (float)System.Math.Acos(q.W);
float den = (float)System.Math.Sqrt(1.0 - q.W * q.W);
axis = q.XYZ;
if (den > 0.0001f)
{
axis = q.XYZ / den;
}
}
#endregion
#region public float Length
/// <summary>
/// Gets the length (magnitude) of the quaternion.
/// </summary>
/// <seealso cref="LengthSquared"/>
public float Length
{
get
{
return (float)System.Math.Sqrt(W * W + XYZ.LengthSquared);
}
}
#endregion
#region public float LengthSquared
/// <summary>
/// Gets the square of the quaternion length (magnitude).
/// </summary>
public float LengthSquared
{
get
{
return W * W + XYZ.LengthSquared;
}
}
#endregion
#region public void Normalize()
/// <summary>
/// Scales the Quaternion to unit length.
/// </summary>
public void Normalize()
{
float scale = 1.0f / this.Length;
XYZ *= scale;
W *= scale;
}
#endregion
#region public void Conjugate()
/// <summary>
/// Convert this quaternion to its conjugate
/// </summary>
public void Conjugate()
{
XYZ = -XYZ;
}
#endregion
#endregion
#region Operator overloads
public static Quaternion operator +(Quaternion left, Quaternion right)
{
left.XYZ += right.XYZ;
left.W += right.W;
return left;
}
public static Quaternion operator -(Quaternion left, Quaternion right)
{
left.XYZ -= right.XYZ;
left.W -= right.W;
return left;
}
public static Quaternion operator *(Quaternion left, Quaternion right)
{
float w = left.W * right.W - Vector3.Dot(left.XYZ, right.XYZ);
left.XYZ = right.W * left.XYZ + left.W * right.XYZ + Vector3.Cross(left.XYZ, right.XYZ);
left.W = w;
return left;
}
[CLSCompliant(false)]
unsafe public static explicit operator float*(Quaternion q)
{
return &q.XYZ.X;
}
public static explicit operator IntPtr(Quaternion q)
{
unsafe
{
return (IntPtr)(&q.XYZ.X);
}
}
#endregion
#region Static functions
#region Add
/// <summary>
/// Add two quaternions
/// </summary>
/// <param name="left">The first operand</param>
/// <param name="right">The second operand</param>
/// <returns>The result of the addition</returns>
public static Quaternion Add(Quaternion left, Quaternion right)
{
left.XYZ += right.XYZ;
left.W += right.W;
return left;
}
/// <summary>
/// Add two quaternions
/// </summary>
/// <param name="left">The first operand</param>
/// <param name="right">The second operand</param>
/// <param name="result">The result of the addition</param>
public static void Add(ref Quaternion left, ref Quaternion right, out Quaternion result)
{
result.XYZ = left.XYZ + right.XYZ;
result.W = left.W + right.W;
}
#endregion
#region Sub
public static Quaternion Sub(Quaternion left, Quaternion right)
{
left.XYZ -= right.XYZ;
left.W -= right.W;
return left;
}
public static void Sub(ref Quaternion left, ref Quaternion right, out Quaternion result)
{
result.XYZ = left.XYZ - right.XYZ;
result.W = left.W - right.W;
}
#endregion
#region Mult
public static Quaternion Mult(Quaternion left, Quaternion right)
{
float w = left.W * right.W - Vector3.Dot(left.XYZ, right.XYZ);
left.XYZ = right.W * left.XYZ + left.W * right.XYZ + Vector3.Cross(left.XYZ, right.XYZ);
left.W = w;
return left;
}
public static void Mult(ref Quaternion left, ref Quaternion right, out Quaternion result)
{
result.W = left.W * right.W - Vector3.Dot(left.XYZ, right.XYZ);
result.XYZ = right.W * left.XYZ + left.W * right.XYZ + Vector3.Cross(left.XYZ, right.XYZ);
}
#endregion
#region Conjugate
/// <summary>
/// Get the conjugate of the given quaternion
/// </summary>
/// <param name="q">The quaternion</param>
/// <returns>The conjugate of the given quaternion</returns>
public static Quaternion Conjugate(Quaternion q)
{
q.XYZ = -q.XYZ;
return q;
}
/// <summary>
/// Get the conjugate of the given quaternion
/// </summary>
/// <param name="q">The quaternion</param>
/// <param name="result">The conjugate of the given quaternion</param>
public static void Conjugate(ref Quaternion q, out Quaternion result)
{
result.XYZ = -q.XYZ;
result.W = q.W;
}
#endregion
#region Invert
/// <summary>
/// Get the inverse of the given quaternion
/// </summary>
/// <param name="q">The quaternion to invert</param>
/// <returns>The inverse of the given quaternion</returns>
public static Quaternion Invert(Quaternion q)
{
float lengthSq = q.LengthSquared;
if (lengthSq != 0.0)
{
float i = 1.0f / lengthSq;
q.XYZ *= -i;
q.W *= i;
}
return q;
}
/// <summary>
/// Get the inverse of the given quaternion
/// </summary>
/// <param name="q">The quaternion to invert</param>
/// <param name="result">The inverse of the given quaternion</param>
public static void Invert(ref Quaternion q, out Quaternion result)
{
float lengthSq = q.LengthSquared;
if (lengthSq != 0.0)
{
float i = 1.0f / lengthSq;
result.XYZ = q.XYZ * -i;
result.W = q.W * i;
}
else
{
result = q;
}
}
#endregion
#region Normalize
/// <summary>
/// Scale the given quaternion to unit length
/// </summary>
/// <param name="q">The quaternion to normalize</param>
/// <returns>The normalized quaternion</returns>
public static Quaternion Normalize(Quaternion q)
{
float scale = 1.0f / q.Length;
q.XYZ *= scale;
q.W *= scale;
return q;
}
/// <summary>
/// Scale the given quaternion to unit length
/// </summary>
/// <param name="q">The quaternion to normalize</param>
/// <param name="result">The normalized quaternion</param>
public static void Normalize(ref Quaternion q, out Quaternion result)
{
float scale = 1.0f / q.Length;
result.XYZ = q.XYZ * scale;
result.W = q.W * scale;
}
#endregion
#region FromAxisAngle
/// <summary>
/// Build a quaternion from the given axis and angle
/// </summary>
/// <param name="axis">The axis to rotate about</param>
/// <param name="angle">The rotation angle in radians</param>
/// <returns></returns>
public static Quaternion FromAxisAngle(Vector3 axis, float angle)
{
if (axis.LengthSquared == 0.0f)
return Identity;
Quaternion result = Identity;
angle *= 0.5f;
axis.Normalize();
result.XYZ = axis * (float)System.Math.Sin(angle);
result.W = (float)System.Math.Cos(angle);
return Normalize(result);
}
#endregion
#region Slerp
/// <summary>
/// Do Spherical linear interpolation between two quaternions
/// </summary>
/// <param name="q1">The first quaternion</param>
/// <param name="q2">The second quaternion</param>
/// <param name="blend">The blend factor</param>
/// <returns>A smooth blend between the given quaternions</returns>
public static Quaternion Slerp(Quaternion q1, Quaternion q2, float blend)
{
// if either input is zero, return the other.
if (q1.LengthSquared == 0.0f)
{
if (q2.LengthSquared == 0.0f)
{
return Identity;
}
return q2;
}
else if (q2.LengthSquared == 0.0f)
{
return q1;
}
float cosHalfAngle = q1.W * q2.W + Vector3.Dot(q1.XYZ, q2.XYZ);
if (cosHalfAngle >= 1.0f || cosHalfAngle <= -1.0f)
{
// angle = 0.0f, so just return one input.
return q1;
}
else if (cosHalfAngle < 0.0f)
{
q2.XYZ = -q2.XYZ;
q2.W = -q2.W;
cosHalfAngle = -cosHalfAngle;
}
float blendA;
float blendB;
if (cosHalfAngle < 0.99f)
{
// do proper slerp for big angles
float halfAngle = (float)System.Math.Acos(cosHalfAngle);
float sinHalfAngle = (float)System.Math.Sin(halfAngle);
float oneOverSinHalfAngle = 1.0f / sinHalfAngle;
blendA = (float)System.Math.Sin(halfAngle * (1.0f - blend)) * oneOverSinHalfAngle;
blendB = (float)System.Math.Sin(halfAngle * blend) * oneOverSinHalfAngle;
}
else
{
// do lerp if angle is really small.
blendA = 1.0f - blend;
blendB = blend;
}
Quaternion result = new Quaternion(blendA * q1.XYZ + blendB * q2.XYZ, blendA * q1.W + blendB * q2.W);
if (result.LengthSquared > 0.0f)
return Normalize(result);
else
return Identity;
}
#endregion
#endregion
#region public override string ToString()
/// <summary>
/// Returns a System.String that represents the current Quaternion.
/// </summary>
/// <returns></returns>
public override string ToString()
{
return String.Format("V: {0}, W: {1}", XYZ, W);
}
#endregion
}
}