Files
Nitrox/NitroxModel/DataStructures/Unity/NitroxQuaternion.cs
2025-07-06 00:23:46 +02:00

142 lines
4.8 KiB
C#

using System;
using System.Numerics;
using System.Runtime.Serialization;
using NitroxModel.Helper;
namespace NitroxModel.DataStructures.Unity
{
[DataContract]
[Serializable]
public struct NitroxQuaternion : IEquatable<NitroxQuaternion>
{
[DataMember(Order = 1)]
public float X;
[DataMember(Order = 2)]
public float Y;
[DataMember(Order = 3)]
public float Z;
[DataMember(Order = 4)]
public float W;
public static NitroxQuaternion Identity { get; } = new NitroxQuaternion(0, 0, 0, 1);
public NitroxQuaternion(float x, float y, float z, float w)
{
X = x;
Y = y;
Z = z;
W = w;
}
public static NitroxQuaternion Normalize(NitroxQuaternion value) => (NitroxQuaternion)Quaternion.Normalize((Quaternion)value);
public static NitroxQuaternion FromEuler(NitroxVector3 vector) => FromEuler(vector.X, vector.Y, vector.Z);
public static NitroxQuaternion FromEuler(float x, float y, float z) => (NitroxQuaternion)Quaternion.CreateFromYawPitchRoll(y * Mathf.DEG2RAD, x * Mathf.DEG2RAD, z * Mathf.DEG2RAD);
//Used https://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/
public NitroxVector3 ToEuler()
{
float sqw = W * W;
float sqx = X * X;
float sqy = Y * Y;
float sqz = Z * Z;
float unit = sqx + sqy + sqz + sqw; // if normalized is one, otherwise is correction factor
float test = X * Y + Z * W;
float heading, attitude, bank;
if (test > 0.499f * unit) // singularity at north pole
{
heading = 2 * Mathf.Atan2(X, W);
attitude = Mathf.PI / 2f;
bank = 0;
}
else if (test < -0.499f * unit) // singularity at south pole
{
heading = -2 * Mathf.Atan2(X, W);
attitude = -Mathf.PI / 2f;
bank = 0;
}
else
{
heading = Mathf.Atan2(2 * Y * W - 2 * X * Z, sqx - sqy - sqz + sqw);
attitude = Mathf.Asin(2 * test / unit);
bank = Mathf.Atan2(2 * X * W - 2 * Y * Z, -sqx + sqy - sqz + sqw);
}
NitroxVector3 euler = new NitroxVector3(bank * Mathf.RAD2DEG, heading * Mathf.RAD2DEG, attitude * Mathf.RAD2DEG);
NormalizeEuler(ref euler);
return euler;
}
public static void NormalizeEuler(ref NitroxVector3 vector3)
{
NormalizeEulerPart(ref vector3.X);
NormalizeEulerPart(ref vector3.Y);
NormalizeEulerPart(ref vector3.Z);
}
private static void NormalizeEulerPart(ref float euler)
{
while (euler > 360)
{
euler -= 360;
}
while (euler < 0)
{
euler += 360;
}
}
public static NitroxQuaternion operator *(NitroxQuaternion lhs, NitroxQuaternion rhs) => (NitroxQuaternion)Quaternion.Multiply((Quaternion)lhs, (Quaternion)rhs);
public static explicit operator Quaternion(NitroxQuaternion q) => new Quaternion(q.X, q.Y, q.Z, q.W);
public static explicit operator NitroxQuaternion(Quaternion q) => new NitroxQuaternion(q.X, q.Y, q.Z, q.W);
public static bool operator ==(NitroxQuaternion left, NitroxQuaternion right) => left.Equals(right);
public static bool operator !=(NitroxQuaternion left, NitroxQuaternion right) => !left.Equals(right);
public override bool Equals(object obj)
{
return obj is NitroxQuaternion other && Equals(other);
}
public bool Equals(NitroxQuaternion other)
{
return Equals(other, float.Epsilon);
}
public bool Equals(NitroxQuaternion other, float tolerance)
{
return X == other.X && Y == other.Y && Z == other.Z && W == other.W ||
Math.Abs(other.X + X) < tolerance && Math.Abs(other.Y + Y) < tolerance && Math.Abs(other.Z + Z) < tolerance && Math.Abs(other.W + W) < tolerance ||
Math.Abs(other.X - X) < tolerance && Math.Abs(other.Y - Y) < tolerance && Math.Abs(other.Z - Z) < tolerance && Math.Abs(other.W - W) < tolerance;
}
public override string ToString()
{
return $"[Quaternion: {X}, {Y}, {Z}, {W}]";
}
public override int GetHashCode()
{
unchecked
{
int hashCode = X.GetHashCode();
hashCode = (hashCode * 397) ^ Y.GetHashCode();
hashCode = (hashCode * 397) ^ Z.GetHashCode();
hashCode = (hashCode * 397) ^ W.GetHashCode();
return hashCode;
}
}
}
}