diff --git a/.gitignore b/.gitignore index 5944fca..b47618a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Visual Studio stuff -*.sln *.csproj +*.editorconfig +*.sln /Nerd_STF/.vs/ /Nerd_STF/obj /Nerd_STF/bin/Debug @@ -8,6 +9,9 @@ /Nerd_STF/bin/Release/net6.0/Nerd_STF.dll /Nerd_STF/bin/Release/net6.0/Nerd_STF.pdb +# Testing project +/Testing + # Nuget /Nerd_STF/LICENSE *.nupkg diff --git a/Changelog.md b/Changelog.md index c76776a..7731b65 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,124 +1,173 @@ -# Nerd_STF v2.2.0 +# Nerd_STF v2.3.0 -This update adds many types of graphics-based objects, as well as some math functions and constants. +This update adds lots of linear algebra tools, like matrixes and vectors, as well as new number systems, like complex numbers and quaternions. ``` * Nerd_STF - + delegate Fill2D(int, int) + + Extensions + + ConversionExtension + + Container2DExtension + + ToFillExtension + + Foreach + + IGroup2D + + Modifier + + Modifier2D + * IGroup + + ToFill() * Exceptions - + FileParsingException - + FileType - + Graphics - + ColorChannel - + CMYKA - + CMYKAByte - + HSVA - + HSVAByte - + IColor - + IColorByte - + IlluminationFlags - + IlluminationModel - + Image - + Material - + RGBA - + RGBAByte + + InvalidSizeException + + NoInverseException + * Graphics + * CMYKA + + ToFill() + + static Round(CMYKA) + = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)` + * CMYKAByte + + ToFill() + = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)` + * HSVA + + ToFill() + + static Round(HSVA, Angle.Type) + = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)` + * HSVAByte + + ToFill() + = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)` + * Image + = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)` + * Material + = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)` + = Merged 2 if statements into 1 in `override bool Equals(object?)` + * RGBA + + ToFill() + + ToVector() + + static Round(RGBA) + = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)` + * RGBAByte + + ToFill() + + ToVector() + = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)` * Mathematics - * Angle - + Normalized - = Made fancier `ToString()` formatting - * Type - + Normalized - + Constants - * Float2 - + static SplitArray(params Float2[]) - = Renamed `static Multiply(params Float2[])` to `Product` - * Float3 - + static SplitArray(params Float3[]) - + explicit operator Float3(RGBA) - + explicit operator Float3(HSVA) - + explicit operator Float3(RGBAByte) - + explicit operator Float3(HSVAByte) - = Renamed `static Multiply(params Float3[])` to `Product` - * Float4 - + static SplitArray(params Float4[]) - + implicit operator Float4(RGBA) - + explicit operator Float4(CMYKA) - + implicit operator Float4(HSVA) - + implicit operator Float4(RGBAByte) - + explicit operator Float4(CMYKAByte) - + implicit operator Float4(HSVAByte) - = Renamed `static Multiply(params Float4[])` to `Product` + + Algebra + + IMatrix + + Matrix2x2 + + Matrix3x3 + + Matrix4x4 + + Vector2d + + Vector3d + + NumberSystems + + Complex + + Quaternion + + Samples + + Equations + + ScaleType + = Moved `Constants` file to Samples folder. * Geometry + * Box2D + = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)` + * Box3D + = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)` * Line - + Midpoint - = Renamed `ToDoubleArray()` to `ToFloatArray` - = Renamed `ToDoubleList()` to `ToFloatList` + + ToFill() + = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)` * Polygon - + Midpoint - = Renamed `ToDoubleArray()` to `ToFloatArray` - = Renamed `ToDoubleList()` to `ToFloatList` - = Renamed `static ToDoubleArrayAll(params Triangle[])` to `ToFloatArrayAll` - = Renamed `static ToDoubleListAll(params Triangle[])` to `ToFloatListAll` + + ToFill() + = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)` * Quadrilateral - + Midpoint - = Renamed `ToDoubleArray()` to `ToFloatArray` - = Renamed `ToDoubleList()` to `ToFloatList` - = Renamed `static ToDoubleArrayAll(params Triangle[])` to `ToFloatArrayAll` - = Renamed `static ToDoubleListAll(params Triangle[])` to `ToFloatListAll` + + ToFill() + = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)` + * Sphere + = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)` * Triangle - + Midpoint - = Renamed `ToDoubleArray()` to `ToFloatArray` - = Renamed `ToDoubleList()` to `ToFloatList` - = Renamed `static ToDoubleArrayAll(params Triangle[])` to `ToFloatArrayAll` - = Renamed `static ToDoubleListAll(params Triangle[])` to `ToFloatListAll` + + ToFill() + = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)` * Vert - = Renamed `static ToDouble3Array(params Vert[])` to `ToFloat3Array` - = Renamed `static ToDouble3List(params Vert[])` to `ToFloat3List` + + ToFill() + + ToVector() + = Made `Vert(Float2)` not recreate a float group, and instead use itself. + = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)` + * Angle + + static Down + + static Left + + static Right + + static Up + + static Round(Angle, Type) + = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)` + * Float2 + + ToFill() + + ToVector() + + static Round(Float2) + + implicit operator Float2(Complex) + + explicit operator Float2(Quaternion) + + explicit operator Float2(Matrix) + + operator *(Float2, Matrix) + + operator /(Float2, Matrix) + = Made `Normalized` multiply by the inverse square root instead of dividing by the square root. + = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)` + * Float3 + + ToFill() + + ToVector() + + static Round(Float3) + + implicit operator Float3(Complex) + + explicit operator Float3(Quaternion) + + explicit operator Float3(Matrix) + + operator *(Float3, Matrix) + + operator /(Float3, Matrix) + = Made `Normalized` multiply by the inverse square root instead of dividing by the square root. + = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)` + * Float4 + + ToFill() + + static Round(Float4) + + implicit operator Float4(Complex) + + implicit operator Float4(Quaternion) + + explicit operator Float4(Matrix) + + operator *(Float4, Matrix) + + operator /(Float4, Matrix) + = Made `Normalized` multiply by the inverse square root instead of dividing by the square root. + = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)` + = Renamed `Float4.Deep` to `Float4.Near` * Int2 - + static SplitArray(params Int[]) - = Renamed `static Multiply(params Int2[])` to `Product` + + ToFill() + + explicit operator Int2(Complex) + + explicit operator Int2(Quaternion) + + explicit operator Int2(Matrix) + + operator *(Int2, Matrix) + + operator /(Int2, Matrix) + = Made `Normalized` multiply by the inverse square root instead of dividing by the square root. + = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)` * Int3 - + static SplitArray(params Int3[]) - + explicit operator Int3(RGBA) - + explicit operator Int3(HSVA) - + explicit operator Int3(RGBAByte) - + explicit operator Int3(HSVAByte) - = Renamed `static Multiply(params Int3[])` to `Product` + + ToFill() + + explicit operator Int3(Complex) + + explicit operator Int3(Quaternion) + + explicit operator Int3(Matrix) + + operator *(Int3, Matrix) + + operator /(Int3, Matrix) + = Made `Normalized` multiply by the inverse square root instead of dividing by the square root. + = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)` * Int4 - + static SplitArray(params Int4[]) - + explicit operator Int4(RGBA) - + explicit operator Int4(CMYKA) - + explicit operator Int4(HSVA) - + implicit operator Int4(RGBAByte) - + explicit operator Int4(CMYKAByte) - + implicit operator Int4(HSVAByte) - = Renamed `static Multiply(params Int4[])` to `Product` + + explicit operator Int4(Complex) + + explicit operator Int4(Quaternion) + + explicit operator Int4(Matrix) + + operator *(Int4, Matrix) + + operator /(Int4, Matrix) + = Made `Normalized` multiply by the inverse square root instead of dividing by the square root. + = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)` * Mathf - + static Combinations(int, int) - + static GreatestCommonFactor(params int[]) - + static InverseSqrt(float) - + static LeastCommonMultiple(params int[]) - + static Mode(params T[]) where T : IEquatable - + static Permutations(int, int) - + static Pow(float, int) - + static Product(Equation, float, float, float) - + static Sum(Equation, float, float, float) - + static UniqueItems(params T[]) where T : IEquatable - + static ZScore(float, params float[]) - + static ZScore(float, float, float) - - const RadToDeg - - const E - - const GoldenRatio - - const HalfPi - - const Pi - - const DegToRad - - const Tau - = GreatestCommonFactor actually works now - = Pow has been fixed - = Mode actually works - * static Average(params int[]) - = Replaced its `int` return type with `float` + + Cos(Angle) + + Cot(Angle) + + Csc(Angle) + + Dot(float[], float[]) + + Dot(float[][]) + + Max(T[]) where T : IComparable + + Median(T[]) + + Min(T[]) where T : IComparable + + Sec(Angle) + + Sin(Angle) + + Tan(Angle) * Miscellaneous * GlobalUsings.cs - + global using Nerd_STF.Graphics; + + global using Nerd_STF.Collections + + global using Nerd_STF.Extensions + + global using Nerd_STF.Mathematics.Algebra + + global using Nerd_STF.Mathematics.NumberSystems + + global using Nerd_STF.Mathematics.Samples ``` diff --git a/Nerd_STF/Exceptions/InvalidSizeException.cs b/Nerd_STF/Exceptions/InvalidSizeException.cs new file mode 100644 index 0000000..6d16e83 --- /dev/null +++ b/Nerd_STF/Exceptions/InvalidSizeException.cs @@ -0,0 +1,12 @@ +using System.Runtime.Serialization; + +namespace Nerd_STF.Exceptions; + +[Serializable] +public class InvalidSizeException : Nerd_STFException +{ + public InvalidSizeException() : this("Argument size is invalid.") { } + public InvalidSizeException(string message) : base(message) { } + public InvalidSizeException(string message, Exception inner) : base(message, inner) { } + protected InvalidSizeException(SerializationInfo info, StreamingContext context) : base(info, context) { } +} diff --git a/Nerd_STF/Exceptions/NoInverseException.cs b/Nerd_STF/Exceptions/NoInverseException.cs new file mode 100644 index 0000000..a7eca4e --- /dev/null +++ b/Nerd_STF/Exceptions/NoInverseException.cs @@ -0,0 +1,18 @@ +using System.Runtime.Serialization; + +namespace Nerd_STF.Exceptions; + +[Serializable] +public class NoInverseException : Exception +{ + public Matrix? Matrix; + + public NoInverseException() : base("This matrix does not have an inverse.") { } + public NoInverseException(string message) : base(message) { } + public NoInverseException(string message, Exception inner) : base(message, inner) { } + public NoInverseException(Matrix? matrix) : this() => Matrix = matrix; + public NoInverseException(Matrix? matrix, string message) : this(message) => Matrix = matrix; + public NoInverseException(Matrix? matrix, string message, Exception inner) : this(message, inner) => + Matrix = matrix; + protected NoInverseException(SerializationInfo info, StreamingContext context) : base(info, context) { } +} diff --git a/Nerd_STF/Extensions/Container2DExtension.cs b/Nerd_STF/Extensions/Container2DExtension.cs new file mode 100644 index 0000000..6a90731 --- /dev/null +++ b/Nerd_STF/Extensions/Container2DExtension.cs @@ -0,0 +1,24 @@ +namespace Nerd_STF.Extensions; + +public static class Container2DExtension +{ + public static T[] Flatten(this T[,] array, Int2 size) + { + T[] res = new T[size.x * size.y]; + for (int x = 0; x < size.x; x++) for (int y = 0; y < size.y; y++) res[x + y * size.y] = array[x, y]; + return res; + } + + public static T[] GetColumn(this T[,] array, int column, int length) + { + T[] res = new T[length]; + for (int i = 0; i < length; i++) res[i] = array[column, i]; + return res; + } + public static T[] GetRow(this T[,] array, int row, int length) + { + T[] res = new T[length]; + for (int i = 0; i < length; i++) res[i] = array[i, row]; + return res; + } +} diff --git a/Nerd_STF/Extensions/ConversionExtension.cs b/Nerd_STF/Extensions/ConversionExtension.cs new file mode 100644 index 0000000..83c9c3c --- /dev/null +++ b/Nerd_STF/Extensions/ConversionExtension.cs @@ -0,0 +1,13 @@ +namespace Nerd_STF.Extensions; + +public static class ConversionExtension +{ + public static Dictionary ToDictionary + (this IEnumerable> pairs) + where TKey : notnull + { + Dictionary res = new(); + foreach (KeyValuePair pair in pairs) res.Add(pair.Key, pair.Value); + return res; + } +} diff --git a/Nerd_STF/Extensions/ToFillExtension.cs b/Nerd_STF/Extensions/ToFillExtension.cs new file mode 100644 index 0000000..a0897ab --- /dev/null +++ b/Nerd_STF/Extensions/ToFillExtension.cs @@ -0,0 +1,6 @@ +namespace Nerd_STF.Extensions; + +public static class ToFillExtension +{ + public static Fill ToFill(this IEnumerable group) => i => group.ElementAt(i); +} diff --git a/Nerd_STF/Foreach.cs b/Nerd_STF/Foreach.cs new file mode 100644 index 0000000..624aa57 --- /dev/null +++ b/Nerd_STF/Foreach.cs @@ -0,0 +1,4 @@ +namespace Nerd_STF; + +public delegate void Foreach(object item); +public delegate void Foreach(T item); diff --git a/Nerd_STF/Graphics/CMYKA.cs b/Nerd_STF/Graphics/CMYKA.cs index 806c02d..65809f0 100644 --- a/Nerd_STF/Graphics/CMYKA.cs +++ b/Nerd_STF/Graphics/CMYKA.cs @@ -146,6 +146,8 @@ public struct CMYKA : IColor, IEquatable (float[] Cs, float[] Ms, float[] Ys, float[] Ks, float[] As) = SplitArray(vals); return new(Mathf.Min(Cs), Mathf.Min(Ms), Mathf.Min(Ys), Mathf.Min(Ks), Mathf.Min(As)); } + public static CMYKA Round(CMYKA val) => new(Mathf.Round(val.C), Mathf.Round(val.M), + Mathf.Round(val.Y), Mathf.Round(val.K), Mathf.Round(val.A)); public static (float[] Cs, float[] Ms, float[] Ys, float[] Ks, float[] As) SplitArray(params CMYKA[] vals) { @@ -169,7 +171,7 @@ public struct CMYKA : IColor, IEquatable && Y == col.Y && K == col.K && A == col.A; public override bool Equals([NotNullWhen(true)] object? obj) { - if (obj == null) return false; + if (obj == null) return base.Equals(obj); Type t = obj.GetType(); if (t == typeof(CMYKA)) return Equals((CMYKA)obj); else if (t == typeof(RGBA)) return Equals((IColor)obj); @@ -180,7 +182,7 @@ public struct CMYKA : IColor, IEquatable else if (t == typeof(HSVAByte)) return Equals((IColorByte)obj); else if (t == typeof(IColorByte)) return Equals((IColorByte)obj); - return false; + return base.Equals(obj); } public override int GetHashCode() => C.GetHashCode() ^ M.GetHashCode() ^ Y.GetHashCode() ^ K.GetHashCode() ^ A.GetHashCode(); public string ToString(IFormatProvider provider) => "C: " + C.ToString(provider) + " M: " + M.ToString(provider) @@ -205,6 +207,11 @@ public struct CMYKA : IColor, IEquatable public HSVAByte ToHSVAByte() => ToRGBA().ToHSVAByte(); public float[] ToArray() => new[] { C, M, Y, K, A }; + public Fill ToFill() + { + CMYKA @this = this; + return i => @this[i]; + } public List ToList() => new() { C, M, Y, K, A }; IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); diff --git a/Nerd_STF/Graphics/CMYKAByte.cs b/Nerd_STF/Graphics/CMYKAByte.cs index 54c8119..92ce249 100644 --- a/Nerd_STF/Graphics/CMYKAByte.cs +++ b/Nerd_STF/Graphics/CMYKAByte.cs @@ -149,7 +149,7 @@ public struct CMYKAByte : IColorByte, IEquatable && Y == col.Y && K == col.K && A == col.A; public override bool Equals([NotNullWhen(true)] object? obj) { - if (obj == null) return false; + if (obj == null) return base.Equals(obj); Type t = obj.GetType(); if (t == typeof(CMYKAByte)) return Equals((CMYKAByte)obj); else if (t == typeof(RGBA)) return Equals((IColor)obj); @@ -160,7 +160,7 @@ public struct CMYKAByte : IColorByte, IEquatable else if (t == typeof(HSVAByte)) return Equals((IColorByte)obj); else if (t == typeof(IColorByte)) return Equals((IColorByte)obj); - return false; + return base.Equals(obj); } public override int GetHashCode() => C.GetHashCode() ^ M.GetHashCode() ^ Y.GetHashCode() ^ K.GetHashCode() ^ A.GetHashCode(); @@ -181,6 +181,11 @@ public struct CMYKAByte : IColorByte, IEquatable public HSVAByte ToHSVAByte() => ToRGBA().ToHSVAByte(); public byte[] ToArray() => new[] { C, M, Y, K, A }; + public Fill ToFill() + { + CMYKAByte @this = this; + return i => @this[i]; + } public List ToList() => new() { C, M, Y, K, A }; IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); diff --git a/Nerd_STF/Graphics/HSVA.cs b/Nerd_STF/Graphics/HSVA.cs index 706b992..f2b2d0f 100644 --- a/Nerd_STF/Graphics/HSVA.cs +++ b/Nerd_STF/Graphics/HSVA.cs @@ -100,15 +100,15 @@ public struct HSVA : IColor, IEquatable for (int i = 0; i < vals.Length; i++) val += vals[i]; return val / vals.Length; } - public static HSVA Ceiling(HSVA val, Angle.Type type) => new(Angle.Ceiling(val.H, type), Mathf.Ceiling(val.S), - Mathf.Ceiling(val.V), Mathf.Ceiling(val.A)); + public static HSVA Ceiling(HSVA val, Angle.Type type = Angle.Type.Degrees) => new(Angle.Ceiling(val.H, type), + Mathf.Ceiling(val.S), Mathf.Ceiling(val.V), Mathf.Ceiling(val.A)); public static HSVA Clamp(HSVA val, HSVA min, HSVA max) => new(Angle.Clamp(val.H, min.H, max.H), Mathf.Clamp(val.S, min.S, max.S), Mathf.Clamp(val.V, min.V, max.V), Mathf.Clamp(val.A, min.A, max.A)); - public static HSVA Floor(HSVA val, Angle.Type type) => new(Angle.Floor(val.H, type), Mathf.Floor(val.S), - Mathf.Floor(val.V), Mathf.Floor(val.A)); + public static HSVA Floor(HSVA val, Angle.Type type = Angle.Type.Degrees) => new(Angle.Floor(val.H, type), + Mathf.Floor(val.S), Mathf.Floor(val.V), Mathf.Floor(val.A)); public static HSVA Lerp(HSVA a, HSVA b, float t, bool clamp = true) => new(Angle.Lerp(a.H, b.H, t, clamp), Mathf.Lerp(a.S, b.S, t, clamp), Mathf.Lerp(a.V, b.V, t, clamp), Mathf.Lerp(a.A, b.A, t, clamp)); @@ -136,6 +136,8 @@ public struct HSVA : IColor, IEquatable (Angle[] Hs, float[] Ss, float[] Vs, float[] As) = SplitArray(vals); return new(Angle.Min(Hs), Mathf.Min(Ss), Mathf.Min(Vs), Mathf.Min(As)); } + public static HSVA Round(HSVA val, Angle.Type type = Angle.Type.Degrees) => new(Angle.Round(val.H, type), + Mathf.Round(val.S), Mathf.Round(val.V), Mathf.Round(val.A)); public static (Angle[] Hs, float[] Ss, float[] Vs, float[] As) SplitArray(params HSVA[] vals) { @@ -171,7 +173,7 @@ public struct HSVA : IColor, IEquatable || H == col.H && S == col.S && V == col.V && A == col.A; public override bool Equals([NotNullWhen(true)] object? obj) { - if (obj == null) return false; + if (obj == null) return base.Equals(obj); Type t = obj.GetType(); if (t == typeof(HSVA)) return Equals((HSVA)obj); else if (t == typeof(CMYKA)) return Equals((IColor)obj); @@ -182,7 +184,7 @@ public struct HSVA : IColor, IEquatable else if (t == typeof(HSVAByte)) return Equals((IColorByte)obj); else if (t == typeof(IColorByte)) return Equals((IColorByte)obj); - return false; + return base.Equals(obj); } public override int GetHashCode() => H.GetHashCode() ^ S.GetHashCode() ^ V.GetHashCode() ^ A.GetHashCode(); public string ToString(IFormatProvider provider) => "H: " + H.ToString(provider) + " S: " + S.ToString(provider) @@ -212,6 +214,11 @@ public struct HSVA : IColor, IEquatable Mathf.RoundInt(V * 255), Mathf.RoundInt(A * 255)); public float[] ToArray() => new[] { H.Normalized, S, V, A }; + public Fill ToFill() + { + HSVA @this = this; + return i => @this[i]; + } public List ToList() => new() { H.Normalized, S, V, A }; IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); diff --git a/Nerd_STF/Graphics/HSVAByte.cs b/Nerd_STF/Graphics/HSVAByte.cs index e8a87c1..2134cdc 100644 --- a/Nerd_STF/Graphics/HSVAByte.cs +++ b/Nerd_STF/Graphics/HSVAByte.cs @@ -138,7 +138,7 @@ public struct HSVAByte : IColorByte, IEquatable || H == col.H && S == col.S && V == col.V && A == col.A; public override bool Equals([NotNullWhen(true)] object? obj) { - if (obj == null) return false; + if (obj == null) return base.Equals(obj); Type t = obj.GetType(); if (t == typeof(HSVAByte)) return Equals((HSVAByte)obj); else if (t == typeof(CMYKA)) return Equals((IColor)obj); @@ -149,7 +149,7 @@ public struct HSVAByte : IColorByte, IEquatable else if (t == typeof(HSVA)) return Equals((IColor)obj); else if (t == typeof(IColorByte)) return Equals((IColorByte)obj); - return false; + return base.Equals(obj); } public override int GetHashCode() => H.GetHashCode() ^ S.GetHashCode() ^ V.GetHashCode() ^ A.GetHashCode(); public string ToString(IFormatProvider provider) => "H: " + H.ToString(provider) + " S: " + S.ToString(provider) @@ -167,6 +167,11 @@ public struct HSVAByte : IColorByte, IEquatable public HSVAByte ToHSVAByte() => this; public byte[] ToArray() => new[] { H, S, V, A }; + public Fill ToFill() + { + HSVAByte @this = this; + return i => @this[i]; + } public List ToList() => new() { H, S, V, A }; IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); diff --git a/Nerd_STF/Graphics/Image.cs b/Nerd_STF/Graphics/Image.cs index 2b7f670..f6a35b1 100644 --- a/Nerd_STF/Graphics/Image.cs +++ b/Nerd_STF/Graphics/Image.cs @@ -75,9 +75,9 @@ public struct Image : ICloneable, IEnumerable, IEquatable public bool Equals(Image other) => Pixels == other.Pixels; public override bool Equals([NotNullWhen(true)] object? obj) { - if (obj == null) return false; + if (obj == null) return base.Equals(obj); if (obj.GetType() == typeof(Image)) return Equals((Image)obj); - return false; + return base.Equals(obj); } public override int GetHashCode() => Pixels.GetHashCode(); diff --git a/Nerd_STF/Graphics/Material.cs b/Nerd_STF/Graphics/Material.cs index 47e2389..d92a20c 100644 --- a/Nerd_STF/Graphics/Material.cs +++ b/Nerd_STF/Graphics/Material.cs @@ -171,9 +171,8 @@ public struct Material : ICloneable, IEquatable SpecularHighlightTexture.Equals(other.SpecularHighlightTexture) && StencilTexture.Equals(other.StencilTexture); public override bool Equals([NotNullWhen(true)] object? obj) { - if (obj == null) return false; - if (obj.GetType() == typeof(Material)) return Equals((Material)obj); - return false; + if (obj == null || obj.GetType() != typeof(Material)) return base.Equals(obj); + return Equals((Material)obj); } public override int GetHashCode() => Alpha.GetHashCode() ^ AmbientColor.GetHashCode() ^ Anisotropy.GetHashCode() ^ AnisotropyRoughness.GetHashCode() ^ ClearcoatRoughness.GetHashCode() ^ ClearcoatThickness.GetHashCode() ^ diff --git a/Nerd_STF/Graphics/RGBA.cs b/Nerd_STF/Graphics/RGBA.cs index 333557b..5f8211d 100644 --- a/Nerd_STF/Graphics/RGBA.cs +++ b/Nerd_STF/Graphics/RGBA.cs @@ -132,6 +132,8 @@ public struct RGBA : IColor, IEquatable (float[] Rs, float[] Gs, float[] Bs, float[] As) = SplitArray(vals); return new(Mathf.Min(Rs), Mathf.Min(Gs), Mathf.Min(Bs), Mathf.Min(As)); } + public static RGBA Round(RGBA val) => + new(Mathf.Round(val.R), Mathf.Round(val.G), Mathf.Round(val.B), Mathf.Round(val.A)); public static (float[] Rs, float[] Gs, float[] Bs, float[] As) SplitArray(params RGBA[] vals) { @@ -152,7 +154,7 @@ public struct RGBA : IColor, IEquatable public bool Equals(RGBA col) => A == 0 && col.A == 0 || R == col.R && G == col.G && B == col.B && A == col.A; public override bool Equals([NotNullWhen(true)] object? obj) { - if (obj == null) return false; + if (obj == null) return base.Equals(obj); Type t = obj.GetType(); if (t == typeof(RGBA)) return Equals((RGBA)obj); else if (t == typeof(CMYKA)) return Equals((IColor)obj); @@ -163,7 +165,7 @@ public struct RGBA : IColor, IEquatable else if (t == typeof(HSVAByte)) return Equals((IColorByte)obj); else if (t == typeof(IColorByte)) return Equals((IColorByte)obj); - return false; + return base.Equals(obj); } public override int GetHashCode() => R.GetHashCode() ^ G.GetHashCode() ^ B.GetHashCode() ^ A.GetHashCode(); public string ToString(IFormatProvider provider) => "R: " + R.ToString(provider) + " G: " + G.ToString(provider) + @@ -205,6 +207,11 @@ public struct RGBA : IColor, IEquatable public HSVAByte ToHSVAByte() => ToHSVA().ToHSVAByte(); public float[] ToArray() => new[] { R, G, B, A }; + public Fill ToFill() + { + RGBA @this = this; + return i => @this[i]; + } public List ToList() => new() { R, G, B, A }; IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); @@ -218,6 +225,8 @@ public struct RGBA : IColor, IEquatable public object Clone() => new RGBA(R, G, B, A); + public Vector3d ToVector() => ((Float3)this).ToVector(); + public static RGBA operator +(RGBA a, RGBA b) => new(a.R + b.R, a.G + b.G, a.B + b.B, a.A + b.A); public static RGBA operator -(RGBA c) => new(1 - c.R, 1 - c.G, 1 - c.B, c.A != 1 ? 1 - c.A : 1); public static RGBA operator -(RGBA a, RGBA b) => new(a.R - b.R, a.G - b.G, a.B - b.B, a.A - b.A); diff --git a/Nerd_STF/Graphics/RGBAByte.cs b/Nerd_STF/Graphics/RGBAByte.cs index d363449..26a07ae 100644 --- a/Nerd_STF/Graphics/RGBAByte.cs +++ b/Nerd_STF/Graphics/RGBAByte.cs @@ -137,7 +137,7 @@ public struct RGBAByte : IColorByte, IEquatable public bool Equals(RGBAByte col) => A == 0 && col.A == 0 || R == col.R && G == col.G && B == col.B && A == col.A; public override bool Equals([NotNullWhen(true)] object? obj) { - if (obj == null) return false; + if (obj == null) return base.Equals(obj); Type t = obj.GetType(); if (t == typeof(RGBAByte)) return Equals((RGBAByte)obj); else if (t == typeof(CMYKA)) return Equals((IColor)obj); @@ -148,7 +148,7 @@ public struct RGBAByte : IColorByte, IEquatable else if (t == typeof(HSVAByte)) return Equals((IColorByte)obj); else if (t == typeof(IColorByte)) return Equals((IColorByte)obj); - return false; + return base.Equals(obj); } public override int GetHashCode() => R.GetHashCode() ^ G.GetHashCode() ^ B.GetHashCode() ^ A.GetHashCode(); public string ToString(IFormatProvider provider) => "R: " + R.ToString(provider) + " G: " + G.ToString(provider) + @@ -166,6 +166,11 @@ public struct RGBAByte : IColorByte, IEquatable public HSVAByte ToHSVAByte() => ToRGBA().ToHSVAByte(); public byte[] ToArray() => new[] { R, G, B, A }; + public Fill ToFill() + { + HSVAByte @this = this; + return i => @this[i]; + } public List ToList() => new() { R, G, B, A }; IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); @@ -179,6 +184,8 @@ public struct RGBAByte : IColorByte, IEquatable public object Clone() => new RGBAByte(R, G, B, A); + public Vector3d ToVector() => ((RGBA)this).ToVector(); + public static RGBAByte operator +(RGBAByte a, RGBAByte b) => new(a.R + b.R, a.G + b.G, a.B + b.B, a.A + b.A); public static RGBAByte operator -(RGBAByte c) => new(255 - c.R, 255 - c.G, 255 - c.B, c.A != 255 ? 255 - c.A : 255); public static RGBAByte operator -(RGBAByte a, RGBAByte b) => new(a.R - b.R, a.G - b.G, a.B - b.B, a.A - b.A); diff --git a/Nerd_STF/IGroup.cs b/Nerd_STF/IGroup.cs index 9b7bc54..17c6b65 100644 --- a/Nerd_STF/IGroup.cs +++ b/Nerd_STF/IGroup.cs @@ -3,5 +3,6 @@ public interface IGroup : IEnumerable { public T[] ToArray(); + public Fill ToFill(); public List ToList(); } diff --git a/Nerd_STF/IGroup2D.cs b/Nerd_STF/IGroup2D.cs new file mode 100644 index 0000000..ae1e3fc --- /dev/null +++ b/Nerd_STF/IGroup2D.cs @@ -0,0 +1,7 @@ +namespace Nerd_STF; + +public interface IGroup2D : IGroup +{ + public T[,] ToArray2D(); + public Fill2D ToFill2D(); +} diff --git a/Nerd_STF/Mathematics/Algebra/IMatrix.cs b/Nerd_STF/Mathematics/Algebra/IMatrix.cs new file mode 100644 index 0000000..12aa38b --- /dev/null +++ b/Nerd_STF/Mathematics/Algebra/IMatrix.cs @@ -0,0 +1,18 @@ +namespace Nerd_STF.Mathematics.Algebra; + +public interface IMatrix : ICloneable, IEnumerable, IEquatable, IGroup2D + where T : IMatrix +{ + public T Adjugate(); + public float Determinant(); + public T Inverse(); + public T Transpose(); + + public Dictionary ToDictionary(); +} + +public interface IMatrix : IMatrix where This : IMatrix + where TMinor : IMatrix +{ + public TMinor[,] Minors(); +} diff --git a/Nerd_STF/Mathematics/Algebra/Matrix.cs b/Nerd_STF/Mathematics/Algebra/Matrix.cs new file mode 100644 index 0000000..a0a16ae --- /dev/null +++ b/Nerd_STF/Mathematics/Algebra/Matrix.cs @@ -0,0 +1,337 @@ +namespace Nerd_STF.Mathematics.Algebra; + +public struct Matrix : IMatrix +{ + public static Matrix Identity(Int2 size) + { + Matrix m = Zero(size); + int max = Mathf.Min(size.x, size.y); + for (int i = 0; i < max; i++) m[i, i] = 1; + return m; + } + public static Matrix One(Int2 size) => new(size, 1); + public static Matrix SignGrid(Int2 size) => new(size, Equations.SgnFill); + public static Matrix Zero(Int2 size) => new(size); + + public bool HasMinors => Size.x > 1 && Size.y > 1; + public bool IsSquare => Size.x == Size.y; + + public Int2 Size { get; private init; } + + private readonly float[,] array; + + public Matrix() : this(Int2.Zero) { } + public Matrix(Int2 size, float all = 0) + { + Size = size; + array = new float[size.x, size.y]; + + if (all == 0) return; + for (int r = 0; r < size.y; r++) for (int c = 0; c < size.x; c++) array[c, r] = all; + } + public Matrix(Int2 size, float[] vals) + { + Size = size; + array = new float[size.x, size.y]; + + if (vals.Length < size.x * size.y) + throw new InvalidSizeException("Array must contain enough values to fill the matrix."); + + for (int r = 0; r < size.y; r++) for (int c = 0; c < size.x; c++) array[c, r] = vals[c + r * size.y]; + } + public Matrix(Int2 size, int[] vals) + { + Size = size; + array = new float[size.x, size.y]; + + if (vals.Length < size.x * size.y) + throw new InvalidSizeException("Array must contain enough values to fill the matrix."); + + for (int r = 0; r < size.y; r++) for (int c = 0; c < size.x; c++) array[c, r] = vals[c + r * size.y]; + } + public Matrix(Int2 size, Fill vals) + { + Size = size; + array = new float[size.x, size.y]; + + for (int r = 0; r < size.y; r++) for (int c = 0; c < size.x; c++) array[c, r] = vals(c + r * size.y); + } + public Matrix(Int2 size, Fill vals) + { + Size = size; + array = new float[size.x, size.y]; + + for (int r = 0; r < size.y; r++) for (int c = 0; c < size.x; c++) array[c, r] = vals(c + r * size.y); + } + public Matrix(Int2 size, float[,] vals) + { + Size = size; + array = new float[size.x, size.y]; + for (int r = 0; r < size.y; r++) for (int c = 0; c < size.x; c++) array[c, r] = vals[c, r]; + } + public Matrix(Int2 size, int[,] vals) + { + Size = size; + array = new float[size.x, size.y]; + for (int r = 0; r < size.y; r++) for (int c = 0; c < size.x; c++) array[c, r] = vals[c, r]; + } + public Matrix(Int2 size, Fill2D vals) + { + Size = size; + array = new float[size.x, size.y]; + for (int r = 0; r < size.y; r++) for (int c = 0; c < size.x; c++) array[c, r] = vals(c, r); + } + public Matrix(Int2 size, Fill2D vals) + { + Size = size; + array = new float[size.x, size.y]; + for (int r = 0; r < size.y; r++) for (int c = 0; c < size.x; c++) array[c, r] = vals(c, r); + } + + public float this[int r, int c] + { + get => array[r, c]; + set => array[r, c] = value; + } + public float this[Int2 index] + { + get => this[index.x, index.y]; + set => this[index.x, index.y] = value; + } + + public static Matrix Absolute(Matrix val) => new(val.Size, (r, c) => Mathf.Absolute(val[r, c])); + public static Matrix Ceiling(Matrix val) => new(val.Size, (r, c) => Mathf.Ceiling(val[r, c])); + public static Matrix Clamp(Matrix val, Matrix min, Matrix max) => + new(val.Size, (r, c) => Mathf.Clamp(val[r, c], min[r, c], max[r, c])); + public static Matrix Divide(Matrix num, params Matrix[] vals) + { + foreach (Matrix m in vals) num /= m; + return num; + } + public static Matrix Floor(Matrix val) => new(val.Size, (r, c) => Mathf.Floor(val[r, c])); + public static Matrix Lerp(Matrix a, Matrix b, float t, bool clamp = true) => + new(a.Size, (r, c) => Mathf.Lerp(a[r, c], b[r, c], t, clamp)); + public static Matrix Product(params Matrix[] vals) + { + if (vals.Length < 1) throw new InvalidSizeException("Array must contain at least one matrix."); + if (!CheckSize(vals)) throw new InvalidSizeException("All matricies must be the same size."); + Matrix val = Identity(vals[0].Size); + foreach (Matrix m in vals) val *= m; + return val; + } + public static Matrix Round(Matrix val) => new(val.Size, (r, c) => Mathf.Round(val[r, c])); + public static Matrix Subtract(Matrix num, params Matrix[] vals) + { + foreach (Matrix m in vals) num -= m; + return num; + } + public static Matrix Sum(params Matrix[] vals) + { + if (!CheckSize(vals)) throw new InvalidSizeException("All matricies must be the same size."); + Matrix val = Zero(vals[0].Size); + foreach (Matrix m in vals) val += m; + return val; + } + + public void Apply(Modifier2D modifier) + { + for (int r = 0; r < Size.y; r++) for (int c = 0; c < Size.x; c++) + array[r, c] = modifier(new(r, c), array[r, c]); + } + + public float[] GetColumn(int column) + { + float[] vals = new float[Size.y]; + for (int i = 0; i < Size.y; i++) vals[i] = array[i, column]; + return vals; + } + public float[] GetRow(int row) + { + float[] vals = new float[Size.x]; + for (int i = 0; i < Size.x; i++) vals[i] = array[row, i]; + return vals; + } + + public void SetColumn(int column, float[] vals) + { + if (vals.Length < Size.y) + throw new InvalidSizeException("Array must contain enough values to fill the column."); + for (int i = 0; i < Size.y; i++) array[i, column] = vals[i]; + } + public void SetRow(int row, float[] vals) + { + if (vals.Length < Size.x) + throw new InvalidSizeException("Array must contain enough values to fill the row."); + for (int i = 0; i < Size.x; i++) array[row, i] = vals[i]; + } + + public Matrix Adjugate() + { + Matrix dets = new(Size); + Matrix[,] minors = Minors(); + for (int r = 0; r < Size.y; r++) for (int c = 0; c < Size.x; c++) dets[c, r] = minors[c, r].Determinant(); + return dets ^ SignGrid(Size); + } + public float Determinant() + { + if (!IsSquare) throw new InvalidSizeException("Matrix must be square to calculate determinant."); + if (Size.x <= 0 || Size.y <= 0) return 0; + if (Size.x == 1 || Size.y == 1) return array[0, 0]; + + Matrix[] minors = Minors().GetRow(0, Size.x); + float det = 0; + for (int i = 0; i < minors.Length; i++) det += minors[i].Determinant() * (i % 2 == 0 ? 1 : -1); + + return det; + } + public Matrix Inverse() + { + float d = Determinant(); + if (d == 0) throw new NoInverseException(); + return Transpose().Adjugate() / d; + } + public Matrix[,] Minors() + { + // This will absolutely blow my mind if it works. + // Remember that whole "don't have a way to test" thing? + + if (!HasMinors) return new Matrix[0,0]; + + Int2 newSize = Size - Int2.One; + Matrix[,] array = new Matrix[Size.x, Size.y]; + for (int r1 = 0; r1 < Size.y; r1++) for (int c1 = 0; c1 < Size.x; c1++) + { + Matrix m = new(newSize); + for (int r2 = 0; r2 < newSize.y; r2++) for (int c2 = 0; c2 < newSize.x; c2++) + { + int toSkip = c2 + r2 * newSize.y; + for (int r3 = 0; r3 < newSize.y; r3++) for (int c3 = 0; c3 < newSize.x; c3++) + { + if (r3 == r1 || c3 == c1) continue; + if (toSkip > 0) + { + toSkip--; + continue; + } + m[c2, r2] = this.array[c3, r3]; + break; + } + } + array[c1, r1] = m; + } + return array; + } + public Matrix Transpose() + { + Matrix m = new(new(Size.y, Size.x)); + for (int r = 0; r < Size.y; r++) m.SetColumn(r, GetRow(r)); + for (int c = 0; c < Size.x; c++) m.SetRow(c, GetColumn(c)); + return m; + } + + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj == null) return base.Equals(obj); + Type t = obj.GetType(); + if (t == typeof(Matrix)) return Equals((Matrix)obj); + else if (t == typeof(Matrix2x2)) return Equals((Matrix)obj); + else if (t == typeof(Matrix3x3)) return Equals((Matrix)obj); + else if (t == typeof(Matrix4x4)) return Equals((Matrix)obj); + + return base.Equals(obj); + } + public bool Equals(Matrix other) => array == other.array; + public override int GetHashCode() => array.GetHashCode(); + public override string ToString() => ToString((string?)null); + public string ToString(string? provider) + { + string res = ""; + for (int r = 0; r < Size.y; r++) + { + for (int c = 0; c < Size.x; c++) res += array[c, r].ToString(provider) + " "; + res += "\n"; + } + return res; + } + public string ToString(IFormatProvider provider) + { + string res = ""; + for (int r = 0; r < Size.y; r++) + { + for (int c = 0; c < Size.x; c++) res += array[c, r].ToString(provider) + " "; + res += "\n"; + } + return res; + } + + public object Clone() => new Matrix(Size, array); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public IEnumerator GetEnumerator() + { + for (int r = 0; r < Size.y; r++) for (int c = 0; c < Size.x; c++) yield return array[c, r]; + } + + public float[] ToArray() => array.Flatten(Size); + public float[,] ToArray2D() => array; + public Dictionary ToDictionary() + { + Dictionary dict = new(); + for (int r = 0; r < Size.y; r++) for (int c = 0; c < Size.x; c++) dict.Add(new(c, r), array[c, r]); + return dict; + } + public Fill ToFill() => ToFillExtension.ToFill(this); + public Fill2D ToFill2D() + { + Matrix @this = this; + return (x, y) => @this[x, y]; + } + public List ToList() => ToArray().ToList(); + + public static Matrix operator +(Matrix a, Matrix b) => new(a.Size, (r, c) => a[r, c] + b[r, c]); + public static Matrix operator -(Matrix m) => m.Inverse(); + public static Matrix operator -(Matrix a, Matrix b) => new(a.Size, (r, c) => a[r, c] - b[r, c]); + public static Matrix operator *(Matrix a, float b) => new(a.Size, (r, c) => a[r, c] * b); + public static Matrix operator *(Matrix a, Matrix b) => + new(new(a.Size.y, b.Size.x), (r, c) => Mathf.Dot(a.GetRow(r), b.GetColumn(c))); + public static Complex operator *(Matrix a, Complex b) => (Complex)(a * (Matrix)b); + public static Quaternion operator *(Matrix a, Quaternion b) => (Quaternion)(a * (Matrix)b); + public static Float2 operator *(Matrix a, Float2 b) => (Float2)(a * (Matrix)b); + public static Float3 operator *(Matrix a, Float3 b) => (Float3)(a * (Matrix)b); + public static Float4 operator *(Matrix a, Float4 b) => (Float4)(a * (Matrix)b); + public static Vector2d operator *(Matrix a, Vector2d b) => (Vector2d)(a * (Matrix)b); + public static Vector3d operator *(Matrix a, Vector3d b) => (Vector3d)(a * (Matrix)b); + public static Matrix operator /(Matrix a, float b) => new(a.Size, (r, c) => a[r, c] / b); + public static Matrix operator /(Matrix a, Matrix b) => a * b.Inverse(); + public static Complex operator /(Matrix a, Complex b) => (Complex)(a / (Matrix)b); + public static Quaternion operator /(Matrix a, Quaternion b) => (Quaternion)(a / (Matrix)b); + public static Float2 operator /(Matrix a, Float2 b) => (Float2)(a / (Matrix)b); + public static Float3 operator /(Matrix a, Float3 b) => (Float3)(a / (Matrix)b); + public static Float4 operator /(Matrix a, Float4 b) => (Float4)(a / (Matrix)b); + public static Vector2d operator /(Matrix a, Vector2d b) => (Vector2d)(a / (Matrix)b); + public static Vector3d operator /(Matrix a, Vector3d b) => (Vector3d)(a / (Matrix)b); + // Single number multiplication + public static Matrix operator ^(Matrix a, Matrix b) => new(a.Size, (r, c) => a[r, c] * b[r, c]); + public static bool operator ==(Matrix a, Matrix b) => a.Equals(b); + public static bool operator !=(Matrix a, Matrix b) => !a.Equals(b); + + public static explicit operator Matrix(Complex c) => (Matrix)(Float2)c; + public static explicit operator Matrix(Quaternion c) => (Matrix)(Float4)c; + public static explicit operator Matrix(Float2 f) => new(new(2, 1), i => f[i]); + public static explicit operator Matrix(Float3 f) => new(new(3, 1), i => f[i]); + public static explicit operator Matrix(Float4 f) => new(new(4, 1), i => f[i]); + public static implicit operator Matrix(Matrix2x2 m) => new(new(2, 2), m.ToFill2D()); + public static implicit operator Matrix(Matrix3x3 m) => new(new(3, 3), m.ToFill2D()); + public static implicit operator Matrix(Matrix4x4 m) => new(new(4, 4), m.ToFill2D()); + public static explicit operator Matrix(Vector2d v) => (Matrix)v.ToXYZ(); + public static explicit operator Matrix(Vector3d v) => (Matrix)v.ToXYZ(); + + private static bool CheckSize(params Matrix[] vals) + { + if (vals.Length <= 1) return true; + Int2 size = vals[0].Size; + for (int i = 1; i < vals.Length; i++) if (size != vals[i].Size) return false; + + return true; + } +} diff --git a/Nerd_STF/Mathematics/Algebra/Matrix2x2.cs b/Nerd_STF/Mathematics/Algebra/Matrix2x2.cs new file mode 100644 index 0000000..ea02672 --- /dev/null +++ b/Nerd_STF/Mathematics/Algebra/Matrix2x2.cs @@ -0,0 +1,275 @@ +namespace Nerd_STF.Mathematics.Algebra; + +public struct Matrix2x2 : IMatrix +{ + public static Matrix2x2 Identity => new(new[,] + { + { 1, 0 }, + { 0, 1 } + }); + public static Matrix2x2 One => new(1); + public static Matrix2x2 SignGrid => new(new[,] + { + { +1, -1 }, + { -1, +1 } + }); + public static Matrix2x2 Zero => new(0); + + public Float2 Column1 + { + get => new(r1c1, r2c1); + set + { + r1c1 = value.x; + r2c1 = value.y; + } + } + public Float2 Column2 + { + get => new(r1c2, r2c2); + set + { + r1c2 = value.x; + r2c2 = value.y; + } + } + public Float2 Row1 + { + get => new(r1c1, r1c2); + set + { + r1c1 = value.x; + r1c2 = value.y; + } + } + public Float2 Row2 + { + get => new(r2c1, r2c2); + set + { + r2c1 = value.x; + r2c2 = value.y; + } + } + + public float r1c1, r2c1, r1c2, r2c2; + + public Matrix2x2(float all) : this(all, all, all, all) { } + public Matrix2x2(float r1c1, float r2c1, float r1c2, float r2c2) + { + this.r1c1 = r1c1; + this.r2c1 = r2c1; + this.r1c2 = r1c2; + this.r2c2 = r2c2; + } + public Matrix2x2(float[] nums) : this(nums[0], nums[1], nums[2], nums[3]) { } + public Matrix2x2(int[] nums) : this(nums[0], nums[1], nums[2], nums[3]) { } + public Matrix2x2(Fill fill) : this(fill(0), fill(1), fill(2), fill(3)) { } + public Matrix2x2(Fill fill) : this(fill(0), fill(1), fill(2), fill(3)) { } + public Matrix2x2(float[,] nums) : this(nums[0, 0], nums[0, 1], nums[1, 0], nums[1, 1]) { } + public Matrix2x2(int[,] nums) : this(nums[0, 0], nums[0, 1], nums[1, 0], nums[1, 1]) { } + public Matrix2x2(Fill2D fill) : this(fill(0, 0), fill(0, 1), fill(1, 0), fill(1, 1)) { } + public Matrix2x2(Fill2D fill) : this(fill(0, 0), fill(0, 1), fill(1, 0), fill(1, 1)) { } + public Matrix2x2(Float2 c1, Float2 c2) : this(c1.x, c1.y, c2.x, c2.y) { } + public Matrix2x2(Fill fill) : this(fill(0), fill(1)) { } + public Matrix2x2(Fill fill) : this((IEnumerable)fill(0), fill(1)) { } + public Matrix2x2(IEnumerable c1, IEnumerable c2) : this(c1.ToFill(), c2.ToFill()) { } + public Matrix2x2(IEnumerable c1, IEnumerable c2) : this(c1.ToFill(), c2.ToFill()) { } + public Matrix2x2(Fill c1, Fill c2) : this(c1(0), c1(1), c2(0), c2(1)) { } + public Matrix2x2(Fill c1, Fill c2) : this(c1(0), c1(1), c2(0), c2(1)) { } + + public float this[int r, int c] + { + get => ToArray2D()[c, r]; + set + { + // Maybe this could be improved? + // It's definitely better than it was before. Trust me. + switch ("r" + (r + 1) + "c" + (c + 1)) + { + case "r1c1": + r1c1 = value; + break; + + case "r2c1": + r2c1 = value; + break; + + case "r1c2": + r1c2 = value; + break; + + case "r2c2": + r2c2 = value; + break; + + default: + string @params = ""; + if (r < 0 || r > 1) @params += r; + if (c < 0 || c > 1) @params += string.IsNullOrEmpty(@params) ? c : " and " + c; + throw new IndexOutOfRangeException(@params); + } + } + } + public float this[Int2 index] + { + get => this[index.x, index.y]; + set => this[index.x, index.y] = value; + } + + public static Matrix2x2 Absolute(Matrix2x2 val) => new(Mathf.Absolute(val.r1c1), Mathf.Absolute(val.r2c1), + Mathf.Absolute(val.r1c2), Mathf.Absolute(val.r2c2)); + public static Matrix2x2 Average(params Matrix2x2[] vals) => Sum(vals) / vals.Length; + public static Matrix2x2 Ceiling(Matrix2x2 val) => new(Mathf.Ceiling(val.r1c1), Mathf.Ceiling(val.r2c1), + Mathf.Ceiling(val.r1c2), Mathf.Ceiling(val.r2c2)); + public static Matrix2x2 Clamp(Matrix2x2 val, Matrix2x2 min, Matrix2x2 max) => + new(Mathf.Clamp(val.r1c1, min.r1c1, max.r1c1), Mathf.Clamp(val.r2c1, min.r2c1, max.r2c1), + Mathf.Clamp(val.r1c2, min.r1c2, max.r1c2), Mathf.Clamp(val.r2c2, min.r2c2, max.r2c2)); + public static Matrix2x2 Divide(Matrix2x2 num, params Matrix2x2[] vals) + { + foreach (Matrix2x2 m in vals) num /= m; + return num; + } + public static Matrix2x2 Floor(Matrix2x2 val) => new(Mathf.Floor(val.r1c1), Mathf.Floor(val.r2c1), + Mathf.Floor(val.r1c2), Mathf.Floor(val.r2c2)); + public static Matrix2x2 Lerp(Matrix2x2 a, Matrix2x2 b, float t, bool clamp = true) => + new(Mathf.Lerp(a.r1c1, b.r1c1, t, clamp), Mathf.Lerp(a.r2c1, b.r2c1, t, clamp), + Mathf.Lerp(a.r1c2, b.r1c2, t, clamp), Mathf.Lerp(a.r2c2, b.r2c2, t, clamp)); + public static Matrix2x2 Median(params Matrix2x2[] vals) + { + float index = Mathf.Average(0, vals.Length - 1); + Matrix2x2 valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)]; + return Average(valA, valB); + } + public static Matrix2x2 Product(params Matrix2x2[] vals) + { + if (vals.Length < 1) return Zero; + Matrix2x2 val = Identity; + foreach (Matrix2x2 m in vals) val *= m; + return val; + } + public static Matrix2x2 Round(Matrix2x2 val) => new(Mathf.Round(val.r1c1), Mathf.Round(val.r2c1), + Mathf.Round(val.r1c2), Mathf.Round(val.r2c2)); + public static Matrix2x2 Subtract(Matrix2x2 num, params Matrix2x2[] vals) + { + foreach (Matrix2x2 m in vals) num -= m; + return num; + } + public static Matrix2x2 Sum(params Matrix2x2[] vals) + { + Matrix2x2 val = Zero; + foreach (Matrix2x2 m in vals) val += m; + return val; + } + + public static (float[] r1c1s, float[] r2c1s, float[] r1c2s, float[] r2c2s) SplitArray(params Matrix2x2[] vals) + { + float[] r1c1s = new float[vals.Length], r2c1s = new float[vals.Length], r1c2s = new float[vals.Length], + r2c2s = new float[vals.Length]; + for (int i = 0; i < vals.Length; i++) + { + r1c1s[i] = vals[i].r1c1; + r2c1s[i] = vals[i].r2c1; + r1c2s[i] = vals[i].r1c2; + r2c2s[i] = vals[i].r2c2; + } + return (r1c1s, r2c1s, r1c2s, r2c2s); + } + + public Matrix2x2 Adjugate() + { + Matrix2x2 swapped = new(new[,] + { + { r2c2, r1c2 }, + { r2c1, r1c1 } + }); + return swapped ^ SignGrid; + } + public float Determinant() => r1c1 * r2c2 - r1c2 * r2c1; + public Matrix2x2 Inverse() + { + float d = Determinant(); + if (d == 0) throw new NoInverseException(); + return Transpose().Adjugate() / d; + } + public Matrix2x2 Transpose() => new(new[,] + { + { r1c1, r2c1 }, + { r1c2, r2c2 } + }); + + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj == null || obj.GetType() != typeof(Matrix2x2)) return base.Equals(obj); + return Equals((Matrix2x2)obj); + } + public bool Equals(Matrix2x2 other) => r1c1 == other.r1c1 && r2c1 == other.r2c1 + && r1c2 == other.r1c2&& r2c2 == other.r2c2; + public override int GetHashCode() => r1c1.GetHashCode() ^ r2c1.GetHashCode() + ^ r1c2.GetHashCode() ^ r2c2.GetHashCode(); + public override string ToString() => ToString((string?)null); + public string ToString(string? provider) => r1c1.ToString(provider) + " " + r1c2.ToString(provider) + "\n" + + r2c1.ToString(provider) + " " + r2c2.ToString(provider); + public string ToString(IFormatProvider provider) => r1c1.ToString(provider) + " " + r1c2.ToString(provider) + "\n" + + r2c1.ToString(provider) + " " + r2c2.ToString(provider); + + public object Clone() => new Matrix2x2(ToArray2D()); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public IEnumerator GetEnumerator() + { + yield return r1c1; + yield return r2c1; + yield return r1c2; + yield return r2c2; + } + + public float[] ToArray() => new[] { r1c1, r2c1, r1c2, r2c2 }; + public float[,] ToArray2D() => new[,] + { + { r1c1, r1c2 }, + { r2c1, r2c2 } + }; + public Dictionary ToDictionary() + { + Dictionary dict = new(); + float[] arr = ToArray(); + for (int i = 0; i < arr.Length; i++) dict.Add(new(i % 2, i / 2), arr[i]); + return dict; + } + public Fill ToFill() => ToFillExtension.ToFill(this); + public Fill2D ToFill2D() + { + Matrix2x2 @this = this; + return (x, y) => @this[x, y]; + } + public List ToList() => new() { r1c1, r2c1, r1c2, r2c2 }; + + public static Matrix2x2 operator +(Matrix2x2 a, Matrix2x2 b) => new(a.r1c1 + b.r1c1, a.r2c1 + b.r2c1, + a.r1c2 + b.r1c2, a.r2c2 + b.r2c2); + public static Matrix2x2? operator -(Matrix2x2 m) => m.Inverse(); + public static Matrix2x2 operator -(Matrix2x2 a, Matrix2x2 b) => new(a.r1c1 - b.r1c1, a.r2c1 - b.r2c1, + a.r1c2 - b.r1c2, a.r2c2 + b.r2c2); + public static Matrix2x2 operator *(Matrix2x2 a, float b) => new(a.r1c1 * b, a.r2c1 * b, a.r1c2 * b, a.r2c2 * b); + public static Matrix2x2 operator *(Matrix2x2 a, Matrix2x2 b) => new(new[,] + { + { Float2.Dot(a.Row1, b.Column1), Float2.Dot(a.Row1, b.Column2) }, + { Float2.Dot(a.Row2, b.Column1), Float2.Dot(a.Row2, b.Column2) }, + }); + public static Matrix2x2 operator /(Matrix2x2 a, float b) => new(a.r1c1 / b, a.r2c1 / b, a.r1c2 / b, a.r2c2 / b); + public static Matrix2x2 operator /(Matrix2x2 a, Matrix2x2 b) => a * b.Inverse(); + public static Matrix2x2 operator ^(Matrix2x2 a, Matrix2x2 b) => // Single number multiplication. + new(a.r1c1 * b.r1c1, a.r2c1 * b.r2c1, a.r1c2 * b.r1c2, a.r2c2 * b.r2c2); + public static bool operator ==(Matrix2x2 a, Matrix2x2 b) => a.Equals(b); + public static bool operator !=(Matrix2x2 a, Matrix2x2 b) => !a.Equals(b); + + public static explicit operator Matrix2x2(Matrix m) + { + Matrix2x2 res = Zero, identity = Identity; + for (int r = 0; r < 2; r++) for (int c = 0; c < 2; c++) + res[c, r] = m.Size.x < c && m.Size.y < r ? m[r, c] : identity[r, c]; + return res; + } + public static explicit operator Matrix2x2(Matrix3x3 m) => new(m.r1c1, m.r2c1, m.r1c2, m.r2c2); + public static explicit operator Matrix2x2(Matrix4x4 m) => new(m.r1c1, m.r2c1, m.r1c2, m.r2c2); +} diff --git a/Nerd_STF/Mathematics/Algebra/Matrix3x3.cs b/Nerd_STF/Mathematics/Algebra/Matrix3x3.cs new file mode 100644 index 0000000..7e81f47 --- /dev/null +++ b/Nerd_STF/Mathematics/Algebra/Matrix3x3.cs @@ -0,0 +1,411 @@ +namespace Nerd_STF.Mathematics.Algebra; + +public struct Matrix3x3 : IMatrix +{ + public static Matrix3x3 Identity => new(new[,] + { + { 1, 0, 0 }, + { 0, 1, 0 }, + { 0, 0, 1 } + }); + public static Matrix3x3 One => new(1); + public static Matrix3x3 SignGrid => new(new[,] + { + { +1, -1, +1 }, + { -1, +1, -1 }, + { +1, -1, +1 } + }); + public static Matrix3x3 Zero => new(0); + + public Float3 Column1 + { + get => new(r1c1, r2c1, r3c1); + set + { + r1c1 = value.x; + r2c1 = value.y; + r3c1 = value.z; + } + } + public Float3 Column2 + { + get => new(r1c2, r2c2, r3c2); + set + { + r1c2 = value.x; + r2c2 = value.y; + r3c2 = value.z; + } + } + public Float3 Column3 + { + get => new(r1c3, r2c3, r3c3); + set + { + r1c3 = value.x; + r2c3 = value.y; + r3c3 = value.z; + } + } + public Float3 Row1 + { + get => new(r1c1, r1c2, r1c3); + set + { + r1c1 = value.x; + r1c2 = value.y; + r1c3 = value.z; + } + } + public Float3 Row2 + { + get => new(r2c1, r2c2, r2c3); + set + { + r2c1 = value.x; + r2c2 = value.y; + r2c3 = value.z; + } + } + public Float3 Row3 + { + get => new(r3c1, r3c2, r3c3); + set + { + r3c1 = value.x; + r3c2 = value.y; + r3c3 = value.z; + } + } + + public float r1c1, r2c1, r3c1, r1c2, r2c2, r3c2, r1c3, r2c3, r3c3; + + public Matrix3x3(float all) : this(all, all, all, all, all, all, all, all, all) { } + public Matrix3x3(float r1c1, float r2c1, float r3c1, float r1c2, + float r2c2, float r3c2, float r1c3, float r2c3, float r3c3) + { + this.r1c1 = r1c1; + this.r2c1 = r2c1; + this.r3c1 = r3c1; + this.r1c2 = r1c2; + this.r2c2 = r2c2; + this.r3c2 = r3c2; + this.r1c3 = r1c3; + this.r2c3 = r2c3; + this.r3c3 = r3c3; + } + public Matrix3x3(float[] nums) : this(nums[0], nums[1], nums[2], + nums[3], nums[4], nums[5], nums[6], nums[7], nums[8]) { } + public Matrix3x3(int[] nums) : this(nums[0], nums[1], nums[2], + nums[3], nums[4], nums[5], nums[6], nums[7], nums[8]) { } + public Matrix3x3(Fill fill) : this(fill(0), fill(1), fill(2), + fill(3), fill(4), fill(5), fill(6), fill(7), fill(8)) { } + public Matrix3x3(Fill fill) : this(fill(0), fill(1), fill(2), + fill(3), fill(4), fill(5), fill(6), fill(7), fill(8)) { } + public Matrix3x3(float[,] nums) : this(nums[0, 0], nums[0, 1], nums[0, 2], + nums[1, 0], nums[1, 1], nums[1, 2], nums[2, 0], nums[2, 1], nums[2, 2]) { } + public Matrix3x3(int[,] nums) : this(nums[0, 0], nums[0, 1], nums[0, 2], + nums[1, 0], nums[1, 1], nums[1, 2], nums[2, 0], nums[2, 1], nums[2, 2]) { } + public Matrix3x3(Fill2D fill) : this(fill(0, 0), fill(0, 1), fill(0, 2), + fill(1, 0), fill(1, 1), fill(1, 2), fill(2, 0), fill(2, 1), fill(2, 2)) { } + public Matrix3x3(Fill2D fill) : this(fill(0, 0), fill(0, 1), fill(0, 2), + fill(1, 0), fill(1, 1), fill(1, 2), fill(2, 0), fill(2, 1), fill(2, 2)) { } + public Matrix3x3(Float3 c1, Float3 c2, Float3 c3) : this(c1.x, c1.y, c1.z, c2.x, c2.y, c2.z, c3.x, c3.y, c3.z) { } + public Matrix3x3(Fill fill) : this(fill(0), fill(1), fill(2)) { } + public Matrix3x3(Fill fill) : this((IEnumerable)fill(0), fill(1), fill(2)) { } + public Matrix3x3(IEnumerable c1, IEnumerable c2, IEnumerable c3) + : this(c1.ToFill(), c2.ToFill(), c3.ToFill()) { } + public Matrix3x3(IEnumerable c1, IEnumerable c2, IEnumerable c3) + : this(c1.ToFill(), c2.ToFill(), c3.ToFill()) { } + public Matrix3x3(Fill c1, Fill c2, Fill c3) + : this(c1(0), c1(1), c1(2), c2(0), c2(1), c2(2), c3(0), c3(1), c3(2)) { } + public Matrix3x3(Fill c1, Fill c2, Fill c3) + : this(c1(0), c1(1), c1(2), c2(0), c2(1), c2(2), c3(0), c3(1), c3(2)) { } + + public float this[int r, int c] + { + get => ToArray2D()[c, r]; + set + { + // Maybe this could be improved? + // It's definitely better than it was before. Trust me. + switch ("r" + (r + 1) + "c" + (c + 1)) + { + case "r1c1": + r1c1 = value; + break; + + case "r2c1": + r2c1 = value; + break; + + case "r3c1": + r3c1 = value; + break; + + case "r1c2": + r1c2 = value; + break; + + case "r2c2": + r2c2 = value; + break; + + case "r3c2": + r3c2 = value; + break; + + case "r1c3": + r1c3 = value; + break; + + case "r2c3": + r2c3 = value; + break; + + case "r3c3": + r3c3 = value; + break; + + default: + string @params = ""; + if (r < 0 || r > 2) @params += r; + if (c < 0 || c > 2) @params += string.IsNullOrEmpty(@params) ? c : " and " + c; + throw new IndexOutOfRangeException(@params); + } + } + } + public float this[Int2 index] + { + get => this[index.x, index.y]; + set => this[index.x, index.y] = value; + } + + public static Matrix3x3 Absolute(Matrix3x3 val) => + new(Mathf.Absolute(val.r1c1), Mathf.Absolute(val.r2c1), Mathf.Absolute(val.r3c1), + Mathf.Absolute(val.r1c2), Mathf.Absolute(val.r2c2), Mathf.Absolute(val.r3c2), + Mathf.Absolute(val.r1c3), Mathf.Absolute(val.r2c3), Mathf.Absolute(val.r3c3)); + public static Matrix3x3 Average(params Matrix3x3[] vals) => Sum(vals) / vals.Length; + public static Matrix3x3 Ceiling(Matrix3x3 val) => + new(Mathf.Ceiling(val.r1c1), Mathf.Ceiling(val.r2c1), Mathf.Ceiling(val.r3c1), + Mathf.Ceiling(val.r1c2), Mathf.Ceiling(val.r2c2), Mathf.Ceiling(val.r3c2), + Mathf.Ceiling(val.r1c3), Mathf.Ceiling(val.r2c3), Mathf.Ceiling(val.r3c3)); + public static Matrix3x3 Clamp(Matrix3x3 val, Matrix3x3 min, Matrix3x3 max) => + new(Mathf.Clamp(val.r1c1, min.r1c1, max.r1c1), Mathf.Clamp(val.r2c1, min.r2c1, max.r2c1), + Mathf.Clamp(val.r3c1, min.r3c1, max.r3c1), Mathf.Clamp(val.r1c2, min.r1c2, max.r1c2), + Mathf.Clamp(val.r2c2, min.r2c2, max.r2c2), Mathf.Clamp(val.r3c2, min.r3c2, max.r3c2), + Mathf.Clamp(val.r1c3, min.r1c3, max.r1c3), Mathf.Clamp(val.r2c3, min.r2c3, max.r2c3), + Mathf.Clamp(val.r3c3, min.r3c3, max.r3c3)); + public static Matrix3x3 Divide(Matrix3x3 num, params Matrix3x3[] vals) + { + foreach (Matrix3x3 m in vals) num /= m; + return num; + } + public static Matrix3x3 Floor(Matrix3x3 val) => + new(Mathf.Floor(val.r1c1), Mathf.Floor(val.r2c1), Mathf.Floor(val.r3c1), + Mathf.Floor(val.r1c2), Mathf.Floor(val.r2c2), Mathf.Floor(val.r3c2), + Mathf.Floor(val.r1c3), Mathf.Floor(val.r2c3), Mathf.Floor(val.r3c3)); + public static Matrix3x3 Lerp(Matrix3x3 a, Matrix3x3 b, float t, bool clamp = true) => + new(Mathf.Lerp(a.r1c1, b.r1c1, t, clamp), Mathf.Lerp(a.r2c1, b.r2c1, t, clamp), + Mathf.Lerp(a.r3c1, b.r3c1, t, clamp), Mathf.Lerp(a.r1c2, b.r1c2, t, clamp), + Mathf.Lerp(a.r2c2, b.r2c2, t, clamp), Mathf.Lerp(a.r3c2, b.r3c2, t, clamp), + Mathf.Lerp(a.r1c3, b.r1c3, t, clamp), Mathf.Lerp(a.r2c3, b.r2c3, t, clamp), + Mathf.Lerp(a.r3c3, b.r3c3, t, clamp)); + public static Matrix3x3 Median(params Matrix3x3[] vals) + { + float index = Mathf.Average(0, vals.Length - 1); + Matrix3x3 valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)]; + return Average(valA, valB); + } + public static Matrix3x3 Product(params Matrix3x3[] vals) + { + if (vals.Length < 1) return Zero; + Matrix3x3 val = Identity; + foreach (Matrix3x3 m in vals) val *= m; + return val; + } + public static Matrix3x3 Round(Matrix3x3 val) => + new(Mathf.Round(val.r1c1), Mathf.Round(val.r2c1), Mathf.Round(val.r3c1), + Mathf.Round(val.r1c2), Mathf.Round(val.r2c2), Mathf.Round(val.r3c2), + Mathf.Round(val.r1c3), Mathf.Round(val.r2c3), Mathf.Round(val.r3c3)); + public static Matrix3x3 Subtract(Matrix3x3 num, params Matrix3x3[] vals) + { + foreach (Matrix3x3 m in vals) num -= m; + return num; + } + public static Matrix3x3 Sum(params Matrix3x3[] vals) + { + Matrix3x3 val = Zero; + foreach (Matrix3x3 m in vals) val += m; + return val; + } + + public static (float[] r1c1s, float[] r2c1s, float[] r3c1s, float[] r1c2s, float[] r2c2s, + float[] r3c2s, float[] r1c3s, float[] r2c3s, float[] r3c3s) SplitArray(params Matrix3x3[] vals) + { + float[] r1c1s = new float[vals.Length], r2c1s = new float[vals.Length], r3c1s = new float[vals.Length], + r1c2s = new float[vals.Length], r2c2s = new float[vals.Length], r3c2s = new float[vals.Length], + r1c3s = new float[vals.Length], r2c3s = new float[vals.Length], r3c3s = new float[vals.Length]; + for (int i = 0; i < vals.Length; i++) + { + r1c1s[i] = vals[i].r1c1; + r2c1s[i] = vals[i].r2c1; + r3c1s[i] = vals[i].r3c1; + r1c2s[i] = vals[i].r1c2; + r2c2s[i] = vals[i].r2c2; + r3c2s[i] = vals[i].r3c2; + r1c3s[i] = vals[i].r1c3; + r2c3s[i] = vals[i].r2c3; + r3c3s[i] = vals[i].r3c3; + } + return (r1c1s, r2c1s, r3c1s, r1c2s, r2c2s, r3c2s, r1c3s, r2c3s, r3c3s); + } + + public Matrix3x3 Adjugate() + { + Matrix3x3 dets = new(); + Matrix2x2[,] minors = Minors(); + for (int r = 0; r < 3; r++) for (int c = 0; c < 3; c++) dets[r, c] = minors[r, c].Determinant(); + return dets ^ SignGrid; + } + public float Determinant() + { + Matrix2x2[,] minors = Minors(); + return (r1c1 * minors[0, 0].Determinant()) - (r1c2 * minors[1, 0].Determinant()) + + (r1c3 * minors[2, 0].Determinant()); + } + public Matrix3x3 Inverse() + { + float d = Determinant(); + if (d == 0) throw new NoInverseException(); + return Transpose().Adjugate() / d; + } + public Matrix2x2[,] Minors() => new Matrix2x2[,] + { + { new(r2c2, r3c2, r2c3, r3c3), new(r2c1, r3c1, r2c3, r3c3), new(r2c1, r3c1, r2c2, r3c2) }, + { new(r1c2, r3c2, r1c3, r3c3), new(r1c1, r3c1, r1c3, r3c3), new(r1c1, r3c1, r1c2, r3c2) }, + { new(r1c2, r2c2, r1c3, r2c3), new(r1c1, r2c1, r1c3, r2c3), new(r1c1, r2c1, r1c2, r2c2) } + }; + public Matrix3x3 Transpose() => new(new[,] + { + { r1c1, r2c1, r3c1 }, + { r1c2, r2c2, r3c2 }, + { r1c3, r2c3, r3c3 } + }); + + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj == null || obj.GetType() != typeof(Matrix3x3)) return base.Equals(obj); + return Equals((Matrix3x3)obj); + } + public bool Equals(Matrix3x3 other) => + r1c1 == other.r1c1 && r2c1 == other.r2c1 && r3c1 == other.r3c1 && + r1c2 == other.r1c2 && r2c2 == other.r2c2 && r3c2 == other.r3c2 && + r1c3 == other.r1c3 && r2c3 == other.r2c3 && r3c3 == other.r3c3; + public override int GetHashCode() => + r1c1.GetHashCode() ^ r2c1.GetHashCode() ^ r3c1.GetHashCode() ^ + r1c2.GetHashCode() ^ r2c2.GetHashCode() ^ r3c2.GetHashCode() ^ + r1c3.GetHashCode() ^ r2c3.GetHashCode() ^ r3c3.GetHashCode(); + public override string ToString() => ToString((string?)null); + public string ToString(string? provider) => + r1c1.ToString(provider) + " " + r1c2.ToString(provider) + " " + r1c3.ToString(provider) + "\n" + + r2c1.ToString(provider) + " " + r2c2.ToString(provider) + " " + r2c3.ToString(provider) + "\n" + + r3c1.ToString(provider) + " " + r3c2.ToString(provider) + " " + r3c3.ToString(provider); + public string ToString(IFormatProvider provider) => + r1c1.ToString(provider) + " " + r1c2.ToString(provider) + " " + r1c3.ToString(provider) + "\n" + + r2c1.ToString(provider) + " " + r2c2.ToString(provider) + " " + r2c3.ToString(provider) + "\n" + + r3c1.ToString(provider) + " " + r3c2.ToString(provider) + " " + r3c3.ToString(provider); + + public object Clone() => new Matrix3x3(ToArray2D()); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public IEnumerator GetEnumerator() + { + yield return r1c1; + yield return r2c1; + yield return r3c1; + yield return r1c2; + yield return r2c2; + yield return r3c2; + yield return r1c3; + yield return r2c3; + yield return r3c3; + } + + public float[] ToArray() => new[] { r1c1, r2c1, r3c1, r1c2, r2c2, r3c2, r1c3, r2c3, r3c3 }; + public float[,] ToArray2D() => new[,] + { + { r1c1, r1c2, r1c3 }, + { r2c1, r2c2, r2c3 }, + { r3c1, r3c2, r3c3 } + }; + public Dictionary ToDictionary() + { + Dictionary dict = new(); + float[] arr = ToArray(); + for (int i = 0; i < arr.Length; i++) dict.Add(new(i % 3, i / 3), arr[i]); + return dict; + } + public Fill ToFill() => ToFillExtension.ToFill(this); + public Fill2D ToFill2D() + { + Matrix3x3 @this = this; + return (x, y) => @this[x, y]; + } + public List ToList() => new() { r1c1, r2c1, r3c1, r1c2, r2c2, r3c2, r1c3, r2c3, r3c3 }; + + public static Matrix3x3 operator +(Matrix3x3 a, Matrix3x3 b) => + new(a.r1c1 + b.r1c1, a.r2c1 + b.r2c1, a.r3c1 + b.r3c1, + a.r1c2 + b.r1c2, a.r2c2 + b.r2c2, a.r3c2 + b.r3c2, + a.r1c3 + b.r1c3, a.r2c3 + b.r2c3, a.r3c3 + b.r3c3); + public static Matrix3x3 operator -(Matrix3x3 m) => m.Inverse(); + public static Matrix3x3 operator -(Matrix3x3 a, Matrix3x3 b) => + new(a.r1c1 - b.r1c1, a.r2c1 - b.r2c1, a.r3c1 - b.r3c1, + a.r1c2 - b.r1c2, a.r2c2 - b.r2c2, a.r3c2 - b.r3c2, + a.r1c3 - b.r1c3, a.r2c3 - b.r2c3, a.r3c3 - b.r3c3); + public static Matrix3x3 operator *(Matrix3x3 a, float b) => + new(a.r1c1 * b, a.r2c1 * b, a.r3c1 * b, + a.r1c2 * b, a.r2c2 * b, a.r3c2 * b, + a.r1c3 * b, a.r2c3 * b, a.r3c3 * b); + public static Matrix3x3 operator *(Matrix3x3 a, Matrix3x3 b) => new(new[,] + { + { Float3.Dot(a.Row1, b.Column1), Float3.Dot(a.Row1, b.Column2), Float3.Dot(a.Row1, b.Column3) }, + { Float3.Dot(a.Row2, b.Column1), Float3.Dot(a.Row2, b.Column2), Float3.Dot(a.Row2, b.Column3) }, + { Float3.Dot(a.Row3, b.Column1), Float3.Dot(a.Row3, b.Column2), Float3.Dot(a.Row3, b.Column3) }, + }); + public static Matrix3x3 operator /(Matrix3x3 a, float b) => + new(a.r1c1 / b, a.r2c1 / b, a.r3c1 / b, + a.r1c2 / b, a.r2c2 / b, a.r3c2 / b, + a.r1c3 / b, a.r2c3 / b, a.r3c3 / b); + public static Matrix3x3 operator /(Matrix3x3 a, Matrix3x3 b) => a * b.Inverse(); + public static Matrix3x3 operator ^(Matrix3x3 a, Matrix3x3 b) => // Single number multiplication + new(a.r1c1 * b.r1c1, a.r2c1 * b.r2c1, a.r3c1 * b.r3c1, + a.r1c2 * b.r1c2, a.r2c2 * b.r2c2, a.r3c2 * b.r3c2, + a.r1c3 * b.r1c3, a.r2c3 * b.r2c3, a.r3c3 * b.r3c3); + public static bool operator ==(Matrix3x3 a, Matrix3x3 b) => a.Equals(b); + public static bool operator !=(Matrix3x3 a, Matrix3x3 b) => !a.Equals(b); + + public static explicit operator Matrix3x3(Matrix m) + { + Matrix3x3 res = Zero, identity = Identity; + for (int r = 0; r < 3; r++) for (int c = 0; c < 3; c++) + res[c, r] = m.Size.x < c && m.Size.y < r ? m[r, c] : identity[r, c]; + return res; + } + public static implicit operator Matrix3x3(Matrix2x2 m) + { + Matrix3x3 identity = Identity; + return new(new[,] + { + { m.r1c1, m.r1c2, identity.r1c3 }, + { m.r2c1, m.r2c2, identity.r2c3 }, + { identity.r3c1, identity.r3c2, identity.r3c3 } + }); + } + public static explicit operator Matrix3x3(Matrix4x4 m) => new(new[,] + { + { m.r1c1, m.r1c2, m.r1c3 }, + { m.r2c1, m.r2c2, m.r2c3 }, + { m.r3c1, m.r3c2, m.r3c3 } + }); +} diff --git a/Nerd_STF/Mathematics/Algebra/Matrix4x4.cs b/Nerd_STF/Mathematics/Algebra/Matrix4x4.cs new file mode 100644 index 0000000..6b0cd2c --- /dev/null +++ b/Nerd_STF/Mathematics/Algebra/Matrix4x4.cs @@ -0,0 +1,566 @@ +namespace Nerd_STF.Mathematics.Algebra; + +public struct Matrix4x4 : IMatrix +{ + public static Matrix4x4 Identity => new(new[,] + { + { 1, 0, 0, 0 }, + { 0, 1, 0, 0 }, + { 0, 0, 1, 0 }, + { 0, 0, 0, 1 }, + }); + public static Matrix4x4 One => new(1); + public static Matrix4x4 SignGrid => new(new[,] + { + { +1, -1, +1, -1 }, + { -1, +1, -1, +1 }, + { +1, -1, +1, -1 }, + { -1, +1, -1, +1 } + }); + public static Matrix4x4 Zero => new(0); + + public Float4 Column1 + { + get => new(r1c1, r2c1, r3c1, r4c1); + set + { + r1c1 = value.x; + r2c1 = value.y; + r3c1 = value.z; + r4c1 = value.w; + } + } + public Float4 Column2 + { + get => new(r1c2, r2c2, r3c2, r4c2); + set + { + r1c2 = value.x; + r2c2 = value.y; + r3c2 = value.z; + r4c2 = value.w; + } + } + public Float4 Column3 + { + get => new(r1c3, r2c3, r3c3, r4c3); + set + { + r1c3 = value.x; + r2c3 = value.y; + r3c3 = value.z; + r4c3 = value.w; + } + } + public Float4 Column4 + { + get => new(r1c4, r2c4, r3c4, r4c4); + set + { + r1c4 = value.x; + r2c4 = value.y; + r3c4 = value.z; + r4c4 = value.w; + } + } + public Float4 Row1 + { + get => new(r1c1, r1c2, r1c3, r1c3); + set + { + r1c1 = value.x; + r1c2 = value.y; + r1c3 = value.z; + r1c4 = value.w; + } + } + public Float4 Row2 + { + get => new(r2c1, r2c2, r2c3, r2c3); + set + { + r2c1 = value.x; + r2c2 = value.y; + r2c3 = value.z; + r2c4 = value.w; + } + } + public Float4 Row3 + { + get => new(r3c1, r3c2, r3c3, r3c3); + set + { + r3c1 = value.x; + r3c2 = value.y; + r3c3 = value.z; + r3c4 = value.w; + } + } + public Float4 Row4 + { + get => new(r4c1, r4c2, r4c3, r4c3); + set + { + r4c1 = value.x; + r4c2 = value.y; + r4c3 = value.z; + r4c4 = value.w; + } + } + + public float r1c1, r2c1, r3c1, r4c1, r1c2, r2c2, r3c2, r4c2, r1c3, r2c3, r3c3, r4c3, r1c4, r2c4, r3c4, r4c4; + + public Matrix4x4(float all) : this(all, all, all, all, all, + all, all, all, all, all, all, all, all, all, all, all) { } + public Matrix4x4(float r1c1, float r2c1, float r3c1, float r4c1, float r1c2, float r2c2, float r3c2, + float r4c2, float r1c3, float r2c3, float r3c3, float r4c3, float r1c4, float r2c4, float r3c4, float r4c4) + { + this.r1c1 = r1c1; + this.r2c1 = r2c1; + this.r3c1 = r3c1; + this.r4c1 = r4c1; + this.r1c2 = r1c2; + this.r2c2 = r2c2; + this.r3c2 = r3c2; + this.r4c2 = r4c2; + this.r1c3 = r1c3; + this.r2c3 = r2c3; + this.r3c3 = r3c3; + this.r4c3 = r4c3; + this.r1c4 = r1c4; + this.r2c4 = r2c4; + this.r3c4 = r3c4; + this.r4c4 = r4c4; + } + public Matrix4x4(float[] nums) : this(nums[0], nums[1], nums[2], nums[3], nums[4], nums[5], nums[6], + nums[7], nums[8], nums[9], nums[10], nums[11], nums[12], nums[13], nums[14], nums[15]) { } + public Matrix4x4(int[] nums) : this(nums[0], nums[1], nums[2], nums[3], nums[4], nums[5], nums[6], + nums[7], nums[8], nums[9], nums[10], nums[11], nums[12], nums[13], nums[14], nums[15]) { } + public Matrix4x4(Fill fill) : this(fill(0), fill(1), fill(2), fill(3), fill(4), fill(5), fill(6), + fill(7), fill(8), fill(9), fill(10), fill(11), fill(12), fill(13), fill(14), fill(15)) { } + public Matrix4x4(Fill fill) : this(fill(0), fill(1), fill(2), fill(3), fill(4), fill(5), fill(6), + fill(7), fill(8), fill(9), fill(10), fill(11), fill(12), fill(13), fill(14), fill(15)) { } + public Matrix4x4(float[,] nums) : this(nums[0, 0], nums[0, 1], nums[0, 2], nums[0, 3], nums[1, 0], + nums[1, 1], nums[1, 2], nums[1, 3], nums[2, 0], nums[2, 1], nums[2, 2], nums[2, 3], nums[3, 0], + nums[3, 1], nums[3, 2], nums[3, 3]) { } + public Matrix4x4(int[,] nums) : this(nums[0, 0], nums[0, 1], nums[0, 2], nums[0, 3], nums[1, 0], + nums[1, 1], nums[1, 2], nums[1, 3], nums[2, 0], nums[2, 1], nums[2, 2], nums[2, 3], nums[3, 0], + nums[3, 1], nums[3, 2], nums[3, 3]) { } + public Matrix4x4(Fill2D fill) : this(fill(0, 0), fill(0, 1), fill(0, 2), fill(0, 3), fill(1, 0), + fill(1, 1), fill(1, 2), fill(1, 3), fill(2, 0), fill(2, 1), fill(2, 2), fill(2, 3), fill(3, 0), + fill(3, 1), fill(3, 2), fill(3, 3)) { } + public Matrix4x4(Fill2D fill) : this(fill(0, 0), fill(0, 1), fill(0, 2), fill(0, 3), fill(1, 0), + fill(1, 1), fill(1, 2), fill(1, 3), fill(2, 0), fill(2, 1), fill(2, 2), fill(2, 3), fill(3, 0), + fill(3, 1), fill(3, 2), fill(3, 3)) { } + public Matrix4x4(Float4 c1, Float4 c2, Float4 c3, Float4 c4) : this(c1.x, c1.y, c1.z, + c1.w, c2.x, c2.y, c2.z, c2.w, c3.x, c3.y, c3.z, c3.w, c4.x, c4.y, c4.z, c4.w) { } + public Matrix4x4(Fill fill) : this(fill(0), fill(1), fill(2), fill(3)) { } + public Matrix4x4(Fill fill) : this((IEnumerable)fill(0), fill(1), fill(2), fill(3)) { } + public Matrix4x4(IEnumerable c1, IEnumerable c2, IEnumerable c3, IEnumerable c4) + : this(c1.ToFill(), c2.ToFill(), c3.ToFill(), c4.ToFill()) { } + public Matrix4x4(IEnumerable c1, IEnumerable c2, IEnumerable c3, IEnumerable c4) + : this(c1.ToFill(), c2.ToFill(), c3.ToFill(), c4.ToFill()) { } + public Matrix4x4(Fill c1, Fill c2, Fill c3, Fill c4) : this(c1(0), c1(1), + c1(2), c1(3), c2(0), c2(1), c2(2), c2(3), c3(0), c3(1), c3(2), c3(3), c4(0), c4(1), c4(2), c4(3)) { } + public Matrix4x4(Fill c1, Fill c2, Fill c3, Fill c4) : this(c1(0), c1(1), + c1(2), c1(3), c2(0), c2(1), c2(2), c2(3), c3(0), c3(1), c3(2), c3(3), c4(0), c4(1), c4(2), c4(3)) { } + + public float this[int r, int c] + { + get => ToArray2D()[c, r]; + set + { + // Maybe this could be improved? + // It's definitely better than it was before. Trust me. + switch ("r" + (r + 1) + "c" + (c + 1)) + { + case "r1c1": + r1c1 = value; + break; + + case "r2c1": + r2c1 = value; + break; + + case "r3c1": + r3c1 = value; + break; + + case "r4c1": + r4c1 = value; + break; + + case "r1c2": + r1c2 = value; + break; + + case "r2c2": + r2c2 = value; + break; + + case "r3c2": + r3c2 = value; + break; + + case "r4c2": + r4c2 = value; + break; + + case "r1c3": + r1c3 = value; + break; + + case "r2c3": + r2c3 = value; + break; + + case "r3c3": + r3c3 = value; + break; + + case "r4c3": + r4c3 = value; + break; + + case "r1c4": + r1c4 = value; + break; + + case "r2c4": + r2c4 = value; + break; + + case "r3c4": + r3c4 = value; + break; + + case "r4c4": + r4c4 = value; + break; + + default: + string @params = ""; + if (r < 0 || r > 2) @params += r; + if (c < 0 || c > 2) @params += string.IsNullOrEmpty(@params) ? c : " and " + c; + throw new IndexOutOfRangeException(@params); + } + } + } + public float this[Int2 index] + { + get => this[index.x, index.y]; + set => this[index.x, index.y] = value; + } + + public static Matrix4x4 Absolute(Matrix4x4 val) => + new(Mathf.Absolute(val.r1c1), Mathf.Absolute(val.r2c1), Mathf.Absolute(val.r3c1), Mathf.Absolute(val.r4c1), + Mathf.Absolute(val.r1c2), Mathf.Absolute(val.r2c2), Mathf.Absolute(val.r3c2), Mathf.Absolute(val.r4c2), + Mathf.Absolute(val.r1c3), Mathf.Absolute(val.r2c3), Mathf.Absolute(val.r3c3), Mathf.Absolute(val.r4c2), + Mathf.Absolute(val.r1c4), Mathf.Absolute(val.r2c4), Mathf.Absolute(val.r3c4), Mathf.Absolute(val.r4c4)); + public static Matrix4x4 Average(params Matrix4x4[] vals) => Sum(vals) / vals.Length; + public static Matrix4x4 Ceiling(Matrix4x4 val) => + new(Mathf.Ceiling(val.r1c1), Mathf.Ceiling(val.r2c1), Mathf.Ceiling(val.r3c1), Mathf.Ceiling(val.r4c1), + Mathf.Ceiling(val.r1c2), Mathf.Ceiling(val.r2c2), Mathf.Ceiling(val.r3c2), Mathf.Ceiling(val.r4c2), + Mathf.Ceiling(val.r1c3), Mathf.Ceiling(val.r2c3), Mathf.Ceiling(val.r3c3), Mathf.Ceiling(val.r4c2), + Mathf.Ceiling(val.r1c4), Mathf.Ceiling(val.r2c4), Mathf.Ceiling(val.r3c4), Mathf.Ceiling(val.r4c4)); + public static Matrix4x4 Clamp(Matrix4x4 val, Matrix4x4 min, Matrix4x4 max) => + new(Mathf.Clamp(val.r1c1, min.r1c1, max.r1c1), Mathf.Clamp(val.r2c1, min.r2c1, max.r2c1), + Mathf.Clamp(val.r3c1, min.r3c1, max.r3c1), Mathf.Clamp(val.r4c1, min.r4c1, max.r4c1), + Mathf.Clamp(val.r1c2, min.r1c2, max.r1c2), Mathf.Clamp(val.r2c2, min.r2c2, max.r2c2), + Mathf.Clamp(val.r3c2, min.r3c2, max.r3c2), Mathf.Clamp(val.r4c2, min.r4c2, max.r4c2), + Mathf.Clamp(val.r1c3, min.r1c3, max.r1c3), Mathf.Clamp(val.r2c3, min.r2c3, max.r2c3), + Mathf.Clamp(val.r3c3, min.r3c3, max.r3c3), Mathf.Clamp(val.r4c3, min.r4c3, max.r4c3), + Mathf.Clamp(val.r1c4, min.r1c4, max.r1c4), Mathf.Clamp(val.r2c4, min.r2c4, max.r2c4), + Mathf.Clamp(val.r3c4, min.r3c4, max.r3c4), Mathf.Clamp(val.r4c4, min.r4c4, max.r4c4)); + public static Matrix4x4 Divide(Matrix4x4 num, params Matrix4x4[] vals) + { + foreach (Matrix4x4 m in vals) num /= m; + return num; + } + public static Matrix4x4 Floor(Matrix4x4 val) => + new(Mathf.Floor(val.r1c1), Mathf.Floor(val.r2c1), Mathf.Floor(val.r3c1), Mathf.Floor(val.r4c1), + Mathf.Floor(val.r1c2), Mathf.Floor(val.r2c2), Mathf.Floor(val.r3c2), Mathf.Floor(val.r4c2), + Mathf.Floor(val.r1c3), Mathf.Floor(val.r2c3), Mathf.Floor(val.r3c3), Mathf.Floor(val.r4c2), + Mathf.Floor(val.r1c4), Mathf.Floor(val.r2c4), Mathf.Floor(val.r3c4), Mathf.Floor(val.r4c4)); + public static Matrix4x4 Lerp(Matrix4x4 a, Matrix4x4 b, float t, bool clamp = true) => + new(Mathf.Lerp(a.r1c1, b.r1c1, t, clamp), Mathf.Lerp(a.r2c1, b.r2c1, t, clamp), + Mathf.Lerp(a.r3c1, b.r3c1, t, clamp), Mathf.Lerp(a.r4c1, b.r4c1, t, clamp), + Mathf.Lerp(a.r1c2, b.r1c2, t, clamp), Mathf.Lerp(a.r2c2, b.r2c2, t, clamp), + Mathf.Lerp(a.r3c2, b.r3c2, t, clamp), Mathf.Lerp(a.r4c2, b.r4c2, t, clamp), + Mathf.Lerp(a.r1c3, b.r1c3, t, clamp), Mathf.Lerp(a.r2c3, b.r2c3, t, clamp), + Mathf.Lerp(a.r3c3, b.r3c3, t, clamp), Mathf.Lerp(a.r4c3, b.r4c3, t, clamp), + Mathf.Lerp(a.r1c4, b.r1c4, t, clamp), Mathf.Lerp(a.r2c4, b.r2c4, t, clamp), + Mathf.Lerp(a.r3c4, b.r3c4, t, clamp), Mathf.Lerp(a.r4c4, b.r4c4, t, clamp)); + public static Matrix4x4 Median(params Matrix4x4[] vals) + { + float index = Mathf.Average(0, vals.Length - 1); + Matrix4x4 valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)]; + return Average(valA, valB); + } + public static Matrix4x4 Product(params Matrix4x4[] vals) + { + if (vals.Length < 1) return Zero; + Matrix4x4 val = Identity; + foreach (Matrix4x4 m in vals) val *= m; + return val; + } + public static Matrix4x4 Round(Matrix4x4 val) => + new(Mathf.Round(val.r1c1), Mathf.Round(val.r2c1), Mathf.Round(val.r3c1), Mathf.Round(val.r4c1), + Mathf.Round(val.r1c2), Mathf.Round(val.r2c2), Mathf.Round(val.r3c2), Mathf.Round(val.r4c2), + Mathf.Round(val.r1c3), Mathf.Round(val.r2c3), Mathf.Round(val.r3c3), Mathf.Round(val.r4c2), + Mathf.Round(val.r1c4), Mathf.Round(val.r2c4), Mathf.Round(val.r3c4), Mathf.Round(val.r4c4)); + public static Matrix4x4 Subtract(Matrix4x4 num, params Matrix4x4[] vals) + { + foreach (Matrix4x4 m in vals) num -= m; + return num; + } + public static Matrix4x4 Sum(params Matrix4x4[] vals) + { + Matrix4x4 val = Zero; + foreach (Matrix4x4 m in vals) val += m; + return val; + } + + public static (float[] r1c1s, float[] r2c1s, float[] r3c1s, float[] r4c1s, float[] r1c2s, float[] r2c2s, + float[] r3c2s, float[] r4c2s, float[] r1c3s, float[] r2c3s, float[] r3c3s, float[] r4c3s, float[] r1c4s, + float[] r2c4s, float[] r3c4s, float[] r4c4s) SplitArray(params Matrix4x4[] vals) + { + float[] r1c1s = new float[vals.Length], r2c1s = new float[vals.Length], r3c1s = new float[vals.Length], + r4c1s = new float[vals.Length], r1c2s = new float[vals.Length], r2c2s = new float[vals.Length], + r3c2s = new float[vals.Length], r4c2s = new float[vals.Length], r1c3s = new float[vals.Length], + r2c3s = new float[vals.Length], r3c3s = new float[vals.Length], r4c3s = new float[vals.Length], + r1c4s = new float[vals.Length], r2c4s = new float[vals.Length], r3c4s = new float[vals.Length], + r4c4s = new float[vals.Length]; + for (int i = 0; i < vals.Length; i++) + { + r1c1s[i] = vals[i].r1c1; + r2c1s[i] = vals[i].r2c1; + r3c1s[i] = vals[i].r3c1; + r4c1s[i] = vals[i].r4c1; + r1c2s[i] = vals[i].r1c2; + r2c2s[i] = vals[i].r2c2; + r3c2s[i] = vals[i].r3c2; + r4c2s[i] = vals[i].r4c2; + r1c3s[i] = vals[i].r1c3; + r2c3s[i] = vals[i].r2c3; + r3c3s[i] = vals[i].r3c3; + r4c3s[i] = vals[i].r4c3; + r4c4s[i] = vals[i].r4c4; + } + return (r1c1s, r2c1s, r3c1s, r4c1s, r1c2s, r2c2s, r3c2s, r4c2s, + r1c3s, r2c3s, r3c3s, r4c3s, r1c4s, r2c4s, r3c4s, r4c4s); + } + + public Matrix4x4 Adjugate() + { + Matrix4x4 dets = new(); + Matrix3x3[,] minors = Minors(); + for (int r = 0; r < 4; r++) for (int c = 0; c < 4; c++) dets[r, c] = minors[r, c].Determinant(); + return dets ^ SignGrid; + } + public float Determinant() + { + Matrix3x3[,] minors = Minors(); + return (r1c1 * minors[0, 0].Determinant()) - (r1c2 * minors[1, 0].Determinant()) + + (r1c3 * minors[2, 0].Determinant()) - (r1c4 * minors[3, 0].Determinant()); + } + public Matrix4x4 Inverse() + { + float d = Determinant(); + if (d == 0) throw new NoInverseException(); + return Transpose().Adjugate() / d; + } + public Matrix3x3[,] Minors() => new Matrix3x3[,] + { + { + new(r2c2, r3c2, r4c2, r2c3, r3c3, r4c3, r2c4, r3c4, r4c4), + new(r2c1, r3c1, r4c1, r2c3, r3c3, r4c3, r2c4, r3c4, r4c4), + new(r2c1, r3c1, r4c1, r2c2, r3c2, r4c2, r2c4, r3c4, r4c4), + new(r2c1, r3c1, r4c1, r2c2, r3c2, r4c2, r2c3, r3c3, r4c3) + }, + { + new(r1c2, r3c2, r4c2, r1c3, r3c3, r4c3, r1c4, r3c4, r4c4), + new(r1c1, r3c1, r4c1, r1c3, r3c3, r4c3, r1c4, r3c4, r4c4), + new(r1c1, r3c1, r4c1, r1c2, r3c2, r4c2, r1c4, r3c4, r4c4), + new(r1c1, r3c1, r4c1, r1c2, r3c2, r4c2, r1c3, r3c3, r4c3) + }, + { + new(r1c2, r2c2, r4c2, r1c3, r2c3, r4c3, r1c4, r2c4, r4c4), + new(r1c1, r2c1, r4c1, r1c3, r2c3, r4c3, r1c4, r2c4, r4c4), + new(r1c1, r2c1, r4c1, r1c2, r2c2, r4c2, r1c4, r2c4, r4c4), + new(r1c1, r2c1, r4c1, r1c2, r2c2, r4c2, r1c3, r2c3, r4c3) + }, + { + new(r1c2, r2c2, r3c2, r1c3, r2c3, r3c3, r1c4, r2c4, r3c4), + new(r1c1, r2c1, r3c1, r1c3, r2c3, r3c3, r1c4, r2c4, r3c4), + new(r1c1, r2c1, r3c1, r1c2, r2c2, r3c2, r1c4, r2c4, r3c4), + new(r1c1, r2c1, r3c1, r1c2, r2c2, r3c2, r1c3, r2c3, r3c3) + } + }; + public Matrix4x4 Transpose() => new(new[,] + { + { r1c1, r2c1, r3c1, r4c1 }, + { r1c2, r2c2, r3c2, r4c2 }, + { r1c3, r2c3, r3c3, r4c3 }, + { r1c4, r2c4, r3c4, r4c4 } + }); + + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj == null || obj.GetType() != typeof(Matrix4x4)) return base.Equals(obj); + return Equals((Matrix4x4)obj); + } + public bool Equals(Matrix4x4 other) => + r1c1 == other.r1c1 && r2c1 == other.r2c1 && r3c1 == other.r3c1 && r4c1 == other.r4c1 && + r1c2 == other.r1c2 && r2c2 == other.r2c2 && r3c2 == other.r3c2 && r4c2 == other.r4c2 && + r1c3 == other.r1c3 && r2c3 == other.r2c3 && r3c3 == other.r3c3 && r4c3 == other.r4c3 && + r1c4 == other.r1c4 && r2c3 == other.r2c4 && r3c4 == other.r3c4 && r4c4 == other.r4c4; + public override int GetHashCode() => + r1c1.GetHashCode() ^ r2c1.GetHashCode() ^ r3c1.GetHashCode() ^ r4c1.GetHashCode() ^ + r1c2.GetHashCode() ^ r2c2.GetHashCode() ^ r3c2.GetHashCode() ^ r4c2.GetHashCode() ^ + r1c3.GetHashCode() ^ r2c3.GetHashCode() ^ r3c3.GetHashCode() ^ r4c3.GetHashCode() ^ + r1c4.GetHashCode() ^ r2c4.GetHashCode() ^ r3c4.GetHashCode() ^ r4c4.GetHashCode(); + public string ToString(string? provider) => + r1c1.ToString(provider) + " " + r1c2.ToString(provider) + " " + r1c3.ToString(provider) + " " + + r1c4.ToString(provider) + "\n" + r2c1.ToString(provider) + " " + r2c2.ToString(provider) + " " + + r2c3.ToString(provider) + " " + r2c4.ToString(provider) + "\n" + r3c1.ToString(provider) + " " + + r3c2.ToString(provider) + " " + r3c3.ToString(provider) + " " + r3c4.ToString(provider) + "\n" + + r4c1.ToString(provider) + " " + r4c2.ToString(provider) + " " + r4c3.ToString(provider) + " " + + r4c4.ToString(provider); + public string ToString(IFormatProvider provider) => + r1c1.ToString(provider) + " " + r1c2.ToString(provider) + " " + r1c3.ToString(provider) + " " + + r1c4.ToString(provider) + "\n" + r2c1.ToString(provider) + " " + r2c2.ToString(provider) + " " + + r2c3.ToString(provider) + " " + r2c4.ToString(provider) + "\n" + r3c1.ToString(provider) + " " + + r3c2.ToString(provider) + " " + r3c3.ToString(provider) + " " + r3c4.ToString(provider) + "\n" + + r4c1.ToString(provider) + " " + r4c2.ToString(provider) + " " + r4c3.ToString(provider) + " " + + r4c4.ToString(provider); + + public object Clone() => new Matrix4x4(ToArray2D()); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public IEnumerator GetEnumerator() + { + yield return r1c1; + yield return r2c1; + yield return r3c1; + yield return r4c1; + yield return r1c2; + yield return r2c2; + yield return r3c2; + yield return r4c2; + yield return r1c3; + yield return r2c3; + yield return r3c3; + yield return r4c3; + yield return r1c4; + yield return r2c4; + yield return r3c4; + yield return r4c4; + } + + public float[] ToArray() => new[] + { + r1c1, r2c1, r3c1, r4c1, + r1c2, r2c2, r3c2, r4c2, + r1c3, r2c3, r3c3, r4c3, + r1c4, r2c4, r3c4, r4c4 + }; + public float[,] ToArray2D() => new[,] + { + { r1c1, r1c2, r1c3, r1c4 }, + { r2c1, r2c2, r2c3, r2c4 }, + { r3c1, r3c2, r3c3, r3c4 }, + { r4c1, r4c2, r4c3, r4c4 } + }; + public Dictionary ToDictionary() + { + Dictionary dict = new(); + float[] arr = ToArray(); + for (int i = 0; i < arr.Length; i++) dict.Add(new(i % 4, i / 4), arr[i]); + return dict; + } + public Fill ToFill() => ToFillExtension.ToFill(this); + public Fill2D ToFill2D() + { + Matrix4x4 @this = this; + return (x, y) => @this[x, y]; + } + public List ToList() => new() + { + r1c1, r2c1, r3c1, r4c1, + r1c2, r2c2, r3c2, r4c2, + r1c3, r2c3, r3c3, r4c3, + r1c4, r2c4, r3c4, r4c4 + }; + + public static Matrix4x4 operator +(Matrix4x4 a, Matrix4x4 b) => + new(a.r1c1 + b.r1c1, a.r2c1 + b.r2c1, a.r3c1 + b.r3c1, a.r4c1 + b.r4c1, + a.r1c2 + b.r1c2, a.r2c2 + b.r2c2, a.r3c2 + b.r3c2, a.r4c2 + b.r4c2, + a.r1c3 + b.r1c3, a.r2c3 + b.r2c3, a.r3c3 + b.r3c3, a.r4c3 + b.r4c3, + a.r1c4 + b.r1c4, a.r2c4 + b.r2c4, a.r3c4 + b.r3c4, a.r4c4 + b.r4c4); + public static Matrix4x4 operator -(Matrix4x4 m) => m.Inverse(); + public static Matrix4x4 operator -(Matrix4x4 a, Matrix4x4 b) => + new(a.r1c1 - b.r1c1, a.r2c1 - b.r2c1, a.r3c1 - b.r3c1, a.r4c1 - b.r4c1, + a.r1c2 - b.r1c2, a.r2c2 - b.r2c2, a.r3c2 - b.r3c2, a.r4c2 - b.r4c2, + a.r1c3 - b.r1c3, a.r2c3 - b.r2c3, a.r3c3 - b.r3c3, a.r4c3 - b.r4c3, + a.r1c4 - b.r1c4, a.r2c4 - b.r2c4, a.r3c4 - b.r3c4, a.r4c4 - b.r4c4); + public static Matrix4x4 operator *(Matrix4x4 a, float b) => + new(a.r1c1 * b, a.r2c1 * b, a.r3c1 * b, a.r4c1 * b, + a.r1c2 * b, a.r2c2 * b, a.r3c2 * b, a.r4c2 * b, + a.r1c3 * b, a.r2c3 * b, a.r3c3 * b, a.r4c3 * b, + a.r1c4 * b, a.r2c4 * b, a.r3c4 * b, a.r4c4 * b); + public static Matrix4x4 operator *(Matrix4x4 a, Matrix4x4 b) => new(new[,] + { + { Float4.Dot(a.Row1, b.Column1), Float4.Dot(a.Row1, b.Column2), + Float4.Dot(a.Row1, b.Column3), Float4.Dot(a.Row1, b.Column4) }, + { Float4.Dot(a.Row2, b.Column1), Float4.Dot(a.Row2, b.Column2), + Float4.Dot(a.Row2, b.Column3), Float4.Dot(a.Row2, b.Column4) }, + { Float4.Dot(a.Row3, b.Column1), Float4.Dot(a.Row3, b.Column2), + Float4.Dot(a.Row3, b.Column3), Float4.Dot(a.Row3, b.Column4) }, + { Float4.Dot(a.Row4, b.Column1), Float4.Dot(a.Row4, b.Column2), + Float4.Dot(a.Row4, b.Column3), Float4.Dot(a.Row4, b.Column4) } + }); + public static Matrix4x4 operator /(Matrix4x4 a, float b) => + new(a.r1c1 / b, a.r2c1 / b, a.r3c1 / b, a.r4c1 / b, + a.r1c2 / b, a.r2c2 / b, a.r3c2 / b, a.r4c2 / b, + a.r1c3 / b, a.r2c3 / b, a.r3c3 / b, a.r4c3 / b, + a.r1c4 / b, a.r2c4 / b, a.r3c4 / b, a.r4c4 / b); + public static Matrix4x4 operator /(Matrix4x4 a, Matrix4x4 b) => a * b.Inverse(); + public static Matrix4x4 operator ^(Matrix4x4 a, Matrix4x4 b) => // Single number multiplication + new(a.r1c1 * b.r1c1, a.r2c1 * b.r2c1, a.r3c1 * b.r3c1, a.r4c1 * b.r4c1, + a.r1c2 * b.r1c2, a.r2c2 * b.r2c2, a.r3c2 * b.r3c2, a.r4c2 * b.r4c2, + a.r1c3 * b.r1c3, a.r2c3 * b.r2c3, a.r3c3 * b.r3c3, a.r4c3 * b.r4c3, + a.r1c4 * b.r1c4, a.r2c4 * b.r2c4, a.r3c4 * b.r3c4, a.r4c4 * b.r4c4); + public static bool operator ==(Matrix4x4 a, Matrix4x4 b) => a.Equals(b); + public static bool operator !=(Matrix4x4 a, Matrix4x4 b) => !a.Equals(b); + + public static explicit operator Matrix4x4(Matrix m) + { + Matrix4x4 res = Zero, identity = Identity; + for (int r = 0; r < 4; r++) for (int c = 0; c < 4; c++) + res[c, r] = m.Size.x < c && m.Size.y < r ? m[r, c] : identity[r, c]; + return res; + } + public static implicit operator Matrix4x4(Matrix2x2 m) + { + Matrix4x4 identity = Identity; + return new(new[,] + { + { m.r1c1, m.r1c2, identity.r1c3, identity.r1c4 }, + { m.r2c1, m.r2c2, identity.r2c3, identity.r2c4 }, + { identity.r3c1, identity.r3c2, identity.r3c3, identity.r3c4 }, + { identity.r4c1, identity.r4c2, identity.r4c3, identity.r4c4 } + }); + } + public static implicit operator Matrix4x4(Matrix3x3 m) + { + Matrix4x4 identity = Identity; + return new(new[,] + { + { m.r1c1, m.r1c2, m.r1c3, identity.r1c4 }, + { m.r2c1, m.r2c2, m.r2c3, identity.r2c4 }, + { m.r3c1, m.r3c2, m.r3c3, identity.r3c4 }, + { identity.r4c1, identity.r4c2, identity.r4c3, identity.r4c4 } + }); + } +} diff --git a/Nerd_STF/Mathematics/Algebra/Vector2d.cs b/Nerd_STF/Mathematics/Algebra/Vector2d.cs new file mode 100644 index 0000000..630959b --- /dev/null +++ b/Nerd_STF/Mathematics/Algebra/Vector2d.cs @@ -0,0 +1,158 @@ +namespace Nerd_STF.Mathematics.Algebra; + +public struct Vector2d : ICloneable, IComparable, IEquatable +{ + public static Vector2d Down => new(Angle.Down); + public static Vector2d Left => new(Angle.Left); + public static Vector2d Right => new(Angle.Right); + public static Vector2d Up => new(Angle.Up); + + public static Vector2d One => new(Angle.Zero); + public static Vector2d Zero => new(Angle.Zero, 0); + + public Vector2d Inverse => new(-theta, magnitude); + public Vector2d Normalized => new(theta, 1); + + public Angle theta; + public float magnitude; + + public Vector2d(Angle theta, float mag = 1) + { + this.theta = theta; + magnitude = mag; + } + public Vector2d(float theta, Angle.Type rotType, float mag = 1) : this(new(theta, rotType), mag) { } + + public static Vector2d Absolute(Vector2d val) => new(Angle.Absolute(val.theta), Mathf.Absolute(val.magnitude)); + public static Vector2d Average(params Vector2d[] vals) + { + (Angle[] thetas, float[] Mags) = SplitArray(vals); + return new(Angle.Average(thetas), Mathf.Average(Mags)); + } + public static Vector2d Ceiling(Vector2d val, Angle.Type angleRound = Angle.Type.Degrees) => + new(Angle.Ceiling(val.theta, angleRound), Mathf.Ceiling(val.magnitude)); + public static Vector2d ClampMagnitude(Vector2d val, float minMag, float maxMag) + { + if (maxMag < minMag) throw new ArgumentOutOfRangeException(nameof(maxMag), + nameof(maxMag) + " must be greater than or equal to " + nameof(minMag)); + float mag = Mathf.Clamp(val.magnitude, minMag, maxMag); + return new(val.theta, mag); + } + public static Vector3d Cross(Vector2d a, Vector2d b, bool normalized = false) => + Float2.Cross(a.ToXYZ(), b.ToXYZ(), normalized).ToVector(); + public static Vector2d Divide(Vector2d num, params Vector2d[] vals) + { + foreach (Vector2d v in vals) num /= v; + return num; + } + public static float Dot(Vector2d a, Vector2d b) => Float2.Dot(a.ToXYZ(), b.ToXYZ()); + public static float Dot(params Vector2d[] vals) + { + Float2[] floats = new Float2[vals.Length]; + for (int i = 0; i < vals.Length; i++) floats[i] = vals[i].ToXYZ(); + return Float2.Dot(floats); + } + public static Vector2d Floor(Vector2d val, Angle.Type angleRound = Angle.Type.Degrees) => + new(Angle.Floor(val.theta, angleRound), Mathf.Floor(val.magnitude)); + public static Vector2d Lerp(Vector2d a, Vector2d b, float t, bool clamp = true) => + new(Angle.Lerp(a.theta, b.theta, t, clamp), Mathf.Lerp(a.magnitude, b.magnitude, t, clamp)); + public static Vector2d Median(params Vector2d[] vals) + { + float index = Mathf.Average(0, vals.Length - 1); + Vector2d valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)]; + return Average(valA, valB); + } + public static Vector2d Max(params Vector2d[] vals) + { + if (vals.Length < 1) return Zero; + Vector2d val = vals[0]; + foreach (Vector2d f in vals) val = f > val ? f : val; + return val; + } + public static Vector2d Min(params Vector2d[] vals) + { + if (vals.Length < 1) return Zero; + Vector2d val = vals[0]; + foreach (Vector2d f in vals) val = f < val ? f : val; + return val; + } + public static Vector2d Product(params Vector2d[] vals) + { + if (vals.Length < 1) return Zero; + Vector2d val = One; + foreach (Vector2d v in vals) val *= v; + return val; + } + public static Vector2d Round(Vector2d val, Angle.Type angleRound = Angle.Type.Degrees) => + new(Angle.Round(val.theta, angleRound), Mathf.Round(val.magnitude)); + public static Vector2d Subtract(Vector2d num, params Vector2d[] vals) + { + foreach (Vector2d v in vals) num -= v; + return num; + } + public static Vector2d Sum(params Vector2d[] vals) + { + if (vals.Length < 1) return Zero; + Vector2d val = One; + foreach (Vector2d v in vals) val += v; + return val; + } + + public static (Angle[] Rots, float[] Mags) SplitArray(params Vector2d[] vals) + { + Angle[] rots = new Angle[vals.Length]; + float[] mags = new float[vals.Length]; + for (int i = 0; i < vals.Length; i++) + { + rots[i] = vals[i].theta; + mags[i] = vals[i].magnitude; + } + return (rots, mags); + } + + public int CompareTo(Vector2d other) => magnitude.CompareTo(other.magnitude); + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj == null || obj.GetType() != typeof(Vector2d)) return base.Equals(obj); + return Equals((Vector2d)obj); + } + public bool Equals(Vector2d other) => theta == other.theta && magnitude == other.magnitude; + public override int GetHashCode() => theta.GetHashCode() ^ magnitude.GetHashCode(); + public override string ToString() => ToString((string?)null); + public string ToString(Angle.Type outputType = Angle.Type.Degrees) => ToString((string?)null, outputType); + public string ToString(string? provider, Angle.Type outputType = Angle.Type.Degrees) => + "Mag: " + magnitude.ToString(provider) + " Rot: " + theta.ToString(provider, outputType); + public string ToString(IFormatProvider provider, Angle.Type outputType = Angle.Type.Degrees) => + "Mag: " + magnitude.ToString(provider) + " Rot: " + theta.ToString(provider, outputType); + + public object Clone() => new Vector2d(theta, magnitude); + + public Float2 ToXYZ() => new(Mathf.Cos(theta)); + + public static Vector2d operator +(Vector2d a, Vector2d b) => new(a.theta + b.theta, a.magnitude + b.magnitude); + public static Vector2d operator -(Vector2d v) => v.Inverse; + public static Vector2d operator -(Vector2d a, Vector2d b) => new(a.theta - b.theta, a.magnitude - b.magnitude); + public static Vector2d operator *(Vector2d a, Angle b) => new(a.theta * b, a.magnitude); + public static Vector2d operator *(Vector2d a, float b) => new(a.theta, a.magnitude * b); + public static Vector2d operator *(Vector2d a, Vector2d b) => new(a.theta * b.theta, a.magnitude * b.magnitude); + public static Vector2d operator *(Vector2d a, Matrix b) => (Vector2d)((Matrix)a * b); + public static Vector2d operator /(Vector2d a, Angle b) => new(a.theta / b, a.magnitude); + public static Vector2d operator /(Vector2d a, float b) => new(a.theta, a.magnitude / b); + public static Vector2d operator /(Vector2d a, Vector2d b) => new(a.theta / b.theta, a.magnitude / b.magnitude); + public static Vector2d operator /(Vector2d a, Matrix b) => (Vector2d)((Matrix)a / b); + public static bool operator ==(Vector2d a, Vector2d b) => a.Equals(b); + public static bool operator !=(Vector2d a, Vector2d b) => !a.Equals(b); + public static bool operator >(Vector2d a, Vector2d b) => a.CompareTo(b) > 0; + public static bool operator <(Vector2d a, Vector2d b) => a.CompareTo(b) < 0; + public static bool operator >=(Vector2d a, Vector2d b) => a == b || a > b; + public static bool operator <=(Vector2d a, Vector2d b) => a == b || a < b; + + public static explicit operator Vector2d(Complex val) => val.ToVector(); + public static explicit operator Vector2d(Float2 val) => val.ToVector(); + public static explicit operator Vector2d(Float3 val) => (Vector2d)val.ToVector(); + public static explicit operator Vector2d(Int2 val) => val.ToVector(); + public static explicit operator Vector2d(Int3 val) => (Vector2d)val.ToVector(); + public static explicit operator Vector2d(Matrix m) => ((Float2)m).ToVector(); + public static explicit operator Vector2d(Vert val) => (Vector2d)val.ToVector(); + public static explicit operator Vector2d(Vector3d val) => new(val.yaw, val.magnitude); +} diff --git a/Nerd_STF/Mathematics/Algebra/Vector3d.cs b/Nerd_STF/Mathematics/Algebra/Vector3d.cs new file mode 100644 index 0000000..6a5393a --- /dev/null +++ b/Nerd_STF/Mathematics/Algebra/Vector3d.cs @@ -0,0 +1,200 @@ +namespace Nerd_STF.Mathematics.Algebra; + +public struct Vector3d : ICloneable, IComparable, IEquatable +{ + public static Vector3d Back => new(Angle.Zero, Angle.Up); + public static Vector3d Down => new(Angle.Down, Angle.Zero); + public static Vector3d Forward => new(Angle.Zero, Angle.Down); + public static Vector3d Left => new(Angle.Left, Angle.Zero); + public static Vector3d Right => new(Angle.Right, Angle.Zero); + public static Vector3d Up => new(Angle.Up, Angle.Zero); + + public static Vector3d One => new(Angle.Zero); + public static Vector3d Zero => new(Angle.Zero, 0); + + public Vector3d Inverse => new(-yaw, -pitch, magnitude); + public Vector3d Normalized => new(yaw, pitch, 1); + + public Angle yaw, pitch; + public float magnitude; + + public Vector3d(Angle allRot, float mag = 1) : this(allRot, allRot, mag) { } + public Vector3d(float allRot, Angle.Type rotType, float mag = 1) : this(allRot, allRot, rotType, mag) { } + public Vector3d(Angle yaw, Angle pitch, float mag = 1) + { + this.yaw = yaw; + this.pitch = pitch; + magnitude = mag; + } + public Vector3d(float yaw, float pitch, Angle.Type rotType, float mag = 1) + : this(new Angle(yaw, rotType), new(pitch, rotType), mag) { } + public Vector3d(Float2 rots, Angle.Type rotType, float mag = 1) : this(rots.x, rots.y, rotType, mag) { } + public Vector3d(Fill fill, float mag = 1) : this(fill(0), fill(1), mag) { } + public Vector3d(Fill fill, Angle.Type rotType, float mag = 1) : this(fill(0), fill(1), rotType, mag) { } + + public Angle this[int index] + { + get => index switch + { + 0 => yaw, + 1 => pitch, + _ => throw new IndexOutOfRangeException(nameof(index)), + }; + set + { + switch (index) + { + case 0: + yaw = value; + break; + + case 1: + pitch = value; + break; + + default: throw new IndexOutOfRangeException(nameof(index)); + } + } + } + + public static Vector3d Absolute(Vector3d val) => new(Angle.Absolute(val.yaw), Angle.Absolute(val.pitch), + Mathf.Absolute(val.magnitude)); + public static Vector3d Average(params Vector3d[] vals) + { + (Angle[] Thetas, Angle[] Phis, float[] Mags) = SplitArray(vals); + return new(Angle.Average(Thetas), Angle.Average(Phis), Mathf.Average(Mags)); + } + public static Vector3d Ceiling(Vector3d val, Angle.Type angleRound = Angle.Type.Degrees) => + new(Angle.Ceiling(val.yaw, angleRound), Angle.Ceiling(val.pitch, angleRound), + Mathf.Ceiling(val.magnitude)); + public static Vector3d ClampMagnitude(Vector3d val, float minMag, float maxMag) + { + if (maxMag < minMag) throw new ArgumentOutOfRangeException(nameof(maxMag), + nameof(maxMag) + " must be greater than or equal to " + nameof(minMag)); + float mag = Mathf.Clamp(val.magnitude, minMag, maxMag); + return new(val.yaw, val.pitch, mag); + } + public static Vector3d Cross(Vector3d a, Vector3d b, bool normalized = false) => + Float3.Cross(a.ToXYZ(), b.ToXYZ(), normalized).ToVector(); + public static Vector3d Divide(Vector3d num, params Vector3d[] vals) + { + foreach (Vector3d v in vals) num /= v; + return num; + } + public static float Dot(Vector3d a, Vector3d b) => Float3.Dot(a.ToXYZ(), b.ToXYZ()); + public static float Dot(params Vector3d[] vals) + { + Float3[] floats = new Float3[vals.Length]; + for (int i = 0; i < vals.Length; i++) floats[i] = vals[i].ToXYZ(); + return Float3.Dot(floats); + } + public static Vector3d Floor(Vector3d val, Angle.Type angleRound = Angle.Type.Degrees) => + new(Angle.Floor(val.yaw, angleRound), Angle.Floor(val.pitch, angleRound), Mathf.Floor(val.magnitude)); + public static Vector3d Lerp(Vector3d a, Vector3d b, float t, bool clamp = true) => + new(Angle.Lerp(a.yaw, b.yaw, t, clamp), Angle.Lerp(a.pitch, b.pitch, t, clamp), + Mathf.Lerp(a.magnitude, b.magnitude, t, clamp)); + public static Vector3d Median(params Vector3d[] vals) + { + float index = Mathf.Average(0, vals.Length - 1); + Vector3d valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)]; + return Average(valA, valB); + } + public static Vector3d Max(params Vector3d[] vals) + { + if (vals.Length < 1) return Zero; + Vector3d val = vals[0]; + foreach (Vector3d f in vals) val = f > val ? f : val; + return val; + } + public static Vector3d Min(params Vector3d[] vals) + { + if (vals.Length < 1) return Zero; + Vector3d val = vals[0]; + foreach (Vector3d f in vals) val = f < val ? f : val; + return val; + } + public static Vector3d Product(params Vector3d[] vals) + { + if (vals.Length < 1) return Zero; + Vector3d val = One; + foreach (Vector3d v in vals) val *= v; + return val; + } + public static Vector3d Round(Vector3d val, Angle.Type angleRound = Angle.Type.Degrees) => + new(Angle.Round(val.yaw, angleRound), Angle.Round(val.pitch, angleRound), Mathf.Round(val.magnitude)); + public static Vector3d Subtract(Vector3d num, params Vector3d[] vals) + { + foreach (Vector3d v in vals) num -= v; + return num; + } + public static Vector3d Sum(params Vector3d[] vals) + { + if (vals.Length < 1) return Zero; + Vector3d val = One; + foreach (Vector3d v in vals) val += v; + return val; + } + + public static (Angle[] Thetas, Angle[] Phis, float[] Mags) SplitArray(params Vector3d[] vals) + { + Angle[] yaws = new Angle[vals.Length], pitchs = new Angle[vals.Length]; + float[] mags = new float[vals.Length]; + for (int i = 0; i < vals.Length; i++) + { + yaws[i] = vals[i].yaw; + pitchs[i] = vals[i].pitch; + mags[i] = vals[i].magnitude; + } + return (yaws, pitchs, mags); + } + + public int CompareTo(Vector3d other) => magnitude.CompareTo(other.magnitude); + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj == null || obj.GetType() != typeof(Vector3d)) return base.Equals(obj); + return Equals((Vector3d)obj); + } + public bool Equals(Vector3d other) => yaw == other.yaw && pitch == other.pitch + && magnitude == other.magnitude; + public override int GetHashCode() => yaw.GetHashCode() ^ pitch.GetHashCode() ^ magnitude.GetHashCode(); + public string ToString(string? provider, Angle.Type outputType = Angle.Type.Degrees) => + "Mag: " + magnitude.ToString(provider) + " Theta: " + yaw.ToString(provider, outputType) + + " Phi: " + pitch.ToString(provider, outputType); + + public object Clone() => new Vector3d(yaw, pitch, magnitude); + + public Float3 ToXYZ() => new(Mathf.Sin(pitch) * Mathf.Cos(yaw) * magnitude, + Mathf.Sin(pitch) * Mathf.Sin(yaw) * magnitude, + Mathf.Cos(pitch) * magnitude); + + public static Vector3d operator +(Vector3d a, Vector3d b) => new(a.yaw + b.yaw, a.pitch + b.pitch, + a.magnitude + b.magnitude); + public static Vector3d operator -(Vector3d v) => v.Inverse; + public static Vector3d operator -(Vector3d a, Vector3d b) => new(a.yaw - b.yaw, a.pitch - b.pitch, + a.magnitude - b.magnitude); + public static Vector3d operator *(Vector3d a, Angle b) => new(a.yaw * b, a.pitch * b, a.magnitude); + public static Vector3d operator *(Vector3d a, float b) => new(a.yaw, a.pitch, a.magnitude * b); + public static Vector3d operator *(Vector3d a, Vector3d b) => new(a.yaw * b.yaw, a.pitch * b.pitch, + a.magnitude * b.magnitude); + public static Vector3d operator *(Vector3d a, Matrix b) => (Vector3d)((Matrix)a * b); + public static Vector3d operator /(Vector3d a, Angle b) => new(a.yaw / b, a.pitch / b, a.magnitude); + public static Vector3d operator /(Vector3d a, float b) => new(a.yaw, a.pitch, a.magnitude / b); + public static Vector3d operator /(Vector3d a, Vector3d b) => new(a.yaw / b.yaw, a.pitch / b.pitch, + a.magnitude / b.magnitude); + public static Vector3d operator /(Vector3d a, Matrix b) => (Vector3d)((Matrix)a / b); + public static bool operator ==(Vector3d a, Vector3d b) => a.Equals(b); + public static bool operator !=(Vector3d a, Vector3d b) => !a.Equals(b); + public static bool operator >(Vector3d a, Vector3d b) => a.CompareTo(b) > 0; + public static bool operator <(Vector3d a, Vector3d b) => a.CompareTo(b) < 0; + public static bool operator >=(Vector3d a, Vector3d b) => a == b || a > b; + public static bool operator <=(Vector3d a, Vector3d b) => a == b || a < b; + + public static explicit operator Vector3d(Complex val) => val.ToVector(); + public static explicit operator Vector3d(Float2 val) => val.ToVector(); + public static explicit operator Vector3d(Float3 val) => val.ToVector(); + public static explicit operator Vector3d(Int2 val) => val.ToVector(); + public static explicit operator Vector3d(Int3 val) => val.ToVector(); + public static explicit operator Vector3d(Matrix m) => ((Float3)m).ToVector(); + public static explicit operator Vector3d(Vert val) => val.ToVector(); + public static implicit operator Vector3d(Vector2d v) => new(v.theta, Angle.Zero, v.magnitude); +} diff --git a/Nerd_STF/Mathematics/Angle.cs b/Nerd_STF/Mathematics/Angle.cs index e0e83fa..9636ca7 100644 --- a/Nerd_STF/Mathematics/Angle.cs +++ b/Nerd_STF/Mathematics/Angle.cs @@ -2,6 +2,11 @@ public struct Angle : ICloneable, IComparable, IEquatable { + public static Angle Down => new(270); + public static Angle Left => new(180); + public static Angle Right => new(0); + public static Angle Up => new(90); + public static Angle Full => new(360); public static Angle Half => new(180); public static Angle One => new(1); @@ -47,14 +52,18 @@ public struct Angle : ICloneable, IComparable, IEquatable public static Angle Absolute(Angle val) => new(Mathf.Absolute(val.p_deg)); public static Angle Average(params Angle[] vals) => new(Mathf.Average(SplitArray(Type.Degrees, vals))); - public static Angle Ceiling(Angle val, Type type = Type.Degrees) => new(Mathf.Ceiling(val.ValueFromType(type))); + public static Angle Ceiling(Angle val, Type type = Type.Degrees) => + new(Mathf.Ceiling(val.ValueFromType(type)), type); public static Angle Clamp(Angle val, Angle min, Angle max) => new(Mathf.Clamp(val.p_deg, min.p_deg, max.p_deg)); - public static Angle Floor(Angle val, Type type = Type.Degrees) => new(Mathf.Floor(val.ValueFromType(type))); + public static Angle Floor(Angle val, Type type = Type.Degrees) => + new(Mathf.Floor(val.ValueFromType(type)), type); public static Angle Lerp(Angle a, Angle b, float t, bool clamp = true) => new(Mathf.Lerp(a.p_deg, b.p_deg, t, clamp)); public static Angle Max(params Angle[] vals) => new(Mathf.Max(SplitArray(Type.Degrees, vals))); public static Angle Median(params Angle[] vals) => new(Mathf.Median(SplitArray(Type.Degrees, vals))); public static Angle Min(params Angle[] vals) => new(Mathf.Min(SplitArray(Type.Degrees, vals))); + public static Angle Round(Angle val, Type type = Type.Degrees) => + new(Mathf.Floor(val.ValueFromType(type)), type); public static float[] SplitArray(Type outputType, params Angle[] vals) { @@ -76,7 +85,7 @@ public struct Angle : ICloneable, IComparable, IEquatable public int CompareTo(Angle other) => p_deg.CompareTo(other.p_deg); public override bool Equals([NotNullWhen(true)] object? obj) { - if (obj == null || obj.GetType() != typeof(Angle)) return false; + if (obj == null || obj.GetType() != typeof(Angle)) return base.Equals(obj); return Equals((Angle)obj); } public bool Equals(Angle other) => p_deg == other.p_deg; diff --git a/Nerd_STF/Mathematics/Calculus.cs b/Nerd_STF/Mathematics/Calculus.cs index a117c75..a39cc1b 100644 --- a/Nerd_STF/Mathematics/Calculus.cs +++ b/Nerd_STF/Mathematics/Calculus.cs @@ -18,11 +18,11 @@ public static class Calculus (equ(x + DefaultStep) - equ(x)) / step; public static float GetIntegral(Equation equ, float lowerBound, float upperBound, float step = DefaultStep) - { - float val = 0; - for (float x = lowerBound; x <= upperBound; x += step) val += equ(x) * step; - return val; - } + { + float val = 0; + for (float x = lowerBound; x <= upperBound; x += step) val += equ(x) * step; + return val; + } public static float GradientDescent(Equation equ, float initial, float rate, float stepCount = 1000, float step = DefaultStep) diff --git a/Nerd_STF/Mathematics/Float2.cs b/Nerd_STF/Mathematics/Float2.cs index 8c52472..274ca9b 100644 --- a/Nerd_STF/Mathematics/Float2.cs +++ b/Nerd_STF/Mathematics/Float2.cs @@ -11,7 +11,7 @@ public struct Float2 : ICloneable, IComparable, IEquatable, IGro public static Float2 Zero => new(0, 0); public float Magnitude => Mathf.Sqrt(x * x + y * y); - public Float2 Normalized => this / Magnitude; + public Float2 Normalized => this * Mathf.InverseSqrt(x * x + y * y); public float x, y; @@ -72,7 +72,7 @@ public struct Float2 : ICloneable, IComparable, IEquatable, IGro Float3.Cross(a, b, normalized); public static Float2 Divide(Float2 num, params Float2[] vals) { - foreach (Float2 d in vals) num /= d; + foreach (Float2 f in vals) num /= f; return num; } public static float Dot(Float2 a, Float2 b) => a.x * b.x + a.y * b.y; @@ -80,10 +80,10 @@ public struct Float2 : ICloneable, IComparable, IEquatable, IGro { if (vals.Length < 1) return 0; float x = 1, y = 1; - foreach (Float2 d in vals) + foreach (Float2 f in vals) { - x *= d.x; - y *= d.y; + x *= f.x; + y *= f.y; } return x + y; } @@ -101,32 +101,34 @@ public struct Float2 : ICloneable, IComparable, IEquatable, IGro { if (vals.Length < 1) return Zero; Float2 val = vals[0]; - foreach (Float2 d in vals) val = d > val ? d : val; + foreach (Float2 f in vals) val = f > val ? f : val; return val; } public static Float2 Min(params Float2[] vals) { if (vals.Length < 1) return Zero; Float2 val = vals[0]; - foreach (Float2 d in vals) val = d < val ? d : val; + foreach (Float2 f in vals) val = f < val ? f : val; return val; } public static Float2 Product(params Float2[] vals) { if (vals.Length < 1) return Zero; Float2 val = One; - foreach (Float2 d in vals) val *= d; + foreach (Float2 f in vals) val *= f; return val; } + public static Float2 Round(Float2 val) => + new(Mathf.Round(val.x), Mathf.Round(val.y)); public static Float2 Subtract(Float2 num, params Float2[] vals) { - foreach (Float2 d in vals) num -= d; + foreach (Float2 f in vals) num -= f; return num; } public static Float2 Sum(params Float2[] vals) { Float2 val = Zero; - foreach (Float2 d in vals) val += d; + foreach (Float2 f in vals) val += f; return val; } @@ -144,7 +146,7 @@ public struct Float2 : ICloneable, IComparable, IEquatable, IGro public int CompareTo(Float2 other) => Magnitude.CompareTo(other.Magnitude); public override bool Equals([NotNullWhen(true)] object? obj) { - if (obj == null || obj.GetType() != typeof(Float2)) return false; + if (obj == null || obj.GetType() != typeof(Float2)) return base.Equals(obj); return Equals((Float2)obj); } public bool Equals(Float2 other) => x == other.x && y == other.y; @@ -165,15 +167,24 @@ public struct Float2 : ICloneable, IComparable, IEquatable, IGro } public float[] ToArray() => new[] { x, y }; + public Fill ToFill() + { + Float2 @this = this; + return i => @this[i]; + } public List ToList() => new() { x, y }; + public Vector2d ToVector() => new(new(Mathf.ArcTan(y / x), Angle.Type.Radians), Magnitude); + public static Float2 operator +(Float2 a, Float2 b) => new(a.x + b.x, a.y + b.y); public static Float2 operator -(Float2 d) => new(-d.x, -d.y); public static Float2 operator -(Float2 a, Float2 b) => new(a.x - b.x, a.y - b.y); public static Float2 operator *(Float2 a, Float2 b) => new(a.x * b.x, a.y * b.y); public static Float2 operator *(Float2 a, float b) => new(a.x * b, a.y * b); + public static Float2 operator *(Float2 a, Matrix b) => (Float2)((Matrix)a * b); public static Float2 operator /(Float2 a, Float2 b) => new(a.x / b.x, a.y / b.y); public static Float2 operator /(Float2 a, float b) => new(a.x / b, a.y / b); + public static Float2 operator /(Float2 a, Matrix b) => (Float2)((Matrix)a / b); public static bool operator ==(Float2 a, Float2 b) => a.Equals(b); public static bool operator !=(Float2 a, Float2 b) => !a.Equals(b); public static bool operator >(Float2 a, Float2 b) => a.CompareTo(b) > 0; @@ -181,11 +192,15 @@ public struct Float2 : ICloneable, IComparable, IEquatable, IGro public static bool operator >=(Float2 a, Float2 b) => a == b || a > b; public static bool operator <=(Float2 a, Float2 b) => a == b || a < b; + public static implicit operator Float2(Complex val) => new(val.u, val.i); + public static explicit operator Float2(Quaternion val) => new(val.u, val.i); public static explicit operator Float2(Float3 val) => new(val.x, val.y); public static explicit operator Float2(Float4 val) => new(val.x, val.y); public static implicit operator Float2(Int2 val) => new(val.x, val.y); public static explicit operator Float2(Int3 val) => new(val.x, val.y); public static explicit operator Float2(Int4 val) => new(val.x, val.y); + public static explicit operator Float2(Matrix m) => new(m[0, 0], m[1, 0]); + public static explicit operator Float2(Vector2d val) => val.ToXYZ(); public static explicit operator Float2(Vert val) => new(val.position.x, val.position.y); public static implicit operator Float2(Fill fill) => new(fill); public static implicit operator Float2(Fill fill) => new(fill); diff --git a/Nerd_STF/Mathematics/Float3.cs b/Nerd_STF/Mathematics/Float3.cs index 4067b79..08dd48d 100644 --- a/Nerd_STF/Mathematics/Float3.cs +++ b/Nerd_STF/Mathematics/Float3.cs @@ -13,7 +13,7 @@ public struct Float3 : ICloneable, IComparable, IEquatable, IGro public static Float3 Zero => new(0, 0, 0); public float Magnitude => Mathf.Sqrt(x * x + y * y + z * z); - public Float3 Normalized => this / Magnitude; + public Float3 Normalized => this * Mathf.InverseSqrt(x * x + y * y + z * z); public Float2 XY => new(x, y); public Float2 XZ => new(x, z); @@ -138,6 +138,8 @@ public struct Float3 : ICloneable, IComparable, IEquatable, IGro foreach (Float3 d in vals) val *= d; return val; } + public static Float3 Round(Float3 val) => + new(Mathf.Round(val.x), Mathf.Round(val.y), Mathf.Round(val.z)); public static Float3 Subtract(Float3 num, params Float3[] vals) { foreach (Float3 d in vals) num -= d; @@ -165,7 +167,7 @@ public struct Float3 : ICloneable, IComparable, IEquatable, IGro public int CompareTo(Float3 other) => Magnitude.CompareTo(other.Magnitude); public override bool Equals([NotNullWhen(true)] object? obj) { - if (obj == null || obj.GetType() != typeof(Float3)) return false; + if (obj == null || obj.GetType() != typeof(Float3)) return base.Equals(obj); return Equals((Float3)obj); } public bool Equals(Float3 other) => x == other.x && y == other.y && z == other.z; @@ -187,15 +189,28 @@ public struct Float3 : ICloneable, IComparable, IEquatable, IGro } public float[] ToArray() => new[] { x, y, z }; + public Fill ToFill() + { + Float3 @this = this; + return i => @this[i]; + } public List ToList() => new() { x, y, z }; + public Vector3d ToVector() + { + float mag = Magnitude; + return new(new Angle(Mathf.ArcTan(y / x), Angle.Type.Radians), new(Mathf.ArcCos(z / mag), Angle.Type.Radians), mag); + } + public static Float3 operator +(Float3 a, Float3 b) => new(a.x + b.x, a.y + b.y, a.z + b.z); public static Float3 operator -(Float3 d) => new(-d.x, -d.y, -d.z); public static Float3 operator -(Float3 a, Float3 b) => new(a.x - b.x, a.y - b.y, a.z - b.z); public static Float3 operator *(Float3 a, Float3 b) => new(a.x * b.x, a.y * b.y, a.z * b.z); public static Float3 operator *(Float3 a, float b) => new(a.x * b, a.y * b, a.z * b); + public static Float3 operator *(Float3 a, Matrix b) => (Float3)((Matrix)a * b); public static Float3 operator /(Float3 a, Float3 b) => new(a.x / b.x, a.y / b.y, a.z / b.z); public static Float3 operator /(Float3 a, float b) => new(a.x / b, a.y / b, a.z / b); + public static Float3 operator /(Float3 a, Matrix b) => (Float3)((Matrix)a / b); public static bool operator ==(Float3 a, Float3 b) => a.Equals(b); public static bool operator !=(Float3 a, Float3 b) => !a.Equals(b); public static bool operator >(Float3 a, Float3 b) => a.CompareTo(b) > 0; @@ -203,11 +218,15 @@ public struct Float3 : ICloneable, IComparable, IEquatable, IGro public static bool operator >=(Float3 a, Float3 b) => a == b || a > b; public static bool operator <=(Float3 a, Float3 b) => a == b || a < b; + public static implicit operator Float3(Complex val) => new(val.u, val.i, 0); + public static explicit operator Float3(Quaternion val) => new(val.u, val.i, val.j); public static implicit operator Float3(Float2 val) => new(val.x, val.y, 0); public static explicit operator Float3(Float4 val) => new(val.x, val.y, val.z); public static implicit operator Float3(Int2 val) => new(val.x, val.y, 0); public static implicit operator Float3(Int3 val) => new(val.x, val.y, val.z); public static explicit operator Float3(Int4 val) => new(val.x, val.y, val.z); + public static explicit operator Float3(Matrix m) => new(m[0, 0], m[1, 0], m[2, 0]); + public static explicit operator Float3(Vector2d val) => val.ToXYZ(); public static implicit operator Float3(Vert val) => new(val.position.x, val.position.y, val.position.z); public static explicit operator Float3(RGBA val) => new(val.R, val.G, val.B); public static explicit operator Float3(HSVA val) => new(val.H.Normalized, val.S, val.V); diff --git a/Nerd_STF/Mathematics/Float4.cs b/Nerd_STF/Mathematics/Float4.cs index 22c471f..7c80274 100644 --- a/Nerd_STF/Mathematics/Float4.cs +++ b/Nerd_STF/Mathematics/Float4.cs @@ -3,11 +3,11 @@ public struct Float4 : ICloneable, IComparable, IEquatable, IGroup { public static Float4 Back => new(0, 0, -1, 0); - public static Float4 Deep => new(0, 0, 0, -1); public static Float4 Down => new(0, -1, 0, 0); public static Float4 Far => new(0, 0, 0, 1); public static Float4 Forward => new(0, 0, 1, 0); public static Float4 Left => new(-1, 0, 0, 0); + public static Float4 Near => new(0, 0, 0, -1); public static Float4 Right => new(1, 0, 0, 0); public static Float4 Up => new(0, 1, 0, 0); @@ -15,7 +15,7 @@ public struct Float4 : ICloneable, IComparable, IEquatable, IGro public static Float4 Zero => new(0, 0, 0, 0); public float Magnitude => Mathf.Sqrt(x * x + y * y + z * z + w * w); - public Float4 Normalized => this / Magnitude; + public Float4 Normalized => this * Mathf.InverseSqrt(x * x + y * y + z * z + w * w); public Float2 XY => new(x, y); public Float2 XZ => new(x, z); @@ -144,6 +144,8 @@ public struct Float4 : ICloneable, IComparable, IEquatable, IGro foreach (Float4 d in vals) val = d < val ? d : val; return val; } + public static Float4 Round(Float4 val) => + new(Mathf.Round(val.x), Mathf.Round(val.y), Mathf.Round(val.z), Mathf.Round(val.w)); public static Float4 Product(params Float4[] vals) { if (vals.Length < 1) return Zero; @@ -180,7 +182,7 @@ public struct Float4 : ICloneable, IComparable, IEquatable, IGro public int CompareTo(Float4 other) => Magnitude.CompareTo(other.Magnitude); public override bool Equals([NotNullWhen(true)] object? obj) { - if (obj == null || obj.GetType() != typeof(Float4)) return false; + if (obj == null || obj.GetType() != typeof(Float4)) return base.Equals(obj); return Equals((Float4)obj); } public bool Equals(Float4 other) => x == other.x && y == other.y && z == other.z && w == other.w; @@ -205,6 +207,11 @@ public struct Float4 : ICloneable, IComparable, IEquatable, IGro } public float[] ToArray() => new[] { x, y, z, w }; + public Fill ToFill() + { + Float4 @this = this; + return i => @this[i]; + } public List ToList() => new() { x, y, z, w }; public static Float4 operator +(Float4 a, Float4 b) => new(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); @@ -212,8 +219,10 @@ public struct Float4 : ICloneable, IComparable, IEquatable, IGro public static Float4 operator -(Float4 a, Float4 b) => new(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); public static Float4 operator *(Float4 a, Float4 b) => new(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); public static Float4 operator *(Float4 a, float b) => new(a.x * b, a.y * b, a.z * b, a.w * b); + public static Float4 operator *(Float4 a, Matrix b) => (Float4)((Matrix)a * b); public static Float4 operator /(Float4 a, Float4 b) => new(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w); public static Float4 operator /(Float4 a, float b) => new(a.x / b, a.y / b, a.z / b, a.w / b); + public static Float4 operator /(Float4 a, Matrix b) => (Float4)((Matrix)a / b); public static bool operator ==(Float4 a, Float4 b) => a.Equals(b); public static bool operator !=(Float4 a, Float4 b) => !a.Equals(b); public static bool operator >(Float4 a, Float4 b) => a.CompareTo(b) > 0; @@ -221,11 +230,15 @@ public struct Float4 : ICloneable, IComparable, IEquatable, IGro public static bool operator >=(Float4 a, Float4 b) => a == b || a > b; public static bool operator <=(Float4 a, Float4 b) => a == b || a < b; + public static implicit operator Float4(Complex val) => new(val.u, val.i, 0, 0); + public static implicit operator Float4(Quaternion val) => new(val.u, val.i, val.j, val.k); public static implicit operator Float4(Float2 val) => new(val.x, val.y, 0, 0); public static implicit operator Float4(Float3 val) => new(val.x, val.y, val.z, 0); public static implicit operator Float4(Int2 val) => new(val.x, val.y, 0, 0); public static implicit operator Float4(Int3 val) => new(val.x, val.y, val.z, 0); public static implicit operator Float4(Int4 val) => new(val.x, val.y, val.z, val.w); + public static explicit operator Float4(Matrix m) => new(m[0, 0], m[1, 0], m[2, 0], m[3, 0]); + public static explicit operator Float4(Vector2d val) => val.ToXYZ(); public static implicit operator Float4(Vert val) => new(val.position.x, val.position.y, val.position.z, 0); public static implicit operator Float4(RGBA val) => new(val.R, val.G, val.B, val.A); public static explicit operator Float4(CMYKA val) => new(val.C, val.M, val.Y, val.K); diff --git a/Nerd_STF/Mathematics/Geometry/Box2D.cs b/Nerd_STF/Mathematics/Geometry/Box2D.cs index 0ba2df7..b938c2f 100644 --- a/Nerd_STF/Mathematics/Geometry/Box2D.cs +++ b/Nerd_STF/Mathematics/Geometry/Box2D.cs @@ -86,7 +86,7 @@ public struct Box2D : ICloneable, IContainer, IEquatable public override bool Equals([NotNullWhen(true)] object? obj) { - if (obj == null || obj.GetType() != typeof(Box2D)) return false; + if (obj == null || obj.GetType() != typeof(Box2D)) return base.Equals(obj); return Equals((Box2D)obj); } public bool Equals(Box2D other) => center == other.center && size == other.size; diff --git a/Nerd_STF/Mathematics/Geometry/Box3D.cs b/Nerd_STF/Mathematics/Geometry/Box3D.cs index 62feb64..7930bff 100644 --- a/Nerd_STF/Mathematics/Geometry/Box3D.cs +++ b/Nerd_STF/Mathematics/Geometry/Box3D.cs @@ -87,7 +87,7 @@ public struct Box3D : ICloneable, IContainer, IEquatable public override bool Equals([NotNullWhen(true)] object? obj) { - if (obj == null || obj.GetType() != typeof(Box3D)) return false; + if (obj == null || obj.GetType() != typeof(Box3D)) return base.Equals(obj); return Equals((Box3D)obj); } public bool Equals(Box3D other) => center == other.center && size == other.size; diff --git a/Nerd_STF/Mathematics/Geometry/Line.cs b/Nerd_STF/Mathematics/Geometry/Line.cs index f117a7e..b76242e 100644 --- a/Nerd_STF/Mathematics/Geometry/Line.cs +++ b/Nerd_STF/Mathematics/Geometry/Line.cs @@ -98,7 +98,7 @@ public struct Line : ICloneable, IClosest, IComparable, IContainer a == other.a && b == other.b; @@ -161,12 +161,17 @@ public struct Line : ICloneable, IClosest, IComparable, IContainer new Vert[] { a, b }; + public Fill ToFill() + { + Line @this = this; + return i => @this[i]; + } public List ToList() => new() { a, b }; public float[] ToFloatArray() => new float[] { a.position.x, a.position.y, a.position.z, - b.position.x, b.position.y, b.position.z }; + b.position.x, b.position.y, b.position.z }; public List ToFloatList() => new() { a.position.x, a.position.y, a.position.z, - b.position.x, b.position.y, b.position.z }; + b.position.x, b.position.y, b.position.z }; public static Line operator +(Line a, Line b) => new(a.a + b.a, a.b + b.b); public static Line operator +(Line a, Vert b) => new(a.a + b, a.b + b); diff --git a/Nerd_STF/Mathematics/Geometry/Polygon.cs b/Nerd_STF/Mathematics/Geometry/Polygon.cs index 290f8bc..006a9f1 100644 --- a/Nerd_STF/Mathematics/Geometry/Polygon.cs +++ b/Nerd_STF/Mathematics/Geometry/Polygon.cs @@ -256,7 +256,7 @@ public struct Polygon : ICloneable, IEquatable, IGroup, ISubdivid public override bool Equals([NotNullWhen(true)] object? obj) { - if (obj == null || obj.GetType() != typeof(Polygon)) return false; + if (obj == null || obj.GetType() != typeof(Polygon)) return base.Equals(obj); return Equals((Polygon)obj); } public bool Equals(Polygon other) @@ -285,6 +285,11 @@ public struct Polygon : ICloneable, IEquatable, IGroup, ISubdivid public IEnumerator GetEnumerator() { foreach (Vert v in Verts) yield return v; } public Vert[] ToArray() => Verts; + public Fill ToFill() + { + Polygon @this = this; + return i => @this[i]; + } public List ToList() => new(Verts); public float[] ToFloatArray() diff --git a/Nerd_STF/Mathematics/Geometry/Quadrilateral.cs b/Nerd_STF/Mathematics/Geometry/Quadrilateral.cs index 8ee6be2..ebf43b4 100644 --- a/Nerd_STF/Mathematics/Geometry/Quadrilateral.cs +++ b/Nerd_STF/Mathematics/Geometry/Quadrilateral.cs @@ -266,7 +266,7 @@ public struct Quadrilateral : ICloneable, IEquatable, IGroup A == other.A && B == other.B && C == other.C && D == other.D; @@ -289,6 +289,11 @@ public struct Quadrilateral : ICloneable, IEquatable, IGroup new Vert[] { A, B, C, D }; + public Fill ToFill() + { + Quadrilateral @this = this; + return i => @this[i]; + } public List ToList() => new() { A, B, C, D }; public float[] ToFloatArray() => new float[] { A.position.x, A.position.y, A.position.z, diff --git a/Nerd_STF/Mathematics/Geometry/Sphere.cs b/Nerd_STF/Mathematics/Geometry/Sphere.cs index 39668ff..850542e 100644 --- a/Nerd_STF/Mathematics/Geometry/Sphere.cs +++ b/Nerd_STF/Mathematics/Geometry/Sphere.cs @@ -69,11 +69,11 @@ public struct Sphere : ICloneable, IClosest, IComparable, ICompara public override bool Equals([NotNullWhen(true)] object? obj) { - if (obj == null) return false; + if (obj == null) return base.Equals(obj); Type type = obj.GetType(); if (type == typeof(Sphere)) return Equals((Sphere)obj); if (type == typeof(float)) return Equals((float)obj); - return false; + return base.Equals(obj); } public bool Equals(float other) => Volume == other; public bool Equals(Sphere other) => center == other.center && radius == other.radius; diff --git a/Nerd_STF/Mathematics/Geometry/Triangle.cs b/Nerd_STF/Mathematics/Geometry/Triangle.cs index 39da3a1..b9464c5 100644 --- a/Nerd_STF/Mathematics/Geometry/Triangle.cs +++ b/Nerd_STF/Mathematics/Geometry/Triangle.cs @@ -218,7 +218,7 @@ public struct Triangle : ICloneable, IEquatable, IGroup public override bool Equals([NotNullWhen(true)] object? obj) { - if (obj == null || obj.GetType() != typeof(Triangle)) return false; + if (obj == null || obj.GetType() != typeof(Triangle)) return base.Equals(obj); return Equals((Triangle)obj); } public bool Equals(Triangle other) => A == other.A && B == other.B && C == other.C; @@ -240,14 +240,19 @@ public struct Triangle : ICloneable, IEquatable, IGroup } public Vert[] ToArray() => new Vert[] { A, B, C }; + public Fill ToFill() + { + Triangle @this = this; + return i => @this[i]; + } public List ToList() => new() { A, B, C }; public float[] ToFloatArray() => new float[] { A.position.x, A.position.y, A.position.z, - B.position.x, B.position.y, B.position.z, - C.position.x, C.position.y, C.position.z }; + B.position.x, B.position.y, B.position.z, + C.position.x, C.position.y, C.position.z }; public List ToFloatList() => new() { A.position.x, A.position.y, A.position.z, - B.position.x, B.position.y, B.position.z, - C.position.x, C.position.y, C.position.z }; + B.position.x, B.position.y, B.position.z, + C.position.x, C.position.y, C.position.z }; public static Triangle operator +(Triangle a, Triangle b) => new(a.A + b.A, a.B + b.B, a.C + b.C); public static Triangle operator +(Triangle a, Vert b) => new(a.A + b, a.B + b, a.C + b); public static Triangle operator -(Triangle t) => new(-t.A, -t.B, -t.C); diff --git a/Nerd_STF/Mathematics/Geometry/Vert.cs b/Nerd_STF/Mathematics/Geometry/Vert.cs index 1c40344..6609ff7 100644 --- a/Nerd_STF/Mathematics/Geometry/Vert.cs +++ b/Nerd_STF/Mathematics/Geometry/Vert.cs @@ -17,7 +17,7 @@ public struct Vert : ICloneable, IEquatable, IGroup public Float3 position; - public Vert(Float2 pos) : this(pos.x, pos.y, 0) { } + public Vert(Float2 pos) : this((Float3)pos) { } public Vert(Float3 pos) => position = pos; public Vert(float x, float y) : this(new Float2(x, y)) { } public Vert(float x, float y, float z) : this(new Float3(x, y, z)) { } @@ -61,7 +61,7 @@ public struct Vert : ICloneable, IEquatable, IGroup public override bool Equals([NotNullWhen(true)] object? obj) { - if (obj == null || obj.GetType() != typeof(Vert)) return false; + if (obj == null || obj.GetType() != typeof(Vert)) return base.Equals(obj); return Equals((Vert)obj); } public bool Equals(Vert other) => position == other.position; @@ -76,8 +76,15 @@ public struct Vert : ICloneable, IEquatable, IGroup public IEnumerator GetEnumerator() => position.GetEnumerator(); public float[] ToArray() => position.ToArray(); + public Fill ToFill() + { + Vert @this = this; + return i => @this[i]; + } public List ToList() => position.ToList(); + public Vector3d ToVector() => ((Float3)this).ToVector(); + public static Vert operator +(Vert a, Vert b) => new(a.position + b.position); public static Vert operator -(Vert d) => new(-d.position); public static Vert operator -(Vert a, Vert b) => new(a.position - b.position); diff --git a/Nerd_STF/Mathematics/Int2.cs b/Nerd_STF/Mathematics/Int2.cs index 9841fa6..ce62c13 100644 --- a/Nerd_STF/Mathematics/Int2.cs +++ b/Nerd_STF/Mathematics/Int2.cs @@ -11,7 +11,7 @@ public struct Int2 : ICloneable, IComparable, IEquatable, IGroup new(0, 0); public float Magnitude => Mathf.Sqrt(x * x + y * y); - public Int2 Normalized => (Int2)((Float2)this / Magnitude); + public Int2 Normalized => (Int2)((Float2)this * Mathf.InverseSqrt(x * x + y * y)); public int x, y; @@ -139,7 +139,7 @@ public struct Int2 : ICloneable, IComparable, IEquatable, IGroup Magnitude.CompareTo(other.Magnitude); public override bool Equals([NotNullWhen(true)] object? obj) { - if (obj == null || obj.GetType() != typeof(Int2)) return false; + if (obj == null || obj.GetType() != typeof(Int2)) return base.Equals(obj); return Equals((Int2)obj); } public bool Equals(Int2 other) => x == other.x && y == other.y; @@ -160,15 +160,24 @@ public struct Int2 : ICloneable, IComparable, IEquatable, IGroup new[] { x, y }; + public Fill ToFill() + { + Int2 @this = this; + return i => @this[i]; + } public List ToList() => new() { x, y }; + public Vector2d ToVector() => ((Float2)this).ToVector(); + public static Int2 operator +(Int2 a, Int2 b) => new(a.x + b.x, a.y + b.y); public static Int2 operator -(Int2 i) => new(-i.x, -i.y); public static Int2 operator -(Int2 a, Int2 b) => new(a.x - b.x, a.y - b.y); public static Int2 operator *(Int2 a, Int2 b) => new(a.x * b.x, a.y * b.y); public static Int2 operator *(Int2 a, int b) => new(a.x * b, a.y * b); + public static Int2 operator *(Int2 a, Matrix b) => (Int2)((Matrix)(Float2)a * b); public static Int2 operator /(Int2 a, Int2 b) => new(a.x / b.x, a.y / b.y); public static Int2 operator /(Int2 a, int b) => new(a.x / b, a.y / b); + public static Int2 operator /(Int2 a, Matrix b) => (Int2)((Matrix)(Float2)a / b); public static Int2 operator &(Int2 a, Int2 b) => new(a.x & b.x, a.y & b.y); public static Int2 operator |(Int2 a, Int2 b) => new(a.x | b.x, a.y | b.y); public static Int2 operator ^(Int2 a, Int2 b) => new(a.x ^ b.x, a.y ^ b.y); @@ -179,9 +188,13 @@ public struct Int2 : ICloneable, IComparable, IEquatable, IGroup=(Int2 a, Int2 b) => a == b || a > b; public static bool operator <=(Int2 a, Int2 b) => a == b || a < b; + public static explicit operator Int2(Complex val) => new((int)val.u, (int)val.i); + public static explicit operator Int2(Quaternion val) => new((int)val.u, (int)val.i); public static explicit operator Int2(Float2 val) => new((int)val.x, (int)val.y); public static explicit operator Int2(Float3 val) => new((int)val.x, (int)val.y); public static explicit operator Int2(Float4 val) => new((int)val.x, (int)val.y); + public static explicit operator Int2(Matrix m) => new((int)m[0, 0], (int)m[1, 0]); + public static explicit operator Int2(Vector2d val) => (Int2)val.ToXYZ(); public static explicit operator Int2(Int3 val) => new(val.x, val.y); public static explicit operator Int2(Int4 val) => new(val.x, val.y); public static explicit operator Int2(Vert val) => new((int)val.position.x, (int)val.position.y); diff --git a/Nerd_STF/Mathematics/Int3.cs b/Nerd_STF/Mathematics/Int3.cs index 0567940..e023215 100644 --- a/Nerd_STF/Mathematics/Int3.cs +++ b/Nerd_STF/Mathematics/Int3.cs @@ -13,7 +13,7 @@ public struct Int3 : ICloneable, IComparable, IEquatable, IGroup new(0, 0, 0); public float Magnitude => Mathf.Sqrt(x * x + y * y + z * z); - public Int3 Normalized => (Int3)((Float3)this / Magnitude); + public Int3 Normalized => (Int3)((Float3)this * Mathf.InverseSqrt(x * x + y * y + z * z)); public Int2 XY => new(x, y); public Int2 XZ => new(x, z); @@ -159,7 +159,7 @@ public struct Int3 : ICloneable, IComparable, IEquatable, IGroup Magnitude.CompareTo(other.Magnitude); public override bool Equals([NotNullWhen(true)] object? obj) { - if (obj == null || obj.GetType() != typeof(Int3)) return false; + if (obj == null || obj.GetType() != typeof(Int3)) return base.Equals(obj); return Equals((Int3)obj); } public bool Equals(Int3 other) => x == other.x && y == other.y && z == other.z; @@ -179,15 +179,24 @@ public struct Int3 : ICloneable, IComparable, IEquatable, IGroup new[] { x, y, z }; + public Fill ToFill() + { + Int3 @this = this; + return i => @this[i]; + } public List ToList() => new() { x, y, z }; + public Vector3d ToVector() => ((Float3)this).ToVector(); + public static Int3 operator +(Int3 a, Int3 b) => new(a.x + b.x, a.y + b.y, a.z + b.z); public static Int3 operator -(Int3 i) => new(-i.x, -i.y, -i.z); public static Int3 operator -(Int3 a, Int3 b) => new(a.x - b.x, a.y - b.y, a.z - b.z); public static Int3 operator *(Int3 a, Int3 b) => new(a.x * b.x, a.y * b.y, a.z * b.z); public static Int3 operator *(Int3 a, int b) => new(a.x * b, a.y * b, a.z * b); + public static Int3 operator *(Int3 a, Matrix b) => (Int3)((Matrix)(Float3)a * b); public static Int3 operator /(Int3 a, Int3 b) => new(a.x / b.x, a.y / b.y, a.z / b.z); public static Int3 operator /(Int3 a, int b) => new(a.x / b, a.y / b, a.z / b); + public static Int3 operator /(Int3 a, Matrix b) => (Int3)((Matrix)(Float3)a / b); public static Int3 operator &(Int3 a, Int3 b) => new(a.x & b.x, a.y & b.y, a.z & b.z); public static Int3 operator |(Int3 a, Int3 b) => new(a.x | b.x, a.y | b.y, a.z | b.z); public static Int3 operator ^(Int3 a, Int3 b) => new(a.x ^ b.x, a.y ^ b.y, a.z ^ b.z); @@ -198,11 +207,15 @@ public struct Int3 : ICloneable, IComparable, IEquatable, IGroup=(Int3 a, Int3 b) => a == b || a > b; public static bool operator <=(Int3 a, Int3 b) => a == b || a < b; + public static explicit operator Int3(Complex val) => new((int)val.u, (int)val.i, 0); + public static explicit operator Int3(Quaternion val) => new((int)val.u, (int)val.i, (int)val.j); public static explicit operator Int3(Float2 val) => new((int)val.x, (int)val.y, 0); public static explicit operator Int3(Float3 val) => new((int)val.x, (int)val.y, (int)val.z); public static explicit operator Int3(Float4 val) => new((int)val.x, (int)val.y, (int)val.z); public static implicit operator Int3(Int2 val) => new(val.x, val.y, 0); public static explicit operator Int3(Int4 val) => new(val.x, val.y, val.z); + public static explicit operator Int3(Matrix m) => new((int)m[0, 0], (int)m[1, 0], (int)m[2, 0]); + public static explicit operator Int3(Vector2d val) => (Int3)val.ToXYZ(); public static explicit operator Int3(Vert val) => new((int)val.position.x, (int)val.position.y, (int)val.position.z); public static explicit operator Int3(RGBA val) => (Int3)val.ToRGBAByte(); diff --git a/Nerd_STF/Mathematics/Int4.cs b/Nerd_STF/Mathematics/Int4.cs index 2157e4f..2007bbc 100644 --- a/Nerd_STF/Mathematics/Int4.cs +++ b/Nerd_STF/Mathematics/Int4.cs @@ -15,7 +15,7 @@ public struct Int4 : ICloneable, IComparable, IEquatable, IGroup new(0, 0, 0, 0); public float Magnitude => Mathf.Sqrt(x * x + y * y + z * z + w * w); - public Int4 Normalized => (Int4)((Float4)this / Magnitude); + public Int4 Normalized => (Int4)((Float4)this * Mathf.InverseSqrt(x * x + y * y + z * z + w * w)); public Int2 XY => new(x, y); public Int2 XZ => new(x, z); @@ -175,7 +175,7 @@ public struct Int4 : ICloneable, IComparable, IEquatable, IGroup Magnitude.CompareTo(other.Magnitude); public override bool Equals([NotNullWhen(true)] object? obj) { - if (obj == null || obj.GetType() != typeof(Int4)) return false; + if (obj == null || obj.GetType() != typeof(Int4)) return base.Equals(obj); return Equals((Int4)obj); } public bool Equals(Int4 other) => x == other.x && y == other.y && z == other.z && w == other.w; @@ -200,6 +200,11 @@ public struct Int4 : ICloneable, IComparable, IEquatable, IGroup new[] { x, y, z, w }; + public Fill ToFill() + { + Int4 @this = this; + return i => @this[i]; + } public List ToList() => new() { x, y, z, w }; public static Int4 operator +(Int4 a, Int4 b) => new(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); @@ -207,8 +212,10 @@ public struct Int4 : ICloneable, IComparable, IEquatable, IGroup new(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); public static Int4 operator *(Int4 a, Int4 b) => new(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); public static Int4 operator *(Int4 a, int b) => new(a.x * b, a.y * b, a.z * b, a.w * b); + public static Int4 operator *(Int4 a, Matrix b) => (Int4)((Matrix)(Float4)a * b); public static Int4 operator /(Int4 a, Int4 b) => new(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w); public static Int4 operator /(Int4 a, int b) => new(a.x / b, a.y / b, a.z / b, a.w / b); + public static Int4 operator /(Int4 a, Matrix b) => (Int4)((Matrix)(Float4)a / b); public static Int4 operator &(Int4 a, Int4 b) => new(a.x & b.x, a.y & b.y, a.z & b.z, a.w & b.w); public static Int4 operator |(Int4 a, Int4 b) => new(a.x | b.x, a.y | b.y, a.z | b.z, a.w | b.w); public static Int4 operator ^(Int4 a, Int4 b) => new(a.x ^ b.x, a.y ^ b.y, a.z ^ b.z, a.w ^ b.w); @@ -219,11 +226,15 @@ public struct Int4 : ICloneable, IComparable, IEquatable, IGroup=(Int4 a, Int4 b) => a == b || a > b; public static bool operator <=(Int4 a, Int4 b) => a == b || a < b; + public static explicit operator Int4(Complex val) => new((int)val.u, (int)val.i, 0, 0); + public static explicit operator Int4(Quaternion val) => new((int)val.u, (int)val.i, (int)val.j, (int)val.k); public static explicit operator Int4(Float2 val) => new((int)val.x, (int)val.y, 0, 0); public static explicit operator Int4(Float3 val) => new((int)val.x, (int)val.y, (int)val.z, 0); public static explicit operator Int4(Float4 val) => new((int)val.x, (int)val.y, (int)val.z, (int)val.w); public static implicit operator Int4(Int2 val) => new(val.x, val.y, 0, 0); public static implicit operator Int4(Int3 val) => new(val.x, val.y, val.z, 0); + public static explicit operator Int4(Matrix m) => new((int)m[0, 0], (int)m[1, 0], (int)m[2, 0], (int)m[3, 0]); + public static explicit operator Int4(Vector2d val) => (Int4)val.ToXYZ(); public static explicit operator Int4(Vert val) => new((int)val.position.x, (int)val.position.y, (int)val.position.z, 0); public static explicit operator Int4(RGBA val) => val.ToRGBAByte(); diff --git a/Nerd_STF/Mathematics/Mathf.cs b/Nerd_STF/Mathematics/Mathf.cs index 30337ca..ec73c8a 100644 --- a/Nerd_STF/Mathematics/Mathf.cs +++ b/Nerd_STF/Mathematics/Mathf.cs @@ -17,6 +17,7 @@ public static class Mathf public static float ArcSin(float value) => (float)Math.Asin(value); public static float ArcTan(float value) => ArcSin(value / Sqrt(1 + value * value)); + public static float ArcTan2(float a, float b) => ArcTan(a / b); public static float Average(Equation equ, float min, float max, float step = Calculus.DefaultStep) { @@ -50,10 +51,13 @@ public static class Mathf public static int Combinations(int total, int size) => Factorial(total) / (Factorial(size) * Factorial(total - size)); + public static float Cos(Angle angle) => Cos(angle.Radians); public static float Cos(float radians) => Sin(radians + Constants.HalfPi); + public static float Cot(Angle angle) => Cot(angle.Radians); public static float Cot(float radians) => Cos(radians) / Sin(radians); + public static float Csc(Angle angle) => Csc(angle.Radians); public static float Csc(float radians) => 1 / Sin(radians); public static float Divide(float val, params float[] dividends) @@ -67,6 +71,25 @@ public static class Mathf return val; } + public static float Dot(float[] a, float[] b) + { + if (a.Length != b.Length) throw new InvalidSizeException("Both arrays must have the same length"); + float[] vals = new float[a.Length]; + for (int i = 0; i < a.Length; i++) vals[i] = a[i] * b[i]; + return Sum(vals); + } + public static float Dot(params float[][] vals) + { + float[] res = new float[vals[0].Length]; + for (int i = 0; i < res.Length; i++) + { + float m = 1; + for (int j = 0; j < vals.Length; j++) m *= vals[j][i]; + res[i] = m; + } + return Sum(res); + } + public static int Factorial(int amount) { if (amount < 0) return 0; @@ -147,6 +170,13 @@ public static class Mathf foreach (int i in vals) val = i > val ? i : val; return val; } + public static T? Max(params T[] vals) where T : IComparable + { + if (vals.Length < 1) return default; + T val = vals[0]; + foreach (T t in vals) val = t.CompareTo(val) > 0 ? t : val; + return val; + } public static float Median(params float[] vals) { @@ -154,7 +184,8 @@ public static class Mathf float valA = vals[Floor(index)], valB = vals[Ceiling(index)]; return Average(valA, valB); } - public static int Median(params int[] vals) => vals[Floor(Average(0, vals.Length - 1))]; + public static int Median(params int[] vals) => Median(vals); + public static T Median(params T[] vals) => vals[Floor(Average(0, vals.Length - 1))]; public static float Min(Equation equ, float min, float max, float step = Calculus.DefaultStep) { @@ -180,6 +211,13 @@ public static class Mathf foreach (int i in vals) val = i < val ? i : val; return val; } + public static T? Min(params T[] vals) where T : IComparable + { + if (vals.Length < 1) return default; + T val = vals[0]; + foreach (T t in vals) val = t.CompareTo(val) < 0 ? t : val; + return val; + } public static (T value, int occurences) Mode(params T[] vals) where T : IEquatable { @@ -245,8 +283,10 @@ public static class Mathf public static float Round(float num, float nearest) => nearest * Round(num / nearest); public static int RoundInt(float num) => (int)Round(num); + public static float Sec(Angle angle) => Sec(angle.Radians); public static float Sec(float radians) => 1 / Cos(radians); + public static float Sin(Angle angle) => Sin(angle.Radians); public static float Sin(float radians) { // Really close polynomial to sin(x) (when modded by 2pi). RMSE of 0.000003833 @@ -303,6 +343,7 @@ public static class Mathf // Known as stdev public static float StandardDeviation(params float[] vals) => Sqrt(Variance(vals)); + public static float Tan(Angle angle) => Tan(angle.Radians); public static float Tan(float radians) => Sin(radians) / Cos(radians); public static T[] UniqueItems(params T[] vals) where T : IEquatable diff --git a/Nerd_STF/Mathematics/NumberSystems/Complex.cs b/Nerd_STF/Mathematics/NumberSystems/Complex.cs new file mode 100644 index 0000000..94a2bc5 --- /dev/null +++ b/Nerd_STF/Mathematics/NumberSystems/Complex.cs @@ -0,0 +1,193 @@ +namespace Nerd_STF.Mathematics.NumberSystems; + +public struct Complex : ICloneable, IComparable, IEquatable, IGroup +{ + public static Complex Down => new(0, -1); + public static Complex Left => new(-1, 0); + public static Complex Right => new(1, 0); + public static Complex Up => new(0, 1); + + public static Complex One => new(1, 1); + public static Complex Zero => new(0, 0); + + public Complex Conjugate => new(u, -i); + public float Magnitude => Mathf.Sqrt(u * u + i * i); + public Complex Normalized => this * Mathf.InverseSqrt(u * u + i * i); + + public float u, i; + + public Complex(float all) : this(all, all) { } + public Complex(float u, float i) + { + this.u = u; + this.i = i; + } + public Complex(Fill fill) : this(fill(0), fill(1)) { } + public Complex(Fill fill) : this(fill(0), fill(1)) { } + + public float this[int index] + { + get => index switch + { + 0 => u, + 1 => i, + _ => throw new IndexOutOfRangeException(nameof(index)), + }; + set + { + switch (index) + { + case 0: + u = value; + break; + + case 1: + i = value; + break; + + default: throw new IndexOutOfRangeException(nameof(index)); + } + } + } + + public static Complex Absolute(Complex val) => Float2.Absolute(val); + public static Complex Average(params Complex[] vals) + { + List floats = new(); + foreach (Complex c in vals) floats.Add(c); + return Float2.Average(floats.ToArray()); + } + public static Complex Ceiling(Complex val) => Float2.Ceiling(val); + public static Complex Clamp(Complex val, Complex min, Complex max) => Float2.Clamp(val, min, max); + public static Complex ClampMagnitude(Complex val, float minMag, float maxMag) => + Float2.ClampMagnitude(val, minMag, maxMag); + public static Complex Divide(Complex num, params Complex[] vals) + { + List floats = new(); + foreach (Complex c in vals) floats.Add(c); + return Float2.Divide(num, floats.ToArray()); + } + public static float Dot(Complex a, Complex b) => Float2.Dot(a, b); + public static float Dot(params Complex[] vals) + { + List floats = new(); + foreach (Complex c in vals) floats.Add(c); + return Float2.Dot(floats.ToArray()); + } + public static Complex Floor(Complex val) => Float2.Floor(val); + public static Complex Lerp(Complex a, Complex b, float t, bool clamp = true) => Float2.Lerp(a, b, t, clamp); + public static Complex Median(params Complex[] vals) + { + List floats = new(); + foreach (Complex c in vals) floats.Add(c); + return Float2.Median(floats.ToArray()); + } + public static Complex Max(params Complex[] vals) + { + List floats = new(); + foreach (Complex c in vals) floats.Add(c); + return Float2.Max(floats.ToArray()); + } + public static Complex Min(params Complex[] vals) + { + List floats = new(); + foreach (Complex c in vals) floats.Add(c); + return Float2.Min(floats.ToArray()); + } + public static Complex Product(params Complex[] vals) + { + List floats = new(); + foreach (Complex c in vals) floats.Add(c); + return Float2.Product(floats.ToArray()); + } + public static Complex Round(Complex val) => Float2.Round(val); + public static Complex Subtract(Complex num, params Complex[] vals) + { + List floats = new(); + foreach (Complex c in vals) floats.Add(c); + return Float2.Subtract(num, floats.ToArray()); + } + public static Complex Sum(params Complex[] vals) + { + List floats = new(); + foreach (Complex c in vals) floats.Add(c); + return Float2.Sum(floats.ToArray()); + } + + public static (float[] Us, float[] Is) SplitArray(params Complex[] vals) + { + float[] Us = new float[vals.Length], Is = new float[vals.Length]; + for (int i = 0; i < vals.Length; i++) + { + Us[i] = vals[i].u; + Is[i] = vals[i].i; + } + return (Us, Is); + } + + public int CompareTo(Complex other) => Magnitude.CompareTo(other.Magnitude); + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj == null || obj.GetType() != typeof(Complex)) return base.Equals(obj); + return Equals((Complex)obj); + } + public bool Equals(Complex other) => u == other.u && i == other.i; + public override int GetHashCode() => u.GetHashCode() ^ i.GetHashCode(); + public override string ToString() => ToString((string?)null); + public string ToString(string? provider) => + u.ToString(provider) + (i >= 0 ? " + " : " - ") + i.ToString(provider) + "i"; + public string ToString(IFormatProvider provider) => + u.ToString(provider) + (i >= 0 ? " + " : " - ") + i.ToString(provider) + "i"; + + public object Clone() => new Complex(u, i); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public IEnumerator GetEnumerator() + { + yield return u; + yield return i; + } + + public float[] ToArray() => new[] { u, i }; + public Fill ToFill() + { + Complex @this = this; + return i => @this[i]; + } + public List ToList() => new() { u, i }; + + public Vector2d ToVector() => ((Float2)this).ToVector(); + + public static Complex operator +(Complex a, Complex b) => new(a.u + b.u, a.i + b.i); + public static Complex operator -(Complex c) => new(-c.u, -c.i); + public static Complex operator -(Complex a, Complex b) => new(a.u - b.u, a.i - b.i); + public static Complex operator *(Complex a, Complex b) => new(a.u * b.u - a.i * b.i, a.u * b.i + a.i * b.u); + public static Complex operator *(Complex a, float b) => new(a.u * b, a.i * b); + public static Complex operator *(Complex a, Matrix b) => (Complex)((Matrix)a * b); + public static Complex operator /(Complex a, Complex b) + { + float c = b.u * b.u + b.i * b.i; + return new((a.u * b.u + a.i * b.i) / c, (a.i * b.u - a.u * b.i) / c); + } + public static Complex operator /(Complex a, float b) => new(a.u / b, a.i / b); + public static Complex operator /(Complex a, Matrix b) => (Complex)((Matrix)a / b); + public static bool operator ==(Complex a, Complex b) => a.Equals(b); + public static bool operator !=(Complex a, Complex b) => !a.Equals(b); + public static bool operator >(Complex a, Complex b) => a.CompareTo(b) > 0; + public static bool operator <(Complex a, Complex b) => a.CompareTo(b) < 0; + public static bool operator >=(Complex a, Complex b) => a == b || a > b; + public static bool operator <=(Complex a, Complex b) => a == b || a < b; + + public static explicit operator Complex(Quaternion val) => new(val.u, val.i); + public static implicit operator Complex(Float2 val) => new(val.x, val.y); + public static explicit operator Complex(Float3 val) => new(val.x, val.y); + public static explicit operator Complex(Float4 val) => new(val.x, val.y); + public static implicit operator Complex(Int2 val) => new(val.x, val.y); + public static explicit operator Complex(Int3 val) => new(val.x, val.y); + public static explicit operator Complex(Int4 val) => new(val.x, val.y); + public static explicit operator Complex(Matrix m) => new(m[0, 0], m[1, 0]); + public static explicit operator Complex(Vector2d val) => val.ToXYZ(); + public static explicit operator Complex(Vert val) => new(val.position.x, val.position.y); + public static implicit operator Complex(Fill fill) => new(fill); + public static implicit operator Complex(Fill fill) => new(fill); +} diff --git a/Nerd_STF/Mathematics/NumberSystems/Quaternion.cs b/Nerd_STF/Mathematics/NumberSystems/Quaternion.cs new file mode 100644 index 0000000..7ef32ec --- /dev/null +++ b/Nerd_STF/Mathematics/NumberSystems/Quaternion.cs @@ -0,0 +1,309 @@ +namespace Nerd_STF.Mathematics.NumberSystems; + +public struct Quaternion : ICloneable, IComparable, IEquatable, IGroup +{ + public static Quaternion Back => new(0, 0, -1, 0); + public static Quaternion Down => new(0, -1, 0, 0); + public static Quaternion Far => new(0, 0, 0, 1); + public static Quaternion Forward => new(0, 0, 1, 0); + public static Quaternion Left => new(-1, 0, 0, 0); + public static Quaternion Near => new(0, 0, 0, -1); + public static Quaternion Right => new(1, 0, 0, 0); + public static Quaternion Up => new(0, 1, 0, 0); + + public static Quaternion One => new(1, 1, 1, 1); + public static Quaternion Zero => new(0, 0, 0, 0); + + public Quaternion Conjugate => new(u, -i, -j, -k); + public float Magnitude => Mathf.Sqrt(u * u + i * i + j * j + k * k); + public Quaternion Normalized => this * Mathf.InverseSqrt(u * u + i * i + j * j + k * k); + + public Float3 IJK => new(i, j, k); + + public float u, i, j, k; + + public Quaternion(float all) : this(all, all, all, all) { } + public Quaternion(float i, float j, float k) : this(0, i, j, k) { } + public Quaternion(Float3 ijk) : this(0, ijk.x, ijk.y, ijk.z) { } + public Quaternion(float u, Float3 ijk) : this(u, ijk.x, ijk.y, ijk.z) { } + public Quaternion(float u, float i, float j, float k) + { + this.u = u; + this.i = i; + this.j = j; + this.k = k; + } + public Quaternion(Fill fill) : this(fill(0), fill(1), fill(2)) { } + public Quaternion(Fill fill) : this(fill(0), fill(1), fill(2)) { } + + public float this[int index] + { + get => index switch + { + 0 => u, + 1 => i, + 2 => j, + 3 => k, + _ => throw new IndexOutOfRangeException(nameof(index)), + }; + set + { + switch (index) + { + case 0: + u = value; + break; + + case 1: + i = value; + break; + + case 2: + j = value; + break; + + case 3: + k = value; + break; + + default: throw new IndexOutOfRangeException(nameof(index)); + } + } + } + + public static Quaternion Absolute(Quaternion val) => Float4.Absolute(val); + public static Quaternion Average(params Quaternion[] vals) + { + List floats = new(); + foreach (Quaternion q in vals) floats.Add(q); + return Float4.Average(floats.ToArray()); + } + public static Quaternion Ceiling(Quaternion val) => Float4.Ceiling(val); + public static Quaternion Clamp(Quaternion val, Quaternion min, Quaternion max) => Float4.Clamp(val, min, max); + public static Quaternion ClampMagnitude(Quaternion val, float minMag, float maxMag) => + Float4.ClampMagnitude(val, minMag, maxMag); + public static Quaternion Divide(Quaternion num, params Quaternion[] vals) + { + List floats = new(); + foreach (Quaternion q in vals) floats.Add(q); + return Float4.Divide(num, floats.ToArray()); + } + public static float Dot(Quaternion a, Quaternion b) => a.u * b.u + a.i * b.i + a.j * b.j + a.k * b.k; + public static float Dot(params Quaternion[] vals) + { + List floats = new(); + foreach (Quaternion q in vals) floats.Add(q); + return Float4.Dot(floats.ToArray()); + } + public static Quaternion Floor(Quaternion val) => Float4.Floor(val); + public static Quaternion Lerp(Quaternion a, Quaternion b, float t, bool clamp = true) => Float4.Lerp(a, b, t, clamp); + public static Quaternion Median(params Quaternion[] vals) + { + List floats = new(); + foreach (Quaternion q in vals) floats.Add(q); + return Float4.Median(floats.ToArray()); + } + public static Quaternion Max(params Quaternion[] vals) + { + List floats = new(); + foreach (Quaternion q in vals) floats.Add(q); + return Float4.Max(floats.ToArray()); + } + public static Quaternion Min(params Quaternion[] vals) + { + List floats = new(); + foreach (Quaternion q in vals) floats.Add(q); + return Float4.Min(floats.ToArray()); + } + public static Quaternion Product(params Quaternion[] vals) + { + List floats = new(); + foreach (Quaternion q in vals) floats.Add(q); + return Float4.Product(floats.ToArray()); + } + public static Quaternion Round(Quaternion val) => Float4.Round(val); + public static Quaternion Subtract(Quaternion num, params Quaternion[] vals) + { + List floats = new(); + foreach (Quaternion q in vals) floats.Add(q); + return Float4.Subtract(num, floats.ToArray()); + } + public static Quaternion Sum(params Quaternion[] vals) + { + List floats = new(); + foreach (Quaternion q in vals) floats.Add(q); + return Float4.Sum(floats.ToArray()); + } + + public static Quaternion FromAngles(Angle yaw, Angle pitch, Angle? roll = null) + { + roll ??= Angle.Zero; + float cosYaw = Mathf.Cos(yaw), cosPitch = Mathf.Cos(pitch), cosRoll = Mathf.Cos(roll.Value), + sinYaw = Mathf.Sin(yaw), sinPitch = Mathf.Sin(pitch), sinRoll = Mathf.Sin(roll.Value); + + float cosYawCosPitch = cosYaw * cosPitch, + cosYawSinPitch = cosYaw * sinPitch, + sinYawCosPitch = sinYaw * cosPitch, + sinYawSinPitch = sinYaw * sinPitch; + + return new(cosYawCosPitch * cosRoll + sinYawSinPitch * sinRoll, + cosYawCosPitch * sinRoll + sinYawSinPitch * cosRoll, + cosYawSinPitch * cosRoll + sinYawCosPitch * sinRoll, + sinYawCosPitch * cosRoll + cosYawSinPitch * sinRoll); + } + public static Quaternion FromAngles(Float3 vals, Angle.Type valType) => + FromAngles(new(vals.x, valType), new(vals.y, valType), new(vals.z, valType)); + public static Quaternion FromVector(Vector3d vec) => FromAngles(vec.yaw, vec.pitch); + + public static (float[] Us, float[] Is, float[] Js, float[] Ks) SplitArray(params Quaternion[] vals) + { + float[] Us = new float[vals.Length], Is = new float[vals.Length], Js = new float[vals.Length], + Ks = new float[vals.Length]; + for (int i = 0; i < vals.Length; i++) + { + Us[i] = vals[i].u; + Is[i] = vals[i].i; + Js[i] = vals[i].j; + Ks[i] = vals[i].k; + } + return (Us, Is, Js, Ks); + } + + public int CompareTo(Quaternion other) => Magnitude.CompareTo(other.Magnitude); + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj == null || obj.GetType() != typeof(Quaternion)) return base.Equals(obj); + return Equals((Quaternion)obj); + } + public bool Equals(Quaternion other) => u == other.u && i == other.i && j == other.j && k == other.k; + public override int GetHashCode() => u.GetHashCode() ^ i.GetHashCode() ^ j.GetHashCode() ^ k.GetHashCode(); + public override string ToString() => ToString((string?)null); + public string ToString(string? provider) => u.ToString(provider) + + (i >= 0 ? " + " : " - ") + i.ToString(provider) + "i" + + (j >= 0 ? " + " : " - ") + j.ToString(provider) + "j" + + (k >= 0 ? " + " : " - ") + k.ToString(provider) + "k"; + public string ToString(IFormatProvider provider) => u.ToString(provider) + + (i >= 0 ? " + " : " - ") + i.ToString(provider) + "i" + + (j >= 0 ? " + " : " - ") + j.ToString(provider) + "j" + + (k >= 0 ? " + " : " - ") + k.ToString(provider) + "k"; + + public object Clone() => new Quaternion(u, i, j, k); + + public Angle GetAngle() => new(2 * Mathf.ArcCos(u), Angle.Type.Radians); + public Float3 GetAxis() + { + Float3 axis = IJK; + float mag = Magnitude; + + if (mag < 0) return Float3.Zero; + return axis / mag; + } + public (Angle yaw, Angle pitch, Angle roll) ToAngles() + { + Quaternion doubled = this; + doubled.u *= u; + doubled.i *= i; + doubled.j *= j; + doubled.k *= k; + + Matrix3x3 rotMatrix = new(new[,] + { + { doubled.u + doubled.i - doubled.j - doubled.k, 0, 0 }, + { 2 * (i * j + u * k), 0, 0 }, + { 2 * (i * k + u * j), 2 * (j * k + u * i), doubled.u - doubled.i - doubled.j + doubled.k } + }); + + Angle yaw, pitch, roll; + + float r3c1Abs = Mathf.Absolute(rotMatrix.r3c1); + if (r3c1Abs >= 1) + { + rotMatrix.r1c2 = 2 * (i * j - u * k); + rotMatrix.r1c3 = 2 * (i * k + u * j); + + yaw = new(Mathf.ArcTan2(-rotMatrix.r1c2, -rotMatrix.r3c1 * rotMatrix.r1c3), Angle.Type.Radians); + pitch = new(-Constants.HalfPi * rotMatrix.r3c1 / r3c1Abs, Angle.Type.Radians); + roll = Angle.Zero; + } + else + { + yaw = new(Mathf.ArcTan2(rotMatrix.r2c1, rotMatrix.r1c1), Angle.Type.Radians); + pitch = new(Mathf.ArcSin(-rotMatrix.r3c1), Angle.Type.Radians); + roll = new(Mathf.ArcTan2(rotMatrix.r3c2, rotMatrix.r3c3), Angle.Type.Radians); + } + + return (yaw, pitch, roll); + } + public Vector3d ToVector() + { + (Angle yaw, Angle pitch, _) = ToAngles(); + return new(yaw, pitch); + } + + public Quaternion Rotate(Quaternion other) => other * this * other.Conjugate; + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public IEnumerator GetEnumerator() + { + yield return u; + yield return i; + yield return j; + yield return k; + } + + public float[] ToArray() => new[] { u, i, j, k }; + public Fill ToFill() + { + Quaternion @this = this; + return i => @this[i]; + } + public List ToList() => new() { u, i, j, k }; + + public static Quaternion operator +(Quaternion a, Quaternion b) => new(a.u + b.u, a.i + b.i, a.j + b.j, a.k + b.k); + public static Quaternion operator -(Quaternion q) => new(q.u, q.i, q.j, q.k); + public static Quaternion operator -(Quaternion a, Quaternion b) => new(a.u - b.u, a.i - b.i, a.j - b.j, a.k - b.k); + public static Quaternion operator *(Quaternion x, Quaternion y) + { + float a = x.u, b = x.i, c = x.j, d = x.k, e = y.u, f = y.i, g = y.j, h = y.k, + u = a * e - b * f - c * g - d * h, + i = a * f + b * e + c * h - d * g, + j = a * g + c * e + b * h - d * f, + k = a * h + b * g + d * e - c * f; + return new(u, i, j, k); + } + public static Quaternion operator *(Quaternion a, float b) => new(a.u * b, a.i * b, a.j * b, a.k * b); + public static Quaternion operator *(Quaternion a, Matrix b) => (Quaternion)((Matrix)a * b); + public static Quaternion operator *(Quaternion a, Float3 b) => a * new Quaternion(b); + public static Quaternion operator /(Quaternion x, Quaternion y) + { + float a = x.u, b = x.i, c = x.j, d = x.k, e = y.u, f = y.i, g = y.j, h = y.k, + u = a * e + b * f + c * g + d * h, + i = b * e + c * h + d * g - a * f, + j = c * e + d * f - a * g - b * h, + k = c * f + d * e - a * h - b * g, + q = e * e + f * f + g * g + h * h; + return new(u / q, i / q, j / q, k / q); + } + public static Quaternion operator /(Quaternion a, float b) => new(a.u / b, a.i / b, a.j / b, a.k / b); + public static Quaternion operator /(Quaternion a, Matrix b) => (Quaternion)((Matrix)a / b); + public static Quaternion operator /(Quaternion a, Float3 b) => a / new Quaternion(b); + public static bool operator ==(Quaternion a, Quaternion b) => a.Equals(b); + public static bool operator !=(Quaternion a, Quaternion b) => !a.Equals(b); + public static bool operator >(Quaternion a, Quaternion b) => a.CompareTo(b) > 0; + public static bool operator <(Quaternion a, Quaternion b) => a.CompareTo(b) < 0; + public static bool operator >=(Quaternion a, Quaternion b) => a == b || a > b; + public static bool operator <=(Quaternion a, Quaternion b) => a == b || a < b; + + public static implicit operator Quaternion(Complex val) => new(val.u, val.i, 0, 0); + public static implicit operator Quaternion(Int2 val) => new(val); + public static implicit operator Quaternion(Int3 val) => new(val); + public static implicit operator Quaternion(Int4 val) => new(val.x, val.y, val.z, val.w); + public static explicit operator Quaternion(Float2 val) => new(val); + public static explicit operator Quaternion(Float3 val) => new(val); + public static implicit operator Quaternion(Float4 val) => new(val.x, val.y, val.z, val.w); + public static explicit operator Quaternion(Matrix m) => new(m[0, 0], m[1, 0], m[2, 0], m[3, 0]); + public static explicit operator Quaternion(Vector2d val) => (Quaternion)val.ToXYZ(); + public static implicit operator Quaternion(Vert val) => new(val); + public static implicit operator Quaternion(Fill fill) => new(fill); + public static implicit operator Quaternion(Fill fill) => new(fill); +} diff --git a/Nerd_STF/Mathematics/Constants.cs b/Nerd_STF/Mathematics/Samples/Constants.cs similarity index 99% rename from Nerd_STF/Mathematics/Constants.cs rename to Nerd_STF/Mathematics/Samples/Constants.cs index 84ddd58..096f5d4 100644 --- a/Nerd_STF/Mathematics/Constants.cs +++ b/Nerd_STF/Mathematics/Samples/Constants.cs @@ -1,4 +1,4 @@ -namespace Nerd_STF.Mathematics; +namespace Nerd_STF.Mathematics.Samples; public static class Constants { diff --git a/Nerd_STF/Mathematics/Samples/Equations.cs b/Nerd_STF/Mathematics/Samples/Equations.cs new file mode 100644 index 0000000..1ded66e --- /dev/null +++ b/Nerd_STF/Mathematics/Samples/Equations.cs @@ -0,0 +1,26 @@ +namespace Nerd_STF.Mathematics.Samples; + +public static class Equations +{ + public static readonly Fill SgnFill = i => i % 2 == 0 ? 1 : -1; + + public static readonly Equation CosWave = x => Mathf.Cos(x); + public static readonly Equation SinWave = x => Mathf.Sin(x); + public static readonly Equation SawWave = x => x % 1; + public static readonly Equation SquareWave = x => x % 2 < 1 ? 1 : 0; + + public static Equation Scale(Equation equ, float value, ScaleType type = ScaleType.Both) => type switch + { + ScaleType.X => x => equ(value / x), + ScaleType.Y => x => x * equ(value), + ScaleType.Both => x => x * equ(value / x), + _ => throw new ArgumentException("Unknown scale type " + type) + }; + + public enum ScaleType + { + X = 1, + Y = 2, + Both = X | Y + } +} diff --git a/Nerd_STF/Miscellaneous/GlobalUsings.cs b/Nerd_STF/Miscellaneous/GlobalUsings.cs index 45bcc57..ed144bb 100644 --- a/Nerd_STF/Miscellaneous/GlobalUsings.cs +++ b/Nerd_STF/Miscellaneous/GlobalUsings.cs @@ -10,5 +10,9 @@ global using System.Threading.Tasks; global using Nerd_STF; global using Nerd_STF.Graphics; global using Nerd_STF.Exceptions; +global using Nerd_STF.Extensions; global using Nerd_STF.Mathematics; +global using Nerd_STF.Mathematics.Algebra; global using Nerd_STF.Mathematics.Geometry; +global using Nerd_STF.Mathematics.NumberSystems; +global using Nerd_STF.Mathematics.Samples; diff --git a/Nerd_STF/Modifier.cs b/Nerd_STF/Modifier.cs new file mode 100644 index 0000000..8e7753d --- /dev/null +++ b/Nerd_STF/Modifier.cs @@ -0,0 +1,5 @@ +namespace Nerd_STF; + +public delegate float Modifier(int index, float value); +public delegate T Modifier(int index, T value); +public delegate VT Modifier(IT index, VT value); diff --git a/Nerd_STF/Modifier2D.cs b/Nerd_STF/Modifier2D.cs new file mode 100644 index 0000000..ddac9b9 --- /dev/null +++ b/Nerd_STF/Modifier2D.cs @@ -0,0 +1,5 @@ +namespace Nerd_STF; + +public delegate float Modifier2D(Int2 index, float value); +public delegate T Modifier2D(Int2 index, T value); +public delegate VT Modifier2D(IT x, IT y, VT value); diff --git a/Nerd_STF/bin/Release/net6.0/ref/Nerd_STF.dll b/Nerd_STF/bin/Release/net6.0/ref/Nerd_STF.dll index 89f2b0d..3020460 100644 Binary files a/Nerd_STF/bin/Release/net6.0/ref/Nerd_STF.dll and b/Nerd_STF/bin/Release/net6.0/ref/Nerd_STF.dll differ