Version 2.3.0 has been released.

More to come in the rest of 2.3!
This commit is contained in:
That-One-Nerd 2022-08-02 12:31:54 -04:00
parent 494d3b2581
commit e06a49c634
50 changed files with 3020 additions and 175 deletions

6
.gitignore vendored
View File

@ -1,6 +1,7 @@
# Visual Studio stuff # Visual Studio stuff
*.sln
*.csproj *.csproj
*.editorconfig
*.sln
/Nerd_STF/.vs/ /Nerd_STF/.vs/
/Nerd_STF/obj /Nerd_STF/obj
/Nerd_STF/bin/Debug /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.dll
/Nerd_STF/bin/Release/net6.0/Nerd_STF.pdb /Nerd_STF/bin/Release/net6.0/Nerd_STF.pdb
# Testing project
/Testing
# Nuget # Nuget
/Nerd_STF/LICENSE /Nerd_STF/LICENSE
*.nupkg *.nupkg

View File

@ -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 * Nerd_STF
+ delegate Fill2D<T>(int, int) + Extensions
+ ConversionExtension
+ Container2DExtension
+ ToFillExtension
+ Foreach
+ IGroup2D
+ Modifier
+ Modifier2D
* IGroup<T>
+ ToFill()
* Exceptions * Exceptions
+ FileParsingException + InvalidSizeException
+ FileType + NoInverseException
+ Graphics * Graphics
+ ColorChannel * CMYKA
+ CMYKA + ToFill()
+ CMYKAByte + static Round(CMYKA)
+ HSVA = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
+ HSVAByte * CMYKAByte
+ IColor + ToFill()
+ IColorByte = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
+ IlluminationFlags * HSVA
+ IlluminationModel + ToFill()
+ Image + static Round(HSVA, Angle.Type)
+ Material = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
+ RGBA * HSVAByte
+ RGBAByte + 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 * Mathematics
* Angle + Algebra
+ Normalized + IMatrix
= Made fancier `ToString()` formatting + Matrix2x2
* Type + Matrix3x3
+ Normalized + Matrix4x4
+ Constants + Vector2d
* Float2 + Vector3d
+ static SplitArray(params Float2[]) + NumberSystems
= Renamed `static Multiply(params Float2[])` to `Product` + Complex
* Float3 + Quaternion
+ static SplitArray(params Float3[]) + Samples
+ explicit operator Float3(RGBA) + Equations
+ explicit operator Float3(HSVA) + ScaleType
+ explicit operator Float3(RGBAByte) = Moved `Constants` file to Samples folder.
+ 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`
* Geometry * 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 * Line
+ Midpoint + ToFill()
= Renamed `ToDoubleArray()` to `ToFloatArray` = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
= Renamed `ToDoubleList()` to `ToFloatList`
* Polygon * Polygon
+ Midpoint + ToFill()
= Renamed `ToDoubleArray()` to `ToFloatArray` = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
= Renamed `ToDoubleList()` to `ToFloatList`
= Renamed `static ToDoubleArrayAll(params Triangle[])` to `ToFloatArrayAll`
= Renamed `static ToDoubleListAll(params Triangle[])` to `ToFloatListAll`
* Quadrilateral * Quadrilateral
+ Midpoint + ToFill()
= Renamed `ToDoubleArray()` to `ToFloatArray` = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
= Renamed `ToDoubleList()` to `ToFloatList` * Sphere
= Renamed `static ToDoubleArrayAll(params Triangle[])` to `ToFloatArrayAll` = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
= Renamed `static ToDoubleListAll(params Triangle[])` to `ToFloatListAll`
* Triangle * Triangle
+ Midpoint + ToFill()
= Renamed `ToDoubleArray()` to `ToFloatArray` = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
= Renamed `ToDoubleList()` to `ToFloatList`
= Renamed `static ToDoubleArrayAll(params Triangle[])` to `ToFloatArrayAll`
= Renamed `static ToDoubleListAll(params Triangle[])` to `ToFloatListAll`
* Vert * Vert
= Renamed `static ToDouble3Array(params Vert[])` to `ToFloat3Array` + ToFill()
= Renamed `static ToDouble3List(params Vert[])` to `ToFloat3List` + 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 * Int2
+ static SplitArray(params Int[]) + ToFill()
= Renamed `static Multiply(params Int2[])` to `Product` + 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 * Int3
+ static SplitArray(params Int3[]) + ToFill()
+ explicit operator Int3(RGBA) + explicit operator Int3(Complex)
+ explicit operator Int3(HSVA) + explicit operator Int3(Quaternion)
+ explicit operator Int3(RGBAByte) + explicit operator Int3(Matrix)
+ explicit operator Int3(HSVAByte) + operator *(Int3, Matrix)
= Renamed `static Multiply(params Int3[])` to `Product` + 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 * Int4
+ static SplitArray(params Int4[]) + explicit operator Int4(Complex)
+ explicit operator Int4(RGBA) + explicit operator Int4(Quaternion)
+ explicit operator Int4(CMYKA) + explicit operator Int4(Matrix)
+ explicit operator Int4(HSVA) + operator *(Int4, Matrix)
+ implicit operator Int4(RGBAByte) + operator /(Int4, Matrix)
+ explicit operator Int4(CMYKAByte) = Made `Normalized` multiply by the inverse square root instead of dividing by the square root.
+ implicit operator Int4(HSVAByte) = Replaced a false statement with `base.Equals(object?)` in `override bool Equals(object?)`
= Renamed `static Multiply(params Int4[])` to `Product`
* Mathf * Mathf
+ static Combinations(int, int) + Cos(Angle)
+ static GreatestCommonFactor(params int[]) + Cot(Angle)
+ static InverseSqrt(float) + Csc(Angle)
+ static LeastCommonMultiple(params int[]) + Dot(float[], float[])
+ static Mode<T>(params T[]) where T : IEquatable<T> + Dot(float[][])
+ static Permutations(int, int) + Max<T>(T[]) where T : IComparable<T>
+ static Pow(float, int) + Median<T>(T[])
+ static Product(Equation, float, float, float) + Min<T>(T[]) where T : IComparable<T>
+ static Sum(Equation, float, float, float) + Sec(Angle)
+ static UniqueItems<T>(params T[]) where T : IEquatable<T> + Sin(Angle)
+ static ZScore(float, params float[]) + Tan(Angle)
+ 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`
* Miscellaneous * Miscellaneous
* GlobalUsings.cs * 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
``` ```

View File

@ -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) { }
}

View File

@ -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) { }
}

View File

