Initial commit

This commit is contained in:
2026-07-13 09:25:40 +07:00
parent c08ff54676
commit bccfb156d7
1938 changed files with 641646 additions and 0 deletions

View 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
}

View 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
}

View 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
}

View 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
}