Initial commit
This commit is contained in:
204
srcs/RobotNet10/Shared/RobotNet10.Shared/Numbers/Matrix3x2.cs
Normal file
204
srcs/RobotNet10/Shared/RobotNet10.Shared/Numbers/Matrix3x2.cs
Normal file
@@ -0,0 +1,204 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace RobotNet10.Shared.Numbers;
|
||||
|
||||
/// <summary>
|
||||
/// Custom 3x2 matrix struct for 2D affine transformations.
|
||||
/// Replacement for System.Numerics.Matrix3x2 which doesn't serialize properly.
|
||||
/// </summary>
|
||||
public struct Matrix3x2 : IEquatable<Matrix3x2>
|
||||
{
|
||||
/// <summary>
|
||||
/// Value at row 1, column 1
|
||||
/// </summary>
|
||||
public double M11 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Value at row 1, column 2
|
||||
/// </summary>
|
||||
public double M12 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Value at row 2, column 1
|
||||
/// </summary>
|
||||
public double M21 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Value at row 2, column 2
|
||||
/// </summary>
|
||||
public double M22 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Value at row 3, column 1 (translation X)
|
||||
/// </summary>
|
||||
public double M31 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Value at row 3, column 2 (translation Y)
|
||||
/// </summary>
|
||||
public double M32 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Matrix3x2
|
||||
/// </summary>
|
||||
public Matrix3x2(double m11, double m12, double m21, double m22, double m31, double m32)
|
||||
{
|
||||
M11 = m11;
|
||||
M12 = m12;
|
||||
M21 = m21;
|
||||
M22 = m22;
|
||||
M31 = m31;
|
||||
M32 = m32;
|
||||
}
|
||||
|
||||
#region Static Properties
|
||||
|
||||
/// <summary>
|
||||
/// Returns the identity matrix
|
||||
/// </summary>
|
||||
public static Matrix3x2 Identity => new(1, 0, 0, 1, 0, 0);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Static Methods
|
||||
|
||||
/// <summary>
|
||||
/// Creates a rotation matrix
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Matrix3x2 CreateRotation(double radians)
|
||||
{
|
||||
double cos = Math.Cos(radians);
|
||||
double sin = Math.Sin(radians);
|
||||
return new Matrix3x2(cos, sin, -sin, cos, 0, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a translation matrix
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Matrix3x2 CreateTranslation(double x, double y)
|
||||
{
|
||||
return new Matrix3x2(1, 0, 0, 1, x, y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a translation matrix from a Vector2
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Matrix3x2 CreateTranslation(Vector2 position)
|
||||
{
|
||||
return new Matrix3x2(1, 0, 0, 1, position.X, position.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a scale matrix
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Matrix3x2 CreateScale(double scale)
|
||||
{
|
||||
return new Matrix3x2(scale, 0, 0, scale, 0, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a scale matrix
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Matrix3x2 CreateScale(double scaleX, double scaleY)
|
||||
{
|
||||
return new Matrix3x2(scaleX, 0, 0, scaleY, 0, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies two matrices
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Matrix3x2 Multiply(Matrix3x2 value1, Matrix3x2 value2)
|
||||
{
|
||||
return value1 * value2;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies two matrices
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Matrix3x2 operator *(Matrix3x2 value1, Matrix3x2 value2)
|
||||
{
|
||||
return new Matrix3x2(
|
||||
value1.M11 * value2.M11 + value1.M12 * value2.M21,
|
||||
value1.M11 * value2.M12 + value1.M12 * value2.M22,
|
||||
value1.M21 * value2.M11 + value1.M22 * value2.M21,
|
||||
value1.M21 * value2.M12 + value1.M22 * value2.M22,
|
||||
value1.M31 * value2.M11 + value1.M32 * value2.M21 + value2.M31,
|
||||
value1.M31 * value2.M12 + value1.M32 * value2.M22 + value2.M32
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two matrices are equal
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(Matrix3x2 left, Matrix3x2 right)
|
||||
{
|
||||
return left.M11 == right.M11 && left.M12 == right.M12 &&
|
||||
left.M21 == right.M21 && left.M22 == right.M22 &&
|
||||
left.M31 == right.M31 && left.M32 == right.M32;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two matrices are not equal
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(Matrix3x2 left, Matrix3x2 right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Equality
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this matrix equals another matrix
|
||||
/// </summary>
|
||||
public readonly bool Equals(Matrix3x2 other)
|
||||
{
|
||||
return M11 == other.M11 && M12 == other.M12 &&
|
||||
M21 == other.M21 && M22 == other.M22 &&
|
||||
M31 == other.M31 && M32 == other.M32;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this matrix equals an object
|
||||
/// </summary>
|
||||
public override readonly bool Equals(object? obj)
|
||||
{
|
||||
return obj is Matrix3x2 matrix && Equals(matrix);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hash code for this matrix
|
||||
/// </summary>
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(M11, M12, M21, M22, M31, M32);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region String
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of this matrix
|
||||
/// </summary>
|
||||
public override readonly string ToString()
|
||||
{
|
||||
return $"{{ {{M11:{M11} M12:{M12}}} {{M21:{M21} M22:{M22}}} {{M31:{M31} M32:{M32}}} }}";
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
605
srcs/RobotNet10/Shared/RobotNet10.Shared/Numbers/Quaternion.cs
Normal file
605
srcs/RobotNet10/Shared/RobotNet10.Shared/Numbers/Quaternion.cs
Normal file
@@ -0,0 +1,605 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace RobotNet10.Shared.Numbers;
|
||||
|
||||
/// <summary>
|
||||
/// Custom quaternion struct that supports JSON serialization.
|
||||
/// Replacement for System.Numerics.Quaternion which doesn't serialize properly.
|
||||
/// Represents rotation in 3D space using the formula: q = w + xi + yj + zk
|
||||
/// </summary>
|
||||
public struct Quaternion : IEquatable<Quaternion>
|
||||
{
|
||||
/// <summary>
|
||||
/// X component of the vector part
|
||||
/// </summary>
|
||||
public double X { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Y component of the vector part
|
||||
/// </summary>
|
||||
public double Y { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Z component of the vector part
|
||||
/// </summary>
|
||||
public double Z { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// W component (scalar/real part)
|
||||
/// </summary>
|
||||
public double W { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Quaternion
|
||||
/// </summary>
|
||||
public Quaternion(double x, double y, double z, double w)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Z = z;
|
||||
W = w;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a quaternion from a vector and scalar parts
|
||||
/// </summary>
|
||||
public Quaternion(Vector3 vectorPart, double scalarPart)
|
||||
{
|
||||
X = vectorPart.X;
|
||||
Y = vectorPart.Y;
|
||||
Z = vectorPart.Z;
|
||||
W = scalarPart;
|
||||
}
|
||||
|
||||
#region Static Properties
|
||||
|
||||
/// <summary>
|
||||
/// Returns the identity quaternion (no rotation)
|
||||
/// </summary>
|
||||
public static Quaternion Identity => new(0, 0, 0, 1);
|
||||
|
||||
public static Quaternion FromYawRadian(double yaw)
|
||||
{
|
||||
var halfYaw = yaw / 2.0;
|
||||
return new Quaternion(0, 0, Math.Sin(halfYaw), Math.Cos(halfYaw));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Returns the length (magnitude) of the quaternion
|
||||
/// </summary>
|
||||
public readonly double Length()
|
||||
{
|
||||
return Math.Sqrt(X * X + Y * Y + Z * Z + W * W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the squared length of the quaternion (faster than Length)
|
||||
/// </summary>
|
||||
public readonly double LengthSquared()
|
||||
{
|
||||
return X * X + Y * Y + Z * Z + W * W;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this is a unit quaternion
|
||||
/// </summary>
|
||||
public readonly bool IsIdentity
|
||||
{
|
||||
get
|
||||
{
|
||||
return X == 0 && Y == 0 && Z == 0 && W == 1;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Returns a normalized copy of this quaternion (unit length)
|
||||
/// </summary>
|
||||
public readonly Quaternion Normalize()
|
||||
{
|
||||
double length = Length();
|
||||
if (length < double.Epsilon)
|
||||
return Identity;
|
||||
|
||||
double invLength = 1.0 / length;
|
||||
return new Quaternion(X * invLength, Y * invLength, Z * invLength, W * invLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes this quaternion in place
|
||||
/// </summary>
|
||||
public void NormalizeInPlace()
|
||||
{
|
||||
double length = Length();
|
||||
if (length < double.Epsilon)
|
||||
{
|
||||
X = Y = Z = 0;
|
||||
W = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
double invLength = 1.0 / length;
|
||||
X *= invLength;
|
||||
Y *= invLength;
|
||||
Z *= invLength;
|
||||
W *= invLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the conjugate of this quaternion (negated vector part)
|
||||
/// For unit quaternions, conjugate equals inverse
|
||||
/// </summary>
|
||||
public readonly Quaternion Conjugate()
|
||||
{
|
||||
return new Quaternion(-X, -Y, -Z, W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the inverse of this quaternion
|
||||
/// </summary>
|
||||
public readonly Quaternion Inverse()
|
||||
{
|
||||
double lengthSq = LengthSquared();
|
||||
if (lengthSq < double.Epsilon)
|
||||
return Identity;
|
||||
|
||||
double invLengthSq = 1.0 / lengthSq;
|
||||
return new Quaternion(-X * invLengthSq, -Y * invLengthSq, -Z * invLengthSq, W * invLengthSq);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Static Methods
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the dot product of two quaternions
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double Dot(Quaternion quaternion1, Quaternion quaternion2)
|
||||
{
|
||||
return quaternion1.X * quaternion2.X +
|
||||
quaternion1.Y * quaternion2.Y +
|
||||
quaternion1.Z * quaternion2.Z +
|
||||
quaternion1.W * quaternion2.W;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the conjugate of a quaternion (static version)
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion Conjugate(Quaternion value)
|
||||
{
|
||||
return value.Conjugate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the inverse of a quaternion (static version)
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion Inverse(Quaternion value)
|
||||
{
|
||||
return value.Inverse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a normalized copy of a quaternion (static version)
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion Normalize(Quaternion value)
|
||||
{
|
||||
return value.Normalize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies two quaternions (static version)
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion Multiply(Quaternion value1, Quaternion value2)
|
||||
{
|
||||
return value1 * value2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs spherical linear interpolation between two quaternions
|
||||
/// </summary>
|
||||
public static Quaternion Slerp(Quaternion quaternion1, Quaternion quaternion2, double amount)
|
||||
{
|
||||
double cosOmega = Dot(quaternion1, quaternion2);
|
||||
|
||||
bool flip = false;
|
||||
if (cosOmega < 0.0)
|
||||
{
|
||||
flip = true;
|
||||
cosOmega = -cosOmega;
|
||||
}
|
||||
|
||||
double s1, s2;
|
||||
|
||||
if (cosOmega > (1.0 - 1e-6))
|
||||
{
|
||||
// Too close, do straight linear interpolation
|
||||
s1 = 1.0 - amount;
|
||||
s2 = flip ? -amount : amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
double omega = Math.Acos(cosOmega);
|
||||
double invSinOmega = 1.0 / Math.Sin(omega);
|
||||
|
||||
s1 = Math.Sin((1.0 - amount) * omega) * invSinOmega;
|
||||
s2 = flip
|
||||
? -Math.Sin(amount * omega) * invSinOmega
|
||||
: Math.Sin(amount * omega) * invSinOmega;
|
||||
}
|
||||
|
||||
return new Quaternion(
|
||||
s1 * quaternion1.X + s2 * quaternion2.X,
|
||||
s1 * quaternion1.Y + s2 * quaternion2.Y,
|
||||
s1 * quaternion1.Z + s2 * quaternion2.Z,
|
||||
s1 * quaternion1.W + s2 * quaternion2.W
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs linear interpolation between two quaternions
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion Lerp(Quaternion quaternion1, Quaternion quaternion2, double amount)
|
||||
{
|
||||
double t = amount;
|
||||
double t1 = 1.0 - t;
|
||||
|
||||
Quaternion result;
|
||||
|
||||
double dot = Dot(quaternion1, quaternion2);
|
||||
|
||||
if (dot >= 0.0)
|
||||
{
|
||||
result = new Quaternion(
|
||||
t1 * quaternion1.X + t * quaternion2.X,
|
||||
t1 * quaternion1.Y + t * quaternion2.Y,
|
||||
t1 * quaternion1.Z + t * quaternion2.Z,
|
||||
t1 * quaternion1.W + t * quaternion2.W
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = new Quaternion(
|
||||
t1 * quaternion1.X - t * quaternion2.X,
|
||||
t1 * quaternion1.Y - t * quaternion2.Y,
|
||||
t1 * quaternion1.Z - t * quaternion2.Z,
|
||||
t1 * quaternion1.W - t * quaternion2.W
|
||||
);
|
||||
}
|
||||
|
||||
return result.Normalize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Concatenates two quaternions (applies rotation1 followed by rotation2)
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion Concatenate(Quaternion value1, Quaternion value2)
|
||||
{
|
||||
// This is equivalent to value2 * value1
|
||||
double q1x = value2.X;
|
||||
double q1y = value2.Y;
|
||||
double q1z = value2.Z;
|
||||
double q1w = value2.W;
|
||||
|
||||
double q2x = value1.X;
|
||||
double q2y = value1.Y;
|
||||
double q2z = value1.Z;
|
||||
double q2w = value1.W;
|
||||
|
||||
// Cross product
|
||||
double cx = q1y * q2z - q1z * q2y;
|
||||
double cy = q1z * q2x - q1x * q2z;
|
||||
double cz = q1x * q2y - q1y * q2x;
|
||||
|
||||
double dot = q1x * q2x + q1y * q2y + q1z * q2z;
|
||||
|
||||
return new Quaternion(
|
||||
q1x * q2w + q2x * q1w + cx,
|
||||
q1y * q2w + q2y * q1w + cy,
|
||||
q1z * q2w + q2z * q1w + cz,
|
||||
q1w * q2w - dot
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a quaternion from an axis and angle
|
||||
/// </summary>
|
||||
public static Quaternion CreateFromAxisAngle(Vector3 axis, double angle)
|
||||
{
|
||||
double halfAngle = angle * 0.5;
|
||||
double s = Math.Sin(halfAngle);
|
||||
double c = Math.Cos(halfAngle);
|
||||
|
||||
return new Quaternion(
|
||||
axis.X * s,
|
||||
axis.Y * s,
|
||||
axis.Z * s,
|
||||
c
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a quaternion from yaw, pitch, and roll angles (in radians)
|
||||
/// </summary>
|
||||
public static Quaternion CreateFromYawPitchRoll(double yaw, double pitch, double roll)
|
||||
{
|
||||
double halfRoll = roll * 0.5;
|
||||
double halfPitch = pitch * 0.5;
|
||||
double halfYaw = yaw * 0.5;
|
||||
|
||||
double sinRoll = Math.Sin(halfRoll);
|
||||
double cosRoll = Math.Cos(halfRoll);
|
||||
double sinPitch = Math.Sin(halfPitch);
|
||||
double cosPitch = Math.Cos(halfPitch);
|
||||
double sinYaw = Math.Sin(halfYaw);
|
||||
double cosYaw = Math.Cos(halfYaw);
|
||||
|
||||
return new Quaternion(
|
||||
cosYaw * sinPitch * cosRoll + sinYaw * cosPitch * sinRoll,
|
||||
sinYaw * cosPitch * cosRoll - cosYaw * sinPitch * sinRoll,
|
||||
cosYaw * cosPitch * sinRoll - sinYaw * sinPitch * cosRoll,
|
||||
cosYaw * cosPitch * cosRoll + sinYaw * sinPitch * sinRoll
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a quaternion from a rotation matrix
|
||||
/// </summary>
|
||||
public static Quaternion CreateFromRotationMatrix(double[,] matrix)
|
||||
{
|
||||
if (matrix.GetLength(0) < 3 || matrix.GetLength(1) < 3)
|
||||
return Identity;
|
||||
|
||||
double trace = matrix[0, 0] + matrix[1, 1] + matrix[2, 2];
|
||||
|
||||
Quaternion q = default;
|
||||
|
||||
if (trace > 0.0)
|
||||
{
|
||||
double s = Math.Sqrt(trace + 1.0);
|
||||
q.W = s * 0.5;
|
||||
s = 0.5 / s;
|
||||
q.X = (matrix[2, 1] - matrix[1, 2]) * s;
|
||||
q.Y = (matrix[0, 2] - matrix[2, 0]) * s;
|
||||
q.Z = (matrix[1, 0] - matrix[0, 1]) * s;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (matrix[0, 0] >= matrix[1, 1] && matrix[0, 0] >= matrix[2, 2])
|
||||
{
|
||||
double s = Math.Sqrt(1.0 + matrix[0, 0] - matrix[1, 1] - matrix[2, 2]);
|
||||
double invS = 0.5 / s;
|
||||
q.X = 0.5 * s;
|
||||
q.Y = (matrix[1, 0] + matrix[0, 1]) * invS;
|
||||
q.Z = (matrix[2, 0] + matrix[0, 2]) * invS;
|
||||
q.W = (matrix[2, 1] - matrix[1, 2]) * invS;
|
||||
}
|
||||
else if (matrix[1, 1] > matrix[2, 2])
|
||||
{
|
||||
double s = Math.Sqrt(1.0 + matrix[1, 1] - matrix[0, 0] - matrix[2, 2]);
|
||||
double invS = 0.5 / s;
|
||||
q.X = (matrix[0, 1] + matrix[1, 0]) * invS;
|
||||
q.Y = 0.5 * s;
|
||||
q.Z = (matrix[1, 2] + matrix[2, 1]) * invS;
|
||||
q.W = (matrix[0, 2] - matrix[2, 0]) * invS;
|
||||
}
|
||||
else
|
||||
{
|
||||
double s = Math.Sqrt(1.0 + matrix[2, 2] - matrix[0, 0] - matrix[1, 1]);
|
||||
double invS = 0.5 / s;
|
||||
q.X = (matrix[0, 2] + matrix[2, 0]) * invS;
|
||||
q.Y = (matrix[1, 2] + matrix[2, 1]) * invS;
|
||||
q.Z = 0.5 * s;
|
||||
q.W = (matrix[1, 0] - matrix[0, 1]) * invS;
|
||||
}
|
||||
}
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
|
||||
/// <summary>
|
||||
/// Adds two quaternions component-wise (rarely used - prefer multiplication for combining rotations)
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion operator +(Quaternion value1, Quaternion value2)
|
||||
{
|
||||
return new Quaternion(
|
||||
value1.X + value2.X,
|
||||
value1.Y + value2.Y,
|
||||
value1.Z + value2.Z,
|
||||
value1.W + value2.W
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts two quaternions component-wise (rarely used)
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion operator -(Quaternion value1, Quaternion value2)
|
||||
{
|
||||
return new Quaternion(
|
||||
value1.X - value2.X,
|
||||
value1.Y - value2.Y,
|
||||
value1.Z - value2.Z,
|
||||
value1.W - value2.W
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Negates a quaternion
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion operator -(Quaternion value)
|
||||
{
|
||||
return new Quaternion(-value.X, -value.Y, -value.Z, -value.W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies two quaternions (combines rotations: first apply value2, then value1)
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion operator *(Quaternion value1, Quaternion value2)
|
||||
{
|
||||
double q1x = value1.X;
|
||||
double q1y = value1.Y;
|
||||
double q1z = value1.Z;
|
||||
double q1w = value1.W;
|
||||
|
||||
double q2x = value2.X;
|
||||
double q2y = value2.Y;
|
||||
double q2z = value2.Z;
|
||||
double q2w = value2.W;
|
||||
|
||||
// Cross product
|
||||
double cx = q1y * q2z - q1z * q2y;
|
||||
double cy = q1z * q2x - q1x * q2z;
|
||||
double cz = q1x * q2y - q1y * q2x;
|
||||
|
||||
double dot = q1x * q2x + q1y * q2y + q1z * q2z;
|
||||
|
||||
return new Quaternion(
|
||||
q1x * q2w + q2x * q1w + cx,
|
||||
q1y * q2w + q2y * q1w + cy,
|
||||
q1z * q2w + q2z * q1w + cz,
|
||||
q1w * q2w - dot
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a quaternion by a scalar
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion operator *(Quaternion value1, double value2)
|
||||
{
|
||||
return new Quaternion(
|
||||
value1.X * value2,
|
||||
value1.Y * value2,
|
||||
value1.Z * value2,
|
||||
value1.W * value2
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides a quaternion by a scalar
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Quaternion operator /(Quaternion value1, double value2)
|
||||
{
|
||||
double invValue = 1.0 / value2;
|
||||
return new Quaternion(
|
||||
value1.X * invValue,
|
||||
value1.Y * invValue,
|
||||
value1.Z * invValue,
|
||||
value1.W * invValue
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two quaternions are equal
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(Quaternion value1, Quaternion value2)
|
||||
{
|
||||
return value1.X == value2.X &&
|
||||
value1.Y == value2.Y &&
|
||||
value1.Z == value2.Z &&
|
||||
value1.W == value2.W;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two quaternions are not equal
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(Quaternion value1, Quaternion value2)
|
||||
{
|
||||
return value1.X != value2.X ||
|
||||
value1.Y != value2.Y ||
|
||||
value1.Z != value2.Z ||
|
||||
value1.W != value2.W;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicit conversion from System.Numerics.Quaternion
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Quaternion(System.Numerics.Quaternion value)
|
||||
{
|
||||
return new Quaternion(value.X, value.Y, value.Z, value.W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicit conversion to System.Numerics.Quaternion
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator System.Numerics.Quaternion(Quaternion value)
|
||||
{
|
||||
return new Quaternion((float)value.X, (float)value.Y, (float)value.Z, (float)value.W);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Equality
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this quaternion equals another quaternion
|
||||
/// </summary>
|
||||
public readonly bool Equals(Quaternion other)
|
||||
{
|
||||
return X == other.X && Y == other.Y && Z == other.Z && W == other.W;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this quaternion equals an object
|
||||
/// </summary>
|
||||
public override readonly bool Equals(object? obj)
|
||||
{
|
||||
return obj is Quaternion quaternion && Equals(quaternion);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hash code for this quaternion
|
||||
/// </summary>
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(X, Y, Z, W);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region String
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of this quaternion
|
||||
/// </summary>
|
||||
public override readonly string ToString()
|
||||
{
|
||||
return $"{{X:{X} Y:{Y} Z:{Z} W:{W}}}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a formatted string representation of this quaternion
|
||||
/// </summary>
|
||||
public readonly string ToString(string format)
|
||||
{
|
||||
return $"{{X:{X.ToString(format)} Y:{Y.ToString(format)} Z:{Z.ToString(format)} W:{W.ToString(format)}}}";
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
384
srcs/RobotNet10/Shared/RobotNet10.Shared/Numbers/Vector2.cs
Normal file
384
srcs/RobotNet10/Shared/RobotNet10.Shared/Numbers/Vector2.cs
Normal file
@@ -0,0 +1,384 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace RobotNet10.Shared.Numbers;
|
||||
|
||||
/// <summary>
|
||||
/// Custom 2D vector struct that supports JSON serialization.
|
||||
/// Replacement for System.Numerics.Vector2 which doesn't serialize properly.
|
||||
/// </summary>
|
||||
public struct Vector2 : IEquatable<Vector2>
|
||||
{
|
||||
/// <summary>
|
||||
/// X component
|
||||
/// </summary>
|
||||
public double X { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Y component
|
||||
/// </summary>
|
||||
public double Y { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Vector2
|
||||
/// </summary>
|
||||
public Vector2(double x, double y)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Vector2 with both components set to the same value
|
||||
/// </summary>
|
||||
public Vector2(double value)
|
||||
{
|
||||
X = Y = value;
|
||||
}
|
||||
|
||||
#region Static Properties
|
||||
|
||||
/// <summary>
|
||||
/// Returns a Vector2 with both components set to zero
|
||||
/// </summary>
|
||||
public static Vector2 Zero => new(0, 0);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a Vector2 with both components set to one
|
||||
/// </summary>
|
||||
public static Vector2 One => new(1, 1);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the unit vector for the X axis (1, 0)
|
||||
/// </summary>
|
||||
public static Vector2 UnitX => new(1, 0);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the unit vector for the Y axis (0, 1)
|
||||
/// </summary>
|
||||
public static Vector2 UnitY => new(0, 1);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Returns the length (magnitude) of the vector
|
||||
/// </summary>
|
||||
public readonly double Length()
|
||||
{
|
||||
return Math.Sqrt(X * X + Y * Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the squared length of the vector (faster than Length)
|
||||
/// </summary>
|
||||
public readonly double LengthSquared()
|
||||
{
|
||||
return X * X + Y * Y;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Returns a normalized copy of this vector (unit length)
|
||||
/// </summary>
|
||||
public readonly Vector2 Normalize()
|
||||
{
|
||||
double length = Length();
|
||||
if (length < double.Epsilon)
|
||||
return Zero;
|
||||
|
||||
double invLength = 1.0 / length;
|
||||
return new Vector2(X * invLength, Y * invLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes this vector in place
|
||||
/// </summary>
|
||||
public void NormalizeInPlace()
|
||||
{
|
||||
double length = Length();
|
||||
if (length < double.Epsilon)
|
||||
{
|
||||
X = Y = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
double invLength = 1.0 / length;
|
||||
X *= invLength;
|
||||
Y *= invLength;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Static Methods
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the dot product of two vectors
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double Dot(Vector2 left, Vector2 right)
|
||||
{
|
||||
return left.X * right.X + left.Y * right.Y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a normalized copy of a vector (static version)
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2 Normalize(Vector2 value)
|
||||
{
|
||||
return value.Normalize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the distance between two vectors
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double Distance(Vector2 value1, Vector2 value2)
|
||||
{
|
||||
double dx = value1.X - value2.X;
|
||||
double dy = value1.Y - value2.Y;
|
||||
return Math.Sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the squared distance between two vectors (faster than Distance)
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double DistanceSquared(Vector2 value1, Vector2 value2)
|
||||
{
|
||||
double dx = value1.X - value2.X;
|
||||
double dy = value1.Y - value2.Y;
|
||||
return dx * dx + dy * dy;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs linear interpolation between two vectors
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2 Lerp(Vector2 value1, Vector2 value2, double amount)
|
||||
{
|
||||
return new Vector2(
|
||||
value1.X + (value2.X - value1.X) * amount,
|
||||
value1.Y + (value2.Y - value1.Y) * amount
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a vector with the minimum components of two vectors
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2 Min(Vector2 value1, Vector2 value2)
|
||||
{
|
||||
return new Vector2(
|
||||
Math.Min(value1.X, value2.X),
|
||||
Math.Min(value1.Y, value2.Y)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a vector with the maximum components of two vectors
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2 Max(Vector2 value1, Vector2 value2)
|
||||
{
|
||||
return new Vector2(
|
||||
Math.Max(value1.X, value2.X),
|
||||
Math.Max(value1.Y, value2.Y)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a vector whose components are the absolute values of the input vector
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2 Abs(Vector2 value)
|
||||
{
|
||||
return new Vector2(
|
||||
Math.Abs(value.X),
|
||||
Math.Abs(value.Y)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps a vector to the specified minimum and maximum values
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2 Clamp(Vector2 value, Vector2 min, Vector2 max)
|
||||
{
|
||||
return new Vector2(
|
||||
Math.Clamp(value.X, min.X, max.X),
|
||||
Math.Clamp(value.Y, min.Y, max.Y)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reflects a vector off a surface with the specified normal
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2 Reflect(Vector2 vector, Vector2 normal)
|
||||
{
|
||||
double dot = Dot(vector, normal);
|
||||
return vector - 2.0 * dot * normal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a Vector2 by a Matrix3x2
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2 Transform(Vector2 position, Matrix3x2 matrix)
|
||||
{
|
||||
return new Vector2(
|
||||
position.X * matrix.M11 + position.Y * matrix.M21 + matrix.M31,
|
||||
position.X * matrix.M12 + position.Y * matrix.M22 + matrix.M32
|
||||
);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
|
||||
/// <summary>
|
||||
/// Adds two vectors
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2 operator +(Vector2 left, Vector2 right)
|
||||
{
|
||||
return new Vector2(left.X + right.X, left.Y + right.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts two vectors
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2 operator -(Vector2 left, Vector2 right)
|
||||
{
|
||||
return new Vector2(left.X - right.X, left.Y - right.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Negates a vector
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2 operator -(Vector2 value)
|
||||
{
|
||||
return new Vector2(-value.X, -value.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies two vectors component-wise
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2 operator *(Vector2 left, Vector2 right)
|
||||
{
|
||||
return new Vector2(left.X * right.X, left.Y * right.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a vector by a scalar
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2 operator *(Vector2 left, double right)
|
||||
{
|
||||
return new Vector2(left.X * right, left.Y * right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a scalar by a vector
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2 operator *(double left, Vector2 right)
|
||||
{
|
||||
return new Vector2(left * right.X, left * right.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides two vectors component-wise
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2 operator /(Vector2 left, Vector2 right)
|
||||
{
|
||||
return new Vector2(left.X / right.X, left.Y / right.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides a vector by a scalar
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2 operator /(Vector2 left, double right)
|
||||
{
|
||||
double invRight = 1.0 / right;
|
||||
return new Vector2(left.X * invRight, left.Y * invRight);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two vectors are equal
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(Vector2 left, Vector2 right)
|
||||
{
|
||||
return left.X == right.X && left.Y == right.Y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two vectors are not equal
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(Vector2 left, Vector2 right)
|
||||
{
|
||||
return left.X != right.X || left.Y != right.Y;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Equality
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this vector equals another vector
|
||||
/// </summary>
|
||||
public readonly bool Equals(Vector2 other)
|
||||
{
|
||||
return X == other.X && Y == other.Y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this vector equals an object
|
||||
/// </summary>
|
||||
public override readonly bool Equals(object? obj)
|
||||
{
|
||||
return obj is Vector2 vector && Equals(vector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hash code for this vector
|
||||
/// </summary>
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(X, Y);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region String
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of this vector
|
||||
/// </summary>
|
||||
public override readonly string ToString()
|
||||
{
|
||||
return $"<{X}, {Y}>";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a formatted string representation of this vector
|
||||
/// </summary>
|
||||
public readonly string ToString(string format)
|
||||
{
|
||||
return $"<{X.ToString(format)}, {Y.ToString(format)}>";
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
452
srcs/RobotNet10/Shared/RobotNet10.Shared/Numbers/Vector3.cs
Normal file
452
srcs/RobotNet10/Shared/RobotNet10.Shared/Numbers/Vector3.cs
Normal file
@@ -0,0 +1,452 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace RobotNet10.Shared.Numbers;
|
||||
|
||||
/// <summary>
|
||||
/// Custom 3D vector struct that supports JSON serialization.
|
||||
/// Replacement for System.Numerics.Vector3 which doesn't serialize properly.
|
||||
/// </summary>
|
||||
public struct Vector3 : IEquatable<Vector3>
|
||||
{
|
||||
/// <summary>
|
||||
/// X component
|
||||
/// </summary>
|
||||
public double X { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Y component
|
||||
/// </summary>
|
||||
public double Y { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Z component
|
||||
/// </summary>
|
||||
public double Z { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Vector3
|
||||
/// </summary>
|
||||
public Vector3(double x, double y, double z)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Z = z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Vector3 with all components set to the same value
|
||||
/// </summary>
|
||||
public Vector3(double value)
|
||||
{
|
||||
X = Y = Z = value;
|
||||
}
|
||||
|
||||
#region Static Properties
|
||||
|
||||
/// <summary>
|
||||
/// Returns a Vector3 with all components set to zero
|
||||
/// </summary>
|
||||
public static Vector3 Zero => new(0, 0, 0);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a Vector3 with all components set to one
|
||||
/// </summary>
|
||||
public static Vector3 One => new(1, 1, 1);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the unit vector for the X axis (1, 0, 0)
|
||||
/// </summary>
|
||||
public static Vector3 UnitX => new(1, 0, 0);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the unit vector for the Y axis (0, 1, 0)
|
||||
/// </summary>
|
||||
public static Vector3 UnitY => new(0, 1, 0);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the unit vector for the Z axis (0, 0, 1)
|
||||
/// </summary>
|
||||
public static Vector3 UnitZ => new(0, 0, 1);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Returns the length (magnitude) of the vector
|
||||
/// </summary>
|
||||
public readonly double Length()
|
||||
{
|
||||
return Math.Sqrt(X * X + Y * Y + Z * Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the squared length of the vector (faster than Length)
|
||||
/// </summary>
|
||||
public readonly double LengthSquared()
|
||||
{
|
||||
return X * X + Y * Y + Z * Z;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Returns a normalized copy of this vector (unit length)
|
||||
/// </summary>
|
||||
public readonly Vector3 Normalize()
|
||||
{
|
||||
double length = Length();
|
||||
if (length < double.Epsilon)
|
||||
return Zero;
|
||||
|
||||
double invLength = 1.0 / length;
|
||||
return new Vector3(X * invLength, Y * invLength, Z * invLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes this vector in place
|
||||
/// </summary>
|
||||
public void NormalizeInPlace()
|
||||
{
|
||||
double length = Length();
|
||||
if (length < double.Epsilon)
|
||||
{
|
||||
X = Y = Z = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
double invLength = 1.0 / length;
|
||||
X *= invLength;
|
||||
Y *= invLength;
|
||||
Z *= invLength;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Static Methods
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the dot product of two vectors
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double Dot(Vector3 left, Vector3 right)
|
||||
{
|
||||
return left.X * right.X + left.Y * right.Y + left.Z * right.Z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the cross product of two vectors
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3 Cross(Vector3 left, Vector3 right)
|
||||
{
|
||||
return new Vector3(
|
||||
left.Y * right.Z - left.Z * right.Y,
|
||||
left.Z * right.X - left.X * right.Z,
|
||||
left.X * right.Y - left.Y * right.X
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a normalized copy of a vector (static version)
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3 Normalize(Vector3 value)
|
||||
{
|
||||
return value.Normalize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the distance between two vectors
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double Distance(Vector3 value1, Vector3 value2)
|
||||
{
|
||||
double dx = value1.X - value2.X;
|
||||
double dy = value1.Y - value2.Y;
|
||||
double dz = value1.Z - value2.Z;
|
||||
return Math.Sqrt(dx * dx + dy * dy + dz * dz);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the squared distance between two vectors (faster than Distance)
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double DistanceSquared(Vector3 value1, Vector3 value2)
|
||||
{
|
||||
double dx = value1.X - value2.X;
|
||||
double dy = value1.Y - value2.Y;
|
||||
double dz = value1.Z - value2.Z;
|
||||
return dx * dx + dy * dy + dz * dz;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs linear interpolation between two vectors
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3 Lerp(Vector3 value1, Vector3 value2, double amount)
|
||||
{
|
||||
return new Vector3(
|
||||
value1.X + (value2.X - value1.X) * amount,
|
||||
value1.Y + (value2.Y - value1.Y) * amount,
|
||||
value1.Z + (value2.Z - value1.Z) * amount
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a vector with the minimum components of two vectors
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3 Min(Vector3 value1, Vector3 value2)
|
||||
{
|
||||
return new Vector3(
|
||||
Math.Min(value1.X, value2.X),
|
||||
Math.Min(value1.Y, value2.Y),
|
||||
Math.Min(value1.Z, value2.Z)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a vector with the maximum components of two vectors
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3 Max(Vector3 value1, Vector3 value2)
|
||||
{
|
||||
return new Vector3(
|
||||
Math.Max(value1.X, value2.X),
|
||||
Math.Max(value1.Y, value2.Y),
|
||||
Math.Max(value1.Z, value2.Z)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a vector whose components are the absolute values of the input vector
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3 Abs(Vector3 value)
|
||||
{
|
||||
return new Vector3(
|
||||
Math.Abs(value.X),
|
||||
Math.Abs(value.Y),
|
||||
Math.Abs(value.Z)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps a vector to the specified minimum and maximum values
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3 Clamp(Vector3 value, Vector3 min, Vector3 max)
|
||||
{
|
||||
return new Vector3(
|
||||
Math.Clamp(value.X, min.X, max.X),
|
||||
Math.Clamp(value.Y, min.Y, max.Y),
|
||||
Math.Clamp(value.Z, min.Z, max.Z)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reflects a vector off a surface with the specified normal
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3 Reflect(Vector3 vector, Vector3 normal)
|
||||
{
|
||||
double dot = Dot(vector, normal);
|
||||
return vector - 2.0 * dot * normal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a Vector3 by a Quaternion rotation
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3 Transform(Vector3 value, Quaternion rotation)
|
||||
{
|
||||
// This is the formula: q * v * q^-1 where v is treated as a quaternion with w=0
|
||||
// Optimized version without creating intermediate quaternions
|
||||
|
||||
double x2 = rotation.X + rotation.X;
|
||||
double y2 = rotation.Y + rotation.Y;
|
||||
double z2 = rotation.Z + rotation.Z;
|
||||
|
||||
double wx2 = rotation.W * x2;
|
||||
double wy2 = rotation.W * y2;
|
||||
double wz2 = rotation.W * z2;
|
||||
double xx2 = rotation.X * x2;
|
||||
double xy2 = rotation.X * y2;
|
||||
double xz2 = rotation.X * z2;
|
||||
double yy2 = rotation.Y * y2;
|
||||
double yz2 = rotation.Y * z2;
|
||||
double zz2 = rotation.Z * z2;
|
||||
|
||||
return new Vector3(
|
||||
value.X * (1.0 - yy2 - zz2) + value.Y * (xy2 - wz2) + value.Z * (xz2 + wy2),
|
||||
value.X * (xy2 + wz2) + value.Y * (1.0 - xx2 - zz2) + value.Z * (yz2 - wx2),
|
||||
value.X * (xz2 - wy2) + value.Y * (yz2 + wx2) + value.Z * (1.0 - xx2 - yy2)
|
||||
);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
|
||||
/// <summary>
|
||||
/// Adds two vectors
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3 operator +(Vector3 left, Vector3 right)
|
||||
{
|
||||
return new Vector3(left.X + right.X, left.Y + right.Y, left.Z + right.Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts two vectors
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3 operator -(Vector3 left, Vector3 right)
|
||||
{
|
||||
return new Vector3(left.X - right.X, left.Y - right.Y, left.Z - right.Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Negates a vector
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3 operator -(Vector3 value)
|
||||
{
|
||||
return new Vector3(-value.X, -value.Y, -value.Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies two vectors component-wise
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3 operator *(Vector3 left, Vector3 right)
|
||||
{
|
||||
return new Vector3(left.X * right.X, left.Y * right.Y, left.Z * right.Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a vector by a scalar
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3 operator *(Vector3 left, double right)
|
||||
{
|
||||
return new Vector3(left.X * right, left.Y * right, left.Z * right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a scalar by a vector
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3 operator *(double left, Vector3 right)
|
||||
{
|
||||
return new Vector3(left * right.X, left * right.Y, left * right.Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides two vectors component-wise
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3 operator /(Vector3 left, Vector3 right)
|
||||
{
|
||||
return new Vector3(left.X / right.X, left.Y / right.Y, left.Z / right.Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides a vector by a scalar
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3 operator /(Vector3 left, double right)
|
||||
{
|
||||
double invRight = 1.0 / right;
|
||||
return new Vector3(left.X * invRight, left.Y * invRight, left.Z * invRight);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two vectors are equal
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(Vector3 left, Vector3 right)
|
||||
{
|
||||
return left.X == right.X && left.Y == right.Y && left.Z == right.Z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two vectors are not equal
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(Vector3 left, Vector3 right)
|
||||
{
|
||||
return left.X != right.X || left.Y != right.Y || left.Z != right.Z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicit conversion from System.Numerics.Vector3
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Vector3(System.Numerics.Vector3 value)
|
||||
{
|
||||
return new Vector3(value.X, value.Y, value.Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicit conversion to System.Numerics.Vector3
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator System.Numerics.Vector3(Vector3 value)
|
||||
{
|
||||
return new Vector3((float)value.X, (float)value.Y, (float)value.Z);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Equality
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this vector equals another vector
|
||||
/// </summary>
|
||||
public readonly bool Equals(Vector3 other)
|
||||
{
|
||||
return X == other.X && Y == other.Y && Z == other.Z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this vector equals an object
|
||||
/// </summary>
|
||||
public override readonly bool Equals(object? obj)
|
||||
{
|
||||
return obj is Vector3 vector && Equals(vector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hash code for this vector
|
||||
/// </summary>
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(X, Y, Z);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region String
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of this vector
|
||||
/// </summary>
|
||||
public override readonly string ToString()
|
||||
{
|
||||
return $"<{X}, {Y}, {Z}>";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a formatted string representation of this vector
|
||||
/// </summary>
|
||||
public readonly string ToString(string format)
|
||||
{
|
||||
return $"<{X.ToString(format)}, {Y.ToString(format)}, {Z.ToString(format)}>";
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user