@ -0,0 +1,24 @@
namespace Nerd_STF.Extensions;
public static class Container2DExtension
{
public static T[] Flatten<T>(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<T>(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<T>(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;
}
}

View File

@ -0,0 +1,13 @@
namespace Nerd_STF.Extensions;
public static class ConversionExtension
{
public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>
(this IEnumerable<KeyValuePair<TKey, TValue>> pairs)
where TKey : notnull
{
Dictionary<TKey, TValue> res = new();
foreach (KeyValuePair<TKey, TValue> pair in pairs) res.Add(pair.Key, pair.Value);
return res;
}
}

View File

@ -0,0 +1,6 @@
namespace Nerd_STF.Extensions;
public static class ToFillExtension
{
public static Fill<T> ToFill<T>(this IEnumerable<T> group) => i => group.ElementAt(i);
}

4
Nerd_STF/Foreach.cs Normal file
View File

@ -0,0 +1,4 @@
namespace Nerd_STF;
public delegate void Foreach(object item);
public delegate void Foreach<T>(T item);

View File

@ -146,6 +146,8 @@ public struct CMYKA : IColor, IEquatable<CMYKA>
(float[] Cs, float[] Ms, float[] Ys, float[] Ks, float[] As) = SplitArray(vals); (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)); 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) 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<CMYKA>
&& Y == col.Y && K == col.K && A == col.A; && Y == col.Y && K == col.K && A == col.A;
public override bool Equals([NotNullWhen(true)] object? obj) public override bool Equals([NotNullWhen(true)] object? obj)
{ {
if (obj == null) return false; if (obj == null) return base.Equals(obj);
Type t = obj.GetType(); Type t = obj.GetType();
if (t == typeof(CMYKA)) return Equals((CMYKA)obj); if (t == typeof(CMYKA)) return Equals((CMYKA)obj);
else if (t == typeof(RGBA)) return Equals((IColor)obj); else if (t == typeof(RGBA)) return Equals((IColor)obj);
@ -180,7 +182,7 @@ public struct CMYKA : IColor, IEquatable<CMYKA>
else if (t == typeof(HSVAByte)) return Equals((IColorByte)obj); else if (t == typeof(HSVAByte)) return Equals((IColorByte)obj);
else if (t == typeof(IColorByte)) 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 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) public string ToString(IFormatProvider provider) => "C: " + C.ToString(provider) + " M: " + M.ToString(provider)
@ -205,6 +207,11 @@ public struct CMYKA : IColor, IEquatable<CMYKA>
public HSVAByte ToHSVAByte() => ToRGBA().ToHSVAByte(); public HSVAByte ToHSVAByte() => ToRGBA().ToHSVAByte();
public float[] ToArray() => new[] { C, M, Y, K, A }; public float[] ToArray() => new[] { C, M, Y, K, A };
public Fill<float> ToFill()
{
CMYKA @this = this;
return i => @this[i];
}
public List<float> ToList() => new() { C, M, Y, K, A }; public List<float> ToList() => new() { C, M, Y, K, A };
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

View File

@ -149,7 +149,7 @@ public struct CMYKAByte : IColorByte, IEquatable<CMYKAByte>
&& Y == col.Y && K == col.K && A == col.A; && Y == col.Y && K == col.K && A == col.A;
public override bool Equals([NotNullWhen(true)] object? obj) public override bool Equals([NotNullWhen(true)] object? obj)
{ {
if (obj == null) return false; if (obj == null) return base.Equals(obj);
Type t = obj.GetType(); Type t = obj.GetType();
if (t == typeof(CMYKAByte)) return Equals((CMYKAByte)obj); if (t == typeof(CMYKAByte)) return Equals((CMYKAByte)obj);
else if (t == typeof(RGBA)) return Equals((IColor)obj); else if (t == typeof(RGBA)) return Equals((IColor)obj);
@ -160,7 +160,7 @@ public struct CMYKAByte : IColorByte, IEquatable<CMYKAByte>
else if (t == typeof(HSVAByte)) return Equals((IColorByte)obj); else if (t == typeof(HSVAByte)) return Equals((IColorByte)obj);
else if (t == typeof(IColorByte)) 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() public override int GetHashCode() => C.GetHashCode() ^ M.GetHashCode() ^ Y.GetHashCode()
^ K.GetHashCode() ^ A.GetHashCode(); ^ K.GetHashCode() ^ A.GetHashCode();
@ -181,6 +181,11 @@ public struct CMYKAByte : IColorByte, IEquatable<CMYKAByte>
public HSVAByte ToHSVAByte() => ToRGBA().ToHSVAByte(); public HSVAByte ToHSVAByte() => ToRGBA().ToHSVAByte();
public byte[] ToArray() => new[] { C, M, Y, K, A }; public byte[] ToArray() => new[] { C, M, Y, K, A };
public Fill<byte> ToFill()
{
CMYKAByte @this = this;
return i => @this[i];
}
public List<byte> ToList() => new() { C, M, Y, K, A }; public List<byte> ToList() => new() { C, M, Y, K, A };
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

View File

@ -100,15 +100,15 @@ public struct HSVA : IColor, IEquatable<HSVA>
for (int i = 0; i < vals.Length; i++) val += vals[i]; for (int i = 0; i < vals.Length; i++) val += vals[i];
return val / vals.Length; return val / vals.Length;
} }
public static HSVA Ceiling(HSVA val, Angle.Type type) => new(Angle.Ceiling(val.H, type), Mathf.Ceiling(val.S), public static HSVA Ceiling(HSVA val, Angle.Type type = Angle.Type.Degrees) => new(Angle.Ceiling(val.H, type),
Mathf.Ceiling(val.V), Mathf.Ceiling(val.A)); Mathf.Ceiling(val.S), Mathf.Ceiling(val.V), Mathf.Ceiling(val.A));
public static HSVA Clamp(HSVA val, HSVA min, HSVA max) => public static HSVA Clamp(HSVA val, HSVA min, HSVA max) =>
new(Angle.Clamp(val.H, min.H, max.H), new(Angle.Clamp(val.H, min.H, max.H),
Mathf.Clamp(val.S, min.S, max.S), Mathf.Clamp(val.S, min.S, max.S),
Mathf.Clamp(val.V, min.V, max.V), Mathf.Clamp(val.V, min.V, max.V),
Mathf.Clamp(val.A, min.A, max.A)); 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), public static HSVA Floor(HSVA val, Angle.Type type = Angle.Type.Degrees) => new(Angle.Floor(val.H, type),
Mathf.Floor(val.V), Mathf.Floor(val.A)); 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) => 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), 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)); Mathf.Lerp(a.A, b.A, t, clamp));
@ -136,6 +136,8 @@ public struct HSVA : IColor, IEquatable<HSVA>
(Angle[] Hs, float[] Ss, float[] Vs, float[] As) = SplitArray(vals); (Angle[] Hs, float[] Ss, float[] Vs, float[] As) = SplitArray(vals);
return new(Angle.Min(Hs), Mathf.Min(Ss), Mathf.Min(Vs), Mathf.Min(As)); 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) public static (Angle[] Hs, float[] Ss, float[] Vs, float[] As) SplitArray(params HSVA[] vals)
{ {
@ -171,7 +173,7 @@ public struct HSVA : IColor, IEquatable<HSVA>
|| H == col.H && S == col.S && V == col.V && A == col.A; || H == col.H && S == col.S && V == col.V && A == col.A;
public override bool Equals([NotNullWhen(true)] object? obj) public override bool Equals([NotNullWhen(true)] object? obj)
{ {
if (obj == null) return false; if (obj == null) return base.Equals(obj);
Type t = obj.GetType(); Type t = obj.GetType();
if (t == typeof(HSVA)) return Equals((HSVA)obj); if (t == typeof(HSVA)) return Equals((HSVA)obj);
else if (t == typeof(CMYKA)) return Equals((IColor)obj); else if (t == typeof(CMYKA)) return Equals((IColor)obj);
@ -182,7 +184,7 @@ public struct HSVA : IColor, IEquatable<HSVA>
else if (t == typeof(HSVAByte)) return Equals((IColorByte)obj); else if (t == typeof(HSVAByte)) return Equals((IColorByte)obj);
else if (t == typeof(IColorByte)) 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 override int GetHashCode() => H.GetHashCode() ^ S.GetHashCode() ^ V.GetHashCode() ^ A.GetHashCode();
public string ToString(IFormatProvider provider) => "H: " + H.ToString(provider) + " S: " + S.ToString(provider) public string ToString(IFormatProvider provider) => "H: " + H.ToString(provider) + " S: " + S.ToString(provider)
@ -212,6 +214,11 @@ public struct HSVA : IColor, IEquatable<HSVA>
Mathf.RoundInt(V * 255), Mathf.RoundInt(A * 255)); Mathf.RoundInt(V * 255), Mathf.RoundInt(A * 255));
public float[] ToArray() => new[] { H.Normalized, S, V, A }; public float[] ToArray() => new[] { H.Normalized, S, V, A };
public Fill<float> ToFill()
{
HSVA @this = this;
return i => @this[i];
}
public List<float> ToList() => new() { H.Normalized, S, V, A }; public List<float> ToList() => new() { H.Normalized, S, V, A };
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

View File

@ -138,7 +138,7 @@ public struct HSVAByte : IColorByte, IEquatable<HSVAByte>
|| H == col.H && S == col.S && V == col.V && A == col.A; || H == col.H && S == col.S && V == col.V && A == col.A;
public override bool Equals([NotNullWhen(true)] object? obj) public override bool Equals([NotNullWhen(true)] object? obj)
{ {
if (obj == null) return false; if (obj == null) return base.Equals(obj);
Type t = obj.GetType(); Type t = obj.GetType();
if (t == typeof(HSVAByte)) return Equals((HSVAByte)obj); if (t == typeof(HSVAByte)) return Equals((HSVAByte)obj);
else if (t == typeof(CMYKA)) return Equals((IColor)obj); else if (t == typeof(CMYKA)) return Equals((IColor)obj);
@ -149,7 +149,7 @@ public struct HSVAByte : IColorByte, IEquatable<HSVAByte>
else if (t == typeof(HSVA)) return Equals((IColor)obj); else if (t == typeof(HSVA)) return Equals((IColor)obj);
else if (t == typeof(IColorByte)) 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 override int GetHashCode() => H.GetHashCode() ^ S.GetHashCode() ^ V.GetHashCode() ^ A.GetHashCode();
public string ToString(IFormatProvider provider) => "H: " + H.ToString(provider) + " S: " + S.ToString(provider) public string ToString(IFormatProvider provider) => "H: " + H.ToString(provider) + " S: " + S.ToString(provider)
@ -167,6 +167,11 @@ public struct HSVAByte : IColorByte, IEquatable<HSVAByte>
public HSVAByte ToHSVAByte() => this; public HSVAByte ToHSVAByte() => this;
public byte[] ToArray() => new[] { H, S, V, A }; public byte[] ToArray() => new[] { H, S, V, A };
public Fill<byte> ToFill()
{
HSVAByte @this = this;
return i => @this[i];
}
public List<byte> ToList() => new() { H, S, V, A }; public List<byte> ToList() => new() { H, S, V, A };
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

View File

@ -75,9 +75,9 @@ public struct Image : ICloneable, IEnumerable, IEquatable<Image>
public bool Equals(Image other) => Pixels == other.Pixels; public bool Equals(Image other) => Pixels == other.Pixels;
public override bool Equals([NotNullWhen(true)] object? obj) 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); if (obj.GetType() == typeof(Image)) return Equals((Image)obj);
return false; return base.Equals(obj);
} }
public override int GetHashCode() => Pixels.GetHashCode(); public override int GetHashCode() => Pixels.GetHashCode();

View File

@ -171,9 +171,8 @@ public struct Material : ICloneable, IEquatable<Material>
SpecularHighlightTexture.Equals(other.SpecularHighlightTexture) && StencilTexture.Equals(other.StencilTexture); SpecularHighlightTexture.Equals(other.SpecularHighlightTexture) && StencilTexture.Equals(other.StencilTexture);
public override bool Equals([NotNullWhen(true)] object? obj) public override bool Equals([NotNullWhen(true)] object? obj)
{ {
if (obj == null) return false; if (obj == null || obj.GetType() != typeof(Material)) return base.Equals(obj);
if (obj.GetType() == typeof(Material)) return Equals((Material)obj); return Equals((Material)obj);
return false;
} }
public override int GetHashCode() => Alpha.GetHashCode() ^ AmbientColor.GetHashCode() ^ Anisotropy.GetHashCode() ^ public override int GetHashCode() => Alpha.GetHashCode() ^ AmbientColor.GetHashCode() ^ Anisotropy.GetHashCode() ^
AnisotropyRoughness.GetHashCode() ^ ClearcoatRoughness.GetHashCode() ^ ClearcoatThickness.GetHashCode() ^ AnisotropyRoughness.GetHashCode() ^ ClearcoatRoughness.GetHashCode() ^ ClearcoatThickness.GetHashCode() ^

View File

@ -132,6 +132,8 @@ public struct RGBA : IColor, IEquatable<RGBA>
(float[] Rs, float[] Gs, float[] Bs, float[] As) = SplitArray(vals); (float[] Rs, float[] Gs, float[] Bs, float[] As) = SplitArray(vals);
return new(Mathf.Min(Rs), Mathf.Min(Gs), Mathf.Min(Bs), Mathf.Min(As)); 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) public static (float[] Rs, float[] Gs, float[] Bs, float[] As) SplitArray(params RGBA[] vals)
{ {
@ -152,7 +154,7 @@ public struct RGBA : IColor, IEquatable<RGBA>
public bool Equals(RGBA col) => A == 0 && col.A == 0 || R == col.R && G == col.G && B == col.B && A == col.A; 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) public override bool Equals([NotNullWhen(true)] object? obj)
{ {
if (obj == null) return false; if (obj == null) return base.Equals(obj);
Type t = obj.GetType(); Type t = obj.GetType();
if (t == typeof(RGBA)) return Equals((RGBA)obj); if (t == typeof(RGBA)) return Equals((RGBA)obj);
else if (t == typeof(CMYKA)) return Equals((IColor)obj); else if (t == typeof(CMYKA)) return Equals((IColor)obj);
@ -163,7 +165,7 @@ public struct RGBA : IColor, IEquatable<RGBA>
else if (t == typeof(HSVAByte)) return Equals((IColorByte)obj); else if (t == typeof(HSVAByte)) return Equals((IColorByte)obj);
else if (t == typeof(IColorByte)) 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 override int GetHashCode() => R.GetHashCode() ^ G.GetHashCode() ^ B.GetHashCode() ^ A.GetHashCode();
public string ToString(IFormatProvider provider) => "R: " + R.ToString(provider) + " G: " + G.ToString(provider) + public string ToString(IFormatProvider provider) => "R: " + R.ToString(provider) + " G: " + G.ToString(provider) +
@ -205,6 +207,11 @@ public struct RGBA : IColor, IEquatable<RGBA>
public HSVAByte ToHSVAByte() => ToHSVA().ToHSVAByte(); public HSVAByte ToHSVAByte() => ToHSVA().ToHSVAByte();
public float[] ToArray() => new[] { R, G, B, A }; public float[] ToArray() => new[] { R, G, B, A };
public Fill<float> ToFill()
{
RGBA @this = this;
return i => @this[i];
}
public List<float> ToList() => new() { R, G, B, A }; public List<float> ToList() => new() { R, G, B, A };
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
@ -218,6 +225,8 @@ public struct RGBA : IColor, IEquatable<RGBA>
public object Clone() => new RGBA(R, G, B, A); 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 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 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); 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);

View File

@ -137,7 +137,7 @@ public struct RGBAByte : IColorByte, IEquatable<RGBAByte>
public bool Equals(RGBAByte col) => A == 0 && col.A == 0 || R == col.R && G == col.G && B == col.B && A == col.A; 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) public override bool Equals([NotNullWhen(true)] object? obj)
{ {
if (obj == null) return false; if (obj == null) return base.Equals(obj);
Type t = obj.GetType(); Type t = obj.GetType();
if (t == typeof(RGBAByte)) return Equals((RGBAByte)obj); if (t == typeof(RGBAByte)) return Equals((RGBAByte)obj);
else if (t == typeof(CMYKA)) return Equals((IColor)obj); else if (t == typeof(CMYKA)) return Equals((IColor)obj);
@ -148,7 +148,7 @@ public struct RGBAByte : IColorByte, IEquatable<RGBAByte>
else if (t == typeof(HSVAByte)) return Equals((IColorByte)obj); else if (t == typeof(HSVAByte)) return Equals((IColorByte)obj);
else if (t == typeof(IColorByte)) 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 override int GetHashCode() => R.GetHashCode() ^ G.GetHashCode() ^ B.GetHashCode() ^ A.GetHashCode();
public string ToString(IFormatProvider provider) => "R: " + R.ToString(provider) + " G: " + G.ToString(provider) + public string ToString(IFormatProvider provider) => "R: " + R.ToString(provider) + " G: " + G.ToString(provider) +
@ -166,6 +166,11 @@ public struct RGBAByte : IColorByte, IEquatable<RGBAByte>
public HSVAByte ToHSVAByte() => ToRGBA().ToHSVAByte(); public HSVAByte ToHSVAByte() => ToRGBA().ToHSVAByte();
public byte[] ToArray() => new[] { R, G, B, A }; public byte[] ToArray() => new[] { R, G, B, A };
public Fill<byte> ToFill()
{
HSVAByte @this = this;
return i => @this[i];
}
public List<byte> ToList() => new() { R, G, B, A }; public List<byte> ToList() => new() { R, G, B, A };
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
@ -179,6 +184,8 @@ public struct RGBAByte : IColorByte, IEquatable<RGBAByte>
public object Clone() => new RGBAByte(R, G, B, A); 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 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 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); 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);

