[RFC] Stride.Math with the new .NET 7 System.Numerics interfaces
See original GitHub issueUsing the newly added abstract static method for interfaces we can reduce code repetition. This shouldn’t affect anything but number of lines of code for the vector implementations.
Some questions to answer before going forward with it :
- Is it usable and comfortable ?
- Is it viable performance wise ?
- Can we do SIMD with it ?
- Does it allow inlining ?
Implementation
using System;
namespace Stride.Maths
public struct Double2 : IVector2<Double2, double>
{
public double X {get;set;}
public double Y {get;set;}
public Double2(double value)
{
X = value;
Y = value;
}
public Double2(double x, double y)
{
X = x;
Y = y;
}
public static double Distance(in Double2 value)
{
return (double)Math.Sqrt(value.X * value.X + value.Y * value.Y);
}
public static Double2 New(double value)
{
return new(value);
}
public static Double2 New(double x, double y)
{
return new(x,y);
}
public static void SquareRoot(in Double2 value, out Double2 result)
{
result = new((double)Math.Sqrt(value.X), (double)Math.Sqrt(value.Y));
}
}
public struct Half2 : IVector2<Half2, Half>
{
public Half X {get;set;}
public Half Y {get;set;}
public Half2(Half value)
{
X = value;
Y = value;
}
public Half2(Half x, Half y)
{
X = x;
Y = y;
}
public static Half Distance(in Half2 value)
{
return (Half)MathF.Sqrt((float)(value.X * value.X + value.Y * value.Y));
}
public static Half2 New(Half value)
{
return new(value);
}
public static Half2 New(Half x, Half y)
{
return new(x,y);
}
public static void SquareRoot(in Half2 value, out Half2 result)
{
result = new((Half)MathF.Sqrt((float)value.X), (Half)MathF.Sqrt((float)value.Y));
}
}
Interface
using System.Numerics;
internal interface IVector2<T, Num>
where T :
struct,
IVector2<T, Num>
where Num : INumber<Num>
{
public Num X { get; set; }
public Num Y { get; set; }
public T One => T.New(Num.One);
public T Zero => T.New(Num.Zero);
public static abstract T New(Num value);
public static abstract T New(Num x, Num y);
public static virtual void Abs(in T value, out T result)
{
result = T.New(Num.Abs(value.X), Num.Abs(value.Y));
}
public static virtual void Add(in T left, Num scalar, out T result)
{
result = T.New(
left.X + scalar,
left.Y + scalar
);
}
public static virtual void Add(in T left, in T right, out T result)
{
result = T.New(
left.X + right.X,
left.Y + right.Y
);
}
public static virtual void Clamp(in T value, in T min, in T max, out T result)
{
result = T.New(
Num.MaxMagnitude(Num.MinMagnitude(value.X,max.X), min.X),
Num.MaxMagnitude(Num.MinMagnitude(value.Y,max.Y), min.Y)
);
}
public static abstract Num Distance(in T value);
public static virtual Num DistanceSquared(in T value)
{
return value.X * value.X + value.Y * value.Y;
}
public static virtual void Divide(Num scalar, in T value, out T result)
{
result = T.New(
scalar / value.X,
scalar / value.Y
);
}
public static virtual void Divide(in T value, Num scalar, out T result)
{
result = T.New(
value.X / scalar,
value.Y / scalar
);
}
public static virtual void Divide(in T left, in T right, out T result)
{
result = T.New(
left.X / right.X,
left.Y / right.Y
);
}
public static virtual Num Dot(in T left, in T right)
{
return left.X * right.X + left.Y * right.Y;
}
public static virtual void Lerp(in T value1, in T value2, Num amount, out T result)
{
T.Subtract(value2, value1, out var sub);
T.Multiply(sub, amount, out var mul);
T.Add(value1, mul, out result);
}
public static virtual void Multiply(in T value, Num scalar, out T result)
{
result = T.New(
value.X * scalar,
value.Y * scalar
);
}
public static virtual void Multiply(in T left, in T right, out T result)
{
result = T.New(
left.X * right.X,
left.Y * right.Y
);
}
public static virtual void Negate(in T value, out T result)
{
result = T.New(
-value.X,
-value.Y
);
}
public static virtual void Normalize(in T value, out T result)
{
var x = value.X;
var y = value.Y;
result = T.New(
x < -Num.One ? -Num.One : x > Num.One ? Num.One : x,
x < -Num.One ? -Num.One : x > Num.One ? Num.One : y
);
}
public static virtual void Reflect(in T vector, in T normal, out T result)
{
Num dot = T.Dot(vector,normal);
result = T.New(
vector.X - (Num.One + Num.One) * dot * normal.X,
vector.Y - (Num.One + Num.One) * dot * normal.Y
);
}
public static abstract void SquareRoot(in T value, out T result);
public static virtual void Subtract(in T left, Num scalar, out T result)
{
result = T.New(
left.X - scalar,
left.Y - scalar
);
}
public static virtual void Subtract(Num scalar, in T left, out T result)
{
result = T.New(
scalar - left.X,
scalar - left.Y
);
}
public static virtual void Subtract(in T left, in T right, out T result)
{
result = T.New(
left.X - right.X,
left.Y - right.Y
);
}
public static virtual void Transform(in T vector, in T transform, out T result)
{
T.Add(vector,transform, out result);
}
// public static virtual void TransformNormal(in T left, in T right, out T result);
}
Issue Analytics
- State:
- Created 10 months ago
- Reactions:1
- Comments:5 (4 by maintainers)
Top Results From Across the Web
.NET 7 Preview 5 - Generic Math - .NET Blog
NET 7 Preview 5 includes significant improvements to the Generic Math ... IParsable; Moving all other new numeric interfaces to the System.
Read more >.NET 7 Adds Generic Math
Along with Static Abstract Methods in interfaces comes support for generic math in .NET 7 via interfaces such as INumber .
Read more >RFC 6716: Definition of the Opus Audio Codec
1. Range Decoder Initialization Let b0 be an 8-bit unsigned integer containing first input byte (or containing zero if there are no bytes...
Read more >Foundations of python network programming john goerzen pdf
Python has made great strides since Apress released the first edition of this book back in the days of Python 2.3. The advances...
Read more >2020 – 2021 Florida Department of Education Curriculum ...
This program offers a sequence of courses that provides coherent and rigorous content aligned with challenging academic standards and relevant.
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
Can I suggest not doing this unless it is part of a replacement for
Stride.Math
? The current implementation is up to at least 40x slower than necessary and the programming experience with it, and/or the availability of a large amount of duplicated operations is not quite right anymore and this also makes changing the code as opposed to rewriting a lot of work.Thanks for the reference to those issues. Yeah, I would be definitely for using those static interfaces to simplify things.
Have you seen this reddit question? https://www.reddit.com/r/csharp/comments/ti11e1/can_i_cast_an_inumber_or_similar_to_systemdouble/ You can use
double.CreateChecked
to cast onto a double and feed it to the Sqrt and thenNum.CreateTruncating
to convert it back onto a Num or something like that. Though my quick check of the source code didn’t find a conversion between Half and Double (maybe I wasn’t looking in the right place)…