using System.Runtime.CompilerServices; namespace RobotNet10.Shared.Numbers; /// /// Custom 3D vector struct that supports JSON serialization. /// Replacement for System.Numerics.Vector3 which doesn't serialize properly. /// public struct Vector3 : IEquatable { /// /// X component /// public double X { get; set; } /// /// Y component /// public double Y { get; set; } /// /// Z component /// public double Z { get; set; } /// /// Creates a new Vector3 /// public Vector3(double x, double y, double z) { X = x; Y = y; Z = z; } /// /// Creates a Vector3 with all components set to the same value /// public Vector3(double value) { X = Y = Z = value; } #region Static Properties /// /// Returns a Vector3 with all components set to zero /// public static Vector3 Zero => new(0, 0, 0); /// /// Returns a Vector3 with all components set to one /// public static Vector3 One => new(1, 1, 1); /// /// Returns the unit vector for the X axis (1, 0, 0) /// public static Vector3 UnitX => new(1, 0, 0); /// /// Returns the unit vector for the Y axis (0, 1, 0) /// public static Vector3 UnitY => new(0, 1, 0); /// /// Returns the unit vector for the Z axis (0, 0, 1) /// public static Vector3 UnitZ => new(0, 0, 1); #endregion #region Properties /// /// Returns the length (magnitude) of the vector /// public readonly double Length() { return Math.Sqrt(X * X + Y * Y + Z * Z); } /// /// Returns the squared length of the vector (faster than Length) /// public readonly double LengthSquared() { return X * X + Y * Y + Z * Z; } #endregion #region Methods /// /// Returns a normalized copy of this vector (unit length) /// 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); } /// /// Normalizes this vector in place /// 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 /// /// Calculates the dot product of two vectors /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Dot(Vector3 left, Vector3 right) { return left.X * right.X + left.Y * right.Y + left.Z * right.Z; } /// /// Calculates the cross product of two vectors /// [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 ); } /// /// Returns a normalized copy of a vector (static version) /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Normalize(Vector3 value) { return value.Normalize(); } /// /// Returns the distance between two vectors /// [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); } /// /// Returns the squared distance between two vectors (faster than Distance) /// [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; } /// /// Performs linear interpolation between two vectors /// [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 ); } /// /// Returns a vector with the minimum components of two vectors /// [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) ); } /// /// Returns a vector with the maximum components of two vectors /// [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) ); } /// /// Returns a vector whose components are the absolute values of the input vector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Abs(Vector3 value) { return new Vector3( Math.Abs(value.X), Math.Abs(value.Y), Math.Abs(value.Z) ); } /// /// Clamps a vector to the specified minimum and maximum values /// [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) ); } /// /// Reflects a vector off a surface with the specified normal /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Reflect(Vector3 vector, Vector3 normal) { double dot = Dot(vector, normal); return vector - 2.0 * dot * normal; } /// /// Transforms a Vector3 by a Quaternion rotation /// [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 /// /// Adds two vectors /// [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); } /// /// Subtracts two vectors /// [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); } /// /// Negates a vector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 operator -(Vector3 value) { return new Vector3(-value.X, -value.Y, -value.Z); } /// /// Multiplies two vectors component-wise /// [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); } /// /// Multiplies a vector by a scalar /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 operator *(Vector3 left, double right) { return new Vector3(left.X * right, left.Y * right, left.Z * right); } /// /// Multiplies a scalar by a vector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 operator *(double left, Vector3 right) { return new Vector3(left * right.X, left * right.Y, left * right.Z); } /// /// Divides two vectors component-wise /// [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); } /// /// Divides a vector by a scalar /// [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); } /// /// Checks if two vectors are equal /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Vector3 left, Vector3 right) { return left.X == right.X && left.Y == right.Y && left.Z == right.Z; } /// /// Checks if two vectors are not equal /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Vector3 left, Vector3 right) { return left.X != right.X || left.Y != right.Y || left.Z != right.Z; } /// /// Implicit conversion from System.Numerics.Vector3 /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Vector3(System.Numerics.Vector3 value) { return new Vector3(value.X, value.Y, value.Z); } /// /// Implicit conversion to System.Numerics.Vector3 /// [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 /// /// Checks if this vector equals another vector /// public readonly bool Equals(Vector3 other) { return X == other.X && Y == other.Y && Z == other.Z; } /// /// Checks if this vector equals an object /// public override readonly bool Equals(object? obj) { return obj is Vector3 vector && Equals(vector); } /// /// Gets the hash code for this vector /// public override readonly int GetHashCode() { return HashCode.Combine(X, Y, Z); } #endregion #region String /// /// Returns a string representation of this vector /// public override readonly string ToString() { return $"<{X}, {Y}, {Z}>"; } /// /// Returns a formatted string representation of this vector /// public readonly string ToString(string format) { return $"<{X.ToString(format)}, {Y.ToString(format)}, {Z.ToString(format)}>"; } #endregion }