View File

@ -3,5 +3,6 @@
public interface IGroup<T> : IEnumerable<T> public interface IGroup<T> : IEnumerable<T>
{ {
public T[] ToArray(); public T[] ToArray();
public Fill<T> ToFill();
public List<T> ToList(); public List<T> ToList();
} }

7
Nerd_STF/IGroup2D.cs Normal file
View File

@ -0,0 +1,7 @@
namespace Nerd_STF;
public interface IGroup2D<T> : IGroup<T>
{
public T[,] ToArray2D();
public Fill2D<T> ToFill2D();
}

View File

@ -0,0 +1,18 @@
namespace Nerd_STF.Mathematics.Algebra;
public interface IMatrix<T> : ICloneable, IEnumerable, IEquatable<T>, IGroup2D<float>
where T : IMatrix<T>
{
public T Adjugate();
public float Determinant();
public T Inverse();
public T Transpose();
public Dictionary<Int2, float> ToDictionary();
}
public interface IMatrix<This, TMinor> : IMatrix<This> where This : IMatrix<This, TMinor>
where TMinor : IMatrix<TMinor>
{
public TMinor[,] Minors();
}

View File

@ -0,0 +1,337 @@
namespace Nerd_STF.Mathematics.Algebra;
public struct Matrix : IMatrix<Matrix, Matrix>
{
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<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 * size.y);
}
public Matrix(Int2 size, Fill<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 * 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<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, Fill2D<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 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<float> 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<Int2, float> ToDictionary()
{
Dictionary<Int2, float> 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<float> ToFill() => ToFillExtension.ToFill(this);
public Fill2D<float> ToFill2D()
{
Matrix @this = this;
return (x, y) => @this[x, y];
}
public List<float> 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;
}
}

