using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Text;
namespace GenericVector;
internal static class Vector
{
/// Gets the element at the specified index.
/// The vector to get the element from.
/// The index of the element to get.
/// The value of the element at .
/// was less than zero or greater than the number of elements.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static T GetElement(this Vector3 vector, int index) where T : INumber
{
if ((uint)(index) >= (uint)(Vector3.Count))
{
throw new ArgumentOutOfRangeException(nameof(index));
}
return vector.GetElementUnsafe(index);
}
/// Creates a new with the element at the specified index set to the specified value and the remaining elements set to the same value as that in the given vector.
/// The vector to get the remaining elements from.
/// The index of the element to set.
/// The value to set the element to.
/// A with the value of the element at set to and the remaining elements set to the same value as that in .
/// was less than zero or greater than the number of elements.
internal static Vector3 WithElement(this Vector3 vector, int index, T value) where T : INumber
{
if ((uint)(index) >= (uint)(Vector3.Count))
{
throw new ArgumentOutOfRangeException(nameof(index));
}
var result = vector;
result.SetElementUnsafe(index, value);
return result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static T GetElementUnsafe(in this Vector3 vector, int index) where T : INumber
{
Debug.Assert((index >= 0) && (index < Vector3.Count));
ref T address = ref Unsafe.AsRef(in vector.X);
return Unsafe.Add(ref address, index);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void SetElementUnsafe(ref this Vector3 vector, int index, T value) where T : INumber
{
Debug.Assert((index >= 0) && (index < Vector3.Count));
Unsafe.Add(ref vector.X, index) = value;
}
}
public struct Vector3 : IEquatable>, ISpanFormattable
where T : INumber
{
/// The X component of the vector.
public T X;
/// The Y component of the vector.
public T Y;
/// The Z component of the vector.
public T Z;
// private unsafe fixed T Components[3];
///
/// Returns the vector (0,0,0).
///
public static Vector3 Zero => new();
///
/// Returns the vector (1,1,1).
///
public static Vector3 One => new(T.CreateTruncating(1), T.CreateTruncating(1), T.CreateTruncating(1));
///
/// Returns the vector (1,0,0).
///
public static Vector3 UnitX => new(T.CreateTruncating(1), T.CreateTruncating(0), T.CreateTruncating(0));
///
/// Returns the vector (0,1,0).
///
public static Vector3 UnitY => new(T.CreateTruncating(0), T.CreateTruncating(1), T.CreateTruncating(0));
///
/// Returns the vector (0,0,1).
///
public static Vector3 UnitZ => new(T.CreateTruncating(0), T.CreateTruncating(0), T.CreateTruncating(1));
public static int Count => 3;
/// Gets or sets the element at the specified index.
/// The index of the element to get or set.
/// The the element at .
/// was less than zero or greater than the number of elements.
public T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
readonly get => this.GetElement(index);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => this = this.WithElement(index, value);
}
/// Creates a new object whose three elements have the same value.
/// The value to assign to all three elements.
public Vector3(T value) : this(value, value, value)
{
}
/// Creates a vector whose elements have the specified values.
/// The value to assign to the field.
/// The value to assign to the field.
/// The value to assign to the field.
public Vector3(T x, T y, T z)
{
X = x;
Y = y;
Z = z;
}
/// Constructs a vector from the given . The span must contain at least 3 elements.
/// The span of elements to assign to the vector.
public Vector3(ReadOnlySpan values)
{
if (values.Length < 3)
{
throw new ArgumentOutOfRangeException(nameof(values));
}
this = Unsafe.ReadUnaligned>(ref Unsafe.As(ref MemoryMarshal.GetReference(values)));
}
public override string ToString()
{
return ToString("");
}
///
/// Returns a String representing this Vector3 instance, using the specified format to format individual elements.
///
/// The format of individual elements.
/// The string representation.
public string ToString(string format)
{
return ToString(format, CultureInfo.CurrentCulture);
}
///
/// Returns a String representing this Vector3 instance, using the specified format to format individual elements
/// and the given IFormatProvider.
///
/// The format of individual elements.
/// The format provider to use when formatting elements.
/// The string representation.
public string ToString(string? format, IFormatProvider? formatProvider)
{
var sb = new StringBuilder();
var separator = NumberFormatInfo.GetInstance(formatProvider).NumberGroupSeparator;
sb.Append('<');
sb.Append(X.ToString(format, formatProvider));
sb.Append(separator);
sb.Append(' ');
sb.Append(Y.ToString(format, formatProvider));
sb.Append(separator);
sb.Append(' ');
sb.Append(Z.ToString(format, formatProvider));
sb.Append('>');
return sb.ToString();
}
/// Returns a value that indicates whether this instance and a specified object are equal.
/// The object to compare with the current instance.
/// if the current instance and are equal; otherwise, . If is , the method returns .
/// The current instance and are equal if is a object and their corresponding elements are equal.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly override bool Equals([NotNullWhen(true)] object? obj)
{
return (obj is Vector3 other) && Equals(other);
}
/// Returns a value that indicates whether this instance and another vector are equal.
/// The other vector.
/// if the two vectors are equal; otherwise, .
/// Two vectors are equal if their , , and elements are equal.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly bool Equals(Vector3 other)
{
// This function needs to account for floating-point equality around NaN
// and so must behave equivalently to the underlying float/double.Equals
if (typeof(T) == typeof(float))
{
if (Vector128.IsHardwareAccelerated)
{
var _this = Unsafe.As, Vector3>(ref Unsafe.AsRef(in this));
var _other = Unsafe.As, Vector3>(ref Unsafe.AsRef(in other));
return _this.AsVector128().Equals(_other.AsVector128());
}
}
return SoftwareFallback(in this, other);
static bool SoftwareFallback(in Vector3 self, Vector3 other)
{
return self.X.Equals(other.X)
&& self.Y.Equals(other.Y)
&& self.Z.Equals(other.Z);
}
}
/// Returns the hash code for this instance.
/// The hash code.
public readonly override int GetHashCode()
{
return HashCode.Combine(X, Y, Z);
}
public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider)
{
throw new NotImplementedException(); // TODO
}
private Vector3 As() where TOther : INumber
{
return new Vector3(
TOther.CreateTruncating(X),
TOther.CreateTruncating(Y),
TOther.CreateTruncating(Z)
);
}
#region Maths
/// Adds two vectors together.
/// The first vector to add.
/// The second vector to add.
/// The summed vector.
/// The method defines the addition operation for objects.
[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 the first vector by the second.
/// The first vector.
/// The second vector.
/// The vector that results from dividing by .
/// The method defines the division operation for objects.
[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 the specified vector by a specified scalar value.
/// The vector.
/// The scalar value.
/// The result of the division.
/// The method defines the division operation for objects.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 operator /(Vector3 value1, T value2)
{
return value1 / new Vector3(value2);
}
/// Returns a value that indicates whether each pair of elements in two specified vectors is equal.
/// The first vector to compare.
/// The second vector to compare.
/// if and are equal; otherwise, .
/// Two objects are equal if each element in is equal to the corresponding element in .
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Vector3 left, Vector3 right)
{
return (left.X == right.X)
&& (left.Y == right.Y)
&& (left.Z == right.Z);
}
/// Returns a value that indicates whether two specified vectors are not equal.
/// The first vector to compare.
/// The second vector to compare.
/// if and are not equal; otherwise, .
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Vector3 left, Vector3 right)
{
return !(left == right);
}
/// Returns a new vector whose values are the product of each pair of elements in two specified vectors.
/// The first vector.
/// The second vector.
/// The element-wise product vector.
/// The method defines the multiplication operation for objects.
[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 the specified vector by the specified scalar value.
/// The vector.
/// The scalar value.
/// The scaled vector.
/// The method defines the multiplication operation for objects.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 operator *(Vector3 left, T right)
{
return left * new Vector3(right);
}
/// Multiplies the scalar value by the specified vector.
/// The vector.
/// The scalar value.
/// The scaled vector.
/// The method defines the multiplication operation for objects.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 operator *(T left, Vector3 right)
{
return right * left;
}
/// Subtracts the second vector from the first.
/// The first vector.
/// The second vector.
/// The vector that results from subtracting from .
/// The method defines the subtraction operation for objects.
[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 the specified vector.
/// The vector to negate.
/// The negated vector.
/// The method defines the unary negation operation for objects.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 operator -(Vector3 value)
{
return Zero - value;
}
/// Returns a vector whose elements are the absolute values of each of the specified vector's elements.
/// A vector.
/// The absolute value vector.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 Abs(Vector3 value)
{
return new Vector3(
T.Abs(value.X),
T.Abs(value.Y),
T.Abs(value.Z)
);
}
/// Adds two vectors together.
/// The first vector to add.
/// The second vector to add.
/// The summed vector.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 Add(Vector3 left, Vector3 right)
{
return left + right;
}
/// Restricts a vector between a minimum and a maximum value.
/// The vector to restrict.
/// The minimum value.
/// The maximum value.
/// The restricted vector.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 Clamp(Vector3 value1, Vector3 min, Vector3 max)
{
// We must follow HLSL behavior in the case user specified min value is bigger than max value.
return Min(Max(value1, min), max);
}
/// Computes the cross product of two vectors.
/// The first vector.
/// The second vector.
/// The cross product.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 Cross(Vector3 vector1, Vector3 vector2)
{
return new Vector3(
(vector1.Y * vector2.Z) - (vector1.Z * vector2.Y),
(vector1.Z * vector2.X) - (vector1.X * vector2.Z),
(vector1.X * vector2.Y) - (vector1.Y * vector2.X)
);
}
/// Computes the cross product of two vectors.
/// The first vector.
/// The second vector.
/// The cross product.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 Cross(Vector3 vector1, Vector3 vector2) where TIntermediate : INumber
{
return new Vector3(
T.CreateTruncating((TIntermediate.CreateTruncating(vector1.Y) * TIntermediate.CreateTruncating(vector2.Z)) - (TIntermediate.CreateTruncating(vector1.Z) * TIntermediate.CreateTruncating(vector2.Y))),
T.CreateTruncating((TIntermediate.CreateTruncating(vector1.Z) * TIntermediate.CreateTruncating(vector2.X)) - (TIntermediate.CreateTruncating(vector1.X) * TIntermediate.CreateTruncating(vector2.Z))),
T.CreateTruncating((TIntermediate.CreateTruncating(vector1.X) * TIntermediate.CreateTruncating(vector2.Y)) - (TIntermediate.CreateTruncating(vector1.Y) * TIntermediate.CreateTruncating(vector2.X)))
);
}
/// Computes the Euclidean distance between the two given points.
/// The first point.
/// The second point.
/// The distance.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TReturn Distance(Vector3 value1, Vector3 value2) where TReturn : INumber, IRootFunctions
{
var distanceSquared = DistanceSquared(value1, value2);
return TReturn.Sqrt(TReturn.CreateTruncating(distanceSquared));
}
/// Returns the Euclidean distance squared between two specified points.
/// The first point.
/// The second point.
/// The distance squared.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T DistanceSquared(Vector3 value1, Vector3 value2)
{
var difference = value1 - value2;
return Dot(difference, difference);
}
/// Returns the Euclidean distance squared between two specified points.
/// The first point.
/// The second point.
/// The distance squared.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TReturn DistanceSquared(Vector3 value1, Vector3 value2) where TReturn : INumber
{
var difference = value1 - value2;
return Dot(difference, difference);
}
/// Divides the first vector by the second.
/// The first vector.
/// The second vector.
/// The vector resulting from the division.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 Divide(Vector3 left, Vector3 right)
{
return left / right;
}
/// Divides the specified vector by a specified scalar value.
/// The vector.
/// The scalar value.
/// The vector that results from the division.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 Divide(Vector3 left, T divisor)
{
return left / divisor;
}
/// Returns the dot product of two vectors.
/// The first vector.
/// The second vector.
/// The dot product.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Dot(Vector3 vector1, Vector3 vector2)
{
return (vector1.X * vector2.X)
+ (vector1.Y * vector2.Y)
+ (vector1.Z * vector2.Z);
}
/// Returns the dot product of two vectors.
/// The first vector.
/// The second vector.
/// The dot product.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TReturn Dot(Vector3 vector1, Vector3 vector2) where TReturn : INumber
{
return (TReturn.CreateTruncating(vector1.X) * TReturn.CreateTruncating(vector2.X))
+ (TReturn.CreateTruncating(vector1.Y) * TReturn.CreateTruncating(vector2.Y))
+ (TReturn.CreateTruncating(vector1.Z) * TReturn.CreateTruncating(vector2.Z));
}
/// Performs a linear interpolation between two vectors based on the given weighting.
/// The first vector.
/// The second vector.
/// A value between 0 and 1 that indicates the weight of .
/// The interpolated vector.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 Lerp(Vector3 value1, Vector3 value2, T amount)
{
return (value1 * (T.One - amount)) + (value2 * amount);
}
/// Performs a linear interpolation between two vectors based on the given weighting.
/// The first vector.
/// The second vector.
/// A value between 0 and 1 that indicates the weight of .
/// The interpolated vector.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 Lerp(Vector3 value1, Vector3 value2, TFloat amount) where TFloat : INumber, IFloatingPoint
{
return (value1.As() * (TFloat.One - amount)) + (value2.As() * amount);
}
/// Returns a vector whose elements are the maximum of each of the pairs of elements in two specified vectors.
/// The first vector.
/// The second vector.
/// The maximized vector.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 Max(Vector3 value1, Vector3 value2)
{
return new Vector3(
(value1.X > value2.X) ? value1.X : value2.X,
(value1.Y > value2.Y) ? value1.Y : value2.Y,
(value1.Z > value2.Z) ? value1.Z : value2.Z
);
}
/// Returns a vector whose elements are the minimum of each of the pairs of elements in two specified vectors.
/// The first vector.
/// The second vector.
/// The minimized vector.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 Min(Vector3 value1, Vector3 value2)
{
return new Vector3(
(value1.X < value2.X) ? value1.X : value2.X,
(value1.Y < value2.Y) ? value1.Y : value2.Y,
(value1.Z < value2.Z) ? value1.Z : value2.Z
);
}
/// Returns a new vector whose values are the product of each pair of elements in two specified vectors.
/// The first vector.
/// The second vector.
/// The element-wise product vector.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 Multiply(Vector3 left, Vector3 right)
{
return left * right;
}
/// Multiplies a vector by a specified scalar.
/// The vector to multiply.
/// The scalar value.
/// The scaled vector.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 Multiply(Vector3 left, T right)
{
return left * right;
}
/// Multiplies a scalar value by a specified vector.
/// The scaled value.
/// The vector.
/// The scaled vector.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 Multiply(T left, Vector3 right)
{
return left * right;
}
/// Negates a specified vector.
/// The vector to negate.
/// The negated vector.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 Negate(Vector3 value)
{
return -value;
}
/// Returns a vector with the same direction as the specified vector, but with a length of one.
/// The vector to normalize.
/// The normalized vector.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 Normalize(Vector3 value) where TReturn : IFloatingPoint, IRootFunctions
{
return value.As() / value.Length();
}
/// Returns the reflection of a vector off a surface that has the specified normal.
/// The source vector.
/// The normal of the surface being reflected off.
/// The reflected vector.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 Reflect(Vector3 vector, Vector3 normal) where TReturn : IFloatingPoint
{
var dot = Dot(vector, normal);
return vector.As() - (TReturn.CreateTruncating(2) * (dot * normal.As()));
}
/// Returns a vector whose elements are the square root of each of a specified vector's elements.
/// A vector.
/// The square root vector.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 SquareRoot(Vector3 value) where TReturn : IFloatingPoint, IRootFunctions
{
return new Vector3(
TReturn.Sqrt(TReturn.CreateTruncating(value.X)),
TReturn.Sqrt(TReturn.CreateTruncating(value.Y)),
TReturn.Sqrt(TReturn.CreateTruncating(value.Z))
);
}
/// Subtracts the second vector from the first.
/// The first vector.
/// The second vector.
/// The difference vector.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 Subtract(Vector3 left, Vector3 right)
{
return left - right;
}
// /// Transforms a vector by a specified 4x4 matrix.
// /// The vector to transform.
// /// The transformation matrix.
// /// The transformed vector.
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// public static Vector3 Transform(Vector3 position, Matrix4x4 matrix)
// {
// return Vector4.Transform(position, in matrix.AsImpl()).AsVector128().AsVector3();
// }
/// Transforms a vector by the specified Quaternion rotation value.
/// The vector to rotate.
/// The rotation to apply.
/// The transformed vector.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 Transform(Vector3 value, Quaternion rotation) where TReturn : INumber
{
var x2 = rotation.X + rotation.X;
var y2 = rotation.Y + rotation.Y;
var z2 = rotation.Z + rotation.Z;
var wx2 = TReturn.CreateTruncating(rotation.W * x2);
var wy2 = TReturn.CreateTruncating(rotation.W * y2);
var wz2 = TReturn.CreateTruncating(rotation.W * z2);
var xx2 = TReturn.CreateTruncating(rotation.X * x2);
var xy2 = TReturn.CreateTruncating(rotation.X * y2);
var xz2 = TReturn.CreateTruncating(rotation.X * z2);
var yy2 = TReturn.CreateTruncating(rotation.Y * y2);
var yz2 = TReturn.CreateTruncating(rotation.Y * z2);
var zz2 = TReturn.CreateTruncating(rotation.Z * z2);
return new Vector3(
TReturn.CreateTruncating(value.X) * (TReturn.One - yy2 - zz2) + TReturn.CreateTruncating(value.Y) * (xy2 - wz2) + TReturn.CreateTruncating(value.Z) * (xz2 + wy2),
TReturn.CreateTruncating(value.X) * (xy2 + wz2) + TReturn.CreateTruncating(value.Y) * (TReturn.One - xx2 - zz2) + TReturn.CreateTruncating(value.Z) * (yz2 - wx2),
TReturn.CreateTruncating(value.X) * (xz2 - wy2) + TReturn.CreateTruncating(value.Y) * (yz2 + wx2) + TReturn.CreateTruncating(value.Z) * (TReturn.One - xx2 - yy2)
);
}
// /// Transforms a vector normal by the given 4x4 matrix.
// /// The source vector.
// /// The matrix.
// /// The transformed vector.
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// public static Vector3 TransformNormal(Vector3 normal, Matrix4x4 matrix)
// {
// return TransformNormal(normal, in matrix.AsImpl());
// }
//
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// internal static Vector3 TransformNormal(Vector3 normal, in Matrix4x4.Impl matrix)
// {
// Vector4 result = matrix.X * normal.X;
//
// result += matrix.Y * normal.Y;
// result += matrix.Z * normal.Z;
//
// return result.AsVector128().AsVector3();
// }
/// Returns the length of this vector object.
/// The vector's length.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly TReturn Length() where TReturn : INumber, IFloatingPoint, IRootFunctions
{
var lengthSquared = LengthSquared();
return TReturn.Sqrt(lengthSquared);
}
/// Returns the length of the vector squared.
/// The vector's length squared.
/// This operation offers better performance than a call to the method.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly T LengthSquared()
{
return Dot(this, this);
}
/// Returns the length of the vector squared.
/// The vector's length squared.
/// This operation offers better performance than a call to the method.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly TReturn LengthSquared() where TReturn : INumber
{
return Dot(this, this);
}
#endregion
}