Add support System.Numerics.Vectors types with double precision
jkotas opened this issue Β· 121 comments
The approved API is here: #24168 (comment)
Due to the size of the approved surface area, it is being split into multiple sections for people who want to help implement it. You can see #24168 (comment) for more details
Vector2, Vector3, Vector4, Matrix3x2, Matrix4x4, Plane, Quaternion are currently implemented with single precission numbers.
Theare are requests (above) and some miror implementations of System.Numerics.* can be found across Internet where double is supported.
These classes should support float and double precission and maybe in future more types. One of possile solution is to miror them as generics
with support for both double and single precission numbers.
Rationale and Usage
This is request that developers ask about, one of usages is CAD software.
For example, to use Vector3 and Matrix4x4 with double precission we will be able to write:
var v1 = new Vector3<double>(4,5,4);
var v2 = new Vector3<double>(1,3,2);
double d = Vector3<double>.Dot(v1, v2);
Matrix4x4<double> xRot = Matrix4x4<double>.CreateTranslation(v1);
Vector3<double> tr = Vector3<double>.Transform(v2,xRot);
Proposed API (mirrored from Vector2, Vector3, Vector4, Matrix3x2, Matrix4x4, Plane)
The following APIs are duplicates of the existing float
based APIs, but with float
replaced with T
to allow for float
, double
, and in the future System.Half
.
File Vector2OfT.cs
public partial struct Vector2<T> : IEquatable<Vector2<T>>, IFormattable
{
public static Vector2<T> Zero { get; }
public static Vector2<T> One { get; }
public static Vector2<T> UnitX { get; }
public static Vector2<T> UnitY { get; }
public override int GetHashCode();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj);
public override string ToString();
public string ToString(string format);
public string ToString(string format, IFormatProvider formatProvider);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T Length();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T LengthSquared();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Distance(Vector2<T> value1, Vector2<T> value2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T DistanceSquared(Vector2<T> value1, Vector2<T> value2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Normalize(Vector2<T> value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Reflect(Vector2<T> vector, Vector2<T> normal);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Clamp(Vector2<T> value1, Vector2<T> min, Vector2<T> max);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Lerp(Vector2<T> value1, Vector2<T> value2, T amount);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Transform(Vector2<T> position, Matrix3x2<T> matrix);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Transform(Vector2<T> position, Matrix4x4<T> matrix);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> TransformNormal(Vector2<T> normal, Matrix3x2<T> matrix);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> TransformNormal(Vector2<T> normal, Matrix4x4<T> matrix);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Transform(Vector2<T> value, Quaternion<T> rotation);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Add(Vector2<T> left, Vector2<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Subtract(Vector2<T> left, Vector2<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Multiply(Vector2<T> left, Vector2<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Multiply(Vector2<T> left, T right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Multiply(T left, Vector2<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Divide(Vector2<T> left, Vector2<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Divide(Vector2<T> left, T divisor);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Negate(Vector2<T> value);
}
File Vector2_IntrinsicsOfT.cs
public partial struct Vector2<T>
{
public T X;
public T Y;
[JitIntrinsic]
public Vector2(T value);
[JitIntrinsic]
public Vector2(T x, T y);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CopyTo(T[] array);
public void CopyTo(T[] array, int index);
[JitIntrinsic]
public bool Equals(global::System.Numerics.Vector2<T> other);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Dot(global::System.Numerics.Vector2<T> value1, global::System.Numerics.Vector2<T> value2);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static global::System.Numerics.Vector2<T> Min(global::System.Numerics.Vector2<T> value1, global::System.Numerics.Vector2<T> value2);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static global::System.Numerics.Vector2<T> Max(global::System.Numerics.Vector2<T> value1, global::System.Numerics.Vector2<T> value2);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static global::System.Numerics.Vector2<T> Abs(global::System.Numerics.Vector2<T> value);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static global::System.Numerics.Vector2<T> SquareRoot(global::System.Numerics.Vector2<T> value);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static global::System.Numerics.Vector2<T> operator +(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static global::System.Numerics.Vector2<T> operator -(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static global::System.Numerics.Vector2<T> operator *(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static global::System.Numerics.Vector2<T> operator *(T left, global::System.Numerics.Vector2<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static global::System.Numerics.Vector2<T> operator *(global::System.Numerics.Vector2<T> left, T right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static global::System.Numerics.Vector2<T> operator /(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static global::System.Numerics.Vector2<T> operator /(global::System.Numerics.Vector2<T> value1, T value2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static global::System.Numerics.Vector2<T> operator -(global::System.Numerics.Vector2<T> value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Vector2<T>(Vector2 value);
}
File Vector3OfT.cs
public partial struct Vector3<T> : IEquatable<Vector3<T>>, IFormattable
{
public static Vector3<T> Zero { get; }
public static Vector3<T> One { get; }
public static Vector3<T> UnitX { get; }
public static Vector3<T> UnitY { get; }
public static Vector3<T> UnitZ { get; }
public override int GetHashCode();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj);
public override string ToString();
public string ToString(string format);
public string ToString(string format, IFormatProvider formatProvider);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T Length();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T LengthSquared();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Distance(Vector3<T> value1, Vector3<T> value2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T DistanceSquared(Vector3<T> value1, Vector3<T> value2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Normalize(Vector3<T> value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Cross(Vector3<T> vector1, Vector3<T> vector2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Reflect(Vector3<T> vector, Vector3<T> normal);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Clamp(Vector3<T> value1, Vector3<T> min, Vector3<T> max);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Lerp(Vector3<T> value1, Vector3<T> value2, T amount);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Transform(Vector3<T> position, Matrix4x4<T> matrix);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> TransformNormal(Vector3<T> normal, Matrix4x4<T> matrix);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Transform(Vector3<T> value, Quaternion<T> rotation);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Add(Vector3<T> left, Vector3<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Subtract(Vector3<T> left, Vector3<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Multiply(Vector3<T> left, Vector3<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Multiply(Vector3<T> left, T right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Multiply(T left, Vector3<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Divide(Vector3<T> left, Vector3<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Divide(Vector3<T> left, T divisor);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Negate(Vector3<T> value);
}
File Vector3_IntrinsicsOfT.cs
public partial struct Vector3<T>
{
public T X;
public T Y;
public T Z;
[JitIntrinsic]
public Vector3(T value);
public Vector3(Vector2<T> value, T z);
[JitIntrinsic]
public Vector3(T x, T y, T z);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CopyTo(T[] array);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CopyTo(T[] array, int index);
[JitIntrinsic]
public bool Equals(Vector3<T> other);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Dot(Vector3<T> vector1, Vector3<T> vector2);
[JitIntrinsic]
public static Vector3<T> Min(Vector3<T> value1, Vector3<T> value2);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Max(Vector3<T> value1, Vector3<T> value2);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Abs(Vector3<T> value);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> SquareRoot(Vector3<T> value);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> operator +(Vector3<T> left, Vector3<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> operator -(Vector3<T> left, Vector3<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> operator *(Vector3<T> left, Vector3<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> operator *(Vector3<T> left, T right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> operator *(T left, Vector3<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> operator /(Vector3<T> left, Vector3<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> operator /(Vector3<T> value1, T value2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> operator -(Vector3<T> value);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Vector3<T> left, Vector3<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Vector3<T> left, Vector3<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Vector3<T>(Vector3 value);
}
File Vector4OfT.cs
public partial struct Vector4<T> : IEquatable<Vector4<T>>, IFormattable
{
public static Vector4<T> Zero { get; }
public static Vector4<T> One { get; }
public static Vector4<T> UnitX { get; }
public static Vector4<T> UnitY { get ; }
public static Vector4<T> UnitZ { get; }
public static Vector4<T> UnitW { get; };
public override int GetHashCode();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj);
public override string ToString();
public string ToString(string format);
public string ToString(string format, IFormatProvider formatProvider);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T Length();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T LengthSquared();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Distance(Vector4<T> value1, Vector4<T> value2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T DistanceSquared(Vector4<T> value1, Vector4<T> value2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Normalize(Vector4<T> vector);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Clamp(Vector4<T> value1, Vector4<T> min, Vector4<T> max);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Lerp(Vector4<T> value1, Vector4<T> value2, T amount);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Transform(Vector2<T> position, Matrix4x4<T> matrix);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Transform(Vector3<T> position, Matrix4x4<T> matrix);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Transform(Vector4<T> vector, Matrix4x4<T> matrix);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Transform(Vector2<T> value, Quaternion<T> rotation);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Transform(Vector3<T> value, Quaternion<T> rotation);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Transform(Vector4<T> value, Quaternion<T> rotation);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Add(Vector4<T> left, Vector4<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Subtract(Vector4<T> left, Vector4<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Multiply(Vector4<T> left, Vector4<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Multiply(Vector4<T> left, T right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Multiply(T left, Vector4<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Divide(Vector4<T> left, Vector4<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Divide(Vector4<T> left, T divisor);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Negate(Vector4<T> value);
}
File Vector4OfT_Intrinsics.cs
public partial struct Vector4<T>
{
public T X;
public T Y;
public T Z;
public T W;
[JitIntrinsic]
public Vector4(T value);
[JitIntrinsic]
public Vector4(T x, T y, T z, T w);
public Vector4(Vector2<T> value, T z, T w);
public Vector4(Vector3<T> value, T w);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CopyTo(T[] array);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CopyTo(T[] array, int index);
[JitIntrinsic]
public bool Equals(Vector4<T> other);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Dot(Vector4<T> vector1, Vector4<T> vector2);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Min(Vector4<T> value1, Vector4<T> value2);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Max(Vector4<T> value1, Vector4<T> value2);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Abs(Vector4<T> value);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> SquareRoot(Vector4<T> value);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> operator +(Vector4<T> left, Vector4<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> operator -(Vector4<T> left, Vector4<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> operator *(Vector4<T> left, Vector4<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> operator *(Vector4<T> left, T right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> operator *(T left, Vector4<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> operator /(Vector4<T> left, Vector4<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> operator /(Vector4<T> value1, T value2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> operator -(Vector4<T> value);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Vector4<T> left, Vector4<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Vector4<T> left, Vector4<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Vector4<T>(Vector4 value);
}
File Matrix3x2OfT.cs
public struct Matrix3x2<T> : IEquatable<Matrix3x2<T>>
{
public T M11;
public T M12;
public T M21;
public T M22;
public T M31;
public T M32;
public static Matrix3x2<T> Identity { get; }
public bool IsIdentity { get; }
public Vector2<T> Translation { get; set; }
public Matrix3x2(T m11, T m12, T m21, T m22, T m31, T m32);
public static Matrix3x2<T> CreateTranslation(Vector2<T> position);
public static Matrix3x2<T> CreateTranslation(T xPosition, T yPosition);
public static Matrix3x2<T> CreateScale(T xScale, T yScale);
public static Matrix3x2<T> CreateScale(T xScale, T yScale, Vector2<T> centerPoint);
public static Matrix3x2<T> CreateScale(Vector2<T> scales);
public static Matrix3x2<T> CreateScale(Vector2<T> scales, Vector2<T> centerPoint);
public static Matrix3x2<T> CreateScale(T scale);
public static Matrix3x2<T> CreateScale(T scale, Vector2<T> centerPoint);
public static Matrix3x2<T> CreateSkew(T radiansX, T radiansY);
public static Matrix3x2<T> CreateSkew(T radiansX, T radiansY, Vector2<T> centerPoint);
public static Matrix3x2<T> CreateRotation(T radians);
public static Matrix3x2<T> CreateRotation(T radians, Vector2<T> centerPoint);
public T GetDeterminant();
public static bool Invert(Matrix3x2<T> matrix, out Matrix3x2<T> result);
public static Matrix3x2<T> Lerp(Matrix3x2<T> matrix1, Matrix3x2<T> matrix2, T amount);
public static Matrix3x2<T> Negate(Matrix3x2<T> value);
public static Matrix3x2<T> Add(Matrix3x2<T> value1, Matrix3x2<T> value2);
public static Matrix3x2<T> Subtract(Matrix3x2<T> value1, Matrix3x2<T> value2);
public static Matrix3x2<T> Multiply(Matrix3x2<T> value1, Matrix3x2<T> value2);
public static Matrix3x2<T> Multiply(Matrix3x2<T> value1, T value2);
public static Matrix3x2<T> operator -(Matrix3x2<T> value);
public static Matrix3x2<T> operator +(Matrix3x2<T> value1, Matrix3x2<T> value2);
public static Matrix3x2<T> operator -(Matrix3x2<T> value1, Matrix3x2<T> value2);
public static Matrix3x2<T> operator *(Matrix3x2<T> value1, Matrix3x2<T> value2);
public static Matrix3x2<T> operator *(Matrix3x2<T> value1, T value2);
public static bool operator ==(Matrix3x2<T> value1, Matrix3x2<T> value2);
public static bool operator !=(Matrix3x2<T> value1, Matrix3x2<T> value2);
public bool Equals(Matrix3x2<T> other);
public override bool Equals(object obj);
public override string ToString();
public override int GetHashCode();
public static explicit operator Matrix3x2<T>(Matrix3x2 value);
}
File Matrix4x4ofT.cs
public struct Matrix4x4<T> : IEquatable<Matrix4x4<T>>
{
public T M11;
public T M12;
public T M13;
public T M14;
public T M21;
public T M22;
public T M23;
public T M24;
public T M31;
public T M32;
public T M33;
public T M34;
public T M41;
public T M42;
public T M43;
public T M44;
public static Matrix4x4<T> Identity { get; }
public bool IsIdentity { get; }
public Vector3<T> Translation { get; set; }
public Matrix4x4(T m11, T m12, T m13, T m14, T m21, T m22, T m23, T m24, T m31, T m32, T m33, T m34, T m41, T m42, T m43, T m44);
public Matrix4x4(Matrix3x2<T> value);
public static Matrix4x4<T> CreateBillboard(Vector3<T> objectPosition, Vector3<T> cameraPosition, Vector3<T> cameraUpVector, Vector3<T> cameraForwardVector);
public static Matrix4x4<T> CreateConstrainedBillboard(Vector3<T> objectPosition, Vector3<T> cameraPosition, Vector3<T> rotateAxis, Vector3<T> cameraForwardVector, Vector3<T> objectForwardVector);
public static Matrix4x4<T> CreateTranslation(Vector3<T> position);
public static Matrix4x4<T> CreateTranslation(T xPosition, T yPosition, T zPosition);
public static Matrix4x4<T> CreateScale(T xScale, T yScale, T zScale);
public static Matrix4x4<T> CreateScale(T xScale, T yScale, T zScale, Vector3<T> centerPoint);
public static Matrix4x4<T> CreateScale(Vector3<T> scales);
public static Matrix4x4<T> CreateScale(Vector3<T> scales, Vector3<T> centerPoint);
public static Matrix4x4<T> CreateScale(T scale);
public static Matrix4x4<T> CreateScale(T scale, Vector3<T> centerPoint);
public static Matrix4x4<T> CreateRotationX(T radians);
public static Matrix4x4<T> CreateRotationX(T radians, Vector3<T> centerPoint);
public static Matrix4x4<T> CreateRotationY(T radians);
public static Matrix4x4<T> CreateRotationY(T radians, Vector3<T> centerPoint);
public static Matrix4x4<T> CreateRotationZ(T radians);
public static Matrix4x4<T> CreateRotationZ(T radians, Vector3<T> centerPoint);
public static Matrix4x4<T> CreateFromAxisAngle(Vector3<T> axis, T angle);
public static Matrix4x4<T> CreatePerspectiveFieldOfView(T fieldOfView, T aspectRatio, T nearPlaneDistance, T farPlaneDistance);
public static Matrix4x4<T> CreatePerspective(T width, T height, T nearPlaneDistance, T farPlaneDistance);
public static Matrix4x4<T> CreatePerspectiveOffCenter(T left, T right, T bottom, T top, T nearPlaneDistance, T farPlaneDistance);
public static Matrix4x4<T> CreateOrthographic(T width, T height, T zNearPlane, T zFarPlane);
public static Matrix4x4<T> CreateOrthographicOffCenter(T left, T right, T bottom, T top, T zNearPlane, T zFarPlane);
public static Matrix4x4<T> CreateLookAt(Vector3<T> cameraPosition, Vector3<T> cameraTarget, Vector3<T> cameraUpVector);
public static Matrix4x4<T> CreateWorld(Vector3<T> position, Vector3<T> forward, Vector3<T> up);
public static Matrix4x4<T> CreateFromQuaternion(Quaternion<T> quaternion);
public static Matrix4x4<T> CreateFromYawPitchRoll(T yaw, T pitch, T roll);
public static Matrix4x4<T> CreateShadow(Vector3<T> lightDirection, Plane<T> plane);
public static Matrix4x4<T> CreateReflection(Plane<T> value);
public T GetDeterminant();
public static bool Invert(Matrix4x4<T> matrix, out Matrix4x4<T> result);
public static bool Decompose(Matrix4x4<T> matrix, out Vector3<T> scale, out Quaternion<T> rotation, out Vector3<T> translation);
public static Matrix4x4<T> Transform(Matrix4x4<T> value, Quaternion<T> rotation);
public static Matrix4x4<T> Transpose(Matrix4x4<T> matrix);
public static Matrix4x4<T> Lerp(Matrix4x4<T> matrix1, Matrix4x4<T> matrix2, T amount);
public static Matrix4x4<T> Negate(Matrix4x4<T> value);
public static Matrix4x4<T> Add(Matrix4x4<T> value1, Matrix4x4<T> value2);
public static Matrix4x4<T> Subtract(Matrix4x4<T> value1, Matrix4x4<T> value2);
public static Matrix4x4<T> Multiply(Matrix4x4<T> value1, Matrix4x4<T> value2);
public static Matrix4x4<T> Multiply(Matrix4x4<T> value1, T value2);
public static Matrix4x4<T> operator -(Matrix4x4<T> value);
public static Matrix4x4<T> operator +(Matrix4x4<T> value1, Matrix4x4<T> value2);
public static Matrix4x4<T> operator -(Matrix4x4<T> value1, Matrix4x4<T> value2);
public static Matrix4x4<T> operator *(Matrix4x4<T> value1, Matrix4x4<T> value2);
public static Matrix4x4<T> operator *(Matrix4x4<T> value1, T value2);
public static bool operator ==(Matrix4x4<T> value1, Matrix4x4<T> value2);
public static bool operator !=(Matrix4x4<T> value1, Matrix4x4<T> value2);
public bool Equals(Matrix4x4<T> other);
public override bool Equals(object obj);
public override string ToString();
public override int GetHashCode();
public static explicit operator Matrix4x4<T>(Matrix4x4 value);
}
File PlaneOfT.cs
public struct Plane<T> : IEquatable<Plane<T>>
{
public Vector3<T> Normal;
public T D;
public Plane(T x, T y, T z, T d);
public Plane(Vector3<T> normal, T d);
public Plane(Vector4<T> value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Plane<T> CreateFromVertices(Vector3<T> point1, Vector3<T> point2, Vector3<T> point3);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Plane<T> Normalize(Plane<T> value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Plane<T> Transform(Plane<T> plane, Matrix4x4<T> matrix);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Plane<T> Transform(Plane<T> plane, Quaternion<T> rotation);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Dot(Plane<T> plane, Vector4<T> value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T DotCoordinate(Plane<T> plane, Vector3<T> value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T DotNormal(Plane<T> plane, Vector3<T> value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Plane<T> value1, Plane<T> value2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Plane<T> value1, Plane<T> value2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Plane<T> other);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj);
public override string ToString();
public override int GetHashCode();
public static explicit operator Plane<T>(Plane value);
}
File QuaternionOfT.cs
public struct Quaternion<T> : IEquatable<Quaternion<T>>
{
public T X;
public T Y;
public T Z;
public T W;
public static Quaternion<T> Identity { get; }
public bool IsIdentity { get; }
public Quaternion(T x, T y, T z, T w);
public Quaternion(Vector3<T> vectorPart, T scalarPart);
public T Length();
public T LengthSquared();
public static Quaternion<T> Normalize(Quaternion<T> value);
public static Quaternion<T> Conjugate(Quaternion<T> value);
public static Quaternion<T> Inverse(Quaternion<T> value);
public static Quaternion<T> CreateFromAxisAngle(Vector3<T> axis, T angle);
public static Quaternion<T> CreateFromYawPitchRoll(T yaw, T pitch, T roll);
public static Quaternion<T> CreateFromRotationMatrix(Matrix4x4<T> matrix);
public static T Dot(Quaternion<T> quaternion1, Quaternion<T> quaternion2);
public static Quaternion<T> Slerp(Quaternion<T> quaternion1, Quaternion<T> quaternion2, T amount);
public static Quaternion<T> Lerp(Quaternion<T> quaternion1, Quaternion<T> quaternion2, T amount);
public static Quaternion<T> Concatenate(Quaternion<T> value1, Quaternion<T> value2);
public static Quaternion<T> Negate(Quaternion<T> value);
public static Quaternion<T> Add(Quaternion<T> value1, Quaternion<T> value2);
public static Quaternion<T> Subtract(Quaternion<T> value1, Quaternion<T> value2);
public static Quaternion<T> Multiply(Quaternion<T> value1, Quaternion<T> value2);
public static Quaternion<T> Multiply(Quaternion<T> value1, T value2);
public static Quaternion<T> Divide(Quaternion<T> value1, Quaternion<T> value2);
public static Quaternion<T> operator -(Quaternion<T> value);
public static Quaternion<T> operator +(Quaternion<T> value1, Quaternion<T> value2);
public static Quaternion<T> operator -(Quaternion<T> value1, Quaternion<T> value2);
public static Quaternion<T> operator *(Quaternion<T> value1, Quaternion<T> value2);
public static Quaternion<T> operator *(Quaternion<T> value1, T value2);
public static Quaternion<T> operator /(Quaternion<T> value1, Quaternion<T> value2);
public static bool operator ==(Quaternion<T> value1, Quaternion<T> value2);
public static bool operator !=(Quaternion<T> value1, Quaternion<T> value2);
public bool Equals(Quaternion<T> other);
public override bool Equals(object obj);
public override string ToString();
public override int GetHashCode();
public static explicit operator Quaternion<T>(Quaternion value);
}
class Vector2 extended with:
public static implicit operator Vector2(Vector2<double> value);
public static implicit operator Vector2(Vector2<float> value);
class Vector3 extended with:
public static implicit operator Vector3(Vector3<double> value);
public static implicit operator Vector3(Vector3<float> value);
class Vector4 extended with:
public static implicit operator Vector4(Vector4<double> value);
public static implicit operator Vector4(Vector4<float> value);
class Matrix3x2 extended with:
public static implicit operator Matrix3x2(Matrix3x2<double> value);
public static implicit operator Matrix3x2(Matrix3x2<float> value);
class Matrix4x4 extended with:
public static implicit operator Matrix4x4(Matrix4x4<double> value);
public static implicit operator Matrix4x4(Matrix4x4<float> value);
class Quaternion extended with:
public static implicit operator Quaternion(Quaternion<double> value);
public static implicit operator Quaternion(Quaternion<float> value);
class Plane extended with:
public static implicit operator Plane(Plane<double> value);
public static implicit operator Plane(Plane<float> value);
Original Text
From @acecebov on August 25, 2017 16:50
Add support System.Numerics.Vectors types with double precision:
Vector2d, Vector3d, Vector4d, Matrix3x2d, Matrix4x4d, PlaneD, QuaternionD.
There are lots of scientific/gaming cases when we want to work with double precision and benefit of SIMD!
Copied from original issue: dotnet/coreclr#13591
From @danmosemsft on August 27, 2017 19:55
@acecebov thanks for the suggestion, can you move the issue to the corefx repo? New api proposals go there.
We needed double as we need the precision for a CAD application so we wrote a port of System.Numerics.Vectors. Mostly just VIM macros replacing float with double everywhere. No SIMD support however.
https://github.com/Weingartner/System.Numerics.DoubleVectors
https://www.nuget.org/packages/System.DoubleNumerics/
Need to add a proposed API, get it reviewed, and then implement the API. After that, need to update the JIT to support SIMD on these new types.
Another feature that is coming in .NET Core 2.1 that will help here is Hardware/Platform Intrinsics.
See https://github.com/dotnet/designs/blob/master/accepted/platform-intrinsics.md. This exposes the underlying SIMD instructions to C# and allows you to call the SIMD instructions directly without needing types to be exposed in System.Numerics.Vectors.
@eerhardt, rather than updating the JIT, might it be better to just wait for Hardware/Platform intrinsics?
That is, just have it be purely software and add the additional intrinsic code paths once @fiigii (and others working on this) finishes adding the JIT support for those.
ARM support could be implemented in the same way, as their is currently work starting to add intrinsics for it as well.
If we wait for the Hardware intrinsics, would it be better to just close this as "won't fix" and consumers can just take advantage of the underlying Hardware intrinsic APIs? I am assuming that was the point of directly exposing the intrinsics - so we didn't need to keep growing our API abstraction over the top of it.
would it be better to just close this as "won't fix"
No, I don't think that should happen.
I don't think the intrinsics are meant to exist so that we don't have to grow our API abstraction. I think they are meant to exist so that library/framework authors can write more performant libraries that better take advantage of the underlying hardware.
That is, even if hardware intrinsics existed today, requests to expose System.Numerics.Vector
for float
and double
would still exist. The Vector
types are general purpose APIs that are useful in a number of applications. Having the additional code paths to take advantage of the underlying hardware and be more performant is a bonus.
My proposal was to expose these APIs today, but with software only implementations. Then, as the hardware intrinsics come online, we can add the more performant code paths (which would be useful for https://github.com/dotnet/coreclr/issues/15490)
Hello, I would like to try to work on this issue, can you please assign me to it?
@mmatras, This (ideally) needs a proper API proposal and should go through the API review process before any work starts on it.
Another big decision is whether this should be implemented the same way as the existing intrinsics (adding codgen support directly to the JIT) or if it can use the new hardware intrinsics functionality (which is still a WIP). The two implementations are widely different and going one vs the other may result in a lot of throw-away work.
@mmatras - as @tannergooding says, this needs to go through the API proposal process. You can start working on this by filling out the original post with the proposed API. Then we can schedule it for a review.
Hello @eerhardt and @tannergooding ,
I've started working on API proposition, but got stuck and I don't know in which direction I should go. For clarity let's stick to Vector2:
My first propositon is to change X and Y types to object, and to add all needed overloads for float and double:
[JitIntrinsic]
public Vector2(Single x, Single y)
[JitIntrinsic]
public Vector2(Double x, Double y)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CopyTo(Single[] array);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CopyTo(Double[] array);
etc.
My second option is to use generic type: public partial struct Vector2, and rewrite all methods to generic. Which option is better in your opinion?
We actually can't do either of those, as it would be a breaking API change.
I think we could probably go two directions here:
- Just add
Vector2d
,Vector3d
, andVector4d
types, which mirror the existing classes but usedouble
- Add generic
Vector2<T>
,Vector3<T>
, andVector4<T>
types, which mirror the existing classes and useT
(which would allow the use of theVector
in generic algorithms, and better extension to other types in the future, if that is desirable)
@eerhardt, thoughts?
In my second option I thought the same as you but I didn't express myself well, maybe this is the way. I will prepare API proposal in the coming week.
I guess I was under the assumption that we were going to do @tannergooding's (1.) proposal - "Just add Vector2d, Vector3d, and Vector4d types, which mirror the existing classes but use double".
If we went with the generic approach, would we support more types than just float
and double
? Say decimal
or short
?
/cc @CarolEidt
Also, with the generic approach, would not having a fixed struct size be a problem? (I don't know if there is - just bringing it up)
If we went with the generic approach, would we support more types than just float and double
My thought on a generic approach is that the vector operations are generally valid on any type of numeric data and it might make extension to support future types easier, if that was desired.
Currently the support is System.Single
(existing code) and System.Double
(this proposal). However, we are looking at adding at least one other floating-point type (System.Numerics.Half
) and could look at adding others in the future. If it was desirable to also support Vector
operations for those types, it might be better to just extend an existing Vector2<T>
(with a static Vector2<T>.IsSupported
check) then also implement a new Vector2h
type.
A downside is that not all math operations required to support vector
operations (such as Sqrt
) are available for all numeric types and there may be weird rounding issues when doing certain operations with integer types (although integer types would be useful for something like Vector2<int>
, which equates to the POINT
struct on Windows and similar structs on other platforms).
Also, with the generic approach, would not having a fixed struct size be a problem
I don't think having a fixed struct size would be a problem, if you needed access to the raw bits you would just offset with Unsafe.SizeOf<Vector2<T>>
instead of some fixed size (16 for single, 32 for double, etc).
Vector2, Vector3, Vector4, Matrix3x2, Matrix4x4, Plane, Quaternion are currently implemented with single precission numbers.
Theare are requests (above) and some miror implementations of System.Numerics.* can be found across Internet where double is supported.
These classes should support float and double precission and maybe in future more types. One of possile solution is to miror them as generics
with support for both double and single precission numbers.
Rationale and Usage
This is request that developers ask about, one of usages is CAD software.
For example, to use Vector3 and Matrix4x4 with double precission we will be able to write:
var v1 = new Vector3<double>(4,5,4);
var v2 = new Vector3<double>(1,3,2);
double d = Vector3<double>.Dot(v1, v2);
Matrix4x4<double> xRot = Matrix4x4<double>.CreateTranslation(v1);
Vector3<double> tr = Vector3<double>.Transform(v2,xRot);
Proposed API (mirrored from Vector2, Vector3, Vector4, Matrix3x2, Matrix4x4, Plane)
File Vector2OfT.cs
public partial struct Vector2<T> : IEquatable<Vector2<T>>, IFormattable
{
public static Vector2<T> Zero { get; }
public static Vector2<T> One { get; }
public static Vector2<T> UnitX { get; }
public static Vector2<T> UnitY { get; }
public override int GetHashCode();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj);
public override string ToString();
public string ToString(string format);
public string ToString(string format, IFormatProvider formatProvider);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T Length();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T LengthSquared();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Distance(Vector2<T> value1, Vector2<T> value2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T DistanceSquared(Vector2<T> value1, Vector2<T> value2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Normalize(Vector2<T> value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Reflect(Vector2<T> vector, Vector2<T> normal);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Clamp(Vector2<T> value1, Vector2<T> min, Vector2<T> max);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Lerp(Vector2<T> value1, Vector2<T> value2, T amount);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Transform(Vector2<T> position, Matrix3x2<T> matrix);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Transform(Vector2<T> position, Matrix4x4<T> matrix);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> TransformNormal(Vector2<T> normal, Matrix3x2<T> matrix);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> TransformNormal(Vector2<T> normal, Matrix4x4<T> matrix);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Transform(Vector2<T> value, Quaternion<T> rotation);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Add(Vector2<T> left, Vector2<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Subtract(Vector2<T> left, Vector2<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Multiply(Vector2<T> left, Vector2<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Multiply(Vector2<T> left, T right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Multiply(T left, Vector2<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Divide(Vector2<T> left, Vector2<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Divide(Vector2<T> left, T divisor);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<T> Negate(Vector2<T> value);
}
File Vector2_IntrinsicsOfT.cs
public partial struct Vector2<T>
{
public T X;
public T Y;
[JitIntrinsic]
public Vector2(T value);
[JitIntrinsic]
public Vector2(T x, T y);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CopyTo(T[] array);
public void CopyTo(T[] array, int index);
[JitIntrinsic]
public bool Equals(global::System.Numerics.Vector2<T> other);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Dot(global::System.Numerics.Vector2<T> value1, global::System.Numerics.Vector2<T> value2);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static global::System.Numerics.Vector2<T> Min(global::System.Numerics.Vector2<T> value1, global::System.Numerics.Vector2<T> value2);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static global::System.Numerics.Vector2<T> Max(global::System.Numerics.Vector2<T> value1, global::System.Numerics.Vector2<T> value2);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static global::System.Numerics.Vector2<T> Abs(global::System.Numerics.Vector2<T> value);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static global::System.Numerics.Vector2<T> SquareRoot(global::System.Numerics.Vector2<T> value);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static global::System.Numerics.Vector2<T> operator +(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static global::System.Numerics.Vector2<T> operator -(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static global::System.Numerics.Vector2<T> operator *(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static global::System.Numerics.Vector2<T> operator *(T left, global::System.Numerics.Vector2<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static global::System.Numerics.Vector2<T> operator *(global::System.Numerics.Vector2<T> left, T right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static global::System.Numerics.Vector2<T> operator /(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static global::System.Numerics.Vector2<T> operator /(global::System.Numerics.Vector2<T> value1, T value2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static global::System.Numerics.Vector2<T> operator -(global::System.Numerics.Vector2<T> value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Vector2<T>(Vector2 value);
}
File Vector3OfT.cs
public partial struct Vector3<T> : IEquatable<Vector3<T>>, IFormattable
{
public static Vector3<T> Zero { get; }
public static Vector3<T> One { get; }
public static Vector3<T> UnitX { get; }
public static Vector3<T> UnitY { get; }
public static Vector3<T> UnitZ { get; }
public override int GetHashCode();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj);
public override string ToString();
public string ToString(string format);
public string ToString(string format, IFormatProvider formatProvider);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T Length();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T LengthSquared();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Distance(Vector3<T> value1, Vector3<T> value2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T DistanceSquared(Vector3<T> value1, Vector3<T> value2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Normalize(Vector3<T> value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Cross(Vector3<T> vector1, Vector3<T> vector2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Reflect(Vector3<T> vector, Vector3<T> normal);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Clamp(Vector3<T> value1, Vector3<T> min, Vector3<T> max);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Lerp(Vector3<T> value1, Vector3<T> value2, T amount);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Transform(Vector3<T> position, Matrix4x4<T> matrix);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> TransformNormal(Vector3<T> normal, Matrix4x4<T> matrix);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Transform(Vector3<T> value, Quaternion<T> rotation);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Add(Vector3<T> left, Vector3<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Subtract(Vector3<T> left, Vector3<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Multiply(Vector3<T> left, Vector3<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Multiply(Vector3<T> left, T right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Multiply(T left, Vector3<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Divide(Vector3<T> left, Vector3<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Divide(Vector3<T> left, T divisor);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Negate(Vector3<T> value);
}
File Vector3_IntrinsicsOfT.cs
public partial struct Vector3<T>
{
public T X;
public T Y;
public T Z;
[JitIntrinsic]
public Vector3(T value);
public Vector3(Vector2<T> value, T z);
[JitIntrinsic]
public Vector3(T x, T y, T z);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CopyTo(T[] array);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CopyTo(T[] array, int index);
[JitIntrinsic]
public bool Equals(Vector3<T> other);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Dot(Vector3<T> vector1, Vector3<T> vector2);
[JitIntrinsic]
public static Vector3<T> Min(Vector3<T> value1, Vector3<T> value2);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Max(Vector3<T> value1, Vector3<T> value2);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> Abs(Vector3<T> value);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> SquareRoot(Vector3<T> value);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> operator +(Vector3<T> left, Vector3<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> operator -(Vector3<T> left, Vector3<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> operator *(Vector3<T> left, Vector3<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> operator *(Vector3<T> left, T right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> operator *(T left, Vector3<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> operator /(Vector3<T> left, Vector3<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> operator /(Vector3<T> value1, T value2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<T> operator -(Vector3<T> value);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Vector3<T> left, Vector3<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Vector3<T> left, Vector3<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Vector3<T>(Vector3 value);
}
File Vector4OfT.cs
public partial struct Vector4<T> : IEquatable<Vector4<T>>, IFormattable
{
public static Vector4<T> Zero { get; }
public static Vector4<T> One { get; }
public static Vector4<T> UnitX { get; }
public static Vector4<T> UnitY { get ; }
public static Vector4<T> UnitZ { get; }
public static Vector4<T> UnitW { get; };
public override int GetHashCode();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj);
public override string ToString();
public string ToString(string format);
public string ToString(string format, IFormatProvider formatProvider);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T Length();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T LengthSquared();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Distance(Vector4<T> value1, Vector4<T> value2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T DistanceSquared(Vector4<T> value1, Vector4<T> value2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Normalize(Vector4<T> vector);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Clamp(Vector4<T> value1, Vector4<T> min, Vector4<T> max);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Lerp(Vector4<T> value1, Vector4<T> value2, T amount);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Transform(Vector2<T> position, Matrix4x4<T> matrix);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Transform(Vector3<T> position, Matrix4x4<T> matrix);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Transform(Vector4<T> vector, Matrix4x4<T> matrix);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Transform(Vector2<T> value, Quaternion<T> rotation);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Transform(Vector3<T> value, Quaternion<T> rotation);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Transform(Vector4<T> value, Quaternion<T> rotation);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Add(Vector4<T> left, Vector4<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Subtract(Vector4<T> left, Vector4<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Multiply(Vector4<T> left, Vector4<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Multiply(Vector4<T> left, T right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Multiply(T left, Vector4<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Divide(Vector4<T> left, Vector4<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Divide(Vector4<T> left, T divisor);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Negate(Vector4<T> value);
}
File Vector4OfT_Intrinsics.cs
public partial struct Vector4<T>
{
public T X;
public T Y;
public T Z;
public T W;
[JitIntrinsic]
public Vector4(T value);
[JitIntrinsic]
public Vector4(T x, T y, T z, T w);
public Vector4(Vector2<T> value, T z, T w);
public Vector4(Vector3<T> value, T w);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CopyTo(T[] array);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CopyTo(T[] array, int index);
[JitIntrinsic]
public bool Equals(Vector4<T> other);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Dot(Vector4<T> vector1, Vector4<T> vector2);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Min(Vector4<T> value1, Vector4<T> value2);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Max(Vector4<T> value1, Vector4<T> value2);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> Abs(Vector4<T> value);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> SquareRoot(Vector4<T> value);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> operator +(Vector4<T> left, Vector4<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> operator -(Vector4<T> left, Vector4<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> operator *(Vector4<T> left, Vector4<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> operator *(Vector4<T> left, T right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> operator *(T left, Vector4<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> operator /(Vector4<T> left, Vector4<T> right);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> operator /(Vector4<T> value1, T value2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<T> operator -(Vector4<T> value);
[JitIntrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Vector4<T> left, Vector4<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Vector4<T> left, Vector4<T> right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Vector4<T>(Vector4 value);
}
File Matrix3x2OfT.cs
public struct Matrix3x2<T> : IEquatable<Matrix3x2<T>>
{
public T M11;
public T M12;
public T M21;
public T M22;
public T M31;
public T M32;
public static Matrix3x2<T> Identity { get; }
public bool IsIdentity { get; }
public Vector2<T> Translation { get; set; }
public Matrix3x2(T m11, T m12, T m21, T m22, T m31, T m32);
public static Matrix3x2<T> CreateTranslation(Vector2<T> position);
public static Matrix3x2<T> CreateTranslation(T xPosition, T yPosition);
public static Matrix3x2<T> CreateScale(T xScale, T yScale);
public static Matrix3x2<T> CreateScale(T xScale, T yScale, Vector2<T> centerPoint);
public static Matrix3x2<T> CreateScale(Vector2<T> scales);
public static Matrix3x2<T> CreateScale(Vector2<T> scales, Vector2<T> centerPoint);
public static Matrix3x2<T> CreateScale(T scale);
public static Matrix3x2<T> CreateScale(T scale, Vector2<T> centerPoint);
public static Matrix3x2<T> CreateSkew(T radiansX, T radiansY);
public static Matrix3x2<T> CreateSkew(T radiansX, T radiansY, Vector2<T> centerPoint);
public static Matrix3x2<T> CreateRotation(T radians);
public static Matrix3x2<T> CreateRotation(T radians, Vector2<T> centerPoint);
public T GetDeterminant();
public static bool Invert(Matrix3x2<T> matrix, out Matrix3x2<T> result);
public static Matrix3x2<T> Lerp(Matrix3x2<T> matrix1, Matrix3x2<T> matrix2, T amount);
public static Matrix3x2<T> Negate(Matrix3x2<T> value);
public static Matrix3x2<T> Add(Matrix3x2<T> value1, Matrix3x2<T> value2);
public static Matrix3x2<T> Subtract(Matrix3x2<T> value1, Matrix3x2<T> value2);
public static Matrix3x2<T> Multiply(Matrix3x2<T> value1, Matrix3x2<T> value2);
public static Matrix3x2<T> Multiply(Matrix3x2<T> value1, T value2);
public static Matrix3x2<T> operator -(Matrix3x2<T> value);
public static Matrix3x2<T> operator +(Matrix3x2<T> value1, Matrix3x2<T> value2);
public static Matrix3x2<T> operator -(Matrix3x2<T> value1, Matrix3x2<T> value2);
public static Matrix3x2<T> operator *(Matrix3x2<T> value1, Matrix3x2<T> value2);
public static Matrix3x2<T> operator *(Matrix3x2<T> value1, T value2);
public static bool operator ==(Matrix3x2<T> value1, Matrix3x2<T> value2);
public static bool operator !=(Matrix3x2<T> value1, Matrix3x2<T> value2);
public bool Equals(Matrix3x2<T> other);
public override bool Equals(object obj);
public override string ToString();
public override int GetHashCode();
public static explicit operator Matrix3x2<T>(Matrix3x2 value);
}
File Matrix4x4ofT.cs
public struct Matrix4x4<T> : IEquatable<Matrix4x4<T>>
{
public T M11;
public T M12;
public T M13;
public T M14;
public T M21;
public T M22;
public T M23;
public T M24;
public T M31;
public T M32;
public T M33;
public T M34;
public T M41;
public T M42;
public T M43;
public T M44;
public static Matrix4x4<T> Identity { get; }
public bool IsIdentity { get; }
public Vector3<T> Translation { get; set; }
public Matrix4x4(T m11, T m12, T m13, T m14, T m21, T m22, T m23, T m24, T m31, T m32, T m33, T m34, T m41, T m42, T m43, T m44);
public Matrix4x4(Matrix3x2<T> value);
public static Matrix4x4<T> CreateBillboard(Vector3<T> objectPosition, Vector3<T> cameraPosition, Vector3<T> cameraUpVector, Vector3<T> cameraForwardVector);
public static Matrix4x4<T> CreateConstrainedBillboard(Vector3<T> objectPosition, Vector3<T> cameraPosition, Vector3<T> rotateAxis, Vector3<T> cameraForwardVector, Vector3<T> objectForwardVector);
public static Matrix4x4<T> CreateTranslation(Vector3<T> position);
public static Matrix4x4<T> CreateTranslation(T xPosition, T yPosition, T zPosition);
public static Matrix4x4<T> CreateScale(T xScale, T yScale, T zScale);
public static Matrix4x4<T> CreateScale(T xScale, T yScale, T zScale, Vector3<T> centerPoint);
public static Matrix4x4<T> CreateScale(Vector3<T> scales);
public static Matrix4x4<T> CreateScale(Vector3<T> scales, Vector3<T> centerPoint);
public static Matrix4x4<T> CreateScale(T scale);
public static Matrix4x4<T> CreateScale(T scale, Vector3<T> centerPoint);
public static Matrix4x4<T> CreateRotationX(T radians);
public static Matrix4x4<T> CreateRotationX(T radians, Vector3<T> centerPoint);
public static Matrix4x4<T> CreateRotationY(T radians);
public static Matrix4x4<T> CreateRotationY(T radians, Vector3<T> centerPoint);
public static Matrix4x4<T> CreateRotationZ(T radians);
public static Matrix4x4<T> CreateRotationZ(T radians, Vector3<T> centerPoint);
public static Matrix4x4<T> CreateFromAxisAngle(Vector3<T> axis, T angle);
public static Matrix4x4<T> CreatePerspectiveFieldOfView(T fieldOfView, T aspectRatio, T nearPlaneDistance, T farPlaneDistance);
public static Matrix4x4<T> CreatePerspective(T width, T height, T nearPlaneDistance, T farPlaneDistance);
public static Matrix4x4<T> CreatePerspectiveOffCenter(T left, T right, T bottom, T top, T nearPlaneDistance, T farPlaneDistance);
public static Matrix4x4<T> CreateOrthographic(T width, T height, T zNearPlane, T zFarPlane);
public static Matrix4x4<T> CreateOrthographicOffCenter(T left, T right, T bottom, T top, T zNearPlane, T zFarPlane);
public static Matrix4x4<T> CreateLookAt(Vector3<T> cameraPosition, Vector3<T> cameraTarget, Vector3<T> cameraUpVector);
public static Matrix4x4<T> CreateWorld(Vector3<T> position, Vector3<T> forward, Vector3<T> up);
public static Matrix4x4<T> CreateFromQuaternion(Quaternion<T> quaternion);
public static Matrix4x4<T> CreateFromYawPitchRoll(T yaw, T pitch, T roll);
public static Matrix4x4<T> CreateShadow(Vector3<T> lightDirection, Plane<T> plane);
public static Matrix4x4<T> CreateReflection(Plane<T> value);
public T GetDeterminant();
public static bool Invert(Matrix4x4<T> matrix, out Matrix4x4<T> result);
public static bool Decompose(Matrix4x4<T> matrix, out Vector3<T> scale, out Quaternion<T> rotation, out Vector3<T> translation);
public static Matrix4x4<T> Transform(Matrix4x4<T> value, Quaternion<T> rotation);
public static Matrix4x4<T> Transpose(Matrix4x4<T> matrix);
public static Matrix4x4<T> Lerp(Matrix4x4<T> matrix1, Matrix4x4<T> matrix2, T amount);
public static Matrix4x4<T> Negate(Matrix4x4<T> value);
public static Matrix4x4<T> Add(Matrix4x4<T> value1, Matrix4x4<T> value2);
public static Matrix4x4<T> Subtract(Matrix4x4<T> value1, Matrix4x4<T> value2);
public static Matrix4x4<T> Multiply(Matrix4x4<T> value1, Matrix4x4<T> value2);
public static Matrix4x4<T> Multiply(Matrix4x4<T> value1, T value2);
public static Matrix4x4<T> operator -(Matrix4x4<T> value);
public static Matrix4x4<T> operator +(Matrix4x4<T> value1, Matrix4x4<T> value2);
public static Matrix4x4<T> operator -(Matrix4x4<T> value1, Matrix4x4<T> value2);
public static Matrix4x4<T> operator *(Matrix4x4<T> value1, Matrix4x4<T> value2);
public static Matrix4x4<T> operator *(Matrix4x4<T> value1, T value2);
public static bool operator ==(Matrix4x4<T> value1, Matrix4x4<T> value2);
public static bool operator !=(Matrix4x4<T> value1, Matrix4x4<T> value2);
public bool Equals(Matrix4x4<T> other);
public override bool Equals(object obj);
public override string ToString();
public override int GetHashCode();
public static explicit operator Matrix4x4<T>(Matrix4x4 value);
}
File PlaneOfT.cs
public struct Plane<T> : IEquatable<Plane<T>>
{
public Vector3<T> Normal;
public T D;
public Plane(T x, T y, T z, T d);
public Plane(Vector3<T> normal, T d);
public Plane(Vector4<T> value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Plane<T> CreateFromVertices(Vector3<T> point1, Vector3<T> point2, Vector3<T> point3);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Plane<T> Normalize(Plane<T> value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Plane<T> Transform(Plane<T> plane, Matrix4x4<T> matrix);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Plane<T> Transform(Plane<T> plane, Quaternion<T> rotation);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Dot(Plane<T> plane, Vector4<T> value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T DotCoordinate(Plane<T> plane, Vector3<T> value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T DotNormal(Plane<T> plane, Vector3<T> value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Plane<T> value1, Plane<T> value2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Plane<T> value1, Plane<T> value2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Plane<T> other);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj);
public override string ToString();
public override int GetHashCode();
public static explicit operator Plane<T>(Plane value);
}
File QuaternionOfT.cs
public struct Quaternion<T> : IEquatable<Quaternion<T>>
{
public T X;
public T Y;
public T Z;
public T W;
public static Quaternion<T> Identity { get; }
public bool IsIdentity { get; }
public Quaternion(T x, T y, T z, T w);
public Quaternion(Vector3<T> vectorPart, T scalarPart);
public T Length();
public T LengthSquared();
public static Quaternion<T> Normalize(Quaternion<T> value);
public static Quaternion<T> Conjugate(Quaternion<T> value);
public static Quaternion<T> Inverse(Quaternion<T> value);
public static Quaternion<T> CreateFromAxisAngle(Vector3<T> axis, T angle);
public static Quaternion<T> CreateFromYawPitchRoll(T yaw, T pitch, T roll);
public static Quaternion<T> CreateFromRotationMatrix(Matrix4x4<T> matrix);
public static T Dot(Quaternion<T> quaternion1, Quaternion<T> quaternion2);
public static Quaternion<T> Slerp(Quaternion<T> quaternion1, Quaternion<T> quaternion2, T amount);
public static Quaternion<T> Lerp(Quaternion<T> quaternion1, Quaternion<T> quaternion2, T amount);
public static Quaternion<T> Concatenate(Quaternion<T> value1, Quaternion<T> value2);
public static Quaternion<T> Negate(Quaternion<T> value);
public static Quaternion<T> Add(Quaternion<T> value1, Quaternion<T> value2);
public static Quaternion<T> Subtract(Quaternion<T> value1, Quaternion<T> value2);
public static Quaternion<T> Multiply(Quaternion<T> value1, Quaternion<T> value2);
public static Quaternion<T> Multiply(Quaternion<T> value1, T value2);
public static Quaternion<T> Divide(Quaternion<T> value1, Quaternion<T> value2);
public static Quaternion<T> operator -(Quaternion<T> value);
public static Quaternion<T> operator +(Quaternion<T> value1, Quaternion<T> value2);
public static Quaternion<T> operator -(Quaternion<T> value1, Quaternion<T> value2);
public static Quaternion<T> operator *(Quaternion<T> value1, Quaternion<T> value2);
public static Quaternion<T> operator *(Quaternion<T> value1, T value2);
public static Quaternion<T> operator /(Quaternion<T> value1, Quaternion<T> value2);
public static bool operator ==(Quaternion<T> value1, Quaternion<T> value2);
public static bool operator !=(Quaternion<T> value1, Quaternion<T> value2);
public bool Equals(Quaternion<T> other);
public override bool Equals(object obj);
public override string ToString();
public override int GetHashCode();
public static explicit operator Quaternion<T>(Quaternion value);
}
class Vector2 extended with:
public static implicit operator Vector2(Vector2<double> value);
public static implicit operator Vector2(Vector2<float> value);
class Vector3 extended with:
public static implicit operator Vector3(Vector3<double> value);
public static implicit operator Vector3(Vector3<float> value);
class Vector4 extended with:
public static implicit operator Vector4(Vector4<double> value);
public static implicit operator Vector4(Vector4<float> value);
class Matrix3x2 extended with:
public static implicit operator Matrix3x2(Matrix3x2<double> value);
public static implicit operator Matrix3x2(Matrix3x2<float> value);
class Matrix4x4 extended with:
public static implicit operator Matrix4x4(Matrix4x4<double> value);
public static implicit operator Matrix4x4(Matrix4x4<float> value);
class Quaternion extended with:
public static implicit operator Quaternion(Quaternion<double> value);
public static implicit operator Quaternion(Quaternion<float> value);
class Plane extended with:
public static implicit operator Plane(Plane<double> value);
public static implicit operator Plane(Plane<float> value);
@eerhardt, I think the proposed API looks reasonable (it looks like everything already exposed, but with float
replaced with T
).
I think the biggest debate might be whether it should be generic (for easy extension in the future, if desired) or if it should be restricted to just double.
@mmatras, it may also be worth considering whether conversion between Vector2<double>
and Vector2
should be allowed.
Yes, casting to and from generic types seems reasonable:
I will extend previous API proposal comment with below methods declarations:
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Vector2<T>(Vector2 value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Vector3<T>(Vector3 value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Vector4<T>(Vector4 value);
public static explicit operator Matrix3x2<T>(Matrix3x2 value);
public static explicit operator Matrix4x4<T>(Matrix4x4 value);
public static explicit operator Plane<T>(Plane value);
public static explicit operator Quaternion<T>(Quaternion value);
The non generic types can be extended with:
Vector2:
public static implicit operator Vector2(Vector2<double> value);
public static implicit operator Vector2(Vector2<float> value);
Vector3:
public static implicit operator Vector3(Vector3<double> value);
public static implicit operator Vector3(Vector3<float> value);
Vector4:
public static implicit operator Vector4(Vector4<double> value);
public static implicit operator Vector4(Vector4<float> value);
Matrix3x2:
public static implicit operator Matrix3x2(Matrix3x2<double> value);
public static implicit operator Matrix3x2(Matrix3x2<float> value);
Matrix4x4:
public static implicit operator Matrix4x4(Matrix4x4<double> value);
public static implicit operator Matrix4x4(Matrix4x4<float> value);
Quaternion:
public static implicit operator Quaternion(Quaternion<double> value);
public static implicit operator Quaternion(Quaternion<float> value);
Plane:
public static implicit operator Plane(Plane<double> value);
public static implicit operator Plane(Plane<float> value);
If the operators are purely generic, they should be explicit.
If they are "exploded", we can allow implicit in a few scenarios (such as float->double).
fixed
@tannergooding, how we can continue with this issue? I was waiting for some feedback or discussion but it took a while now and it got stuck?
@eerhardt is the "area owner" and is the person who should mark the API as "ready-to-review".
Provided he agrees that the proposed API looks good, we can update the top post and marg it with the appropriate tags.
Marking as ready to review.
/cc @CarolEidt @jkotas - in case they want to chime in on the proposed API.
This looks like an idea case to use in parameters and maybe to make them readonly structs?
This looks like an idea case to use in parameters and maybe to make them readonly structs?
Not really, these are the type of structs that should be taking advantage of the underlying hardware and that should be getting passed around in the SIMD registers.
Not really, these are the type of structs that should be taking advantage of the underlying hardware and that should be getting passed around in the SIMD registers.
And even if not passed in registers, the best performance should be achieved by leaving them as by-value (see https://github.com/dotnet/coreclr/issues/16619)
I think the proposed API is reasonable; I think that the generic form is more appealing than Vectord
, and leaves room for additional types in future.
Can't we use "in" at operators in Matrix4x4 ?
The common use case that benefits the most from optimization is transforming an array of points with a single matrix, c.f. DirectXMath's:
- XMVector2TransformStream
- XMVector3TransformCoordStream
So I think we need something like:
public static Vector4 Transform(Vector4<T>[] positions, Matrix4x4 matrix);
public static Vector2 Transform(Vector2<T>[] positions, Matrix3x2 matrix);
The large matrix param is then only passed once, so the in/not-in debate becomes mute.
Can't we use "in" at operators in Matrix4x4 ?
No in
only works for readonly struct
types
No in only works for readonly struct types
@benaadams - I don't think that is true. I think they work great together, but the in
keyword doesn't require the parameter to be a readonly struct
.
https://docs.microsoft.com/en-us/dotnet/csharp/reference-semantics-with-value-types
I think they work great together
Right, for implicit in
, you should get better IL for readonly struct
(as the compiler should not need to create a copy of the struct). However, you can get the same IL for any struct by explicitly specifying in
(which tells the compiler you are explicitly opting into the behavior).
The use of
in
parameters avoids the potential performance costs of making copies. It does not change the semantics of any method call. Therefore, you do not need to specify thein
modifier at the call site. However, omitting thein
modifier at the call site informs the compiler that it is allowed to make a copy of the argument for the following reasons:
- There is an implicit conversion but not an identity conversion from the argument type to the parameter type.
- The argument is an expression but does not have a known storage variable.
- An overload exists that differs by the presence or absence of
in
. In that case, the by value overload is a better match.
No in only works for readonly struct types
@benaadams - I don't think that is true. I think they work great together, but the in keyword doesn't require the parameter to be a readonly struct.
@eerhardt only if you stay away from the instance methods in the proposal; else it will do a copy when they are called
@benaadams, it (the emitted IL) should never create a copy if you explicitly specify in
at the callsite, as per the language specification (see above in the quoted section I copied over).
Excellent blog post on the reality here The βinβ-modifier and the readonly structs in C#
I'd vote for these structs being immutable.
@benaadams, it (the emitted IL) should never create a copy if you explicitly specify in at the callsite, as per the language specification (see above in the quoted section I copied over).
It still will make a copy in the called function when a property is accessed or method call made on the variable so it does not potentially change the original?
I'd vote for these structs being immutable.
Then you have the flip side; if you want to change a single value in the struct you now have to copy 16 doubles (128 bytes)
I donβt think changing one element of a transform matrix is a common use case. They usually change as a result of an operation on the whole matrix.
It would still be better pass by value and and support vectorcall
When any of the first six arguments in order from left to right are vector type arguments, they are passed by value in SSE vector registers 0 to 5 according to argument position. Floating-point and __m128 types are passed in XMM registers, and __m256 types are passed in YMM registers. This differs from the standard x64 calling convention, because the vector types are passed by value instead of by reference, and additional registers are used.
Itβs a shame that whether to use in or not in the language is down to performance and not to whether the method mutates the parameter.
Either way it would be beneficial for performance to be able to pass an array of points and a single matrix to the transform method.
I'm jumping in. I have basically the need to develop this (at least for myself) right away, I was planing doing something simple at first, but I could ideally help @mmatras if he wants to.
Couple of observations/questions though:
- Floating point arithmetic have been discussed so far but why not also considering integers numeric data types? (byte, short, int and long) I typically need a Vector3 right now for what I'm doing.
- AFAIK there is no generic constraint such as "numeric data type" to allow the usage of arithmetic operators, so how the implementation of such generic classes should be ideally done?
- Are you sure the
in
keyword is not useful when passing a Matrix as parameter? 16 double values may be a lot on some CPU don't you think?
AFAIK there is no generic constraint such as "numeric data type" to allow the usage of arithmetic operators, so how the implementation of such generic classes should be ideally done?
That's a good question. Part of the implementation could be done in IL by using directly mul/add instructions... but there are IL instructions that are not generic-able for this (e.g ldc_r4/ldc_r8), so I'm wondering how this would be handled here in practice (It is like we would like to have "generic" specialization here
Are you sure the in keyword is not useful when passing a Matrix as parameter? 16 double values may be a lot on some CPU don't you think?
I concur. in
works even on non readonly structs assuming that you don't use methods on them (which would be the case for a Matrix multiplication where you access only fields) but not sure you can use in with indirect generics (like in this issue)
Overall, not sure I like the idea of a generic version. As it would be mainly viable for float and double. As we have already Vector3
for float, It would feel awkward to have a new generic type that could also accept float and double... while having Vector3d
would be straightforward enough.
On the subject, we could introduce instead float2/float3/float4/double2/double3/double3 as native types instead (so to coreclr, not corefx)... these types would not contain anything more than there fields (and the swizzles and the default operators)... all other operations would be stored in a different API. I remember suggesting to the .NET team at the time Vector3 was designed to use types like float3
/float4
instead double3
more balanced than Vector3d
while we don't have a Vector3f
... We did something like this for Unity.Mathematics and while there is a resistance for it (lowercase naming), if it was part of the core types, people would not complain about the lowercasing anymore...
We could argue that we could use the Vector<T>
in some generic code, sure, it would be nice, but as we can't do anything about the T in our own code (due to the non existence of numeric constraints), I'm failing to see the value of a generic version...
Side question for codegen for SSE4 likely to be convoluted as there are no 4xdouble hardware registers (compare to AVX)
Introducing native types would be a great thing imho, but I fear that we double the total count. But having float4, double4, (u)int4, (u)long4 among others would be very easy for us to build dedicated operations in different API because integer based types won't have the same API surface than floating ones I think.
My own (I say it, it's subjectif) reference point is comparing what we want to the OpenGL Mathematics library, I don't care about the highp/mediump/lowp precision but I do like the fact that we have a very broad set of types that I can have confidence into how they will be compiled to asm down the road. It's not important if some operations are missing they will be added and compiled into very efficient code.
I think @xoofx and I may be a little biased as we're seeing this from the prism of 3D programming but things like swizzles would be huge!
Down the road I think what we need is (basic or not) data types that will have a very good IL/ASM generation otherwise people will keep tweaking things to get some extra juice and I believe that is the number 1 thing to address right here. The API surface can be (should be?) a separate concern as it's hard to please everyone on this one without implementing a lot of APIs.
Mathematics will be more and more present in the future and taking advantage of a broad set of types and APIs, high optimized, will matter a lot.
Any update on this?
This has been pushed back the past couple of review sessions as we want to spend some more in-depth time reviewing it (due to its size).
CC. @terrajobst
Ah okay. As long as it's happening soon :) Do you reckon this will release alongside .NET Core 3?
EDIT: I know it's not part of that milestone, but can we expect it around that date?
Once reviewed, and if approved, I would guess it will be in the next release for the given branch following its implementation being merged (if it is merged to master, and master is still targeting 3.0, it should make the 3.0 release).
The OpenTK team is still hugely interested in this. Would be an enormous boon for our performance!
Thanks for the feedback @varon. I definitely want to see this get done, we just didn't have enough resources to do it for 3.0.
Once we start reviewing issues for .NET 5, I'll be sure to try and push this through API review again (we previously determined it requires a dedicated review session) and then, once approved, work can begin on the API.
It's good to see that this has been added to the .NET 5 milestone :)
Removing untriaged label as it is labeled as "api-ready-for-review"
cc: area owners: @tannergooding @pgovind
I have added an additional proposal at #35980 which continues to expand the System.Numerics APIs with other types that add other missing features and types that other maths libraries have. This proposal is based off of this one.
Stumbled upon this while looking for a Vector3 for other types (int, double)
Is there any traction with introducing int2/int3/int4/float2/float3/float4/double2/double3/double4 as builtin types as mentioned by @xoofx above? Since there is usually a corresponding SIMD type, it would make a lot of sense to treat them as builtin types. It would also greatly simplify the .NET API surface for math stuff.
I am curious what would be the process for such proposal.
In preparation for the API review this coming Thursday (11th of June), I simplified the proposal into each individual type being proposed (rather than mirroring the existing file structure).
The proposal is ultimately still the same, but it adds a couple of missing APIs (namely around Span<T>
and ReadOnlySpan<T>
) and calls out some notes.
For example, the existing types expose the fields publicly which goes against the normal .NET Framework Design Guidelines.
Exposing these as properties better fits the general guidelines and allows later tweaks to the layout (such as for Plane
or Quaternion
) if that is beneficial.
The remaining question would be if this should be fully readonly and have a wither
or if it should be the only mutable method and have a setter
Additionally there are cases like Length()
which are currently methods but which may be better suited as properties.
Vector2
public struct Vector2<T> : IEquatable<Vector2<T>>, IFormattable
where T : struct
{
// Fields
public T X { get; } // Should this have a setter, a Vector2 WithX(T value), or be a public field like Vector2
public T Y { get; } // Should this have a setter, a Vector2 WithY(T value), or be a public field like Vector2
// Constructors
public Vector2(T value);
public Vector2(T x, T y);
public Vector2(T[] value); // Doesn't exist for Vector2
public Vector2(T[] value, int offset); // Doesn't exist for Vector2
public Vector2(ReadOnlySpan<T> value); // Doesn't exist for Vector2
// Static Properties
public static Vector2<T> One { get; }
public static Vector2<T> UnitX { get; }
public static Vector2<T> UnitY { get; }
public static Vector2<T> Zero { get; }
// Operators
public static bool operator ==(Vector2<T> left, Vector2<T> right);
public static bool operator !=(Vector2<T> left, Vector2<T> right);
public static Vector2<T> operator +(Vector2<T> value); // Doesn't exist for Vector2
public static Vector2<T> operator -(Vector2<T> value);
public static Vector2<T> operator +(Vector2<T> left, Vector2<T> right);
public static Vector2<T> operator -(Vector2<T> left, Vector2<T> right);
public static Vector2<T> operator *(Vector2<T> left, Vector2<T> right);
public static Vector2<T> operator /(Vector2<T> left, Vector2<T> right);
public static Vector2<T> operator *(Vector2<T> left, T right);
public static Vector2<T> operator /(Vector2<T> left, T right);
public static Vector2<T> operator *(T left, Vector2<T> right);
// "Friendly" Operators
public static Vector2<T> Plus(Vector2<T> value); // Doesn't exist for Vector2
public static Vector2<T> Negate(Vector2<T> value);
public static Vector2<T> Add(Vector2<T> left, Vector2<T> right);
public static Vector2<T> Subtract(Vector2<T> left, Vector2<T> right);
public static Vector2<T> Multiply(Vector2<T> left, Vector2<T> right);
public static Vector2<T> Divide(Vector2<T> left, Vector2<T> right);
public static Vector2<T> Multiply(Vector2<T> left, T right);
public static Vector2<T> Divide(Vector2<T> left, T right);
public static Vector2<T> Multiply(T left, Vector2<T> right);
// Static Methods
public static Vector2<T> Abs(Vector2<T> value);
public static Vector2<T> Clamp(Vector2<T> value, Vector2<T> min, Vector2<T> max);
public static T Distance(Vector2<T> left, Vector2<T> right);
public static T DistanceSquared(Vector2<T> left, Vector2<T> right);
public static T Dot(Vector2<T> left, Vector2<T> right);
public static Vector2<T> Lerp(Vector2<T> min, Vector<T> max, T amount);
public static Vector2<T> Min(Vector2<T> left, Vector2<T> right);
public static Vector2<T> Max(Vector2<T> left, Vector2<T> right);
public static Vector2<T> Normalize(Vector2<T> value);
public static Vector2<T> Reflect(Vector2<T> incident, Vector2<T> normal);
public static Vector2<T> SquareRoot(Vector2<T> value);
public static Vector2<T> Transform(Vector2<T> position, Matrix3x2<T> matrix);
public static Vector2<T> Transform(Vector2<T> position, Matrix4x4<T> matrix);
public static Vector2<T> Transform(Vector2<T> position, Quaternion<T> rotation); // Rotate is a better name?
public static Vector2<T> TransformNormal(Vector2<T> normal, Matrix3x2<T> matrix);
public static Vector2<T> TransformNormal(Vector2<T> normal, Matrix4x4<T> matrix);
// Methods
public readonly void CopyTo(T[] array);
public readonly void CopyTo(T[] array, int index);
public readonly void CopyTo(Span<T> destination); // Doesn't exist for Vector2
public override readonly bool Equals(object? obj);
public readonly bool Equals(Vector2<T> other);
public override readonly int GetHashCode();
public readonly T Length(); // Better as a property?
public readonly T LengthSquared(); // Better as a property?
public readonly override string ToString();
public readonly string ToString(string format);
public readonly string ToString(string format, IFormatProvider formatProvider);
}
Vector3
public struct Vector3<T> : IEquatable<Vector3<T>>, IFormattable
where T : struct
{
// Fields
public T X { get; } // Should this have a setter, a Vector3 WithX(T value), or be a public field like Vector3
public T Y { get; } // Should this have a setter, a Vector3 WithY(T value), or be a public field like Vector3
public T Z { get; } // Should this have a setter, a Vector3 WithZ(T value), or be a public field like Vector3
// Constructors
public Vector3(T value);
public Vector3(T x, T y, T z);
public Vector3(Vector2<T> value, T z);
public Vector3(T[] value); // Doesn't exist for Vector3
public Vector3(T[] value, int offset); // Doesn't exist for Vector3
public Vector3(ReadOnlySpan<T> value); // Doesn't exist for Vector3
// Static Properties
public static Vector3<T> One { get; }
public static Vector3<T> UnitX { get; }
public static Vector3<T> UnitY { get; }
public static Vector3<T> UnitZ { get; }
public static Vector3<T> Zero { get; }
// Operators
public static bool operator ==(Vector3<T> left, Vector3<T> right);
public static bool operator !=(Vector3<T> left, Vector3<T> right);
public static Vector3<T> operator +(Vector3<T> value); // Doesn't exist for Vector3
public static Vector3<T> operator -(Vector3<T> value);
public static Vector3<T> operator +(Vector3<T> left, Vector3<T> right);
public static Vector3<T> operator -(Vector3<T> left, Vector3<T> right);
public static Vector3<T> operator *(Vector3<T> left, Vector3<T> right);
public static Vector3<T> operator /(Vector3<T> left, Vector3<T> right);
public static Vector3<T> operator *(Vector3<T> left, T right);
public static Vector3<T> operator /(Vector3<T> left, T right);
public static Vector3<T> operator *(T left, Vector3<T> right);
// "Friendly" Operators
public static Vector3<T> Plus(Vector3<T> value); // Doesn't exist for Vector3
public static Vector3<T> Negate(Vector3<T> value);
public static Vector3<T> Add(Vector3<T> left, Vector3<T> right);
public static Vector3<T> Subtract(Vector3<T> left, Vector3<T> right);
public static Vector3<T> Multiply(Vector3<T> left, Vector3<T> right);
public static Vector3<T> Divide(Vector3<T> left, Vector3<T> right);
public static Vector3<T> Multiply(Vector3<T> left, T right);
public static Vector3<T> Divide(Vector3<T> left, T right);
public static Vector3<T> Multiply(T left, Vector3<T> right);
// Static Methods
public static Vector3<T> Abs(Vector3<T> value);
public static Vector3<T> Clamp(Vector3<T> value, Vector3<T> min, Vector3<T> max);
public static T Distance(Vector3<T> left, Vector3<T> right);
public static T DistanceSquared(Vector3<T> left, Vector3<T> right);
public static Vector3 Cross(Vector3<T> left, Vector3<T> right);
public static T Dot(Vector3<T> left, Vector3<T> right);
public static Vector3<T> Lerp(Vector3<T> min, Vector<T> max, T amount);
public static Vector3<T> Min(Vector3<T> left, Vector3<T> right);
public static Vector3<T> Max(Vector3<T> left, Vector3<T> right);
public static Vector3<T> Normalize(Vector3<T> value);
public static Vector3<T> Reflect(Vector3<T> incident, Vector3<T> normal);
public static Vector3<T> SquareRoot(Vector3<T> value);
public static Vector3<T> Transform(Vector3<T> position, Matrix4x4<T> matrix);
public static Vector3<T> Transform(Vector3<T> position, Quaternion<T> rotation); // Rotate is a better name?
public static Vector3<T> TransformNormal(Vector3<T> normal, Matrix4x4<T> matrix);
// Methods
public readonly void CopyTo(T[] array);
public readonly void CopyTo(T[] array, int index);
public readonly void CopyTo(Span<T> destination); // Doesn't exist for Vector3
public override readonly bool Equals(object? obj);
public readonly bool Equals(Vector3<T> other);
public override readonly int GetHashCode();
public readonly T Length(); // Better as a property?
public readonly T LengthSquared(); // Better as a property?
public readonly override string ToString();
public readonly string ToString(string format);
public readonly string ToString(string format, IFormatProvider formatProvider);
}
Vector4
public struct Vector4<T> : IEquatable<Vector4<T>>, IFormattable
where T : struct
{
// Fields
public T X { get; } // Should this have a setter, a Vector4 WithX(T value), or be a public field like Vector4
public T Y { get; } // Should this have a setter, a Vector4 WithY(T value), or be a public field like Vector4
public T Z { get; } // Should this have a setter, a Vector4 WithZ(T value), or be a public field like Vector4
public T W { get; } // Should this have a setter, a Vector4 WithW(T value), or be a public field like Vector4
// Constructors
public Vector4(T value);
public Vector4(T x, T y, T z, T w);
public Vector4(Vector2<T> value, T z, T, w);
public Vector4(Vector3<T> value, T, w);
public Vector4(T[] value); // Doesn't exist for Vector4
public Vector4(T[] value, int offset); // Doesn't exist for Vector4
public Vector4(ReadOnlySpan<T> value); // Doesn't exist for Vector4
// Static Properties
public static Vector4<T> One { get; }
public static Vector4<T> UnitX { get; }
public static Vector4<T> UnitY { get; }
public static Vector4<T> UnitZ { get; }
public static Vector4<T> UnitW { get; }
public static Vector4<T> Zero { get; }
// Operators
public static bool operator ==(Vector4<T> left, Vector4<T> right);
public static bool operator !=(Vector4<T> left, Vector4<T> right);
public static Vector4<T> operator +(Vector4<T> value); // Doesn't exist for Vector4
public static Vector4<T> operator -(Vector4<T> value);
public static Vector4<T> operator +(Vector4<T> left, Vector4<T> right);
public static Vector4<T> operator -(Vector4<T> left, Vector4<T> right);
public static Vector4<T> operator *(Vector4<T> left, Vector4<T> right);
public static Vector4<T> operator /(Vector4<T> left, Vector4<T> right);
public static Vector4<T> operator *(Vector4<T> left, T right);
public static Vector4<T> operator /(Vector4<T> left, T right);
public static Vector4<T> operator *(T left, Vector4<T> right);
// "Friendly" Operators
public static Vector4<T> Plus(Vector4<T> value); // Doesn't exist for Vector4
public static Vector4<T> Negate(Vector4<T> value);
public static Vector4<T> Add(Vector4<T> left, Vector4<T> right);
public static Vector4<T> Subtract(Vector4<T> left, Vector4<T> right);
public static Vector4<T> Multiply(Vector4<T> left, Vector4<T> right);
public static Vector4<T> Divide(Vector4<T> left, Vector4<T> right);
public static Vector4<T> Multiply(Vector4<T> left, T right);
public static Vector4<T> Divide(Vector4<T> left, T right);
public static Vector4<T> Multiply(T left, Vector4<T> right);
// Static Methods
public static Vector4<T> Abs(Vector4<T> value);
public static Vector4<T> Clamp(Vector4<T> value, Vector4<T> min, Vector4<T> max);
public static T Distance(Vector4<T> left, Vector4<T> right);
public static T DistanceSquared(Vector4<T> left, Vector4<T> right);
public static T Dot(Vector4<T> left, Vector4<T> right);
public static Vector4<T> Lerp(Vector4<T> min, Vector<T> max, T amount);
public static Vector4<T> Min(Vector4<T> left, Vector4<T> right);
public static Vector4<T> Max(Vector4<T> left, Vector4<T> right);
public static Vector4<T> Normalize(Vector4<T> value);
public static Vector4<T> SquareRoot(Vector4<T> value);
public static Vector4<T> Transform(Vector4<T> position, Matrix4x4<T> matrix);
public static Vector4<T> Transform(Vector4<T> position, Quaternion<T> rotation); // Rotate is a better name?
// Methods
public readonly void CopyTo(T[] array);
public readonly void CopyTo(T[] array, int index);
public readonly void CopyTo(Span<T> destination); // Doesn't exist for Vector4
public override readonly bool Equals(object? obj);
public readonly bool Equals(Vector4<T> other);
public override readonly int GetHashCode();
public readonly T Length(); // Better as a property?
public readonly T LengthSquared(); // Better as a property?
public readonly override string ToString();
public readonly string ToString(string format);
public readonly string ToString(string format, IFormatProvider formatProvider);
}
Plane
public struct Plane<T> : IEquatable<Plane<T>> //, IFormattable
{
// Fields
public T Distance { get; } // Should this have a setter, a T WithDistance(T value), or be a public field like Plan
public Vector3<T> Normal { get; } // Should this have a setter, a Vector3<T> WithNormal(Vector3<T> value), or be a public field like Plan
// Constructors
public Plane(T x, T y, T z, T distance);
public Plane(Vector3<T> normal, T distance);
public Plane(Vector4<T> value);
// Operators
public static bool operator ==(Plane<T> left, Plane<T> right);
public static bool operator !=(Plane<T> left, Plane<T> right);
// Static Methods
public static Plane<T> CreateFromVertices(Vector3<T> point1, Vector3<T> point2, Vector3<T> point3); // CreateFromPoints is a better name?
public static T Dot(Plane<T> left, Vector4<T> right); // DotProduct is a better name?
public static T DotCoordinate(Plane<T> left, Vector3<T> right);
public static T DotNormal(Plane<T> left, Vector3<T> right);
public static Plane<T> Normalize(Plane<T> value);
public static Plane<T> Transform(Plane<T> plane, Matrix4x4<T> matrix);
public static Plane<T> Transform(Plane<T> plane, Quaternion<T> matrix); // Rotate is a better name?
// Methods
public override readonly bool Equals(object? obj);
public readonly bool Equals(Plane<T> other);
public override readonly int GetHashCode();
public readonly override string ToString();
// public readonly string ToString(string format);
// public readonly string ToString(string format, IFormatProvider formatProvider);
}
Quaternion
public struct Quaternion<T> : IEquatable<Quaternion<T>> //, IFormattable
{
// Fields
public T X { get; } // Should this have a setter, a Quaternion<T> WithX(T value), or be a public field like Quaternion
public T Y { get; } // Should this have a setter, a Quaternion<T> WithY(T value), or be a public field like Quaternion
public T Z { get; } // Should this have a setter, a Quaternion<T> WithZ(T value), or be a public field like Quaternion
public T W { get; } // Should this have a setter, a Quaternion<T> WithW(T value), or be a public field like Quaternion
// Constructors
public Quaternion(T x, T y, T z, T w);
public Quaternion(Vector3<T> vector, T rotation);
// Static Properties
public static Quaternion<T> Identity { get; }
// Properties
public bool IsIdentity { get; }
// Operators
public static bool operator ==(Quaternion<T> left, Quaternion<T> right);
public static bool operator !=(Quaternion<T> left, Quaternion<T> right);
public static Quaternion<T> operator +(Quaternion<T> value); // Doesn't exist for Quaternion
public static Quaternion<T> operator -(Quaternion<T> value);
public static Quaternion<T> operator +(Quaternion<T> left, Quaternion<T> right);
public static Quaternion<T> operator -(Quaternion<T> left, Quaternion<T> right);
public static Quaternion<T> operator *(Quaternion<T> left, Quaternion<T> right);
public static Quaternion<T> operator /(Quaternion<T> left, Quaternion<T> right);
public static Quaternion<T> operator *(Quaternion<T> left, T right);
// "Friendly" Operators
public static Quaternion<T> Plus(Quaternion<T> value); // Doesn't exist for Quaternion
public static Quaternion<T> Negate(Quaternion<T> value);
public static Quaternion<T> Add(Quaternion<T>left, Quaternion<T>right);
public static Quaternion<T> Subtract(Quaternion<T>left, Quaternion<T>right);
public static Quaternion<T> Multiply(Quaternion<T>left, Quaternion<T>right);
public static Quaternion<T> Divide(Quaternion<T>left, Quaternion<T>right);
public static Quaternion<T> Multiply(Quaternion<T>left, T right);
// Static Methods
public static Quaternion<T> Concatenate(Quaternion<T>left, Quaternion<T>right);
public static Quaternion<T> Conjugate(Quaternion<T> value);
public static Quaternion<T> CreateFromAxisAngle(Vector3<T> axis, T angle); // CreateFromRotationAxis is abetter name?
public static Quaternion<T> CreateFromRotationMatrix(Matrix4x4 matrix);
public static Quaternion<T> CreateFromYawPitchRoll(T yaw, T pitch, T roll);
public T Dot(Quaternion<T> left, Quaternion<T> right); // DotProduct is a better name?
public Quaternion<T> Inverse(Quaternion<T> value);
public Quaternion<T> Lerp(Quaternion<T> min, Quaternion<T> max, T amount);
public Quaternion<T> Slerp(Quaternion<T> min, Quaternion<T> max, T amount);
public Quaternion<T> Normalize(Quaternion<T> value);
// Methods
public override readonly bool Equals(object? obj);
public readonly bool Equals(Quaternion<T> other);
public override readonly int GetHashCode();
public readonly T Length(); // Better as a property?
public readonly T LengthSquared(); // Better as a property?
public readonly override string ToString();
// public readonly string ToString(string format);
// public readonly string ToString(string format, IFormatProvider formatProvider);
}
Matrix3x2
public struct Matrix3x2<T> : IEquatable<Matrix3x2<T>> //, IFormattable
{
// Fields
public T M11 { get; } // Should this have a setter, a Matrix3x2<T> WithM11(T value), or be a public field like Matrix3x2
public T M12 { get; } // Should this have a setter, a Matrix3x2<T> WithM12(T value), or be a public field like Matrix3x2
public T M21 { get; } // Should this have a setter, a Matrix3x2<T> WithM21(T value), or be a public field like Matrix3x2
public T M22 { get; } // Should this have a setter, a Matrix3x2<T> WithM22(T value), or be a public field like Matrix3x2
public T M31 { get; } // Should this have a setter, a Matrix3x2<T> WithM31(T value), or be a public field like Matrix3x2
public T M32 { get; } // Should this have a setter, a Matrix3x2<T> WithM32(T value), or be a public field like Matrix3x2
// Constructors
public Matrix3x2(T m11, T m12, T m21, T m22, T m31, T m32);
// Static Properties
public static Quaternion<T> Identity { get; }
// Properties
public bool IsIdentity { get; }
public Vector2<T> Translation { get; } // Should this have a setter or a wither
// Operators
public static bool operator ==(Matrix3x2<T> left, Matrix3x2<T> right);
public static bool operator !=(Matrix3x2<T> left, Matrix3x2<T> right);
public static Matrix3x2<T> operator +(Matrix3x2<T> value); // Doesn't exist for Matrix3x2
public static Matrix3x2<T> operator -(Matrix3x2<T> value);
public static Matrix3x2<T> operator +(Matrix3x2<T> left, Matrix3x2<T> right);
public static Matrix3x2<T> operator -(Matrix3x2<T> left, Matrix3x2<T> right);
public static Matrix3x2<T> operator *(Matrix3x2<T> left, Matrix3x2<T> right);
public static Matrix3x2<T> operator *(Matrix3x2<T> left, T right);
// "Friendly" Operators
public static Matrix3x2<T> Plus(Matrix3x2<T> value); // Doesn't exist for Matrix3x2
public static Matrix3x2<T> Negate(Matrix3x2<T> value);
public static Matrix3x2<T> Add(Matrix3x2<T>left, Matrix3x2<T>right);
public static Matrix3x2<T> Subtract(Matrix3x2<T>left, Matrix3x2<T>right);
public static Matrix3x2<T> Multiply(Matrix3x2<T>left, Matrix3x2<T>right);
public static Matrix3x2<T> Multiply(Matrix3x2<T>left, T right);
// Static Methods
public static Matrix3x2<T> CreateRotation(T radians);
public static Matrix3x2<T> CreateRotation(T radians, Vector2<T> centerPoint);
public static Matrix3x2<T> CreateScale(T scale);
public static Matrix3x2<T> CreateScale(T scale, Vector2<T> centerPoint);
public static Matrix3x2<T> CreateScale(T scaleX, T scaleY);
public static Matrix3x2<T> CreateScale(T scaleX, T scaleY, Vector2<T> centerPoint);
public static Matrix3x2<T> CreateScale(Vector2<T> scale);
public static Matrix3x2<T> CreateScale(Vector2<T> scale, Vector2<T> centerPoint);
public static Matrix3x2<T> CreateSkew(T radiansX, T radiansY);
public static Matrix3x2<T> CreateSkew(T radiansX, T radiansY, Vector2<T> centerPoint);
public static Matrix3x2<T> CreateTranslation(T positionX, T positionY);
public static Matrix3x2<T> CreateTranslation(Vector2<T> position);
public static bool Invert(Matrix3x2<T> matrix, out Matrix3x2<T> result); // TryInvert is a better name?
public static Matrix3x2<T> Lerp(Matrix3x2<T> min, Matrix3x2<T> max, T amount);
// Methods
public override readonly bool Equals(object? obj);
public readonly bool Equals(Matrix3x2<T> other);
public readonly T GetDeterminant(); // Better as a property?
public override readonly int GetHashCode();
public readonly override string ToString();
// public readonly string ToString(string format);
// public readonly string ToString(string format, IFormatProvider formatProvider);
}
Matrix4x4
public struct Matrix4x4<T> : IEquatable<Matrix4x4<T>> //, IFormattable
{
// Fields
public T M11 { get; } // Should this have a setter, a Matrix4x4<T> WithM11(T value), or be a public field like Matrix4x4
public T M12 { get; } // Should this have a setter, a Matrix4x4<T> WithM12(T value), or be a public field like Matrix4x4
public T M13 { get; } // Should this have a setter, a Matrix4x4<T> WithM13(T value), or be a public field like Matrix4x4
public T M14 { get; } // Should this have a setter, a Matrix4x4<T> WithM14(T value), or be a public field like Matrix4x4
public T M21 { get; } // Should this have a setter, a Matrix4x4<T> WithM21(T value), or be a public field like Matrix4x4
public T M22 { get; } // Should this have a setter, a Matrix4x4<T> WithM22(T value), or be a public field like Matrix4x4
public T M23 { get; } // Should this have a setter, a Matrix4x4<T> WithM23(T value), or be a public field like Matrix4x4
public T M24 { get; } // Should this have a setter, a Matrix4x4<T> WithM24(T value), or be a public field like Matrix4x4
public T M31 { get; } // Should this have a setter, a Matrix4x4<T> WithM31(T value), or be a public field like Matrix4x4
public T M32 { get; } // Should this have a setter, a Matrix4x4<T> WithM32(T value), or be a public field like Matrix4x4
public T M33 { get; } // Should this have a setter, a Matrix4x4<T> WithM33(T value), or be a public field like Matrix4x4
public T M34 { get; } // Should this have a setter, a Matrix4x4<T> WithM34(T value), or be a public field like Matrix4x4
public T M41 { get; } // Should this have a setter, a Matrix4x4<T> WithM41(T value), or be a public field like Matrix4x4
public T M42 { get; } // Should this have a setter, a Matrix4x4<T> WithM42(T value), or be a public field like Matrix4x4
public T M43 { get; } // Should this have a setter, a Matrix4x4<T> WithM43(T value), or be a public field like Matrix4x4
public T M44 { get; } // Should this have a setter, a Matrix4x4<T> WithM44(T value), or be a public field like Matrix4x4
// Constructors
public Matrix4x4(Matrix3x2 value);
public Matrix4x4(T m11, T m12, T m13, T m14, T m21, T m22, T m23, T m24, T m31, T m32, T m33, T m34, T m41, T m42, T m43, T m44);
// Static Properties
public static Quaternion<T> Identity { get; }
// Properties
public bool IsIdentity { get; }
public Vector2<T> Translation { get; } // Should this have a setter or a wither
// Operators
public static bool operator ==(Matrix4x4<T> left, Matrix4x4<T> right);
public static bool operator !=(Matrix4x4<T> left, Matrix4x4<T> right);
public static Matrix4x4<T> operator +(Matrix4x4<T> value); // Doesn't exist for Matrix4x4
public static Matrix4x4<T> operator -(Matrix4x4<T> value);
public static Matrix4x4<T> operator +(Matrix4x4<T> left, Matrix4x4<T> right);
public static Matrix4x4<T> operator -(Matrix4x4<T> left, Matrix4x4<T> right);
public static Matrix4x4<T> operator *(Matrix4x4<T> left, Matrix4x4<T> right);
public static Matrix4x4<T> operator *(Matrix4x4<T> left, T right);
// "Friendly" Operators
public static Matrix4x4<T> Plus(Matrix4x4<T> value); // Doesn't exist for Matrix4x4
public static Matrix4x4<T> Negate(Matrix4x4<T> value);
public static Matrix4x4<T> Add(Matrix4x4<T>left, Matrix4x4<T>right);
public static Matrix4x4<T> Subtract(Matrix4x4<T>left, Matrix4x4<T>right);
public static Matrix4x4<T> Multiply(Matrix4x4<T>left, Matrix4x4<T>right);
public static Matrix4x4<T> Multiply(Matrix4x4<T>left, T right);
// Static Methods
public static Matrix4x4<T> CreateBillboard(Vector3<T> objectPosition, Vector3<T> cameraPosition, Vector3<T> cameraUpVector, Vector3<T> cameraForwardVector);
public static Matrix4x4<T> CreateConstrainedBillboard(Vector3<T> objectPosition, Vector3<T> cameraPosition, Vector3<T> rotationAxis, Vector3<T> cameraForwardVector, Vector3<T> objectForwardVector);
public static Matrix4x4<T> CreateFromAxisAngle(Vector3<T> axis, T angle);
public static Matrix4x4<T> CreateFromQuaternion(Quaternion quaternion);
public static Matrix4x4<T> CreateFromYawPitchRoll(T yaw, T pitch, T roll);
public static Matrix4x4<T> CreateLookAt(Vector3<T> cameraPosition, Vector3<T> cameraTarget, Vector3<T> cameraUpVector); // Ideally has LH and RH
public static Matrix4x4<T> CreateOrthographic(T width, T height, T nearPlane, T farPlane); // Ideally has LH and RH
public static Matrix4x4<T> CreateOrthographicOffCenter(T left, T right, T bottom, T top, T nearPlane, T farPlane); // Ideally has LH and RH
public static Matrix4x4<T> CreatePerspective(T width, T height, T nearPlaneDistance, T farPlaneDistance); // Ideally has LH and RH
public static Matrix4x4<T> CreatePerspectiveFieldOfView(T fieldOfView, T aspectRatio, T nearPlaneDistance, T farPlaneDistance); // Ideally has LH and RH
public static Matrix4x4<T> CreatePerspectiveOffCenter(T left, T right, T bottom, T top, T nearPlaneDistance, T farPlaneDistance); // Ideally has LH and RH
public static Matrix4x4<T> CreateReflection(Plane<T> value);
public static Matrix4x4<T> CreateRotationX(T radians);
public static Matrix4x4<T> CreateRotationX(T radians, Vector3<T> centerPoint);
public static Matrix4x4<T> CreateRotationY(T radians);
public static Matrix4x4<T> CreateRotationY(T radians, Vector3<T> centerPoint);
public static Matrix4x4<T> CreateRotationZ(T radians);
public static Matrix4x4<T> CreateRotationZ(T radians, Vector3<T> centerPoint);
public static Matrix4x4<T> CreateScale(T scale);
public static Matrix4x4<T> CreateScale(T scale, Vector3<T> centerPoint);
public static Matrix4x4<T> CreateScale(T scaleX, T scaleY, T scaleZ);
public static Matrix4x4<T> CreateScale(T scaleX, T scaleY, T scaleZ, Vector3<T> centerPoint);
public static Matrix4x4<T> CreateScale(Vector3<T> scale);
public static Matrix4x4<T> CreateScale(Vector3<T> scale, Vector3<T> centerPoint);
public static Matrix4x4<T> CreateShadow(Vector3<T> lightDirection, Plane<T> plane);
public static Matrix4x4<T> CreateTranslation(T positionX, T positionY, T positionZ);
public static Matrix4x4<T> CreateTranslation(Vector3<T> position);
public static Matrix4x4<T> CreateWorld(Vector3<T> position, Vector3<T> forward, Vector3<T> up);
public static bool Decompose(Matrix4x4<T> matrix, out Vector3<T> scale, out Quaternion rotation, out Vector3<T> translation); // TryDecompose is a better name?
public static bool Invert(Matrix4x4<T> matrix, out Matrix4x4<T> result); // TryInvert is a better name? Better to out determinant and return result?
public static Matrix4x4<T> Lerp(Matrix4x4<T> min, Matrix4x4<T> max, T amount);
public static Matrix4x4<T> Transform(Matrix4x4<T> value, Quaternion<T> rotation); // Rotate is a better name
public static Matrix4x4<T> Transpose(Matrix4x4<T> value);
// Methods
public override readonly bool Equals(object? obj);
public readonly bool Equals(Matrix4x4<T> other);
public readonly T GetDeterminant(); // Better as a property?
public override readonly int GetHashCode();
public readonly override string ToString();
// public readonly string ToString(string format);
// public readonly string ToString(string format, IFormatProvider formatProvider);
}
@tannergooding I probably missed this, do we support the marshaling of blittable generic types today? So this will work just fine in case of interop as the current numerics?
Yes, support for passing blittable generics was added in .NET Core 3.0.
Vector2<T>
- We should keep the API in sync with
Vector2
, so all APIs we added toVector2<T>
we should also add toVector2
- Adding identity operators makes sense from a math standpoint, but we should make sure we don't make it slower
- We shouldn't "fix" names like
Transform
toRotate
because it makes it harder to go betweenVector2
andVector2<T>
- We should move the static methods to
Vector2
to avoid having to type the type - We considered making the methods non-generic by exploding them into the concrete types but this would mean follks can't write generic code that works with either float or double.
- If we keep the properties as get-only we should mark the type as read-only
- We should keep the API in sync with
Vector3<T>
,Vector4<T>
- Same comments as
Vector2<T>
- Same comments as
Plane<T>
- Rename
D
toDistance
seems fine
- Rename
Matrix3x2<T>
- Consider exposing the same ctors as we have on
Vector2<T>
- Consider exposing the same ctors as we have on
Matrix4x4<T>
- Consider exposing the same ctors as we have on
Vector2<T>
- Consider exposing a property for vectors
- Consider exposing the same ctors as we have on
public struct Vector2<T> : IEquatable<Vector2<T>>, IFormattable
where T : struct
{
// Fields
public T X { get; } // Should this have a setter, a Vector2 WithX(T value), or be a public field like Vector2
public T Y { get; } // Should this have a setter, a Vector2 WithY(T value), or be a public field like Vector2
// Constructors
public Vector2(T value);
public Vector2(T x, T y);
public Vector2(T[] value); // Doesn't exist for Vector2
public Vector2(T[] value, int offset); // Doesn't exist for Vector2
public Vector2(ReadOnlySpan<T> value); // Doesn't exist for Vector2
// Static Properties
public static Vector2<T> One { get; }
public static Vector2<T> UnitX { get; }
public static Vector2<T> UnitY { get; }
public static Vector2<T> Zero { get; }
// Operators
public static bool operator ==(Vector2<T> left, Vector2<T> right);
public static bool operator !=(Vector2<T> left, Vector2<T> right);
public static Vector2<T> operator +(Vector2<T> value); // Doesn't exist for Vector2
public static Vector2<T> operator -(Vector2<T> value);
public static Vector2<T> operator +(Vector2<T> left, Vector2<T> right);
public static Vector2<T> operator -(Vector2<T> left, Vector2<T> right);
public static Vector2<T> operator *(Vector2<T> left, Vector2<T> right);
public static Vector2<T> operator /(Vector2<T> left, Vector2<T> right);
public static Vector2<T> operator *(Vector2<T> left, T right);
public static Vector2<T> operator /(Vector2<T> left, T right);
public static Vector2<T> operator *(T left, Vector2<T> right);
// "Friendly" Operators
public static Vector2<T> Plus(Vector2<T> value); // Doesn't exist for Vector2
public static Vector2<T> Negate(Vector2<T> value);
public static Vector2<T> Add(Vector2<T> left, Vector2<T> right);
public static Vector2<T> Subtract(Vector2<T> left, Vector2<T> right);
public static Vector2<T> Multiply(Vector2<T> left, Vector2<T> right);
public static Vector2<T> Divide(Vector2<T> left, Vector2<T> right);
public static Vector2<T> Multiply(Vector2<T> left, T right);
public static Vector2<T> Divide(Vector2<T> left, T right);
public static Vector2<T> Multiply(T left, Vector2<T> right);
// Static Methods
public static Vector2<T> Abs(Vector2<T> value);
public static Vector2<T> Clamp(Vector2<T> value, Vector2<T> min, Vector2<T> max);
public static T Distance(Vector2<T> left, Vector2<T> right);
public static T DistanceSquared(Vector2<T> left, Vector2<T> right);
public static T Dot(Vector2<T> left, Vector2<T> right);
public static Vector2<T> Lerp(Vector2<T> min, Vector<T> max, T amount);
public static Vector2<T> Min(Vector2<T> left, Vector2<T> right);
public static Vector2<T> Max(Vector2<T> left, Vector2<T> right);
public static Vector2<T> Normalize(Vector2<T> value);
public static Vector2<T> Reflect(Vector2<T> incident, Vector2<T> normal);
public static Vector2<T> SquareRoot(Vector2<T> value);
public static Vector2<T> Transform(Vector2<T> position, Matrix3x2<T> matrix);
public static Vector2<T> Transform(Vector2<T> position, Matrix4x4<T> matrix);
public static Vector2<T> Transform(Vector2<T> position, Quaternion<T> rotation); // Rotate is a better name?
public static Vector2<T> TransformNormal(Vector2<T> normal, Matrix3x2<T> matrix);
public static Vector2<T> TransformNormal(Vector2<T> normal, Matrix4x4<T> matrix);
// Methods
public readonly void CopyTo(T[] array);
public readonly void CopyTo(T[] array, int index);
public readonly void CopyTo(Span<T> destination); // Doesn't exist for Vector2
public override readonly bool Equals(object? obj);
public readonly bool Equals(Vector2<T> other);
public override readonly int GetHashCode();
public readonly T Length(); // Better as a property?
public readonly T LengthSquared(); // Better as a property?
public readonly override string ToString();
public readonly string ToString(string format);
public readonly string ToString(string format, IFormatProvider formatProvider);
}
public struct Vector3<T> : IEquatable<Vector3<T>>, IFormattable
where T : struct
{
// Fields
public T X { get; } // Should this have a setter, a Vector3 WithX(T value), or be a public field like Vector3
public T Y { get; } // Should this have a setter, a Vector3 WithY(T value), or be a public field like Vector3
public T Z { get; } // Should this have a setter, a Vector3 WithZ(T value), or be a public field like Vector3
// Constructors
public Vector3(T value);
public Vector3(T x, T y, T z);
public Vector3(Vector2<T> value, T z);
public Vector3(T[] value); // Doesn't exist for Vector3
public Vector3(T[] value, int offset); // Doesn't exist for Vector3
public Vector3(ReadOnlySpan<T> value); // Doesn't exist for Vector3
// Static Properties
public static Vector3<T> One { get; }
public static Vector3<T> UnitX { get; }
public static Vector3<T> UnitY { get; }
public static Vector3<T> UnitZ { get; }
public static Vector3<T> Zero { get; }
// Operators
public static bool operator ==(Vector3<T> left, Vector3<T> right);
public static bool operator !=(Vector3<T> left, Vector3<T> right);
public static Vector3<T> operator +(Vector3<T> value); // Doesn't exist for Vector3
public static Vector3<T> operator -(Vector3<T> value);
public static Vector3<T> operator +(Vector3<T> left, Vector3<T> right);
public static Vector3<T> operator -(Vector3<T> left, Vector3<T> right);
public static Vector3<T> operator *(Vector3<T> left, Vector3<T> right);
public static Vector3<T> operator /(Vector3<T> left, Vector3<T> right);
public static Vector3<T> operator *(Vector3<T> left, T right);
public static Vector3<T> operator /(Vector3<T> left, T right);
public static Vector3<T> operator *(T left, Vector3<T> right);
// "Friendly" Operators
public static Vector3<T> Plus(Vector3<T> value); // Doesn't exist for Vector3
public static Vector3<T> Negate(Vector3<T> value);
public static Vector3<T> Add(Vector3<T> left, Vector3<T> right);
public static Vector3<T> Subtract(Vector3<T> left, Vector3<T> right);
public static Vector3<T> Multiply(Vector3<T> left, Vector3<T> right);
public static Vector3<T> Divide(Vector3<T> left, Vector3<T> right);
public static Vector3<T> Multiply(Vector3<T> left, T right);
public static Vector3<T> Divide(Vector3<T> left, T right);
public static Vector3<T> Multiply(T left, Vector3<T> right);
// Static Methods
public static Vector3<T> Abs(Vector3<T> value);
public static Vector3<T> Clamp(Vector3<T> value, Vector3<T> min, Vector3<T> max);
public static T Distance(Vector3<T> left, Vector3<T> right);
public static T DistanceSquared(Vector3<T> left, Vector3<T> right);
public static Vector3 Cross(Vector3<T> left, Vector3<T> right);
public static T Dot(Vector3<T> left, Vector3<T> right);
public static Vector3<T> Lerp(Vector3<T> min, Vector<T> max, T amount);
public static Vector3<T> Min(Vector3<T> left, Vector3<T> right);
public static Vector3<T> Max(Vector3<T> left, Vector3<T> right);
public static Vector3<T> Normalize(Vector3<T> value);
public static Vector3<T> Reflect(Vector3<T> incident, Vector3<T> normal);
public static Vector3<T> SquareRoot(Vector3<T> value);
public static Vector3<T> Transform(Vector3<T> position, Matrix4x4<T> matrix);
public static Vector3<T> Transform(Vector3<T> position, Quaternion<T> rotation); // Rotate is a better name?
public static Vector3<T> TransformNormal(Vector3<T> normal, Matrix4x4<T> matrix);
// Methods
public readonly void CopyTo(T[] array);
public readonly void CopyTo(T[] array, int index);
public readonly void CopyTo(Span<T> destination); // Doesn't exist for Vector3
public override readonly bool Equals(object? obj);
public readonly bool Equals(Vector3<T> other);
public override readonly int GetHashCode();
public readonly T Length(); // Better as a property?
public readonly T LengthSquared(); // Better as a property?
public readonly override string ToString();
public readonly string ToString(string format);
public readonly string ToString(string format, IFormatProvider formatProvider);
}
public struct Vector4<T> : IEquatable<Vector4<T>>, IFormattable
where T : struct
{
// Fields
public T X { get; } // Should this have a setter, a Vector4 WithX(T value), or be a public field like Vector4
public T Y { get; } // Should this have a setter, a Vector4 WithY(T value), or be a public field like Vector4
public T Z { get; } // Should this have a setter, a Vector4 WithZ(T value), or be a public field like Vector4
public T W { get; } // Should this have a setter, a Vector4 WithW(T value), or be a public field like Vector4
// Constructors
public Vector4(T value);
public Vector4(T x, T y, T z, T w);
public Vector4(Vector2<T> value, T z, T, w);
public Vector4(Vector3<T> value, T, w);
public Vector4(T[] value); // Doesn't exist for Vector4
public Vector4(T[] value, int offset); // Doesn't exist for Vector4
public Vector4(ReadOnlySpan<T> value); // Doesn't exist for Vector4
// Static Properties
public static Vector4<T> One { get; }
public static Vector4<T> UnitX { get; }
public static Vector4<T> UnitY { get; }
public static Vector4<T> UnitZ { get; }
public static Vector4<T> UnitW { get; }
public static Vector4<T> Zero { get; }
// Operators
public static bool operator ==(Vector4<T> left, Vector4<T> right);
public static bool operator !=(Vector4<T> left, Vector4<T> right);
public static Vector4<T> operator +(Vector4<T> value); // Doesn't exist for Vector4
public static Vector4<T> operator -(Vector4<T> value);
public static Vector4<T> operator +(Vector4<T> left, Vector4<T> right);
public static Vector4<T> operator -(Vector4<T> left, Vector4<T> right);
public static Vector4<T> operator *(Vector4<T> left, Vector4<T> right);
public static Vector4<T> operator /(Vector4<T> left, Vector4<T> right);
public static Vector4<T> operator *(Vector4<T> left, T right);
public static Vector4<T> operator /(Vector4<T> left, T right);
public static Vector4<T> operator *(T left, Vector4<T> right);
// "Friendly" Operators
public static Vector4<T> Plus(Vector4<T> value); // Doesn't exist for Vector4
public static Vector4<T> Negate(Vector4<T> value);
public static Vector4<T> Add(Vector4<T> left, Vector4<T> right);
public static Vector4<T> Subtract(Vector4<T> left, Vector4<T> right);
public static Vector4<T> Multiply(Vector4<T> left, Vector4<T> right);
public static Vector4<T> Divide(Vector4<T> left, Vector4<T> right);
public static Vector4<T> Multiply(Vector4<T> left, T right);
public static Vector4<T> Divide(Vector4<T> left, T right);
public static Vector4<T> Multiply(T left, Vector4<T> right);
// Static Methods
public static Vector4<T> Abs(Vector4<T> value);
public static Vector4<T> Clamp(Vector4<T> value, Vector4<T> min, Vector4<T> max);
public static T Distance(Vector4<T> left, Vector4<T> right);
public static T DistanceSquared(Vector4<T> left, Vector4<T> right);
public static T Dot(Vector4<T> left, Vector4<T> right);
public static Vector4<T> Lerp(Vector4<T> min, Vector<T> max, T amount);
public static Vector4<T> Min(Vector4<T> left, Vector4<T> right);
public static Vector4<T> Max(Vector4<T> left, Vector4<T> right);
public static Vector4<T> Normalize(Vector4<T> value);
public static Vector4<T> SquareRoot(Vector4<T> value);
public static Vector4<T> Transform(Vector4<T> position, Matrix4x4<T> matrix);
public static Vector4<T> Transform(Vector4<T> position, Quaternion<T> rotation); // Rotate is a better name?
// Methods
public readonly void CopyTo(T[] array);
public readonly void CopyTo(T[] array, int index);
public readonly void CopyTo(Span<T> destination); // Doesn't exist for Vector4
public override readonly bool Equals(object? obj);
public readonly bool Equals(Vector4<T> other);
public override readonly int GetHashCode();
public readonly T Length(); // Better as a property?
public readonly T LengthSquared(); // Better as a property?
public readonly override string ToString();
public readonly string ToString(string format);
public readonly string ToString(string format, IFormatProvider formatProvider);
}
public struct Plane<T> : IEquatable<Plane<T>> //, IFormattable
{
// Fields
public T Distance { get; } // Should this have a setter, a T WithDistance(T value), or be a public field like Plan
public Vector3<T> Normal { get; } // Should this have a setter, a Vector3<T> WithNormal(Vector3<T> value), or be a public field like Plan
// Constructors
public Plane(T x, T y, T z, T distance);
public Plane(Vector3<T> normal, T distance);
public Plane(Vector4<T> value);
// Operators
public static bool operator ==(Plane<T> left, Plane<T> right);
public static bool operator !=(Plane<T> left, Plane<T> right);
// Static Methods
public static Plane<T> CreateFromVertices(Vector3<T> point1, Vector3<T> point2, Vector3<T> point3); // CreateFromPoints is a better name?
public static T Dot(Plane<T> left, Vector4<T> right); // DotProduct is a better name?
public static T DotCoordinate(Plane<T> left, Vector3<T> right);
public static T DotNormal(Plane<T> left, Vector3<T> right);
public static Plane<T> Normalize(Plane<T> value);
public static Plane<T> Transform(Plane<T> plane, Matrix4x4<T> matrix);
public static Plane<T> Transform(Plane<T> plane, Quaternion<T> matrix); // Rotate is a better name?
// Methods
public override readonly bool Equals(object? obj);
public readonly bool Equals(Plane<T> other);
public override readonly int GetHashCode();
public readonly override string ToString();
// public readonly string ToString(string format);
// public readonly string ToString(string format, IFormatProvider formatProvider);
}
public struct Quaternion<T> : IEquatable<Quaternion<T>> //, IFormattable
{
// Fields
public T X { get; } // Should this have a setter, a Quaternion<T> WithX(T value), or be a public field like Quaternion
public T Y { get; } // Should this have a setter, a Quaternion<T> WithY(T value), or be a public field like Quaternion
public T Z { get; } // Should this have a setter, a Quaternion<T> WithZ(T value), or be a public field like Quaternion
public T W { get; } // Should this have a setter, a Quaternion<T> WithW(T value), or be a public field like Quaternion
// Constructors
public Quaternion(T x, T y, T z, T w);
public Quaternion(Vector3<T> vector, T rotation);
// Static Properties
public static Quaternion<T> Identity { get; }
// Properties
public bool IsIdentity { get; }
// Operators
public static bool operator ==(Quaternion<T> left, Quaternion<T> right);
public static bool operator !=(Quaternion<T> left, Quaternion<T> right);
public static Quaternion<T> operator +(Quaternion<T> value); // Doesn't exist for Quaternion
public static Quaternion<T> operator -(Quaternion<T> value);
public static Quaternion<T> operator +(Quaternion<T> left, Quaternion<T> right);
public static Quaternion<T> operator -(Quaternion<T> left, Quaternion<T> right);
public static Quaternion<T> operator *(Quaternion<T> left, Quaternion<T> right);
public static Quaternion<T> operator /(Quaternion<T> left, Quaternion<T> right);
public static Quaternion<T> operator *(Quaternion<T> left, T right);
// "Friendly" Operators
public static Quaternion<T> Plus(Quaternion<T> value); // Doesn't exist for Quaternion
public static Quaternion<T> Negate(Quaternion<T> value);
public static Quaternion<T> Add(Quaternion<T>left, Quaternion<T>right);
public static Quaternion<T> Subtract(Quaternion<T>left, Quaternion<T>right);
public static Quaternion<T> Multiply(Quaternion<T>left, Quaternion<T>right);
public static Quaternion<T> Divide(Quaternion<T>left, Quaternion<T>right);
public static Quaternion<T> Multiply(Quaternion<T>left, T right);
// Static Methods
public static Quaternion<T> Concatenate(Quaternion<T>left, Quaternion<T>right);
public static Quaternion<T> Conjugate(Quaternion<T> value);
public static Quaternion<T> CreateFromAxisAngle(Vector3<T> axis, T angle); // CreateFromRotationAxis is abetter name?
public static Quaternion<T> CreateFromRotationMatrix(Matrix4x4 matrix);
public static Quaternion<T> CreateFromYawPitchRoll(T yaw, T pitch, T roll);
public T Dot(Quaternion<T> left, Quaternion<T> right); // DotProduct is a better name?
public Quaternion<T> Inverse(Quaternion<T> value);
public Quaternion<T> Lerp(Quaternion<T> min, Quaternion<T> max, T amount);
public Quaternion<T> Slerp(Quaternion<T> min, Quaternion<T> max, T amount);
public Quaternion<T> Normalize(Quaternion<T> value);
// Methods
public override readonly bool Equals(object? obj);
public readonly bool Equals(Quaternion<T> other);
public override readonly int GetHashCode();
public readonly T Length(); // Better as a property?
public readonly T LengthSquared(); // Better as a property?
public readonly override string ToString();
// public readonly string ToString(string format);
// public readonly string ToString(string format, IFormatProvider formatProvider);
}
public struct Matrix3x2<T> : IEquatable<Matrix3x2<T>> //, IFormattable
{
// Fields
public T M11 { get; } // Should this have a setter, a Matrix3x2<T> WithM11(T value), or be a public field like Matrix3x2
public T M12 { get; } // Should this have a setter, a Matrix3x2<T> WithM12(T value), or be a public field like Matrix3x2
public T M21 { get; } // Should this have a setter, a Matrix3x2<T> WithM21(T value), or be a public field like Matrix3x2
public T M22 { get; } // Should this have a setter, a Matrix3x2<T> WithM22(T value), or be a public field like Matrix3x2
public T M31 { get; } // Should this have a setter, a Matrix3x2<T> WithM31(T value), or be a public field like Matrix3x2
public T M32 { get; } // Should this have a setter, a Matrix3x2<T> WithM32(T value), or be a public field like Matrix3x2
// Constructors
public Matrix3x2(T m11, T m12, T m21, T m22, T m31, T m32);
// Static Properties
public static Quaternion<T> Identity { get; }
// Properties
public bool IsIdentity { get; }
public Vector2<T> Translation { get; } // Should this have a setter or a wither
// Operators
public static bool operator ==(Matrix3x2<T> left, Matrix3x2<T> right);
public static bool operator !=(Matrix3x2<T> left, Matrix3x2<T> right);
public static Matrix3x2<T> operator +(Matrix3x2<T> value); // Doesn't exist for Matrix3x2
public static Matrix3x2<T> operator -(Matrix3x2<T> value);
public static Matrix3x2<T> operator +(Matrix3x2<T> left, Matrix3x2<T> right);
public static Matrix3x2<T> operator -(Matrix3x2<T> left, Matrix3x2<T> right);
public static Matrix3x2<T> operator *(Matrix3x2<T> left, Matrix3x2<T> right);
public static Matrix3x2<T> operator *(Matrix3x2<T> left, T right);
// "Friendly" Operators
public static Matrix3x2<T> Plus(Matrix3x2<T> value); // Doesn't exist for Matrix3x2
public static Matrix3x2<T> Negate(Matrix3x2<T> value);
public static Matrix3x2<T> Add(Matrix3x2<T>left, Matrix3x2<T>right);
public static Matrix3x2<T> Subtract(Matrix3x2<T>left, Matrix3x2<T>right);
public static Matrix3x2<T> Multiply(Matrix3x2<T>left, Matrix3x2<T>right);
public static Matrix3x2<T> Multiply(Matrix3x2<T>left, T right);
// Static Methods
public static Matrix3x2<T> CreateRotation(T radians);
public static Matrix3x2<T> CreateRotation(T radians, Vector2<T> centerPoint);
public static Matrix3x2<T> CreateScale(T scale);
public static Matrix3x2<T> CreateScale(T scale, Vector2<T> centerPoint);
public static Matrix3x2<T> CreateScale(T scaleX, T scaleY);
public static Matrix3x2<T> CreateScale(T scaleX, T scaleY, Vector2<T> centerPoint);
public static Matrix3x2<T> CreateScale(Vector2<T> scale);
public static Matrix3x2<T> CreateScale(Vector2<T> scale, Vector2<T> centerPoint);
public static Matrix3x2<T> CreateSkew(T radiansX, T radiansY);
public static Matrix3x2<T> CreateSkew(T radiansX, T radiansY, Vector2<T> centerPoint);
public static Matrix3x2<T> CreateTranslation(T positionX, T positionY);
public static Matrix3x2<T> CreateTranslation(Vector2<T> position);
public static bool Invert(Matrix3x2<T> matrix, out Matrix3x2<T> result); // TryInvert is a better name?
public static Matrix3x2<T> Lerp(Matrix3x2<T> min, Matrix3x2<T> max, T amount);
// Methods
public override readonly bool Equals(object? obj);
public readonly bool Equals(Matrix3x2<T> other);
public readonly T GetDeterminant(); // Better as a property?
public override readonly int GetHashCode();
public readonly override string ToString();
// public readonly string ToString(string format);
// public readonly string ToString(string format, IFormatProvider formatProvider);
}
public struct Matrix4x4<T> : IEquatable<Matrix4x4<T>> //, IFormattable
{
// Fields
public T M11 { get; } // Should this have a setter, a Matrix4x4<T> WithM11(T value), or be a public field like Matrix4x4
public T M12 { get; } // Should this have a setter, a Matrix4x4<T> WithM12(T value), or be a public field like Matrix4x4
public T M13 { get; } // Should this have a setter, a Matrix4x4<T> WithM13(T value), or be a public field like Matrix4x4
public T M14 { get; } // Should this have a setter, a Matrix4x4<T> WithM14(T value), or be a public field like Matrix4x4
public T M21 { get; } // Should this have a setter, a Matrix4x4<T> WithM21(T value), or be a public field like Matrix4x4
public T M22 { get; } // Should this have a setter, a Matrix4x4<T> WithM22(T value), or be a public field like Matrix4x4
public T M23 { get; } // Should this have a setter, a Matrix4x4<T> WithM23(T value), or be a public field like Matrix4x4
public T M24 { get; } // Should this have a setter, a Matrix4x4<T> WithM24(T value), or be a public field like Matrix4x4
public T M31 { get; } // Should this have a setter, a Matrix4x4<T> WithM31(T value), or be a public field like Matrix4x4
public T M32 { get; } // Should this have a setter, a Matrix4x4<T> WithM32(T value), or be a public field like Matrix4x4
public T M33 { get; } // Should this have a setter, a Matrix4x4<T> WithM33(T value), or be a public field like Matrix4x4
public T M34 { get; } // Should this have a setter, a Matrix4x4<T> WithM34(T value), or be a public field like Matrix4x4
public T M41 { get; } // Should this have a setter, a Matrix4x4<T> WithM41(T value), or be a public field like Matrix4x4
public T M42 { get; } // Should this have a setter, a Matrix4x4<T> WithM42(T value), or be a public field like Matrix4x4
public T M43 { get; } // Should this have a setter, a Matrix4x4<T> WithM43(T value), or be a public field like Matrix4x4
public T M44 { get; } // Should this have a setter, a Matrix4x4<T> WithM44(T value), or be a public field like Matrix4x4
// Constructors
public Matrix4x4(Matrix3x2 value);
public Matrix4x4(T m11, T m12, T m13, T m14, T m21, T m22, T m23, T m24, T m31, T m32, T m33, T m34, T m41, T m42, T m43, T m44);
// Static Properties
public static Quaternion<T> Identity { get; }
// Properties
public bool IsIdentity { get; }
public Vector2<T> Translation { get; } // Should this have a setter or a wither
// Operators
public static bool operator ==(Matrix4x4<T> left, Matrix4x4<T> right);
public static bool operator !=(Matrix4x4<T> left, Matrix4x4<T> right);
public static Matrix4x4<T> operator +(Matrix4x4<T> value); // Doesn't exist for Matrix4x4
public static Matrix4x4<T> operator -(Matrix4x4<T> value);
public static Matrix4x4<T> operator +(Matrix4x4<T> left, Matrix4x4<T> right);
public static Matrix4x4<T> operator -(Matrix4x4<T> left, Matrix4x4<T> right);
public static Matrix4x4<T> operator *(Matrix4x4<T> left, Matrix4x4<T> right);
public static Matrix4x4<T> operator *(Matrix4x4<T> left, T right);
// "Friendly" Operators
public static Matrix4x4<T> Plus(Matrix4x4<T> value); // Doesn't exist for Matrix4x4
public static Matrix4x4<T> Negate(Matrix4x4<T> value);
public static Matrix4x4<T> Add(Matrix4x4<T>left, Matrix4x4<T>right);
public static Matrix4x4<T> Subtract(Matrix4x4<T>left, Matrix4x4<T>right);
public static Matrix4x4<T> Multiply(Matrix4x4<T>left, Matrix4x4<T>right);
public static Matrix4x4<T> Multiply(Matrix4x4<T>left, T right);
// Static Methods
public static Matrix4x4<T> CreateBillboard(Vector3<T> objectPosition, Vector3<T> cameraPosition, Vector3<T> cameraUpVector, Vector3<T> cameraForwardVector);
public static Matrix4x4<T> CreateConstrainedBillboard(Vector3<T> objectPosition, Vector3<T> cameraPosition, Vector3<T> rotationAxis, Vector3<T> cameraForwardVector, Vector3<T> objectForwardVector);
public static Matrix4x4<T> CreateFromAxisAngle(Vector3<T> axis, T angle);
public static Matrix4x4<T> CreateFromQuaternion(Quaternion quaternion);
public static Matrix4x4<T> CreateFromYawPitchRoll(T yaw, T pitch, T roll);
public static Matrix4x4<T> CreateLookAt(Vector3<T> cameraPosition, Vector3<T> cameraTarget, Vector3<T> cameraUpVector); // Ideally has LH and RH
public static Matrix4x4<T> CreateOrthographic(T width, T height, T nearPlane, T farPlane); // Ideally has LH and RH
public static Matrix4x4<T> CreateOrthographicOffCenter(T left, T right, T bottom, T top, T nearPlane, T farPlane); // Ideally has LH and RH
public static Matrix4x4<T> CreatePerspective(T width, T height, T nearPlaneDistance, T farPlaneDistance); // Ideally has LH and RH
public static Matrix4x4<T> CreatePerspectiveFieldOfView(T fieldOfView, T aspectRatio, T nearPlaneDistance, T farPlaneDistance); // Ideally has LH and RH
public static Matrix4x4<T> CreatePerspectiveOffCenter(T left, T right, T bottom, T top, T nearPlaneDistance, T farPlaneDistance); // Ideally has LH and RH
public static Matrix4x4<T> CreateReflection(Plane<T> value);
public static Matrix4x4<T> CreateRotationX(T radians);
public static Matrix4x4<T> CreateRotationX(T radians, Vector3<T> centerPoint);
public static Matrix4x4<T> CreateRotationY(T radians);
public static Matrix4x4<T> CreateRotationY(T radians, Vector3<T> centerPoint);
public static Matrix4x4<T> CreateRotationZ(T radians);
public static Matrix4x4<T> CreateRotationZ(T radians, Vector3<T> centerPoint);
public static Matrix4x4<T> CreateScale(T scale);
public static Matrix4x4<T> CreateScale(T scale, Vector3<T> centerPoint);
public static Matrix4x4<T> CreateScale(T scaleX, T scaleY, T scaleZ);
public static Matrix4x4<T> CreateScale(T scaleX, T scaleY, T scaleZ, Vector3<T> centerPoint);
public static Matrix4x4<T> CreateScale(Vector3<T> scale);
public static Matrix4x4<T> CreateScale(Vector3<T> scale, Vector3<T> centerPoint);
public static Matrix4x4<T> CreateShadow(Vector3<T> lightDirection, Plane<T> plane);
public static Matrix4x4<T> CreateTranslation(T positionX, T positionY, T positionZ);
public static Matrix4x4<T> CreateTranslation(Vector3<T> position);
public static Matrix4x4<T> CreateWorld(Vector3<T> position, Vector3<T> forward, Vector3<T> up);
public static bool Decompose(Matrix4x4<T> matrix, out Vector3<T> scale, out Quaternion rotation, out Vector3<T> translation); // TryDecompose is a better name?
public static bool Invert(Matrix4x4<T> matrix, out Matrix4x4<T> result); // TryInvert is a better name? Better to out determinant and return result?
public static Matrix4x4<T> Lerp(Matrix4x4<T> min, Matrix4x4<T> max, T amount);
public static Matrix4x4<T> Transform(Matrix4x4<T> value, Quaternion<T> rotation); // Rotate is a better name
public static Matrix4x4<T> Transpose(Matrix4x4<T> value);
// Methods
public override readonly bool Equals(object? obj);
public readonly bool Equals(Matrix4x4<T> other);
public readonly T GetDeterminant(); // Better as a property?
public override readonly int GetHashCode();
public readonly override string ToString();
// public readonly string ToString(string format);
// public readonly string ToString(string format, IFormatProvider formatProvider);
}
Can I take Vector2<T>
for the software impls?
Approved API is here: #24168 (comment)
Types are the following and should support float
and double
:
- Vector2, Assigned to @john-h-k
- Vector3, Assigned to @HurricanKai
- Vector4, Assigned to @Perksey
- Plane, Up for Grabs
- Quaternion, Up for Grabs
- Matrix3x2, Up for Grabs
- Matrix4x4, Up for Grabs
The initial PR should just focus on the software implementation. Hardware acceleration is being looked at and I will update with more details.
I'd like to take the software impl of Vector3
Thanks @HurricanKai and @Perksey.
I've marked those bits as assigned. As per the discussion on Discord, given the work size and that we don't want to get only a partial implementation in, the work will go into the generic-vector
branch of my fork: https://github.com/tannergooding/runtime/tree/generic-vector
Once everything is completed, it can then be PR'd altogether for a final review.
Once the first couple of types are in, I will see how easy it is to extend the SimdAsHWIntrinsic
support to support the new types.
On initial investigation, this should be just recognizing the new type for float
. It may require some minor changes to support double
code when AVX is not supported.
@eerhardt reminded me we have https://github.com/dotnet/runtimelab now, so I will set that up instead
As discussed in Discord, John and I would like to claim Matrix4x4 and Matrix3x2 respectively. Let me know if the runtimelab branch is still going ahead, if not I'll just use tanner's fork.
Whatever happened to this? In many applications doubles are absolutely critical for accuracy. In the signal processing or RF world floats can affect phase drastically, and on modern 64 bit computers double math and float math are pretty close in performance. Why wasn't System.Numerics done as doubles originally? I mean that's why System.Math was done in doubles to begin with and MathF was added later. Basically this is unusable in any situation that requires accuracy, and has forced us to use alternative math libraries that use hardware.instrinsics. Also someone has already done this, so please get this included. I'd do it myself but for various company policy reasons I can't.
https://github.com/Weingartner/System.Numerics.DoubleVectors
Also doing it as Decimal would be welcome too, although that's probably pretty niche.
Lastly, is JIT smart enough in .net core to use AVX, AVX2, AVX256, and AVX512 instructions?
Whatever happened to this
It's a work in progress and hasn't been completed yet. I'd like to get it in for .NET 6, but we still need to see about scheduling and prioritization.
In the signal processing or RF world floats can affect phase drastically, and on modern 64 bit computers double math and float math are pretty close in performance
For single operations, double
and float
indeed take approximately the same number of cycles. For vectorized operations however, single-precision floats tends to be ~2x faster as each instruction can operate on 4/8 rather than 2/4 values at a time.
Why wasn't System.Numerics done as doubles originally
Most of the types in System.Numerics were oriented around graphics programming (things like XNA, MonoGame, etc) which tends to prefer single-precision floats. Many CPUs also correspondingly favor single-precision vectorized operations in the instructions they expose. That is, not all functionality is equally exposed between float
and double
.
Lastly, is JIT smart enough in .net core to use AVX, AVX2, AVX256, and AVX512 instructions?
Yes. Just as Vector2/3/4
are internally accelerated today, these new types will likewise be internally accelerated when possible. We also expose System.Runtime.Intrinsics
for cases where you want more explicit control over the codegen.
We do not support AVX512 today and there is no AVX256 ISA. 256-bit variants of the instructions as part of AVX and AVX2 and are supported that way.
I will move this along for the .NET 6 wave and co-ordinate PRs in runtimelab with @tannergooding. Assigning to myself
Whatever happened to this
It's a work in progress and hasn't been completed yet. I'd like to get it in for .NET 6, but we still need to see about scheduling and prioritization.
In the signal processing or RF world floats can affect phase drastically, and on modern 64 bit computers double math and float math are pretty close in performance
For single operations,
double
andfloat
indeed take approximately the same number of cycles. For vectorized operations however, single-precision floats tends to be ~2x faster as each instruction can operate on 4/8 rather than 2/4 values at a time.Why wasn't System.Numerics done as doubles originally
Most of the types in System.Numerics were oriented around graphics programming (things like XNA, MonoGame, etc) which tends to prefer single-precision floats. Many CPUs also correspondingly favor single-precision vectorized operations in the instructions they expose. That is, not all functionality is equally exposed between
float
anddouble
.Lastly, is JIT smart enough in .net core to use AVX, AVX2, AVX256, and AVX512 instructions?
Yes. Just as
Vector2/3/4
are internally accelerated today, these new types will likewise be internally accelerated when possible. We also exposeSystem.Runtime.Intrinsics
for cases where you want more explicit control over the codegen.We do not support AVX512 today and there is no AVX256 ISA. 256-bit variants of the instructions as part of AVX and AVX2 and are supported that way.
Thank you for all this information and additional context that makes a lot of sense
@pgovind and @tannergooding, is there any update on this for .NET6 given that the first previews are out already?
I'm having a use case where I'd like to work with sets of currency-related data, where I would like to perform parallelized operations on the elements of these sets. Think of arrays of decimal
s.
The operations I would like to perform are element-wise multiplication and element-wise summing of multiple arrays having the same length.
I thought to use Vector<decimal>
for this, only to find out that it will throw a NotSupportedException
....
This isn't being actively prioritized right now.
I've done quite a bit of cleanup in the JIT and so adding the acceleration for these types should be much more doable now, but it still needs some infrastructure work to make happen.
Ok, and is there still a chance to get it in? Can anything be done to prioritize it higher?
It would already help a bit if it wasn't hardware accelerated yet but if it at least was usable from a developer perspective (rather than having to build for loops or use LINQ's Zip method to achieve element-wise vector-scalar multiplication and element summing of multiple vectors).
Now for every method, ThrowHelper.ThrowForUnsupportedNumericsVectorBaseType<T>();
is called...
Edit: if I'm misunderstanding this and if I should be using a different approach for (parallel) element-wise operations on lists of decimal
, I'd be also curious to hear the feedback :)
See https://dotnetfiddle.net/DXpMUP for an example of why you could do element-wise operations on sequences of decimal
.
is there still a chance to get it in
It will happen eventually. When it gets in is largely up to my own free time. Even if a community member were to provide this, there needs to be availability to review it.
Can anything be done to prioritize it higher?
Not readily. This isn't something that falls into the "nice to have"/"general framework improvements" category and so it falls below other work on my plate.
Now for every method, ThrowHelper.ThrowForUnsupportedNumericsVectorBaseType(); is called...
This is elided by the JIT since T
must be a struct
type.
See https://dotnetfiddle.net/DXpMUP for an example of why you could do element-wise operations on sequences of decimal.
These types are largely oriented around what can be hardware accelerated. We could conceivably support more with the new generic math proposal, but it isn't clear how much benefit that will have.
CC. @jeffhandley
When it gets in is largely up to my own free time.
Ok, I understand. Thank you for providing feedback in any case!
This is elided by the JIT since T must be a struct type.
Ok, but decimal
is a struct, too, and still it will throw a NotSupportedException
. I do see now that it comes from the Vector<T>
constructor though.
We could conceivably support more with the new generic math proposal
Likely you mean #18244? (dotnet/csharplang#4436 also is called "generic math")
#18244 seems to focus on scalar types - I don't know enough about it to understand how that proposal will improve the Vector<T>
support for decimal
?
Ok, but decimal is a struct, too, and still it will throw a NotSupportedException
Yes, but we can't support any struct type. We can only support a limited subset, which is currently float
, double
, byte
, sbyte
, short
, ushort
, int
, uint
, long
, ulong
, nint
, and nuint
(the exact types supported can vary however).
decimal
is a large and slow type oriented around a specific use-case (largely currency
). It isn't suitable for use in some other spaces where concepts like NaN
, Infinity
or Negative Zero
are important.
Likely you mean #18244? (dotnet/csharplang#4436 also is called "generic math")
The latter. The former is one that I missed and have just closed
We are tracking some more extensive work in supporting "generic math" via some interfaces, the rough draft design is tracked here: dotnet/designs#205
This would theoretically allow Vector2<T> where T : INumber<T>
or similar, but given the orientation of Vector2
around graphics and perf, that might not be desirable.
decimal is a large and slow type oriented around a specific use-case (largely currency)
Yes, I agree, that is why I was wondering whether it was possible to support the missing builtin numeric types without hardware acceleration to make it at least "code-complete" (if the hardware acceleration support would be the issue).
I'd be willing to entertain supporting decimal
here, but I think it needs more thought, including how it would restrict or impact the ability to extend Vector2/3/4 with additional support in the future.
It would need to be a separate API proposal that discusses extending the types approved in this issue. You can find the new proposal template here: https://github.com/dotnet/runtime/issues/new?assignees=&labels=api-suggestion&template=02_api_proposal.md
#36160 is an example of an issue that requested Vector<T>
be expanded too support nint
and nuint
The main concerns are how this impacts the lack of APIs like decimal.Sqrt
and how it might impact the ability to expose methods like Sin
, Cos
, or other "common" functionality exposed by Vector2/3/4
types: https://docs.microsoft.com/en-us/windows/win32/dxmath/ovw-xnamath-reference-functions-vector2.
The same concerns exist for the integral types such as int
or byte
.
Also, for reference decimal
isn't an industry standard type (unlike float
, or double
or half
), isn't supported by any of the ABI (application binary interface) specifications, and isn't actually a runtime built-in type.
It's actually a Microsoft specific type that is fully defined by the framework and has special support in C#, VB, and F#.
Ok, in that case the barrier feels a bit too high for me. Thank you for thinking along, though!
Wow, I love it! Although one rarely needs extremely precise vectors, I do think the System.Numerics
API needs to be more extensive. How about long
for vectors and matrices?
@FilipToth that would require a (predictably extensive) chat in API review to figure out what to do with things that just donβt work outside of floating points - like vector lengths and normalisation: should they throw? or return a really imprecise value?
How about long for vectors and matrices?
The same concerns as I raised for decimal
exist, but more-so since it doesn't carry a fractional part.
What's the behavior for standard operations like Length
or Sqrt
. What will be the behavior if we want to expose Sin
or Cos
in the future?
The type is generic and so having these in the future will be possible, but it certainly needs more though and design work to see if that's desirable.
@Perksey @tannergooding Good to know. Thank you for your clarification.
The type is generic and so having these in the future will be possible, but it certainly needs more thought and design work to see if that's desirable.
I agree, it would amazing to have that in the API. I feel maybe the methods that do require a decimal point but the vector is say a long
could just round it up.
Now that generic math is a thing, this issue seems like the perfect use case for it.
To that end I have been creating generic math versions here.
I have implemented Vector2<T>, Vector3<T>, Vector4<T>, Matrix3x2<T>, Matrix4x4<T>, Plane<T> and Quaternion<T> together with the associated tests, all based on the existing non-generic float versions code.
Feedback welcome!
@sparkie108 1000000000% agree, though before coming up with an alternative API proposal we should probably look at which APIs are suitable for just floating point numbers and which APIs could work with any numeric type, as generic math would allow us to open these new types up to every single numeric type (but there are some of the methods which don't really make sense if you're working with integers)
Agreed a discussion is needed as to how theses types should be progressed now that Generic Math is a thing.
My own considered opinion, for what it's worth, is that these types are primary designed to support 2D and 3D graphical applications and that's what they should focus on. If you look at the methods, particalarly of the Matrix types, most are orientated towards graphics. Perhaps they should even be in System.Numerics.Graphics? The scalar types currently used for graphics are Half, Single and Double which can all be supported using the approved api and Generic Math as shown in my repo.
I can see the case for supplementary vector/matrix types that:
- support more, and maybe even arbitary, dimensions.
- support additional numeric types that make sense for non-graphics applications, such as Decimal and Complex.
- don't support Plane or Quaternion or provide any graphics specific methods.
The ML world probably has some of this covered.
I suspect just adding support to these types for Complex and Decimal won't really meet anybody's needs.
Ideally the graphics types would be a 'subclass' of the more general types, but as they are structs I'm not sure how that is possible. Generic Math would certainly make it easier for the two sets of types to interact.
Whether integer support ever makes sense I'm unsure of, beyond a 2D pixel point which you could regard as a 2D vector. As soon as you try to invert a matrix of integers you risk getting floats.
@tannergooding, I would be very interested in your views.
This is no longer marked api-approved
or up-for-grabs
as there have been both new innovations and new potential conflicts that have come to light which warrant it getting re-designed and re-reviewed.
In particular, generic math now exists and may make much of this area simpler to design around/implement or such that it can be designed "better". Likewise, when the T
for these generic types are int
, there are potential conflicts with things such as C# list and index patterns and its highly possible one of the other domain specific should be used instead of Length
. dotnet/csharplang#5278 tracks the language providing an opt-out for this "issue", but it needs additional discussion/consideration by API review in either case.
So after 5 years of debate, back to the beginning. In the meantime, we're forced to do stuff like this.
Sometimes, the best solution is not the perfect solution. Sometimes the best solution is the one that arrives in time. How I wished to have System.Numerics.Vectors at the time I was using Net3.5
Yes, sometimes the best solution is not the perfect solution. But when there are practical and real world concerns that come up which will actively hurt or complicate the thing in question, its likewise worth the additional bake time to get it out right.
There are a lot of features that everyone is asking for across the ecosystem and this one in particular is one of the "big asks" and because it is a "big ask" it requires additional design and consideration, particularly if it is going to lay the foundations for future support and not simply get invalidated in another 3-5 years.
I've been actively pushing on getting this feature, among others in the numerics and SIMD related areas since I became the area owner and I am still committed to ensuring this happens.
In particular, we are not back to square 1 here, there is largely an open question around Length
. However, on top of that, there are a couple other "breaks" with regards to Vector2/3/4
that have consistently come up that are worth considering; in particularly explicitly calling out when something is LHS vs RHS based. These are the minimal open questions that API review needs to reconsider before this can actually move forward.
Beyond that, there is opportunity to correctly integrate this with generic math
for .NET 7. That isn't a requirement, but it is something worth considering given that this hasn't shipped yet. And finally, there is the overall need to ensure that this is performant and therefore actually usable in production apps. .NET 5 and .NET 6 laid a lot of foundational work to make that possible and to ensure we could trivially ensure Vector2/3/4<float>
and the other T
could be trivially accelerated.
I agree .NET could end up with a better solution if this was approached again with a clean sheet.
- it could fully embrace generic math with specific interfaces for vectors and matrices. It might be possible to resolve the issues of some members being suitable for integers and others not.
- split out the 2d/3d graphics specific methods, perhaps into extension methods. The core vector/matrix methods could then be used with imaginary numbers for example.
- resolve the Length vs Magnitude issue.
Generic math would make these types far more useful as the code consuming them could also be made generic, for example you could create LineSegment<T> with line-based math with would work with both floats and doubles.
I did manage to get generic math to work with the current api with one exception, see SparkieLabs/generic-vectors-matrices#3.
@tannergooding am I missing a trick here or is it a problem with current generic math api?
Is there anything we can do to help move this forward in .NET 7?
@tannergooding Hey, one question I had on the implementation of this. Looking at the code for float I don't see any of the SIMD logic like Avx.Add or Avx2 or SSE2, etc. How exactly does this work in the current implementation. Is JIT somehow smart enough to automatically insert the SIMD instructions where appropriate? Until the generic math comes into existence. I was thinking of just taking the float code that already exists, adding a D at the end of every method, and finding and replacing float with double.
Coming late to the party as I dived into this issue due to some raytracing experiments, which naturally use double and floats are just too small for the range of values causing visual distortions.
So basically the only way to guarantee that we can have some kind of stable accelerated API for this kind of purposes is to drop into C++, from what I understand from this discussion, as neither Systems.Numerics is going forward nor System.Runtime.Intrinsics provide the comfort of the previous one, and naturally it is still a big question how .NET 7 will look like in this regard.
So basically the only way to guarantee that we can have some kind of stable accelerated API for this kind of purposes is to drop into C++, from what I understand from this discussion, as neither Systems.Numerics is going forward nor System.Runtime.Intrinsics provide the comfort of the previous one, and naturally it is still a big question how .NET 7 will look like in this regard.
Could you elaborate on what parts above make you think this? System.Numerics
is not going away and is still being developed and moved forward. Likewise, System.Runtime.Intrinsics
is getting explicit improvements in .NET 7 to improve the xplat experience and make it more comfortable to use (most of the work is already done and in .NET 7 P1).
The main issues here are that exposing Vector2/3/4 (and equivalent generic versions of the other types) is a lot of work. The scope of the work is also complex as users want to support integer types in the future, there has been feedback for several years on the existing Vector2/3/4 types having a layout that makes certain things hard to understand/use, and there has since been the introduction of the hardware intrinsics, xplat intrinsic helpers (new in .NET 7), and generic math (also new in .NET 7). All of this culminates in a need to give this API another think over and taking it back to API review.
I certainly could push this forward faster and ignore some of the big feedback points or design changes that have come up. But that also wouldn't make me a very good area owner here. Likewise, I do not want this sitting around for ever; it is a beneficial set of APIs to expose and I don't want it to get stuck in design limbo. I do expect this next design round will be the last as I'm not currently planning any other "major" changes that would be impactful enough to do warrant that and I've also gotten the overall JIT support here into a good place where accelerating these APIs is going to be much easier/straightforward.
which naturally use double and floats are just too small for the range of values causing visual distortions.
I expect this is more to do with implementation than to do with the precision range of float
. Consider that many GPUs still don't have double-precision
support and that Vulkan
, DirectX 12
, and many of the best ray tracing engines still use float
.
I would generally expect a render engine/framework to take into account the concept of a "floating-origin" to ensure that you never have to consider floating-point values large enough to cause inaccuracies and to "normalize" the scale so that the most commonly used values are between -1
and +1
(allowing ~2 billion uniquely representable single-precision floating-point values).
I confess I haven't read every single post, however I got the feeling that other stuff was kind of having higher priority on .NET roadmap and we have what we have.
Good to know that I was wrong and should probably read the whole thread with more care.
As for the float vs double, maybe some issues on blindly converting a C++ rendering sample into C#, replacing glm with System.Numeric (and downgrading double to float in the process), which is what prompt me to try to find out the current stat of affairs, thus landing here, maybe I should look more closely for arithmetic conversion bugs.
Thanks for the lengthy reply, naturally I prefer to stay in .NET land even for this kind of experiments and looking forward to what is coming.
As for the float vs double, maybe some issues on blindly converting a C++ rendering sample into C#, replacing glm with System.Numeric (and downgrading double to float in the process), which is what prompt me to try to find out the current stat of affairs, thus landing here, maybe I should look more closely for arithmetic conversion bugs.
It's likely not conversion bugs, but rather to do with how floating-point works under the covers.
float
is 32-bits and can represent approximately 4-billion unique values. However, the distribution is not linear and the distance between representable values differs depending on how big they are.
If you were to say that 1.0f
was a meter than it can accurately represent things at approx the "micrometer" level. Where-as at 1024.0
meters you can only represent things at approx the millimeter level. After 2^24 (approx 16.7 million) you can no longer even represent whole integers and only even numbers (and then only every 4th number at 2^25 and so on).
The biggest thing that double
typically enables is that you can represent whole integers up to 2^52
and so you are less likely to run into issues with a simple algorithm. However, you can still run into the same issues especially if you are working with "large scales" (like space games representing the solar system).
Due to how float
/double
are represented and because approx half of the representable values are between -1
and +1
, you generally want to "normalize" your inputs so that most values fall between -1
and +1
. You then likely also want to separate your world into cells or chunks and to change where the origin is as you move throughout the world (either moving the world around the camera, rather than the camera through the world; or changing the origin every 'x' cells so that you never move far enough away from it to start seeing issues).
@tannergooding Many thanks for the tips, even though it isn't really related to the ticket, much appreciated.
I've just found Silk.NET.Maths which seems to be quite complete and using generic maths.
I've just found Silk.NET.Maths which seems to be quite complete and using generic maths.
The project in general looks pretty sweet, thanks for posting it. Their work is quite impressive. But do the math operations you listed there actually get compiled to use SIMD efficiently? Like are they going to use avx instructions?
@serp777 see dotnet/Silk.NET#666 and #904
As for the float vs double, maybe some issues on blindly converting a C++ rendering sample into C#, replacing glm with System.Numeric (and downgrading double to float in the process), which is what prompt me to try to find out the current stat of affairs, thus landing here, maybe I should look more closely for arithmetic conversion bugs.
It's likely not conversion bugs, but rather to do with how floating-point works under the covers.
float
is 32-bits and can represent approximately 4-billion unique values. However, the distribution is not linear and the distance between representable values differs depending on how big they are.If you were to say that
1.0f
was a meter than it can accurately represent things at approx the "micrometer" level. Where-as at1024.0
meters you can only represent things at approx the millimeter level. After 2^24 (approx 16.7 million) you can no longer even represent whole integers and only even numbers (and then only every 4th number at 2^25 and so on).The biggest thing that
double
typically enables is that you can represent whole integers up to2^52
and so you are less likely to run into issues with a simple algorithm. However, you can still run into the same issues especially if you are working with "large scales" (like space games representing the solar system).Due to how
float
/double
are represented and because approx half of the representable values are between-1
and+1
, you generally want to "normalize" your inputs so that most values fall between-1
and+1
. You then likely also want to separate your world into cells or chunks and to change where the origin is as you move throughout the world (either moving the world around the camera, rather than the camera through the world; or changing the origin every 'x' cells so that you never move far enough away from it to start seeing issues).
Interesting, this makes me think about how kerbal space program (written in Unity) does their math. You've got very large numbers in the hundreds of millions or billions with precision at the 0.01 level. At the same time though all double math would likely be too expensive.
this makes me think about how kerbal space program (written in Unity) does their math.
Explained here: https://youtu.be/mXTxQko-JH0?t=259
Interesting, this makes me think about how kerbal space program (written in Unity) does their math. You've got very large numbers in the hundreds of millions or billions with precision at the 0.01 level. At the same time though all double math would likely be too expensive.
@serp777, yes they cover a few of the standard approaches to solving this in the video. You can see this in other games as well particularly those with modding support such as Minecraft (chunks), or Elder Scrolls (cells), etc. There are tons of papers covering the techniques and approaches that allow all of this to work, including at scale.
Its also worth noting that double
math isn't itself "expensive". That is float + float
and double + double
both execute in about 2-3 cycles (going back to at least 2016) and this also applies to most other operations that have hardware support. What makes float
faster is when you're using SIMD, as you can then do twice as many operations in the same instruction or when memory is a bottleneck since you can do the same number of operations while using half the memory.