View File

@ -0,0 +1,275 @@
namespace Nerd_STF.Mathematics.Algebra;
public struct Matrix2x2 : IMatrix<Matrix2x2>
{
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<float> fill) : this(fill(0), fill(1), fill(2), fill(3)) { }
public Matrix2x2(Fill<int> 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<float> fill) : this(fill(0, 0), fill(0, 1), fill(1, 0), fill(1, 1)) { }
public Matrix2x2(Fill2D<int> 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<Float2> fill) : this(fill(0), fill(1)) { }
public Matrix2x2(Fill<Int2> fill) : this((IEnumerable<int>)fill(0), fill(1)) { }
public Matrix2x2(IEnumerable<float> c1, IEnumerable<float> c2) : this(c1.ToFill(), c2.ToFill()) { }
public Matrix2x2(IEnumerable<int> c1, IEnumerable<int> c2) : this(c1.ToFill(), c2.ToFill()) { }
public Matrix2x2(Fill<float> c1, Fill<float> c2) : this(c1(0), c1(1), c2(0), c2(1)) { }
public Matrix2x2(Fill<int> c1, Fill<int> 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<float> 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<Int2, float> ToDictionary()
{
Dictionary<Int2, float> 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<float> ToFill() => ToFillExtension.ToFill(this);
public Fill2D<float> ToFill2D()
{
Matrix2x2 @this = this;
return (x, y) => @this[x, y];
}
public List<float> 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);
}

View File

@ -0,0 +1,411 @@
namespace Nerd_STF.Mathematics.Algebra;
public struct Matrix3x3 : IMatrix<Matrix3x3, Matrix2x2>
{
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<float> fill) : this(fill(0), fill(1), fill(2),
fill(3), fill(4), fill(5), fill(6), fill(7), fill(8)) { }
public Matrix3x3(Fill<int> 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<float> 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<int> 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<Float3> fill) : this(fill(0), fill(1), fill(2)) { }
public Matrix3x3(Fill<Int3> fill) : this((IEnumerable<int>)fill(0), fill(1), fill(2)) { }
public Matrix3x3(IEnumerable<float> c1, IEnumerable<float> c2, IEnumerable<float> c3)
: this(c1.ToFill(), c2.ToFill(), c3.ToFill()) { }
public Matrix3x3(IEnumerable<int> c1, IEnumerable<int> c2, IEnumerable<int> c3)
: this(c1.ToFill(), c2.ToFill(), c3.ToFill()) { }
public Matrix3x3(Fill<float> c1, Fill<float> c2, Fill<float> c3)
: this(c1(0), c1(1), c1(2), c2(0), c2(1), c2(2), c3(0), c3(1), c3(2)) { }
public Matrix3x3(Fill<int> c1, Fill<int> c2, Fill<int> 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<float> 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<Int2, float> ToDictionary()
{
Dictionary<Int2, float> 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<float> ToFill() => ToFillExtension.ToFill(this);
public Fill2D<float> ToFill2D()
{
Matrix3x3 @this = this;
return (x, y) => @this[x, y];
}
public List<float> 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 }
});
}

View File

@ -0,0 +1,566 @@
namespace Nerd_STF.Mathematics.Algebra;
public struct Matrix4x4 : IMatrix<Matrix4x4, Matrix3x3>
{
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<float> 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<int> 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<float> 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<int> 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<Float4> fill) : this(fill(0), fill(1), fill(2), fill(3)) { }
public Matrix4x4(Fill<Int4> fill) : this((IEnumerable<int>)fill(0), fill(1), fill(2), fill(3)) { }
public Matrix4x4(IEnumerable<float> c1, IEnumerable<float> c2, IEnumerable<float> c3, IEnumerable<float> c4)
: this(c1.ToFill(), c2.ToFill(), c3.ToFill(), c4.ToFill()) { }
public Matrix4x4(IEnumerable<int> c1, IEnumerable<int> c2, IEnumerable<int> c3, IEnumerable<int> c4)
: this(c1.ToFill(), c2.ToFill(), c3.ToFill(), c4.ToFill()) { }
public Matrix4x4(Fill<float> c1, Fill<float> c2, Fill<float> c3, Fill<float> 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<int> c1, Fill<int> c2, Fill<int> c3, Fill<int> 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<float> 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<Int2, float> ToDictionary()
{
Dictionary<Int2, float> 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<float> ToFill() => ToFillExtension.ToFill(this);
public Fill2D<float> ToFill2D()
{
Matrix4x4 @this = this;
return (x, y) => @this[x, y];
}
public List<float> 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 }
});
}
}

View File

@ -0,0 +1,158 @@
namespace Nerd_STF.Mathematics.Algebra;
public struct Vector2d : ICloneable, IComparable<Vector2d>, IEquatable<Vector2d>
{
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);
}

View File

@ -0,0 +1,200 @@
namespace Nerd_STF.Mathematics.Algebra;
public struct Vector3d : ICloneable, IComparable<Vector3d>, IEquatable<Vector3d>
{
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<Angle> fill, float mag = 1) : this(fill(0), fill(1), mag) { }
public Vector3d(Fill<float> 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);
}

View File

@ -2,6 +2,11 @@
public struct Angle : ICloneable, IComparable<Angle>, IEquatable<Angle> public struct Angle : ICloneable, IComparable<Angle>, IEquatable<Angle>
{ {
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 Full => new(360);
public static Angle Half => new(180); public static Angle Half => new(180);
public static Angle One => new(1); public static Angle One => new(1);
@ -47,14 +52,18 @@ public struct Angle : ICloneable, IComparable<Angle>, IEquatable<Angle>
public static Angle Absolute(Angle val) => new(Mathf.Absolute(val.p_deg)); 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 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 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) => public static Angle Lerp(Angle a, Angle b, float t, bool clamp = true) =>
new(Mathf.Lerp(a.p_deg, b.p_deg, t, clamp)); 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 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 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 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) public static float[] SplitArray(Type outputType, params Angle[] vals)
{ {
@ -76,7 +85,7 @@ public struct Angle : ICloneable, IComparable<Angle>, IEquatable<Angle>
public int CompareTo(Angle other) => p_deg.CompareTo(other.p_deg); public int CompareTo(Angle other) => p_deg.CompareTo(other.p_deg);
public override bool Equals([NotNullWhen(true)] object? obj) 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); return Equals((Angle)obj);
} }
public bool Equals(Angle other) => p_deg == other.p_deg; public bool Equals(Angle other) => p_deg == other.p_deg;

View File

@ -11,7 +11,7 @@ public struct Float2 : ICloneable, IComparable<Float2>, IEquatable<Float2>, IGro
public static Float2 Zero => new(0, 0); public static Float2 Zero => new(0, 0);
public float Magnitude => Mathf.Sqrt(x * x + y * y); 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; public float x, y;
@ -72,7 +72,7 @@ public struct Float2 : ICloneable, IComparable<Float2>, IEquatable<Float2>, IGro
Float3.Cross(a, b, normalized); Float3.Cross(a, b, normalized);
public static Float2 Divide(Float2 num, params Float2[] vals) 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; return num;
} }
public static float Dot(Float2 a, Float2 b) => a.x * b.x + a.y * b.y; 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<Float2>, IEquatable<Float2>, IGro
{ {
if (vals.Length < 1) return 0; if (vals.Length < 1) return 0;
float x = 1, y = 1; float x = 1, y = 1;
foreach (Float2 d in vals) foreach (Float2 f in vals)
{ {
x *= d.x; x *= f.x;
y *= d.y; y *= f.y;
} }
return x + y; return x + y;
} }
@ -101,32 +101,34 @@ public struct Float2 : ICloneable, IComparable<Float2>, IEquatable<Float2>, IGro
{ {
if (vals.Length < 1) return Zero; if (vals.Length < 1) return Zero;
Float2 val = vals[0]; 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; return val;
} }
public static Float2 Min(params Float2[] vals) public static Float2 Min(params Float2[] vals)
{ {
if (vals.Length < 1) return Zero; if (vals.Length < 1) return Zero;
Float2 val = vals[0]; 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; return val;
} }
public static Float2 Product(params Float2[] vals) public static Float2 Product(params Float2[] vals)
{ {
if (vals.Length < 1) return Zero; if (vals.Length < 1) return Zero;
Float2 val = One; Float2 val = One;
foreach (Float2 d in vals) val *= d; foreach (Float2 f in vals) val *= f;
return val; 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) 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; return num;
} }
public static Float2 Sum(params Float2[] vals) public static Float2 Sum(params Float2[] vals)
{ {
Float2 val = Zero; Float2 val = Zero;
foreach (Float2 d in vals) val += d; foreach (Float2 f in vals) val += f;
return val; return val;
} }
@ -144,7 +146,7 @@ public struct Float2 : ICloneable, IComparable<Float2>, IEquatable<Float2>, IGro
public int CompareTo(Float2 other) => Magnitude.CompareTo(other.Magnitude); public int CompareTo(Float2 other) => Magnitude.CompareTo(other.Magnitude);
public override bool Equals([NotNullWhen(true)] object? obj) 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); return Equals((Float2)obj);
} }
public bool Equals(Float2 other) => x == other.x && y == other.y; public bool Equals(Float2 other) => x == other.x && y == other.y;
@ -165,15 +167,24 @@ public struct Float2 : ICloneable, IComparable<Float2>, IEquatable<Float2>, IGro
} }
public float[] ToArray() => new[] { x, y }; public float[] ToArray() => new[] { x, y };
public Fill<float> ToFill()
{
Float2 @this = this;
return i => @this[i];
}
public List<float> ToList() => new() { x, y }; public List<float> 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 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 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, 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, 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, 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, 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.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; public static bool operator >(Float2 a, Float2 b) => a.CompareTo(b) > 0;
@ -181,11 +192,15 @@ public struct Float2 : ICloneable, IComparable<Float2>, IEquatable<Float2>, 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 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(Float3 val) => new(val.x, val.y);
public static explicit operator Float2(Float4 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 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(Int3 val) => new(val.x, val.y);
public static explicit operator Float2(Int4 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 explicit operator Float2(Vert val) => new(val.position.x, val.position.y);
public static implicit operator Float2(Fill<float> fill) => new(fill); public static implicit operator Float2(Fill<float> fill) => new(fill);
public static implicit operator Float2(Fill<int> fill) => new(fill); public static implicit operator Float2(Fill<int> fill) => new(fill);

View File

@ -13,7 +13,7 @@ public struct Float3 : ICloneable, IComparable<Float3>, IEquatable<Float3>, IGro
public static Float3 Zero => new(0, 0, 0); public static Float3 Zero => new(0, 0, 0);
public float Magnitude => Mathf.Sqrt(x * x + y * y + z * z); 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 XY => new(x, y);
public Float2 XZ => new(x, z); public Float2 XZ => new(x, z);
@ -138,6 +138,8 @@ public struct Float3 : ICloneable, IComparable<Float3>, IEquatable<Float3>, IGro
foreach (Float3 d in vals) val *= d; foreach (Float3 d in vals) val *= d;
return val; 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) public static Float3 Subtract(Float3 num, params Float3[] vals)
{ {
foreach (Float3 d in vals) num -= d; foreach (Float3 d in vals) num -= d;
@ -165,7 +167,7 @@ public struct Float3 : ICloneable, IComparable<Float3>, IEquatable<Float3>, IGro
public int CompareTo(Float3 other) => Magnitude.CompareTo(other.Magnitude); public int CompareTo(Float3 other) => Magnitude.CompareTo(other.Magnitude);
public override bool Equals([NotNullWhen(true)] object? obj) 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); return Equals((Float3)obj);
} }
public bool Equals(Float3 other) => x == other.x && y == other.y && z == other.z; public bool Equals(Float3 other) => x == other.x && y == other.y && z == other.z;
@ -187,15 +189,28 @@ public struct Float3 : ICloneable, IComparable<Float3>, IEquatable<Float3>, IGro
} }
public float[] ToArray() => new[] { x, y, z }; public float[] ToArray() => new[] { x, y, z };
public Fill<float> ToFill()
{
Float3 @this = this;
return i => @this[i];
}
public List<float> ToList() => new() { x, y, z }; public List<float> 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 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 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, 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, 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, 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, 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.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; public static bool operator >(Float3 a, Float3 b) => a.CompareTo(b) > 0;
@ -203,11 +218,15 @@ public struct Float3 : ICloneable, IComparable<Float3>, IEquatable<Float3>, 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 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 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 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(Int2 val) => new(val.x, val.y, 0);
public static implicit operator Float3(Int3 val) => new(val.x, val.y, val.z); 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(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 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(RGBA val) => new(val.R, val.G, val.B);
public static explicit operator Float3(HSVA val) => new(val.H.Normalized, val.S, val.V); public static explicit operator Float3(HSVA val) => new(val.H.Normalized, val.S, val.V);

View File

@ -3,11 +3,11 @@
public struct Float4 : ICloneable, IComparable<Float4>, IEquatable<Float4>, IGroup<float> public struct Float4 : ICloneable, IComparable<Float4>, IEquatable<Float4>, IGroup<float>
{ {
public static Float4 Back => new(0, 0, -1, 0); 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 Down => new(0, -1, 0, 0);
public static Float4 Far => new(0, 0, 0, 1); public static Float4 Far => new(0, 0, 0, 1);
public static Float4 Forward => new(0, 0, 1, 0); public static Float4 Forward => new(0, 0, 1, 0);
public static Float4 Left => new(-1, 0, 0, 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 Right => new(1, 0, 0, 0);
public static Float4 Up => new(0, 1, 0, 0); public static Float4 Up => new(0, 1, 0, 0);
@ -15,7 +15,7 @@ public struct Float4 : ICloneable, IComparable<Float4>, IEquatable<Float4>, IGro
public static Float4 Zero => new(0, 0, 0, 0); public static Float4 Zero => new(0, 0, 0, 0);
public float Magnitude => Mathf.Sqrt(x * x + y * y + z * z + w * w); 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 XY => new(x, y);
public Float2 XZ => new(x, z); public Float2 XZ => new(x, z);
@ -144,6 +144,8 @@ public struct Float4 : ICloneable, IComparable<Float4>, IEquatable<Float4>, IGro
foreach (Float4 d in vals) val = d < val ? d : val; foreach (Float4 d in vals) val = d < val ? d : val;
return 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) public static Float4 Product(params Float4[] vals)
{ {
if (vals.Length < 1) return Zero; if (vals.Length < 1) return Zero;
@ -180,7 +182,7 @@ public struct Float4 : ICloneable, IComparable<Float4>, IEquatable<Float4>, IGro
public int CompareTo(Float4 other) => Magnitude.CompareTo(other.Magnitude); public int CompareTo(Float4 other) => Magnitude.CompareTo(other.Magnitude);
public override bool Equals([NotNullWhen(true)] object? obj) 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); return Equals((Float4)obj);
} }
public bool Equals(Float4 other) => x == other.x && y == other.y && z == other.z && w == other.w; 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<Float4>, IEquatable<Float4>, IGro
} }
public float[] ToArray() => new[] { x, y, z, w }; public float[] ToArray() => new[] { x, y, z, w };
public Fill<float> ToFill()
{
Float4 @this = this;
return i => @this[i];
}
public List<float> ToList() => new() { x, y, z, w }; public List<float> 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); 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<Float4>, IEquatable<Float4>, 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, 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, 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, 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, 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.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; public static bool operator >(Float4 a, Float4 b) => a.CompareTo(b) > 0;
@ -221,11 +230,15 @@ public struct Float4 : ICloneable, IComparable<Float4>, IEquatable<Float4>, 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 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(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(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(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(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 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(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 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); public static explicit operator Float4(CMYKA val) => new(val.C, val.M, val.Y, val.K);

View File

@ -86,7 +86,7 @@ public struct Box2D : ICloneable, IContainer<Vert>, IEquatable<Box2D>
public override bool Equals([NotNullWhen(true)] object? obj) 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); return Equals((Box2D)obj);
} }
public bool Equals(Box2D other) => center == other.center && size == other.size; public bool Equals(Box2D other) => center == other.center && size == other.size;

View File

@ -87,7 +87,7 @@ public struct Box3D : ICloneable, IContainer<Vert>, IEquatable<Box3D>
public override bool Equals([NotNullWhen(true)] object? obj) 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); return Equals((Box3D)obj);
} }
public bool Equals(Box3D other) => center == other.center && size == other.size; public bool Equals(Box3D other) => center == other.center && size == other.size;

View File

@ -98,7 +98,7 @@ public struct Line : ICloneable, IClosest<Vert>, IComparable<Line>, IContainer<V
public override bool Equals([NotNullWhen(true)] object? obj) public override bool Equals([NotNullWhen(true)] object? obj)
{ {
if (obj == null || obj.GetType() != typeof(Line)) return false; if (obj == null || obj.GetType() != typeof(Line)) return base.Equals(obj);
return Equals((Line)obj); return Equals((Line)obj);
} }
public bool Equals(Line other) => a == other.a && b == other.b; public bool Equals(Line other) => a == other.a && b == other.b;
@ -161,6 +161,11 @@ public struct Line : ICloneable, IClosest<Vert>, IComparable<Line>, IContainer<V
} }
public Vert[] ToArray() => new Vert[] { a, b }; public Vert[] ToArray() => new Vert[] { a, b };
public Fill<Vert> ToFill()
{
Line @this = this;
return i => @this[i];
}
public List<Vert> ToList() => new() { a, b }; public List<Vert> ToList() => new() { a, b };
public float[] ToFloatArray() => new float[] { a.position.x, a.position.y, a.position.z, public float[] ToFloatArray() => new float[] { a.position.x, a.position.y, a.position.z,

View File

@ -256,7 +256,7 @@ public struct Polygon : ICloneable, IEquatable<Polygon>, IGroup<Vert>, ISubdivid
public override bool Equals([NotNullWhen(true)] object? obj) 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); return Equals((Polygon)obj);
} }
public bool Equals(Polygon other) public bool Equals(Polygon other)
@ -285,6 +285,11 @@ public struct Polygon : ICloneable, IEquatable<Polygon>, IGroup<Vert>, ISubdivid
public IEnumerator<Vert> GetEnumerator() { foreach (Vert v in Verts) yield return v; } public IEnumerator<Vert> GetEnumerator() { foreach (Vert v in Verts) yield return v; }
public Vert[] ToArray() => Verts; public Vert[] ToArray() => Verts;
public Fill<Vert> ToFill()
{
Polygon @this = this;
return i => @this[i];
}
public List<Vert> ToList() => new(Verts); public List<Vert> ToList() => new(Verts);
public float[] ToFloatArray() public float[] ToFloatArray()

View File

@ -266,7 +266,7 @@ public struct Quadrilateral : ICloneable, IEquatable<Quadrilateral>, IGroup<Vert
public override bool Equals([NotNullWhen(true)] object? obj) public override bool Equals([NotNullWhen(true)] object? obj)
{ {
if (obj == null || obj.GetType() != typeof(Quadrilateral)) return false; if (obj == null || obj.GetType() != typeof(Quadrilateral)) return base.Equals(obj);
return Equals((Quadrilateral)obj); return Equals((Quadrilateral)obj);
} }
public bool Equals(Quadrilateral other) => A == other.A && B == other.B && C == other.C && D == other.D; public bool Equals(Quadrilateral other) => A == other.A && B == other.B && C == other.C && D == other.D;
@ -289,6 +289,11 @@ public struct Quadrilateral : ICloneable, IEquatable<Quadrilateral>, IGroup<Vert
} }
public Vert[] ToArray() => new Vert[] { A, B, C, D }; public Vert[] ToArray() => new Vert[] { A, B, C, D };
public Fill<Vert> ToFill()
{
Quadrilateral @this = this;
return i => @this[i];
}
public List<Vert> ToList() => new() { A, B, C, D }; public List<Vert> ToList() => new() { A, B, C, D };
public float[] ToFloatArray() => new float[] { A.position.x, A.position.y, A.position.z, public float[] ToFloatArray() => new float[] { A.position.x, A.position.y, A.position.z,

View File

@ -69,11 +69,11 @@ public struct Sphere : ICloneable, IClosest<Vert>, IComparable<Sphere>, ICompara
public override bool Equals([NotNullWhen(true)] object? obj) public override bool Equals([NotNullWhen(true)] object? obj)
{ {
if (obj == null) return false; if (obj == null) return base.Equals(obj);
Type type = obj.GetType(); Type type = obj.GetType();
if (type == typeof(Sphere)) return Equals((Sphere)obj); if (type == typeof(Sphere)) return Equals((Sphere)obj);
if (type == typeof(float)) return Equals((float)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(float other) => Volume == other;
public bool Equals(Sphere other) => center == other.center && radius == other.radius; public bool Equals(Sphere other) => center == other.center && radius == other.radius;

View File

@ -218,7 +218,7 @@ public struct Triangle : ICloneable, IEquatable<Triangle>, IGroup<Vert>
public override bool Equals([NotNullWhen(true)] object? obj) 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); return Equals((Triangle)obj);
} }
public bool Equals(Triangle other) => A == other.A && B == other.B && C == other.C; public bool Equals(Triangle other) => A == other.A && B == other.B && C == other.C;
@ -240,6 +240,11 @@ public struct Triangle : ICloneable, IEquatable<Triangle>, IGroup<Vert>
} }
public Vert[] ToArray() => new Vert[] { A, B, C }; public Vert[] ToArray() => new Vert[] { A, B, C };
public Fill<Vert> ToFill()
{
Triangle @this = this;
return i => @this[i];
}
public List<Vert> ToList() => new() { A, B, C }; public List<Vert> ToList() => new() { A, B, C };
public float[] ToFloatArray() => new float[] { A.position.x, A.position.y, A.position.z, public float[] ToFloatArray() => new float[] { A.position.x, A.position.y, A.position.z,

View File

@ -17,7 +17,7 @@ public struct Vert : ICloneable, IEquatable<Vert>, IGroup<float>
public Float3 position; 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(Float3 pos) => position = pos;
public Vert(float x, float y) : this(new Float2(x, y)) { } 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)) { } public Vert(float x, float y, float z) : this(new Float3(x, y, z)) { }
@ -61,7 +61,7 @@ public struct Vert : ICloneable, IEquatable<Vert>, IGroup<float>
public override bool Equals([NotNullWhen(true)] object? obj) 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); return Equals((Vert)obj);
} }
public bool Equals(Vert other) => position == other.position; public bool Equals(Vert other) => position == other.position;
@ -76,8 +76,15 @@ public struct Vert : ICloneable, IEquatable<Vert>, IGroup<float>
public IEnumerator<float> GetEnumerator() => position.GetEnumerator(); public IEnumerator<float> GetEnumerator() => position.GetEnumerator();
public float[] ToArray() => position.ToArray(); public float[] ToArray() => position.ToArray();
public Fill<float> ToFill()
{
Vert @this = this;
return i => @this[i];
}
public List<float> ToList() => position.ToList(); public List<float> 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 a, Vert b) => new(a.position + b.position);
public static Vert operator -(Vert d) => new(-d.position); public static Vert operator -(Vert d) => new(-d.position);
public static Vert operator -(Vert a, Vert b) => new(a.position - b.position); public static Vert operator -(Vert a, Vert b) => new(a.position - b.position);

View File

@ -11,7 +11,7 @@ public struct Int2 : ICloneable, IComparable<Int2>, IEquatable<Int2>, IGroup<int
public static Int2 Zero => new(0, 0); public static Int2 Zero => new(0, 0);
public float Magnitude => Mathf.Sqrt(x * x + y * y); 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; public int x, y;
@ -139,7 +139,7 @@ public struct Int2 : ICloneable, IComparable<Int2>, IEquatable<Int2>, IGroup<int
public int CompareTo(Int2 other) => Magnitude.CompareTo(other.Magnitude); public int CompareTo(Int2 other) => Magnitude.CompareTo(other.Magnitude);
public override bool Equals([NotNullWhen(true)] object? obj) 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); return Equals((Int2)obj);
} }
public bool Equals(Int2 other) => x == other.x && y == other.y; public bool Equals(Int2 other) => x == other.x && y == other.y;
@ -160,15 +160,24 @@ public struct Int2 : ICloneable, IComparable<Int2>, IEquatable<Int2>, IGroup<int
} }
public int[] ToArray() => new[] { x, y }; public int[] ToArray() => new[] { x, y };
public Fill<int> ToFill()
{
Int2 @this = this;
return i => @this[i];
}
public List<int> ToList() => new() { x, y }; public List<int> 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 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 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, 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, 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, int b) => new(a.x / b, a.y / b); 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); 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<Int2>, IEquatable<Int2>, IGroup<int
public static bool operator >=(Int2 a, Int2 b) => a == b || a > b; public static bool operator >=(Int2 a, Int2 b) => a == b || a > b;
public static bool operator <=(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(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(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(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(Int3 val) => new(val.x, val.y);
public static explicit operator Int2(Int4 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); public static explicit operator Int2(Vert val) => new((int)val.position.x, (int)val.position.y);

View File

@ -13,7 +13,7 @@ public struct Int3 : ICloneable, IComparable<Int3>, IEquatable<Int3>, IGroup<int
public static Int3 Zero => new(0, 0, 0); public static Int3 Zero => new(0, 0, 0);
public float Magnitude => Mathf.Sqrt(x * x + y * y + z * z); 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 XY => new(x, y);
public Int2 XZ => new(x, z); public Int2 XZ => new(x, z);
@ -159,7 +159,7 @@ public struct Int3 : ICloneable, IComparable<Int3>, IEquatable<Int3>, IGroup<int
public int CompareTo(Int3 other) => Magnitude.CompareTo(other.Magnitude); public int CompareTo(Int3 other) => Magnitude.CompareTo(other.Magnitude);
public override bool Equals([NotNullWhen(true)] object? obj) 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); return Equals((Int3)obj);
} }
public bool Equals(Int3 other) => x == other.x && y == other.y && z == other.z; public bool Equals(Int3 other) => x == other.x && y == other.y && z == other.z;
@ -179,15 +179,24 @@ public struct Int3 : ICloneable, IComparable<Int3>, IEquatable<Int3>, IGroup<int
} }
public int[] ToArray() => new[] { x, y, z }; public int[] ToArray() => new[] { x, y, z };
public Fill<int> ToFill()
{
Int3 @this = this;
return i => @this[i];
}
public List<int> ToList() => new() { x, y, z }; public List<int> 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 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 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, 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, 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, int b) => new(a.x / b, a.y / b, a.z / b); 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); 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<Int3>, IEquatable<Int3>, IGroup<int
public static bool operator >=(Int3 a, Int3 b) => a == b || a > b; public static bool operator >=(Int3 a, Int3 b) => a == b || a > b;
public static bool operator <=(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(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(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 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 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(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, public static explicit operator Int3(Vert val) => new((int)val.position.x, (int)val.position.y,
(int)val.position.z); (int)val.position.z);
public static explicit operator Int3(RGBA val) => (Int3)val.ToRGBAByte(); public static explicit operator Int3(RGBA val) => (Int3)val.ToRGBAByte();

View File

@ -15,7 +15,7 @@ public struct Int4 : ICloneable, IComparable<Int4>, IEquatable<Int4>, IGroup<int
public static Int4 Zero => new(0, 0, 0, 0); public static Int4 Zero => new(0, 0, 0, 0);
public float Magnitude => Mathf.Sqrt(x * x + y * y + z * z + w * w); 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 XY => new(x, y);
public Int2 XZ => new(x, z); public Int2 XZ => new(x, z);
@ -175,7 +175,7 @@ public struct Int4 : ICloneable, IComparable<Int4>, IEquatable<Int4>, IGroup<int
public int CompareTo(Int4 other) => Magnitude.CompareTo(other.Magnitude); public int CompareTo(Int4 other) => Magnitude.CompareTo(other.Magnitude);
public override bool Equals([NotNullWhen(true)] object? obj) 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); return Equals((Int4)obj);
} }
public bool Equals(Int4 other) => x == other.x && y == other.y && z == other.z && w == other.w; 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<Int4>, IEquatable<Int4>, IGroup<int
} }
public int[] ToArray() => new[] { x, y, z, w }; public int[] ToArray() => new[] { x, y, z, w };
public Fill<int> ToFill()
{
Int4 @this = this;
return i => @this[i];
}
public List<int> ToList() => new() { x, y, z, w }; public List<int> 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); 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<Int4>, IEquatable<Int4>, IGroup<int
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); 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, 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, int b) => new(a.x / b, a.y / b, a.z / b, a.w / b); 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); 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<Int4>, IEquatable<Int4>, IGroup<int
public static bool operator >=(Int4 a, Int4 b) => a == b || a > b; public static bool operator >=(Int4 a, Int4 b) => a == b || a > b;
public static bool operator <=(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(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(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 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(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 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, public static explicit operator Int4(Vert val) => new((int)val.position.x, (int)val.position.y,
(int)val.position.z, 0); (int)val.position.z, 0);
public static explicit operator Int4(RGBA val) => val.ToRGBAByte(); public static explicit operator Int4(RGBA val) => val.ToRGBAByte();

View File

@ -17,6 +17,7 @@ public static class Mathf
public static float ArcSin(float value) => (float)Math.Asin(value); 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 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) 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) / public static int Combinations(int total, int size) => Factorial(total) /
(Factorial(size) * Factorial(total - size)); (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 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 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 Csc(float radians) => 1 / Sin(radians);
public static float Divide(float val, params float[] dividends) public static float Divide(float val, params float[] dividends)
@ -67,6 +71,25 @@ public static class Mathf
return val; 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) public static int Factorial(int amount)
{ {
if (amount < 0) return 0; if (amount < 0) return 0;
@ -147,6 +170,13 @@ public static class Mathf
foreach (int i in vals) val = i > val ? i : val; foreach (int i in vals) val = i > val ? i : val;
return val; return val;
} }
public static T? Max<T>(params T[] vals) where T : IComparable<T>
{
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) public static float Median(params float[] vals)
{ {
@ -154,7 +184,8 @@ public static class Mathf
float valA = vals[Floor(index)], valB = vals[Ceiling(index)]; float valA = vals[Floor(index)], valB = vals[Ceiling(index)];
return Average(valA, valB); 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<int>(vals);
public static T Median<T>(params T[] vals) => vals[Floor(Average(0, vals.Length - 1))];
public static float Min(Equation equ, float min, float max, float step = Calculus.DefaultStep) 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; foreach (int i in vals) val = i < val ? i : val;
return val; return val;
} }
public static T? Min<T>(params T[] vals) where T : IComparable<T>
{
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<T>(params T[] vals) where T : IEquatable<T> public static (T value, int occurences) Mode<T>(params T[] vals) where T : IEquatable<T>
{ {
@ -245,8 +283,10 @@ public static class Mathf
public static float Round(float num, float nearest) => nearest * Round(num / nearest); public static float Round(float num, float nearest) => nearest * Round(num / nearest);
public static int RoundInt(float num) => (int)Round(num); 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 Sec(float radians) => 1 / Cos(radians);
public static float Sin(Angle angle) => Sin(angle.Radians);
public static float Sin(float radians) public static float Sin(float radians)
{ {
// Really close polynomial to sin(x) (when modded by 2pi). RMSE of 0.000003833 // 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 // Known as stdev
public static float StandardDeviation(params float[] vals) => Sqrt(Variance(vals)); 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 float Tan(float radians) => Sin(radians) / Cos(radians);
public static T[] UniqueItems<T>(params T[] vals) where T : IEquatable<T> public static T[] UniqueItems<T>(params T[] vals) where T : IEquatable<T>

View File

@ -0,0 +1,193 @@
namespace Nerd_STF.Mathematics.NumberSystems;
public struct Complex : ICloneable, IComparable<Complex>, IEquatable<Complex>, IGroup<float>
{
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<float> fill) : this(fill(0), fill(1)) { }
public Complex(Fill<int> 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<Float2> 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<Float2> 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<Float2> 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<Float2> floats = new();
foreach (Complex c in vals) floats.Add(c);
return Float2.Median(floats.ToArray());
}
public static Complex Max(params Complex[] vals)
{
List<Float2> floats = new();
foreach (Complex c in vals) floats.Add(c);
return Float2.Max(floats.ToArray());
}
public static Complex Min(params Complex[] vals)
{
List<Float2> floats = new();
foreach (Complex c in vals) floats.Add(c);
return Float2.Min(floats.ToArray());
}
public static Complex Product(params Complex[] vals)
{
List<Float2> 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<Float2> floats = new();
foreach (Complex c in vals) floats.Add(c);
return Float2.Subtract(num, floats.ToArray());
}
public static Complex Sum(params Complex[] vals)
{
List<Float2> 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<float> GetEnumerator()
{
yield return u;
yield return i;
}
public float[] ToArray() => new[] { u, i };
public Fill<float> ToFill()
{
Complex @this = this;
return i => @this[i];
}
public List<float> 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<float> fill) => new(fill);
public static implicit operator Complex(Fill<int> fill) => new(fill);
}

View File

@ -0,0 +1,309 @@
namespace Nerd_STF.Mathematics.NumberSystems;
public struct Quaternion : ICloneable, IComparable<Quaternion>, IEquatable<Quaternion>, IGroup<float>
{
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<float> fill) : this(fill(0), fill(1), fill(2)) { }
public Quaternion(Fill<int> 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<Float4> 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<Float4> 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<Float4> 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<Float4> floats = new();
foreach (Quaternion q in vals) floats.Add(q);
return Float4.Median(floats.ToArray());
}
public static Quaternion Max(params Quaternion[] vals)
{
List<Float4> floats = new();
foreach (Quaternion q in vals) floats.Add(q);
return Float4.Max(floats.ToArray());
}
public static Quaternion Min(params Quaternion[] vals)
{
List<Float4> floats = new();
foreach (Quaternion q in vals) floats.Add(q);
return Float4.Min(floats.ToArray());
}
public static Quaternion Product(params Quaternion[] vals)
{
List<Float4> 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<Float4> floats = new();
foreach (Quaternion q in vals) floats.Add(q);
return Float4.Subtract(num, floats.ToArray());
}
public static Quaternion Sum(params Quaternion[] vals)
{
List<Float4> 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<float> GetEnumerator()
{
yield return u;
yield return i;
yield return j;
yield return k;
}
public float[] ToArray() => new[] { u, i, j, k };
public Fill<float> ToFill()
{
Quaternion @this = this;
return i => @this[i];
}
public List<float> 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<float> fill) => new(fill);
public static implicit operator Quaternion(Fill<int> fill) => new(fill);
}

View File

@ -1,4 +1,4 @@
namespace Nerd_STF.Mathematics; namespace Nerd_STF.Mathematics.Samples;
public static class Constants public static class Constants
{ {

View File

@ -0,0 +1,26 @@
namespace Nerd_STF.Mathematics.Samples;
public static class Equations
{
public static readonly Fill<int> 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
}
}

View File

@ -10,5 +10,9 @@ global using System.Threading.Tasks;
global using Nerd_STF; global using Nerd_STF;
global using Nerd_STF.Graphics; global using Nerd_STF.Graphics;
global using Nerd_STF.Exceptions; global using Nerd_STF.Exceptions;
global using Nerd_STF.Extensions;
global using Nerd_STF.Mathematics; global using Nerd_STF.Mathematics;
global using Nerd_STF.Mathematics.Algebra;
global using Nerd_STF.Mathematics.Geometry; global using Nerd_STF.Mathematics.Geometry;
global using Nerd_STF.Mathematics.NumberSystems;
global using Nerd_STF.Mathematics.Samples;

5
Nerd_STF/Modifier.cs Normal file
View File

@ -0,0 +1,5 @@
namespace Nerd_STF;
public delegate float Modifier(int index, float value);
public delegate T Modifier<T>(int index, T value);
public delegate VT Modifier<IT, VT>(IT index, VT value);

5
Nerd_STF/Modifier2D.cs Normal file
View File

@ -0,0 +1,5 @@
namespace Nerd_STF;
public delegate float Modifier2D(Int2 index, float value);
public delegate T Modifier2D<T>(Int2 index, T value);
public delegate VT Modifier2D<IT, VT>(IT x, IT y, VT value);