Compare commits

...

8 Commits

Author SHA1 Message Date
6d44cbe438 Released v2.3.2 2023-03-09 16:44:23 -05:00
fe83380366 the exact changelog looks weird on the website. 2022-11-12 11:24:34 -05:00
ed967797e6 Released v2.3.1 2022-11-12 11:21:34 -05:00
e895bbb5b2 Removed the .sln file from the git.
It was causing cli build errors because the "Testing" project doesn't exist.
Now it works first try.
2022-11-02 07:34:12 -04:00
efcf2b94e9 The logo is now included in the build files. 2022-10-31 12:12:36 -04:00
9a1821cb10
Merge pull request #14 from That-One-Nerd/main
Released v2.3.1.52
2022-10-31 11:56:15 -04:00
deff917a60
No longer in the gitignore 2022-10-31 11:50:49 -04:00
07cb6a2369 Released alpha v2.3.1.52 2022-10-31 11:39:51 -04:00
106 changed files with 3629 additions and 1670 deletions

18
.gitignore vendored
View File

@ -1,13 +1,14 @@
# Visual Studio stuff
*.csproj
*.editorconfig
*.sln
# Useless Visual Studio stuff
.vs/
/Nerd_STF/.vs/
/Nerd_STF/Nerd_STF.csproj.user
*.sln
# Build Stuff
/Nerd_STF/obj
/Nerd_STF/bin/Debug
/Nerd_STF/bin/Release/net6.0/Nerd_STF.deps.json
/Nerd_STF/bin/Release/net6.0/Nerd_STF.dll
/Nerd_STF/bin/Release/net6.0/Nerd_STF.pdb
/Nerd_STF/bin
*.dll
*.pdb
# Testing project
/Testing
@ -15,6 +16,7 @@
# Nuget
/Nerd_STF/LICENSE
*.nupkg
*.snupkg
# Personal
/Nerd_STF/TODO.md

File diff suppressed because it is too large Load Diff

BIN
Extras/Logo Square.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

4
Nerd_STF/.editorconfig Normal file
View File

@ -0,0 +1,4 @@
[*.cs]
# CA1050: Declare types in namespaces
dotnet_diagnostic.CA1050.severity = warning

View File

@ -1,8 +1,7 @@
using System.Runtime.Serialization;
namespace Nerd_STF.Exceptions;
namespace Nerd_STF.Exceptions;
[Serializable]
[Obsolete("The Polygon struct is a garbage fire, and will be fixed in v2.4.0", false)]
public class DifferingVertCountException : Nerd_STFException
{
public string? ParamName;

View File

@ -1,6 +1,4 @@
using System.Runtime.Serialization;
namespace Nerd_STF.Exceptions;
namespace Nerd_STF.Exceptions;
[Serializable]
public class DisconnectedLinesException : Nerd_STFException

View File

@ -1,11 +1,9 @@
using System.Runtime.Serialization;
namespace Nerd_STF.Exceptions;
namespace Nerd_STF.Exceptions;
[Serializable]
public class InvalidSizeException : Nerd_STFException
{
public InvalidSizeException() : this("Argument size is invalid.") { }
public InvalidSizeException() : base("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

@ -1,11 +1,9 @@
using System.Runtime.Serialization;
namespace Nerd_STF.Exceptions;
namespace Nerd_STF.Exceptions;
[Serializable]
public class MathException : Nerd_STFException
{
public MathException() : base() { }
public MathException() : base("A calculation error occured.") { }
public MathException(string message) : base(message) { }
public MathException(string message, Exception inner) : base(message, inner) { }
protected MathException(SerializationInfo info, StreamingContext context) : base(info, context) { }

View File

@ -1,11 +1,11 @@
using System.Runtime.Serialization;
namespace Nerd_STF.Exceptions;
namespace Nerd_STF.Exceptions;
[Serializable]
public class Nerd_STFException : Exception
{
public Nerd_STFException() { }
public Nerd_STFException() : base("An unknown error occured within Nerd_STF.") { }
public Nerd_STFException(Exception inner)
: base("An unknown error occured within Nerd_STF.", inner) { }
public Nerd_STFException(string message) : base(message) { }
public Nerd_STFException(string message, Exception inner) : base(message, inner) { }
protected Nerd_STFException(SerializationInfo info, StreamingContext context) : base(info, context) { }

View File

@ -1,9 +1,7 @@
using System.Runtime.Serialization;
namespace Nerd_STF.Exceptions;
namespace Nerd_STF.Exceptions;
[Serializable]
public class NoInverseException : Exception
public class NoInverseException : Nerd_STFException
{
public Matrix? Matrix;

View File

@ -1,11 +1,9 @@
using System.Runtime.Serialization;
namespace Nerd_STF.Exceptions;
namespace Nerd_STF.Exceptions;
[Serializable]
public class UndefinedException : MathException
{
public UndefinedException() : this("The equation calculated resulted in an undefined number.") { }
public UndefinedException() : this("A calculation has produced an undefined number.") { }
public UndefinedException(string message) : base(message) { }
public UndefinedException(string message, Exception inner) : base(message, inner) { }
protected UndefinedException(SerializationInfo info, StreamingContext context) : base(info, context) { }

View File

@ -2,23 +2,60 @@
public static class Container2DExtension
{
public static T[] Flatten<T>(this T[,] array, Int2 size)
public static T[] Flatten<T>(this T[,] array, Int2? size = null)
{
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];
size ??= GetSize(array);
T[] res = new T[size.Value.x * size.Value.y];
for (int x = 0; x < size.Value.x; x++) for (int y = 0; y < size.Value.y; y++)
res[x + y * size.Value.x] = array[y, x];
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];
for (int i = 0; i < length; i++) res[i] = array[i, column];
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];
for (int i = 0; i < length; i++) res[i] = array[row, i];
return res;
}
public static Int2 GetSize<T>(this T[,] array)
{
Int2 size = Int2.Zero;
try
{
while (true)
{
_ = array[size.x, 0];
size.x++;
}
}
catch (IndexOutOfRangeException) { }
try
{
while (true)
{
_ = array[0, size.y];
size.y++;
}
}
catch (IndexOutOfRangeException) { }
return size;
}
public static T[,] SwapDimensions<T>(this T[,] array, Int2? size = null)
{
size ??= GetSize(array);
T[,] vals = new T[size.Value.y, size.Value.x];
for (int x = 0; x < size.Value.y; x++) for (int y = 0; y < size.Value.x; y++) vals[x, y] = array[y, x];
return vals;
}
}

View File

@ -2,6 +2,8 @@
public static class ConversionExtension
{
[Obsolete("This extension turns out to already exist as a constructor in the " +
"System.Collections.Generic.Dictionary<TKey, TValue> type. This will be removed in v2.4.0")]
public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>
(this IEnumerable<KeyValuePair<TKey, TValue>> pairs)
where TKey : notnull
@ -10,4 +12,8 @@ public static class ConversionExtension
foreach (KeyValuePair<TKey, TValue> pair in pairs) res.Add(pair.Key, pair.Value);
return res;
}
public static Fill<T> ToFill<T>(this T[] arr) => i => arr[i];
public static Fill<T> ToFill<T>(this T[,] arr, Int2? size) => arr.Flatten(size).ToFill();
public static Fill2D<T> ToFill2D<T>(this T[,] arr) => (x, y) => arr[x, y];
}

View File

@ -4,16 +4,9 @@ public static class EquationExtension
{
public static Equation Scale(this 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),
ScaleType.X => x => equ(x / value),
ScaleType.Y => x => value * equ(x),
ScaleType.Both => x => value * equ(x / value),
_ => throw new ArgumentException("Unknown scale type " + type)
};
public enum ScaleType
{
X = 1,
Y = 2,
Both = X | Y
}
}

View File

@ -1,4 +1,6 @@
namespace Nerd_STF;
[Obsolete("This delegate is kind of useless and will be removed in Nerd_STF v2.4.0.")]
public delegate void Foreach(object item);
[Obsolete("This delegate is kind of useless and will be removed in Nerd_STF v2.4.0.")]
public delegate void Foreach<T>(T item);

View File

@ -0,0 +1,29 @@
using System.Numerics;
namespace Nerd_STF.Graphics.Abstract;
public interface IColor : IEquatable<IColor>
{
public CMYKA ToCMYKA();
public HSVA ToHSVA();
public RGBA ToRGBA();
public CMYKAByte ToCMYKAByte();
public HSVAByte ToHSVAByte();
public RGBAByte ToRGBAByte();
}
public interface IColor<T> : IColor where T : IColor<T>
{
public static abstract bool operator ==(T a, CMYKA b);
public static abstract bool operator !=(T a, CMYKA b);
public static abstract bool operator ==(T a, CMYKAByte b);
public static abstract bool operator !=(T a, CMYKAByte b);
public static abstract bool operator ==(T a, HSVA b);
public static abstract bool operator !=(T a, HSVA b);
public static abstract bool operator ==(T a, HSVAByte b);
public static abstract bool operator !=(T a, HSVAByte b);
public static abstract bool operator ==(T a, RGBA b);
public static abstract bool operator !=(T a, RGBA b);
public static abstract bool operator ==(T a, RGBAByte b);
public static abstract bool operator !=(T a, RGBAByte b);
}

View File

@ -0,0 +1,9 @@
namespace Nerd_STF.Graphics.Abstract;
public interface IColorByte : IColor, IGroup<byte>
{
public int[] ToArrayInt();
public Fill<int> ToFillInt();
public List<int> ToListInt();
}
public interface IColorByte<T> : IColor<T>, IColorByte where T : struct, IColorByte<T> { }

View File

@ -0,0 +1,4 @@
namespace Nerd_STF.Graphics.Abstract;
public interface IColorFloat : IColor, IGroup<float> { }
public interface IColorFloat<T> : IColor<T>, IColorFloat where T : struct, IColorFloat<T> { }

View File

@ -0,0 +1,17 @@
namespace Nerd_STF.Graphics.Abstract;
public interface IColorPresets<T> where T : IColorPresets<T>
{
public static abstract T Black { get; }
public static abstract T Blue { get; }
public static abstract T Clear { get; }
public static abstract T Cyan { get; }
public static abstract T Gray { get; }
public static abstract T Green { get; }
public static abstract T Magenta { get; }
public static abstract T Orange { get; }
public static abstract T Purple { get; }
public static abstract T Red { get; }
public static abstract T White { get; }
public static abstract T Yellow { get; }
}

View File

@ -1,6 +1,8 @@
namespace Nerd_STF.Graphics;
public struct CMYKA : IColor, IEquatable<CMYKA>
public record struct CMYKA : IAverage<CMYKA>, IClamp<CMYKA>, IColorFloat<CMYKA>, IColorPresets<CMYKA>,
IEquatable<CMYKA>, IIndexAll<float>, IIndexRangeAll<float>, ILerp<CMYKA, float>, IMedian<CMYKA>,
ISplittable<CMYKA, (float[] Cs, float[] Ms, float[] Ys, float[] Ks, float[] As)>
{
public static CMYKA Black => new(0, 0, 0, 1);
public static CMYKA Blue => new(1, 1, 0, 0);
@ -103,6 +105,28 @@ public struct CMYKA : IColor, IEquatable<CMYKA>
}
}
}
public float this[Index index]
{
get => this[index.IsFromEnd ? 5 - index.Value : index.Value];
set => this[index.IsFromEnd ? 5 - index.Value : index.Value] = value;
}
public float[] this[Range range]
{
get
{
int start = range.Start.IsFromEnd ? 5 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 5 - range.End.Value : range.End.Value;
List<float> res = new();
for (int i = start; i < end; i++) res.Add(this[i]);
return res.ToArray();
}
set
{
int start = range.Start.IsFromEnd ? 5 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 5 - range.End.Value : range.End.Value;
for (int i = start; i < end; i++) this[i] = value[i];
}
}
public static CMYKA Average(params CMYKA[] vals)
{
@ -111,16 +135,12 @@ public struct CMYKA : IColor, IEquatable<CMYKA>
return val / vals.Length;
}
public static CMYKA Ceiling(CMYKA val) => new(Mathf.Ceiling(val.C), Mathf.Ceiling(val.M),
Mathf.Ceiling(val.Y), Mathf.Ceiling(val.K), Mathf.Ceiling(val.A));
public static CMYKA Clamp(CMYKA val, CMYKA min, CMYKA max) =>
new(Mathf.Clamp(val.C, min.C, max.C),
Mathf.Clamp(val.M, min.M, max.M),
Mathf.Clamp(val.Y, min.Y, max.Y),
Mathf.Clamp(val.K, min.K, max.K),
Mathf.Clamp(val.A, min.A, max.A));
public static CMYKA Floor(CMYKA val) => new(Mathf.Floor(val.C), Mathf.Floor(val.M),
Mathf.Floor(val.Y), Mathf.Floor(val.K), Mathf.Floor(val.A));
public static CMYKA Lerp(CMYKA a, CMYKA b, float t, bool clamp = true) =>
new(Mathf.Lerp(a.C, b.C, t, clamp), Mathf.Lerp(a.M, b.M, t, clamp), Mathf.Lerp(a.Y, b.Y, t, clamp),
Mathf.Lerp(a.K, b.K, t, clamp), Mathf.Lerp(a.A, b.A, t, clamp));
@ -136,18 +156,6 @@ public struct CMYKA : IColor, IEquatable<CMYKA>
CMYKA valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
return Average(valA, valB);
}
public static CMYKA Max(params CMYKA[] vals)
{
(float[] Cs, float[] Ms, float[] Ys, float[] Ks, float[] As) = SplitArray(vals);
return new(Mathf.Max(Cs), Mathf.Max(Ms), Mathf.Max(Ys), Mathf.Max(Ks), Mathf.Max(As));
}
public static CMYKA Min(params CMYKA[] 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));
}
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)
{
@ -166,37 +174,14 @@ public struct CMYKA : IColor, IEquatable<CMYKA>
}
public bool Equals(IColor? col) => col != null && Equals(col.ToCMYKA());
public bool Equals(IColorByte? col) => col != null && Equals(col.ToCMYKA());
public bool Equals(CMYKA col) => A == 0 && col.A == 0 || K == 1 && col.K == 1 || C == col.C && M == col.M
&& Y == col.Y && K == col.K && A == col.A;
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null) return base.Equals(obj);
Type t = obj.GetType();
if (t == typeof(CMYKA)) return Equals((CMYKA)obj);
else if (t == typeof(RGBA)) return Equals((IColor)obj);
else if (t == typeof(HSVA)) return Equals((IColor)obj);
else if (t == typeof(IColor)) return Equals((IColor)obj);
else if (t == typeof(RGBAByte)) return Equals((IColorByte)obj);
else if (t == typeof(CMYKAByte)) return Equals((IColorByte)obj);
else if (t == typeof(HSVAByte)) return Equals((IColorByte)obj);
else if (t == typeof(IColorByte)) return Equals((IColorByte)obj);
return base.Equals(obj);
}
public override int GetHashCode() => C.GetHashCode() ^ M.GetHashCode() ^ Y.GetHashCode() ^ K.GetHashCode() ^ A.GetHashCode();
public string ToString(IFormatProvider provider) => "C: " + C.ToString(provider) + " M: " + M.ToString(provider)
+ " Y: " + Y.ToString(provider) + " K: " + K.ToString(provider)
+ " A: " + A.ToString(provider);
public string ToString(string? provider) => "C: " + C.ToString(provider) + " M: " + M.ToString(provider)
+ " Y: " + Y.ToString(provider) + " K: " + K.ToString(provider)
+ " A: " + A.ToString(provider);
public override string ToString() => ToString((string?)null);
public override int GetHashCode() => base.GetHashCode();
public RGBA ToRGBA()
{
float kInv = 1 - K, r = 1 - C, g = 1 - M, b = 1 - Y;
return new(r * kInv, g * kInv, b * kInv);
return new(r * kInv, g * kInv, b * kInv, A);
}
public CMYKA ToCMYKA() => this;
public HSVA ToHSVA() => ToRGBA().ToHSVA();
@ -224,7 +209,20 @@ public struct CMYKA : IColor, IEquatable<CMYKA>
yield return A;
}
public object Clone() => new CMYKA(C, M, Y, K, A);
private bool PrintMembers(StringBuilder builder)
{
builder.Append("C = ");
builder.Append(C);
builder.Append(", M = ");
builder.Append(M);
builder.Append(", Y = ");
builder.Append(Y);
builder.Append(", K = ");
builder.Append(K);
builder.Append(", A = ");
builder.Append(A);
return true;
}
public static CMYKA operator +(CMYKA a, CMYKA b) => new(a.C + b.C, a.M + b.M, a.Y + b.Y, a.K + b.K, a.A + b.A);
public static CMYKA operator -(CMYKA c) => new(1 - c.C, 1 - c.M, 1 - c.Y, 1 - c.K, c.A != 1 ? 1 - c.A : 1);
@ -233,25 +231,23 @@ public struct CMYKA : IColor, IEquatable<CMYKA>
public static CMYKA operator *(CMYKA a, float b) => new(a.C * b, a.M * b, a.Y * b, a.K * b, a.A * b);
public static CMYKA operator /(CMYKA a, CMYKA b) => new(a.C / b.C, a.M / b.M, a.Y / b.Y, a.K / b.K, a.A / b.A);
public static CMYKA operator /(CMYKA a, float b) => new(a.C / b, a.M / b, a.Y / b, a.K / b, a.A / b);
public static bool operator ==(CMYKA a, RGBA b) => a.Equals(b);
public static bool operator !=(CMYKA a, RGBA b) => !a.Equals(b);
public static bool operator ==(CMYKA a, CMYKA b) => a.Equals(b);
public static bool operator !=(CMYKA a, CMYKA b) => !a.Equals(b);
public static bool operator ==(CMYKA a, HSVA b) => a.Equals(b);
public static bool operator !=(CMYKA a, HSVA b) => !a.Equals(b);
public static bool operator ==(CMYKA a, RGBAByte b) => a.Equals((IColorByte?)b);
public static bool operator !=(CMYKA a, RGBAByte b) => !a.Equals((IColorByte?)b);
public static bool operator ==(CMYKA a, CMYKAByte b) => a.Equals((IColorByte?)b);
public static bool operator !=(CMYKA a, CMYKAByte b) => !a.Equals((IColorByte?)b);
public static bool operator ==(CMYKA a, HSVAByte b) => a.Equals((IColorByte?)b);
public static bool operator !=(CMYKA a, HSVAByte b) => !a.Equals((IColorByte?)b);
public static bool operator !=(CMYKA a, HSVA b) => a.Equals(b);
public static bool operator ==(CMYKA a, RGBA b) => a.Equals(b);
public static bool operator !=(CMYKA a, RGBA b) => a.Equals(b);
public static bool operator ==(CMYKA a, CMYKAByte b) => a.Equals(b);
public static bool operator !=(CMYKA a, CMYKAByte b) => a.Equals(b);
public static bool operator ==(CMYKA a, HSVAByte b) => a.Equals(b);
public static bool operator !=(CMYKA a, HSVAByte b) => a.Equals(b);
public static bool operator ==(CMYKA a, RGBAByte b) => a.Equals(b);
public static bool operator !=(CMYKA a, RGBAByte b) => a.Equals(b);
public static explicit operator CMYKA(Float3 val) => new(val.x, val.y, val.z, 0);
public static implicit operator CMYKA(Float4 val) => new(val.x, val.y, val.z, val.w);
public static implicit operator CMYKA(RGBA val) => val.ToCMYKA();
public static implicit operator CMYKA(HSVA val) => val.ToCMYKA();
public static implicit operator CMYKA(RGBAByte val) => val.ToCMYKA();
public static implicit operator CMYKA(RGBA val) => val.ToCMYKA();
public static implicit operator CMYKA(CMYKAByte val) => val.ToCMYKA();
public static implicit operator CMYKA(HSVAByte val) => val.ToCMYKA();
public static implicit operator CMYKA(RGBAByte val) => val.ToCMYKA();
public static implicit operator CMYKA(Fill<float> val) => new(val);
}

View File

@ -1,21 +1,50 @@
namespace Nerd_STF.Graphics;
public struct CMYKAByte : IColorByte, IEquatable<CMYKAByte>
public record struct CMYKAByte : IAverage<CMYKAByte>, IClamp<CMYKAByte>, IColorByte<CMYKAByte>,
IColorPresets<CMYKAByte>, IEquatable<CMYKAByte>, IIndexAll<int>, IIndexRangeAll<int>,
ILerp<CMYKAByte, float>, IMedian<CMYKAByte>,
ISplittable<CMYKAByte, (byte[] Cs, byte[] Ms, byte[] Ys, byte[] Ks, byte[] As)>
{
public static CMYKA Black => new(0, 0, 0, 255);
public static CMYKA Blue => new(255, 255, 0, 0);
public static CMYKA Clear => new(0, 0, 0, 0, 0);
public static CMYKA Cyan => new(255, 0, 0, 0);
public static CMYKA Gray => new(0, 0, 0, 127);
public static CMYKA Green => new(255, 0, 255, 0);
public static CMYKA Magenta => new(0, 255, 0, 0);
public static CMYKA Orange => new(0, 127, 255, 0);
public static CMYKA Purple => new(127, 255, 0, 0);
public static CMYKA Red => new(0, 255, 255, 0);
public static CMYKA White => new(0, 0, 0, 0);
public static CMYKA Yellow => new(0, 0, 255, 0);
public static CMYKAByte Black => new(0, 0, 0, 255);
public static CMYKAByte Blue => new(255, 255, 0, 0);
public static CMYKAByte Clear => new(0, 0, 0, 0, 0);
public static CMYKAByte Cyan => new(255, 0, 0, 0);
public static CMYKAByte Gray => new(0, 0, 0, 127);
public static CMYKAByte Green => new(255, 0, 255, 0);
public static CMYKAByte Magenta => new(0, 255, 0, 0);
public static CMYKAByte Orange => new(0, 127, 255, 0);
public static CMYKAByte Purple => new(127, 255, 0, 0);
public static CMYKAByte Red => new(0, 255, 255, 0);
public static CMYKAByte White => new(0, 0, 0, 0);
public static CMYKAByte Yellow => new(0, 0, 255, 0);
public byte C, M, Y, K, A;
public int C
{
get => p_c;
set => p_c = (byte)Mathf.Clamp(value, byte.MinValue, byte.MaxValue);
}
public int M
{
get => p_m;
set => p_m = (byte)Mathf.Clamp(value, byte.MinValue, byte.MaxValue);
}
public int Y
{
get => p_y;
set => p_y = (byte)Mathf.Clamp(value, byte.MinValue, byte.MaxValue);
}
public int K
{
get => p_k;
set => p_k = (byte)Mathf.Clamp(value, byte.MinValue, byte.MaxValue);
}
public int A
{
get => p_a;
set => p_a = (byte)Mathf.Clamp(value, byte.MinValue, byte.MaxValue);
}
private byte p_c, p_m, p_y, p_k, p_a;
public bool HasCyan => C > 0;
public bool HasMagenta => M > 0;
@ -30,16 +59,16 @@ public struct CMYKAByte : IColorByte, IEquatable<CMYKAByte>
public CMYKAByte(int c, int m, int y, int k) : this(c, m, y, k, 255) { }
public CMYKAByte(int c, int m, int y, int k, int a)
{
C = (byte)Mathf.Clamp(c, 0, 255);
M = (byte)Mathf.Clamp(m, 0, 255);
Y = (byte)Mathf.Clamp(y, 0, 255);
K = (byte)Mathf.Clamp(k, 0, 255);
A = (byte)Mathf.Clamp(a, 0, 255);
p_c = (byte)Mathf.Clamp(c, 0, 255);
p_m = (byte)Mathf.Clamp(m, 0, 255);
p_y = (byte)Mathf.Clamp(y, 0, 255);
p_k = (byte)Mathf.Clamp(k, 0, 255);
p_a = (byte)Mathf.Clamp(a, 0, 255);
}
public CMYKAByte(Fill<byte> fill) : this(fill(0), fill(1), fill(2), fill(3), fill(4)) { }
public CMYKAByte(Fill<int> fill) : this(fill(0), fill(1), fill(2), fill(3), fill(4)) { }
public byte this[int index]
public int this[int index]
{
get => index switch
{
@ -78,6 +107,28 @@ public struct CMYKAByte : IColorByte, IEquatable<CMYKAByte>
}
}
}
public int this[Index index]
{
get => this[index.IsFromEnd ? 5 - index.Value : index.Value];
set => this[index.IsFromEnd ? 5 - index.Value : index.Value] = value;
}
public int[] this[Range range]
{
get
{
int start = range.Start.IsFromEnd ? 5 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 5 - range.End.Value : range.End.Value;
List<int> res = new();
for (int i = start; i < end; i++) res.Add(this[i]);
return res.ToArray();
}
set
{
int start = range.Start.IsFromEnd ? 5 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 5 - range.End.Value : range.End.Value;
for (int i = start; i < end; i++) this[i] = value[i];
}
}
public static CMYKAByte Average(params CMYKAByte[] vals)
{
@ -101,16 +152,6 @@ public struct CMYKAByte : IColorByte, IEquatable<CMYKAByte>
CMYKAByte valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
return Average(valA, valB);
}
public static CMYKAByte Max(params CMYKAByte[] vals)
{
(int[] Cs, int[] Ms, int[] Ys, int[] Ks, int[] As) = SplitArrayInt(vals);
return new(Mathf.Max(Cs), Mathf.Max(Ms), Mathf.Max(Ys), Mathf.Max(Ks), Mathf.Max(As));
}
public static CMYKAByte Min(params CMYKAByte[] vals)
{
(int[] Cs, int[] Ms, int[] Ys, int[] Ks, int[] As) = SplitArrayInt(vals);
return new(Mathf.Min(Cs), Mathf.Min(Ms), Mathf.Min(Ys), Mathf.Min(Ks), Mathf.Min(As));
}
public static (byte[] Cs, byte[] Ms, byte[] Ys, byte[] Ks, byte[] As) SplitArray(params CMYKAByte[] vals)
{
@ -119,11 +160,11 @@ public struct CMYKAByte : IColorByte, IEquatable<CMYKAByte>
As = new byte[vals.Length];
for (int i = 0; i < vals.Length; i++)
{
Cs[i] = vals[i].C;
Ms[i] = vals[i].M;
Ys[i] = vals[i].Y;
Ks[i] = vals[i].K;
As[i] = vals[i].A;
Cs[i] = vals[i].p_c;
Ms[i] = vals[i].p_m;
Ys[i] = vals[i].p_y;
Ks[i] = vals[i].p_k;
As[i] = vals[i].p_a;
}
return (Cs, Ms, Ys, Ks, As);
}
@ -143,62 +184,58 @@ public struct CMYKAByte : IColorByte, IEquatable<CMYKAByte>
return (Cs, Ms, Ys, Ks, As);
}
public bool Equals(IColor? col) => col != null && Equals(col.ToCMYKAByte());
public bool Equals(IColorByte? col) => col != null && Equals(col.ToCMYKAByte());
public bool Equals(CMYKAByte col) => A == 0 && col.A == 0 || K == 1 && col.K == 255 || C == col.C && M == col.M
&& Y == col.Y && K == col.K && A == col.A;
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null) return base.Equals(obj);
Type t = obj.GetType();
if (t == typeof(CMYKAByte)) return Equals((CMYKAByte)obj);
else if (t == typeof(RGBA)) return Equals((IColor)obj);
else if (t == typeof(HSVA)) return Equals((IColor)obj);
else if (t == typeof(IColor)) return Equals((IColor)obj);
else if (t == typeof(RGBAByte)) return Equals((IColorByte)obj);
else if (t == typeof(CMYKA)) return Equals((IColor)obj);
else if (t == typeof(HSVAByte)) return Equals((IColorByte)obj);
else if (t == typeof(IColorByte)) return Equals((IColorByte)obj);
return base.Equals(obj);
}
public override int GetHashCode() => C.GetHashCode() ^ M.GetHashCode() ^ Y.GetHashCode()
^ K.GetHashCode() ^ A.GetHashCode();
public string ToString(IFormatProvider provider) => "C: " + C.ToString(provider)
+ " M: " + M.ToString(provider) + " Y: " + Y.ToString(provider)
+ " K: " + K.ToString(provider) + " A: " + A.ToString(provider);
public string ToString(string? provider) => "C: " + C.ToString(provider)
+ " M: " + M.ToString(provider) + " Y: " + Y.ToString(provider)
+ " K: " + K.ToString(provider) + " A: " + A.ToString(provider);
public override string ToString() => ToString((string?)null);
public bool Equals(IColor? col) => col != null && Equals(col.ToCMYKAByte());
public override int GetHashCode() => base.GetHashCode();
public RGBA ToRGBA() => ToCMYKA().ToRGBA();
public CMYKA ToCMYKA() => new(C / 255f, M / 255f, Y / 255f, K / 255f, A / 255f);
public HSVA ToHSVA() => ToRGBA().ToHSVA();
public HSVA ToHSVA() => ToCMYKA().ToHSVA();
public RGBAByte ToRGBAByte() => ToCMYKA().ToRGBAByte();
public CMYKAByte ToCMYKAByte() => this;
public HSVAByte ToHSVAByte() => ToRGBA().ToHSVAByte();
public HSVAByte ToHSVAByte() => ToCMYKA().ToHSVAByte();
public byte[] ToArray() => new[] { C, M, Y, K, A };
public byte[] ToArray() => new[] { p_c, p_m, p_y, p_k, p_a };
public int[] ToArrayInt() => new[] { C, M, Y, K, A };
public Fill<byte> ToFill()
{
CMYKAByte @this = this;
return i => (byte)@this[i];
}
public Fill<int> ToFillInt()
{
CMYKAByte @this = this;
return i => @this[i];
}
public List<byte> ToList() => new() { C, M, Y, K, A };
public List<byte> ToList() => new() { p_c, p_m, p_y, p_k, p_a };
public List<int> ToListInt() => new() { C, M, Y, K, A };
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<byte> GetEnumerator()
{
yield return C;
yield return M;
yield return Y;
yield return K;
yield return A;
yield return p_c;
yield return p_m;
yield return p_y;
yield return p_k;
yield return p_a;
}
public object Clone() => new CMYKAByte(C, M, Y, K, A);
private bool PrintMembers(StringBuilder builder)
{
builder.Append("C = ");
builder.Append(C);
builder.Append(", M = ");
builder.Append(M);
builder.Append(", Y = ");
builder.Append(Y);
builder.Append(", K = ");
builder.Append(K);
builder.Append(", A = ");
builder.Append(A);
return true;
}
public static CMYKAByte operator +(CMYKAByte a, CMYKAByte b) =>
new(a.C + b.C, a.M + b.M, a.Y + b.Y, a.K + b.K, a.A + b.A);
@ -216,26 +253,24 @@ public struct CMYKAByte : IColorByte, IEquatable<CMYKAByte>
public static CMYKAByte operator /(CMYKAByte a, int b) =>
new(a.C / b, a.M / b, a.Y / b, a.K / b, a.A / b);
public static CMYKAByte operator /(CMYKAByte a, float b) => (a.ToCMYKA() / b).ToCMYKAByte();
public static bool operator ==(CMYKAByte a, RGBA b) => a.Equals((IColor?)b);
public static bool operator !=(CMYKAByte a, RGBA b) => !a.Equals((IColor?)b);
public static bool operator ==(CMYKAByte a, CMYKA b) => a.Equals((IColor?)b);
public static bool operator !=(CMYKAByte a, CMYKA b) => !a.Equals((IColor?)b);
public static bool operator ==(CMYKAByte a, HSVA b) => a.Equals((IColor?)b);
public static bool operator !=(CMYKAByte a, HSVA b) => !a.Equals((IColor?)b);
public static bool operator ==(CMYKAByte a, RGBAByte b) => a.Equals(b);
public static bool operator !=(CMYKAByte a, RGBAByte b) => !a.Equals(b);
public static bool operator ==(CMYKAByte a, CMYKAByte b) => a.Equals(b);
public static bool operator !=(CMYKAByte a, CMYKAByte b) => !a.Equals(b);
public static bool operator ==(CMYKAByte a, CMYKA b) => a.Equals(b);
public static bool operator !=(CMYKAByte a, CMYKA b) => a.Equals(b);
public static bool operator ==(CMYKAByte a, HSVA b) => a.Equals(b);
public static bool operator !=(CMYKAByte a, HSVA b) => a.Equals(b);
public static bool operator ==(CMYKAByte a, RGBA b) => a.Equals(b);
public static bool operator !=(CMYKAByte a, RGBA b) => a.Equals(b);
public static bool operator ==(CMYKAByte a, HSVAByte b) => a.Equals(b);
public static bool operator !=(CMYKAByte a, HSVAByte b) => !a.Equals(b);
public static bool operator !=(CMYKAByte a, HSVAByte b) => a.Equals(b);
public static bool operator ==(CMYKAByte a, RGBAByte b) => a.Equals(b);
public static bool operator !=(CMYKAByte a, RGBAByte b) => a.Equals(b);
public static explicit operator CMYKAByte(Int3 val) => new(val.x, val.y, val.z, 0);
public static implicit operator CMYKAByte(Int4 val) => new(val.x, val.y, val.z, val.w);
public static implicit operator CMYKAByte(RGBA val) => val.ToCMYKAByte();
public static implicit operator CMYKAByte(HSVA val) => val.ToCMYKAByte();
public static implicit operator CMYKAByte(RGBAByte val) => val.ToCMYKAByte();
public static implicit operator CMYKAByte(RGBA val) => val.ToCMYKAByte();
public static implicit operator CMYKAByte(CMYKA val) => val.ToCMYKAByte();
public static implicit operator CMYKAByte(HSVAByte val) => val.ToCMYKAByte();
public static implicit operator CMYKAByte(RGBAByte val) => val.ToCMYKAByte();
public static implicit operator CMYKAByte(Fill<byte> val) => new(val);
public static implicit operator CMYKAByte(Fill<int> val) => new(val);
}

View File

@ -1,6 +1,8 @@
namespace Nerd_STF.Graphics;
public struct HSVA : IColor, IEquatable<HSVA>
public record struct HSVA : IAverage<HSVA>, IClamp<HSVA>, IColorFloat<HSVA>, IColorPresets<HSVA>, IEquatable<HSVA>,
IIndexAll<float>, IIndexRangeAll<float>, ILerp<HSVA, float>, IMedian<HSVA>,
ISplittable<HSVA, (Angle[] Hs, float[] Ss, float[] Vs, float[] As)>
{
public static HSVA Black => new(Angle.Zero, 0, 0);
public static HSVA Blue => new(new Angle(240), 1, 1);
@ -74,25 +76,47 @@ public struct HSVA : IColor, IEquatable<HSVA>
switch (index)
{
case 0:
H = new(Mathf.Clamp(value, 0, 1), Angle.Type.Normalized);
H = new(value, Angle.Type.Normalized);
break;
case 1:
S = Mathf.Clamp(value, 0, 1);
S = value;
break;
case 2:
V = Mathf.Clamp(value, 0, 1);
V = value;
break;
case 3:
A = Mathf.Clamp(value, 0, 1);
A = value;
break;
default: throw new IndexOutOfRangeException(nameof(index));
}
}
}
public float this[Index index]
{
get => this[index.IsFromEnd ? 4 - index.Value : index.Value];
set => this[index.IsFromEnd ? 4 - index.Value : index.Value] = value;
}
public float[] this[Range range]
{
get
{
int start = range.Start.IsFromEnd ? 4 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 4 - range.End.Value : range.End.Value;
List<float> res = new();
for (int i = start; i < end; i++) res.Add(this[i]);
return res.ToArray();
}
set
{
int start = range.Start.IsFromEnd ? 4 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 4 - range.End.Value : range.End.Value;
for (int i = start; i < end; i++) this[i] = value[i];
}
}
public static HSVA Average(params HSVA[] vals)
{
@ -100,15 +124,11 @@ public struct HSVA : IColor, IEquatable<HSVA>
for (int i = 0; i < vals.Length; i++) val += vals[i];
return val / vals.Length;
}
public static HSVA Ceiling(HSVA val, Angle.Type type = Angle.Type.Degrees) => new(Angle.Ceiling(val.H, type),
Mathf.Ceiling(val.S), Mathf.Ceiling(val.V), Mathf.Ceiling(val.A));
public static HSVA Clamp(HSVA val, HSVA min, HSVA max) =>
new(Angle.Clamp(val.H, min.H, max.H),
Mathf.Clamp(val.S, min.S, max.S),
Mathf.Clamp(val.V, min.V, max.V),
Mathf.Clamp(val.A, min.A, max.A));
public static HSVA Floor(HSVA val, Angle.Type type = Angle.Type.Degrees) => new(Angle.Floor(val.H, type),
Mathf.Floor(val.S), Mathf.Floor(val.V), Mathf.Floor(val.A));
public static HSVA Lerp(HSVA a, HSVA b, float t, bool clamp = true) =>
new(Angle.Lerp(a.H, b.H, t, clamp), Mathf.Lerp(a.S, b.S, t, clamp), Mathf.Lerp(a.V, b.V, t, clamp),
Mathf.Lerp(a.A, b.A, t, clamp));
@ -118,18 +138,6 @@ public struct HSVA : IColor, IEquatable<HSVA>
HSVA valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
return Average(valA, valB);
}
public static HSVA Max(params HSVA[] vals)
{
(Angle[] Hs, float[] Ss, float[] Vs, float[] As) = SplitArray(vals);
return new(Angle.Max(Hs), Mathf.Max(Ss), Mathf.Max(Vs), Mathf.Max(As));
}
public static HSVA Min(params HSVA[] 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));
}
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)
{
@ -160,30 +168,9 @@ public struct HSVA : IColor, IEquatable<HSVA>
}
public bool Equals(IColor? col) => col != null && Equals(col.ToHSVA());
public bool Equals(IColorByte? col) => col != null && Equals(col.ToHSVA());
public bool Equals(HSVA col) => S == 0 && col.S == 0 || V == 0 && col.V == 0 || A == 0 && col.A == 0
|| H == col.H && S == col.S && V == col.V && A == col.A;
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null) return base.Equals(obj);
Type t = obj.GetType();
if (t == typeof(HSVA)) return Equals((HSVA)obj);
else if (t == typeof(CMYKA)) return Equals((IColor)obj);
else if (t == typeof(RGBA)) return Equals((IColor)obj);
else if (t == typeof(IColor)) return Equals((IColor)obj);
else if (t == typeof(RGBAByte)) return Equals((IColorByte)obj);
else if (t == typeof(CMYKAByte)) return Equals((IColorByte)obj);
else if (t == typeof(HSVAByte)) return Equals((IColorByte)obj);
else if (t == typeof(IColorByte)) return Equals((IColorByte)obj);
return base.Equals(obj);
}
public override int GetHashCode() => H.GetHashCode() ^ S.GetHashCode() ^ V.GetHashCode() ^ A.GetHashCode();
public string ToString(IFormatProvider provider) => "H: " + H.ToString(provider) + " S: " + S.ToString(provider)
+ " V: " + V.ToString(provider) + " A: " + A.ToString(provider);
public string ToString(string? provider) => "H: " + H.ToString(provider) + " S: " + S.ToString(provider)
+ " V: " + V.ToString(provider) + " A: " + A.ToString(provider);
public override string ToString() => ToString((string?)null);
public override int GetHashCode() => base.GetHashCode();
public RGBA ToRGBA()
{
@ -195,7 +182,7 @@ public struct HSVA : IColor, IEquatable<HSVA>
else if (d < 240) vals = (0, x, c);
else if (d < 300) vals = (x, 0, c);
else if (d < 360) vals = (c, 0, x);
return new(vals.r + m, vals.g + m, vals.b + m);
return new(vals.r + m, vals.g + m, vals.b + m, A);
}
public CMYKA ToCMYKA() => ToRGBA().ToCMYKA();
public HSVA ToHSVA() => this;
@ -222,32 +209,41 @@ public struct HSVA : IColor, IEquatable<HSVA>
yield return A;
}
public object Clone() => new HSVA(H, S, V, A);
private bool PrintMembers(StringBuilder builder)
{
builder.Append("H = ");
builder.Append(H);
builder.Append(", S = ");
builder.Append(S);
builder.Append(", V = ");
builder.Append(V);
builder.Append(", A = ");
builder.Append(A);
return true;
}
public static HSVA operator +(HSVA a, HSVA b) => new(a.H + b.H, a.S + b.S, a.V + b.V, a.A + b.A);
public static HSVA operator -(HSVA c) => new(1 - c.H.Normalized, 1 - c.S, 1 - c.V, c.A != 1 ? 1 - c.A : 1);
public static HSVA operator -(HSVA a, HSVA b) => new(a.H - b.H, a.S - b.S, a.V - b.V, a.A - b.A);
public static HSVA operator *(HSVA a, float b) => new(a.H * b, a.S * b, a.V * b, a.A * b);
public static HSVA operator /(HSVA a, float b) => new(a.H / b, a.S / b, a.V / b, a.A / b);
public static bool operator ==(HSVA a, RGBA b) => a.Equals(b);
public static bool operator !=(HSVA a, RGBA b) => !a.Equals(b);
public static bool operator ==(HSVA a, CMYKA b) => a.Equals(b);
public static bool operator !=(HSVA a, CMYKA b) => !a.Equals(b);
public static bool operator ==(HSVA a, HSVA b) => a.Equals(b);
public static bool operator !=(HSVA a, HSVA b) => !a.Equals(b);
public static bool operator ==(HSVA a, RGBAByte b) => a.Equals((IColorByte?)b);
public static bool operator !=(HSVA a, RGBAByte b) => !a.Equals((IColorByte?)b);
public static bool operator ==(HSVA a, CMYKAByte b) => a.Equals((IColorByte?)b);
public static bool operator !=(HSVA a, CMYKAByte b) => !a.Equals((IColorByte?)b);
public static bool operator ==(HSVA a, HSVAByte b) => a.Equals((IColorByte?)b);
public static bool operator !=(HSVA a, HSVAByte b) => !a.Equals((IColorByte?)b);
public static bool operator !=(HSVA a, CMYKA b) => a.Equals(b);
public static bool operator ==(HSVA a, RGBA b) => a.Equals(b);
public static bool operator !=(HSVA a, RGBA b) => a.Equals(b);
public static bool operator ==(HSVA a, CMYKAByte b) => a.Equals(b);
public static bool operator !=(HSVA a, CMYKAByte b) => a.Equals(b);
public static bool operator ==(HSVA a, HSVAByte b) => a.Equals(b);
public static bool operator !=(HSVA a, HSVAByte b) => a.Equals(b);
public static bool operator ==(HSVA a, RGBAByte b) => a.Equals(b);
public static bool operator !=(HSVA a, RGBAByte b) => a.Equals(b);
public static explicit operator HSVA(Float3 val) => new(val.x, val.y, val.z);
public static explicit operator HSVA(Float4 val) => new(val.x, val.y, val.z, val.w);
public static implicit operator HSVA(CMYKA val) => val.ToHSVA();
public static implicit operator HSVA(RGBA val) => val.ToHSVA();
public static implicit operator HSVA(RGBAByte val) => val.ToHSVA();
public static implicit operator HSVA(CMYKAByte val) => val.ToHSVA();
public static implicit operator HSVA(HSVAByte val) => val.ToHSVA();
public static implicit operator HSVA(RGBAByte val) => val.ToHSVA();
public static implicit operator HSVA(Fill<float> val) => new(val);
}

View File

@ -1,21 +1,44 @@
namespace Nerd_STF.Graphics;
public struct HSVAByte : IColorByte, IEquatable<HSVAByte>
public record struct HSVAByte : IAverage<HSVAByte>, IClamp<HSVAByte>, IColorByte<HSVAByte>, IColorPresets<HSVAByte>,
IEquatable<HSVAByte>, IIndexAll<int>, IIndexRangeAll<int>, ILerp<HSVAByte, float>, IMedian<HSVAByte>,
ISplittable<HSVAByte, (byte[] Hs, byte[] Ss, byte[] Vs, byte[] As)>
{
public static HSVA Black => new(Angle.Zero, 0, 0);
public static HSVA Blue => new(new Angle(240), 255, 255);
public static HSVA Clear => new(Angle.Zero, 0, 0, 0);
public static HSVA Cyan => new(new Angle(180), 255, 255);
public static HSVA Gray => new(Angle.Zero, 0, 127);
public static HSVA Green => new(new Angle(120), 255, 255);
public static HSVA Magenta => new(new Angle(300), 255, 255);
public static HSVA Orange => new(new Angle(30), 255, 255);
public static HSVA Purple => new(new Angle(270), 255, 255);
public static HSVA Red => new(Angle.Zero, 255, 255);
public static HSVA White => new(Angle.Zero, 0, 255);
public static HSVA Yellow => new(new Angle(60), 255, 255);
public static HSVAByte Black => new(Angle.Zero, 0, 0);
public static HSVAByte Blue => new(new Angle(240), 255, 255);
public static HSVAByte Clear => new(Angle.Zero, 0, 0, 0);
public static HSVAByte Cyan => new(new Angle(180), 255, 255);
public static HSVAByte Gray => new(Angle.Zero, 0, 127);
public static HSVAByte Green => new(new Angle(120), 255, 255);
public static HSVAByte Magenta => new(new Angle(300), 255, 255);
public static HSVAByte Orange => new(new Angle(30), 255, 255);
public static HSVAByte Purple => new(new Angle(270), 255, 255);
public static HSVAByte Red => new(Angle.Zero, 255, 255);
public static HSVAByte White => new(Angle.Zero, 0, 255);
public static HSVAByte Yellow => new(new Angle(60), 255, 255);
public byte H, S, V, A;
public int H
{
get => p_h;
set => p_h = (byte)Mathf.Clamp(value, byte.MinValue, byte.MaxValue);
}
public int S
{
get => p_s;
set => p_s = (byte)Mathf.Clamp(value, byte.MinValue, byte.MaxValue);
}
public int V
{
get => p_v;
set => p_v = (byte)Mathf.Clamp(value, byte.MinValue, byte.MaxValue);
}
public int A
{
get => p_a;
set => p_a = (byte)Mathf.Clamp(value, byte.MinValue, byte.MaxValue);
}
private byte p_h, p_s, p_v, p_a;
public bool HasColor => S != 0 && V != 0;
public bool IsOpaque => A == 255;
@ -27,17 +50,17 @@ public struct HSVAByte : IColorByte, IEquatable<HSVAByte>
public HSVAByte(int h, int s, int v) : this(h, s, v, 255) { }
public HSVAByte(int h, int s, int v, int a)
{
H = (byte)Mathf.Clamp(h, 0, 255);
S = (byte)Mathf.Clamp(s, 0, 255);
V = (byte)Mathf.Clamp(v, 0, 255);
A = (byte)Mathf.Clamp(a, 0, 255);
p_h = (byte)Mathf.Clamp(h, 0, 255);
p_s = (byte)Mathf.Clamp(s, 0, 255);
p_v = (byte)Mathf.Clamp(v, 0, 255);
p_a = (byte)Mathf.Clamp(a, 0, 255);
}
public HSVAByte(Angle h, int s, int v) : this(h, s, v, 255) { }
public HSVAByte(Angle h, int s, int v, int a) : this(Mathf.RoundInt(h.Normalized * 255), s, v, a) { }
public HSVAByte(Fill<byte> fill) : this(fill(0), fill(1), fill(2), fill(3)) { }
public HSVAByte(Fill<int> fill) : this(fill(0), fill(1), fill(2), fill(3)) { }
public byte this[int index]
public int this[int index]
{
get => index switch
{
@ -71,6 +94,28 @@ public struct HSVAByte : IColorByte, IEquatable<HSVAByte>
}
}
}
public int this[Index index]
{
get => this[index.IsFromEnd ? 4 - index.Value : index.Value];
set => this[index.IsFromEnd ? 4 - index.Value : index.Value] = value;
}
public int[] this[Range range]
{
get
{
int start = range.Start.IsFromEnd ? 4 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 4 - range.End.Value : range.End.Value;
List<int> res = new();
for (int i = start; i < end; i++) res.Add(this[i]);
return res.ToArray();
}
set
{
int start = range.Start.IsFromEnd ? 4 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 4 - range.End.Value : range.End.Value;
for (int i = start; i < end; i++) this[i] = value[i];
}
}
public static HSVAByte Average(params HSVAByte[] vals)
{
@ -83,7 +128,7 @@ public struct HSVAByte : IColorByte, IEquatable<HSVAByte>
Mathf.Clamp(val.S, min.S, max.S),
Mathf.Clamp(val.V, min.V, max.V),
Mathf.Clamp(val.A, min.A, max.A));
public static HSVAByte Lerp(HSVAByte a, HSVAByte b, byte t, bool clamp = true) =>
public static HSVAByte Lerp(HSVAByte a, HSVAByte b, float t, bool clamp = true) =>
new(Mathf.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));
public static HSVAByte Median(params HSVAByte[] vals)
@ -92,16 +137,6 @@ public struct HSVAByte : IColorByte, IEquatable<HSVAByte>
HSVAByte valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
return Average(valA, valB);
}
public static HSVAByte Max(params HSVAByte[] vals)
{
(int[] Hs, int[] Ss, int[] Vs, int[] As) = SplitArrayInt(vals);
return new(Mathf.Max(Hs), Mathf.Max(Ss), Mathf.Max(Vs), Mathf.Max(As));
}
public static HSVAByte Min(params HSVAByte[] vals)
{
(int[] Hs, int[] Ss, int[] Vs, int[] As) = SplitArrayInt(vals);
return new(Mathf.Min(Hs), Mathf.Min(Ss), Mathf.Min(Vs), Mathf.Min(As));
}
public static (byte[] Hs, byte[] Ss, byte[] Vs, byte[] As) SplitArray(params HSVAByte[] vals)
{
@ -109,10 +144,10 @@ public struct HSVAByte : IColorByte, IEquatable<HSVAByte>
Vs = new byte[vals.Length], As = new byte[vals.Length];
for (int i = 0; i < vals.Length; i++)
{
Hs[i] = vals[i].H;
Ss[i] = vals[i].S;
Vs[i] = vals[i].V;
As[i] = vals[i].A;
Hs[i] = vals[i].p_h;
Ss[i] = vals[i].p_s;
Vs[i] = vals[i].p_v;
As[i] = vals[i].p_a;
}
return (Hs, Ss, Vs, As);
}
@ -131,30 +166,9 @@ public struct HSVAByte : IColorByte, IEquatable<HSVAByte>
}
public bool Equals(IColor? col) => col != null && Equals(col.ToHSVAByte());
public bool Equals(IColorByte? col) => col != null && Equals(col.ToHSVAByte());
public bool Equals(HSVAByte col) => S == 0 && col.S == 0 || V == 0 && col.V == 0 || A == 0 && col.A == 0
|| H == col.H && S == col.S && V == col.V && A == col.A;
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null) return base.Equals(obj);
Type t = obj.GetType();
if (t == typeof(HSVAByte)) return Equals((HSVAByte)obj);
else if (t == typeof(CMYKA)) return Equals((IColor)obj);
else if (t == typeof(RGBA)) return Equals((IColor)obj);
else if (t == typeof(IColor)) return Equals((IColor)obj);
else if (t == typeof(RGBAByte)) return Equals((IColorByte)obj);
else if (t == typeof(CMYKAByte)) return Equals((IColorByte)obj);
else if (t == typeof(HSVA)) return Equals((IColor)obj);
else if (t == typeof(IColorByte)) return Equals((IColorByte)obj);
return base.Equals(obj);
}
public override int GetHashCode() => H.GetHashCode() ^ S.GetHashCode() ^ V.GetHashCode() ^ A.GetHashCode();
public string ToString(IFormatProvider provider) => "H: " + H.ToString(provider) + " S: " + S.ToString(provider)
+ " V: " + V.ToString(provider) + " A: " + A.ToString(provider);
public string ToString(string? provider) => "H: " + H.ToString(provider) + " S: " + S.ToString(provider)
+ " V: " + V.ToString(provider) + " A: " + A.ToString(provider);
public override string ToString() => ToString((string?)null);
public override int GetHashCode() => base.GetHashCode();
public RGBA ToRGBA() => ToHSVA().ToRGBA();
public CMYKA ToCMYKA() => ToHSVA().ToCMYKA();
@ -164,24 +178,42 @@ public struct HSVAByte : IColorByte, IEquatable<HSVAByte>
public CMYKAByte ToCMYKAByte() => ToHSVA().ToCMYKAByte();
public HSVAByte ToHSVAByte() => this;
public byte[] ToArray() => new[] { H, S, V, A };
public byte[] ToArray() => new[] { p_h, p_s, p_v, p_a };
public int[] ToArrayInt() => new[] { H, S, V, A };
public Fill<byte> ToFill()
{
HSVAByte @this = this;
return i => (byte)@this[i];
}
public Fill<int> ToFillInt()
{
HSVAByte @this = this;
return i => @this[i];
}
public List<byte> ToList() => new() { H, S, V, A };
public List<byte> ToList() => new() { p_h, p_s, p_v, p_a };
public List<int> ToListInt() => new() { H, S, V, A };
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<byte> GetEnumerator()
{
yield return H;
yield return S;
yield return V;
yield return A;
yield return p_h;
yield return p_s;
yield return p_v;
yield return p_a;
}
public object Clone() => new HSVAByte(H, S, V, A);
private bool PrintMembers(StringBuilder builder)
{
builder.Append("H = ");
builder.Append(H);
builder.Append(", S = ");
builder.Append(S);
builder.Append(", V = ");
builder.Append(V);
builder.Append(", A = ");
builder.Append(A);
return true;
}
public static HSVAByte operator +(HSVAByte a, HSVAByte b) => new(a.H + b.H, a.S + b.S, a.V + b.V, a.A + b.A);
public static HSVAByte operator -(HSVAByte c) => new(255 - c.H, 255 - c.S, 255 - c.V, c.A != 255 ? 255 - c.A : 255);
@ -192,18 +224,16 @@ public struct HSVAByte : IColorByte, IEquatable<HSVAByte>
public static HSVAByte operator /(HSVAByte a, HSVAByte b) => new(a.H / b.H, a.S / b.S, a.V / b.V, a.A / b.A);
public static HSVAByte operator /(HSVAByte a, int b) => new(a.H / b, a.S / b, a.V / b, a.A / b);
public static HSVAByte operator /(HSVAByte a, float b) => (a.ToHSVA() * b).ToHSVAByte();
public static bool operator ==(HSVAByte a, RGBA b) => a.Equals((IColor?)b);
public static bool operator !=(HSVAByte a, RGBA b) => !a.Equals((IColor?)b);
public static bool operator ==(HSVAByte a, CMYKA b) => a.Equals((IColor?)b);
public static bool operator !=(HSVAByte a, CMYKA b) => !a.Equals((IColor?)b);
public static bool operator ==(HSVAByte a, HSVA b) => a.Equals((IColor?)b);
public static bool operator !=(HSVAByte a, HSVA b) => !a.Equals((IColor?)b);
public static bool operator ==(HSVAByte a, RGBAByte b) => a.Equals(b);
public static bool operator !=(HSVAByte a, RGBAByte b) => !a.Equals(b);
public static bool operator ==(HSVAByte a, CMYKA b) => a.Equals(b);
public static bool operator !=(HSVAByte a, CMYKA b) => a.Equals(b);
public static bool operator ==(HSVAByte a, HSVA b) => a.Equals(b);
public static bool operator !=(HSVAByte a, HSVA b) => a.Equals(b);
public static bool operator ==(HSVAByte a, RGBA b) => a.Equals(b);
public static bool operator !=(HSVAByte a, RGBA b) => a.Equals(b);
public static bool operator ==(HSVAByte a, CMYKAByte b) => a.Equals(b);
public static bool operator !=(HSVAByte a, CMYKAByte b) => !a.Equals(b);
public static bool operator ==(HSVAByte a, HSVAByte b) => a.Equals(b);
public static bool operator !=(HSVAByte a, HSVAByte b) => !a.Equals(b);
public static bool operator !=(HSVAByte a, CMYKAByte b) => a.Equals(b);
public static bool operator ==(HSVAByte a, RGBAByte b) => a.Equals(b);
public static bool operator !=(HSVAByte a, RGBAByte b) => a.Equals(b);
public static implicit operator HSVAByte(Int3 val) => new(val.x, val.y, val.z);
public static implicit operator HSVAByte(Int4 val) => new(val.x, val.y, val.z, val.w);

View File

@ -1,12 +0,0 @@
namespace Nerd_STF.Graphics;
public interface IColor : ICloneable, IEquatable<IColor?>, IEquatable<IColorByte?>, IGroup<float>
{
public RGBA ToRGBA();
public CMYKA ToCMYKA();
public HSVA ToHSVA();
public RGBAByte ToRGBAByte();
public CMYKAByte ToCMYKAByte();
public HSVAByte ToHSVAByte();
}

View File

@ -1,12 +0,0 @@
namespace Nerd_STF.Graphics;
public interface IColorByte : ICloneable, IEquatable<IColor?>, IEquatable<IColorByte?>, IGroup<byte>
{
public RGBA ToRGBA();
public CMYKA ToCMYKA();
public HSVA ToHSVA();
public RGBAByte ToRGBAByte();
public CMYKAByte ToCMYKAByte();
public HSVAByte ToHSVAByte();
}

View File

@ -1,9 +1,9 @@
namespace Nerd_STF.Graphics;
public struct Image : ICloneable, IEnumerable, IEquatable<Image>
public class Image : ICloneable, IEnumerable<IColor>, IEquatable<Image>
{
public IColor[,] Pixels { get; init; }
public Int2 Size { get; init; }
public IColor[,] Pixels { get; private set; }
public Int2 Size { get; private set; }
public Image(int width, int height)
{
@ -15,12 +15,13 @@ public struct Image : ICloneable, IEnumerable, IEquatable<Image>
{
Pixels = new IColor[width, height];
Size = new(width, height);
for (int y = 0; y < width; y++) for (int x = 0; x < height; x++) Pixels[x, y] = cols[y * width + x];
for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) Pixels[x, y] = cols[y * width + x];
}
public Image(int width, int height, IColor[,] cols)
{
Pixels = cols;
Pixels = new IColor[width, height];
Size = new(width, height);
for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) Pixels[x, y] = cols[y, x];
}
public Image(int width, int height, Fill<IColor> fill)
{
@ -32,28 +33,13 @@ public struct Image : ICloneable, IEnumerable, IEquatable<Image>
{
Pixels = new IColor[width, height];
Size = new(width, height);
for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) Pixels[x, y] = fill(x, y);
}
public Image(int width, int height, Fill<IColorByte> fill)
{
Pixels = new IColor[width, height];
Size = new(width, height);
for (int y = 0; y < height; y++) for (int x = 0; x < width; x++)
Pixels[x, y] = (IColor)fill(y * width + x);
}
public Image(int width, int height, Fill2D<IColorByte> fill)
{
Pixels = new IColor[width, height];
Size = new(width, height);
for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) Pixels[x, y] = (IColor)fill(x, y);
for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) Pixels[x, y] = fill(y, x);
}
public Image(Int2 size) : this(size.x, size.y) { }
public Image(Int2 size, IColor[] cols) : this(size.x, size.y, cols) { }
public Image(Int2 size, IColor[,] cols) : this(size.x, size.y, cols) { }
public Image(Int2 size, Fill<IColor> fill) : this(size.x, size.y, fill) { }
public Image(Int2 size, Fill2D<IColor> fill) : this(size.x, size.y, fill) { }
public Image(Int2 size, Fill<IColorByte> fill) : this(size.x, size.y, fill) { }
public Image(Int2 size, Fill2D<IColorByte> fill) : this(size.x, size.y, fill) { }
public IColor this[int indexX, int indexY]
{
@ -72,7 +58,7 @@ public struct Image : ICloneable, IEnumerable, IEquatable<Image>
public object Clone() => new Image(Size, Pixels);
public bool Equals(Image other) => Pixels == other.Pixels;
public bool Equals(Image? other) => other is not null && Pixels == other.Pixels;
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null) return base.Equals(obj);
@ -124,7 +110,7 @@ public struct Image : ICloneable, IEnumerable, IEquatable<Image>
for (int y = 0; y < Size.y; y++) for (int x = 0; x < Size.x; x++)
{
HSVA col = Pixels[x, y].ToHSVA();
col.S = (set ? 0 : col.S) + value;
col.S = (set ? 1 : col.S) * value;
Pixels[x, y] = col;
}
}
@ -144,10 +130,11 @@ public struct Image : ICloneable, IEnumerable, IEquatable<Image>
for (int y = 0; y < newSize.y; y++) for (int x = 0; x < newSize.x; x++)
{
Float2 f = new((float)x / newSize.x, (float)y / newSize.y);
RGBA col = Pixels[Mathf.RoundInt(f.x * Size.x), Mathf.RoundInt(f.y * Size.y)].ToRGBA();
RGBA col = Pixels[Mathf.Floor(f.x * Size.x), Mathf.Floor(f.y * Size.y)].ToRGBA();
img[x, y] = col;
}
this = img;
Pixels = img.Pixels;
Size = img.Size;
}
public IColor[] ToArray()

View File

@ -1,21 +1,23 @@
namespace Nerd_STF.Graphics;
using Nerd_STF.Graphics.Abstract;
namespace Nerd_STF.Graphics;
public struct Material : ICloneable, IEquatable<Material>
{
public float Alpha;
public float Anisotropy;
public float AnisotropyRoughness;
public IColor AmbientColor;
public IColorFloat AmbientColor;
public float ClearcoatRoughness;
public float ClearcoatThickness;
public IColor DiffuseColor;
public IColor Emissive;
public IColorFloat DiffuseColor;
public IColorFloat Emissive;
public IlluminationModel IllumModel;
public float Metallic;
public float OpticalDensity;
public float Roughness;
public float Sheen;
public IColor SpecularColor;
public IColorFloat SpecularColor;
public float SpecularExponent;
public (Image Image, TextureConfig Config) AlphaTexture;
@ -67,17 +69,17 @@ public struct Material : ICloneable, IEquatable<Material>
Alpha = (float)fill(0);
Anisotropy = (float)fill(1);
AnisotropyRoughness = (float)fill(2);
AmbientColor = (IColor)fill(3);
AmbientColor = (IColorFloat)fill(3);
ClearcoatRoughness = (float)fill(4);
ClearcoatThickness = (float)fill(5);
DiffuseColor = (IColor)fill(6);
Emissive = (IColor)fill(7);
DiffuseColor = (IColorFloat)fill(6);
Emissive = (IColorFloat)fill(7);
IllumModel = (IlluminationModel)fill(8);
Metallic = (float)fill(9);
OpticalDensity = (float)fill(10);
Roughness = (float)fill(11);
Sheen = (float)fill(12);
SpecularColor = (IColor)fill(13);
SpecularColor = (IColorFloat)fill(13);
SpecularExponent = (float)fill(14);
AlphaTexture = ((Image, TextureConfig))fill(15);
@ -93,7 +95,7 @@ public struct Material : ICloneable, IEquatable<Material>
SpecularHighlightTexture = ((Image, TextureConfig))fill(25);
StencilTexture = ((Image, TextureConfig))fill(26);
}
public Material(IlluminationModel illum, Fill<float> floats, Fill<IColor> colors, Fill<(Image, TextureConfig)> images)
public Material(IlluminationModel illum, Fill<float> floats, Fill<IColorFloat> colors, Fill<(Image, TextureConfig)> images)
{
Alpha = floats(0);
Anisotropy = floats(1);

View File

@ -1,6 +1,8 @@
namespace Nerd_STF.Graphics;
public struct RGBA : IColor, IEquatable<RGBA>
public record struct RGBA : IAverage<RGBA>, IClamp<RGBA>, IColorFloat<RGBA>, IEquatable<RGBA>, IIndexAll<float>,
IIndexRangeAll<float>, ILerp<RGBA, float>, IMedian<RGBA>,
ISplittable<RGBA, (float[] Rs, float[] Gs, float[] Bs, float[] As)>
{
public static RGBA Black => new(0, 0, 0);
public static RGBA Blue => new(0, 0, 1);
@ -91,6 +93,28 @@ public struct RGBA : IColor, IEquatable<RGBA>
}
}
}
public float this[Index index]
{
get => this[index.IsFromEnd ? 4 - index.Value : index.Value];
set => this[index.IsFromEnd ? 4 - index.Value : index.Value] = value;
}
public float[] this[Range range]
{
get
{
int start = range.Start.IsFromEnd ? 4 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 4 - range.End.Value : range.End.Value;
List<float> res = new();
for (int i = start; i < end; i++) res.Add(this[i]);
return res.ToArray();
}
set
{
int start = range.Start.IsFromEnd ? 4 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 4 - range.End.Value : range.End.Value;
for (int i = start; i < end; i++) this[i] = value[i];
}
}
public static RGBA Average(params RGBA[] vals)
{
@ -98,15 +122,11 @@ public struct RGBA : IColor, IEquatable<RGBA>
for (int i = 0; i < vals.Length; i++) val += vals[i];
return val / vals.Length;
}
public static RGBA Ceiling(RGBA val) =>
new(Mathf.Ceiling(val.R), Mathf.Ceiling(val.G), Mathf.Ceiling(val.B), Mathf.Ceiling(val.A));
public static RGBA Clamp(RGBA val, RGBA min, RGBA max) =>
new(Mathf.Clamp(val.R, min.R, max.R),
Mathf.Clamp(val.G, min.G, max.G),
Mathf.Clamp(val.B, min.B, max.B),
Mathf.Clamp(val.A, min.A, max.A));
public static RGBA Floor(RGBA val) =>
new(Mathf.Floor(val.R), Mathf.Floor(val.G), Mathf.Floor(val.B), Mathf.Floor(val.A));
public static RGBA Lerp(RGBA a, RGBA b, float t, bool clamp = true) =>
new(Mathf.Lerp(a.R, b.R, t, clamp), Mathf.Lerp(a.G, b.G, t, clamp), Mathf.Lerp(a.B, b.B, t, clamp),
Mathf.Lerp(a.A, b.A, t, clamp));
@ -122,18 +142,6 @@ public struct RGBA : IColor, IEquatable<RGBA>
RGBA valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
return Average(valA, valB);
}
public static RGBA Max(params RGBA[] vals)
{
(float[] Rs, float[] Gs, float[] Bs, float[] As) = SplitArray(vals);
return new(Mathf.Max(Rs), Mathf.Max(Gs), Mathf.Max(Bs), Mathf.Max(As));
}
public static RGBA Min(params RGBA[] 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));
}
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)
{
@ -150,29 +158,8 @@ public struct RGBA : IColor, IEquatable<RGBA>
}
public bool Equals(IColor? col) => col != null && Equals(col.ToRGBA());
public bool Equals(IColorByte? col) => col != null && Equals(col.ToRGBA());
public bool Equals(RGBA col) => A == 0 && col.A == 0 || R == col.R && G == col.G && B == col.B && A == col.A;
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null) return base.Equals(obj);
Type t = obj.GetType();
if (t == typeof(RGBA)) return Equals((RGBA)obj);
else if (t == typeof(CMYKA)) return Equals((IColor)obj);
else if (t == typeof(HSVA)) return Equals((IColor)obj);
else if (t == typeof(IColor)) return Equals((IColor)obj);
else if (t == typeof(RGBAByte)) return Equals((IColorByte)obj);
else if (t == typeof(CMYKAByte)) return Equals((IColorByte)obj);
else if (t == typeof(HSVAByte)) return Equals((IColorByte)obj);
else if (t == typeof(IColorByte)) return Equals((IColorByte)obj);
return base.Equals(obj);
}
public override int GetHashCode() => R.GetHashCode() ^ G.GetHashCode() ^ B.GetHashCode() ^ A.GetHashCode();
public string ToString(IFormatProvider provider) => "R: " + R.ToString(provider) + " G: " + G.ToString(provider) +
" B: " + B.ToString(provider) + " A: " + A.ToString(provider);
public string ToString(string? provider) => "R: " + R.ToString(provider) + " G: " + G.ToString(provider) +
" B: " + B.ToString(provider) + " A: " + A.ToString(provider);
public override string ToString() => ToString((string?)null);
public override int GetHashCode() => base.GetHashCode();
public RGBA ToRGBA() => this;
public CMYKA ToCMYKA()
@ -223,10 +210,21 @@ public struct RGBA : IColor, IEquatable<RGBA>
yield return A;
}
public object Clone() => new RGBA(R, G, B, A);
public Vector3d ToVector() => ((Float3)this).ToVector();
private bool PrintMembers(StringBuilder builder)
{
builder.Append("R = ");
builder.Append(R);
builder.Append(", G = ");
builder.Append(G);
builder.Append(", B = ");
builder.Append(B);
builder.Append(", A = ");
builder.Append(A);
return true;
}
public static RGBA operator +(RGBA a, RGBA b) => new(a.R + b.R, a.G + b.G, a.B + b.B, a.A + b.A);
public static RGBA operator -(RGBA c) => new(1 - c.R, 1 - c.G, 1 - c.B, c.A != 1 ? 1 - c.A : 1);
public static RGBA operator -(RGBA a, RGBA b) => new(a.R - b.R, a.G - b.G, a.B - b.B, a.A - b.A);
@ -234,25 +232,23 @@ public struct RGBA : IColor, IEquatable<RGBA>
public static RGBA operator *(RGBA a, float b) => new(a.R * b, a.G * b, a.B * b, a.A * b);
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, float b) => new(a.R / b, a.G / b, a.B / b, a.A / b);
public static bool operator ==(RGBA a, RGBA b) => a.Equals(b);
public static bool operator !=(RGBA a, RGBA b) => !a.Equals(b);
public static bool operator ==(RGBA a, CMYKA b) => a.Equals(b);
public static bool operator !=(RGBA a, CMYKA b) => !a.Equals(b);
public static bool operator !=(RGBA a, CMYKA b) => a.Equals(b);
public static bool operator ==(RGBA a, HSVA b) => a.Equals(b);
public static bool operator !=(RGBA a, HSVA b) => !a.Equals(b);
public static bool operator ==(RGBA a, RGBAByte b) => a.Equals((IColorByte?)b);
public static bool operator !=(RGBA a, RGBAByte b) => !a.Equals((IColorByte?)b);
public static bool operator ==(RGBA a, CMYKAByte b) => a.Equals((IColorByte?)b);
public static bool operator !=(RGBA a, CMYKAByte b) => !a.Equals((IColorByte?)b);
public static bool operator ==(RGBA a, HSVAByte b) => a.Equals((IColorByte?)b);
public static bool operator !=(RGBA a, HSVAByte b) => !a.Equals((IColorByte?)b);
public static bool operator !=(RGBA a, HSVA b) => a.Equals(b);
public static bool operator ==(RGBA a, CMYKAByte b) => a.Equals(b);
public static bool operator !=(RGBA a, CMYKAByte b) => a.Equals(b);
public static bool operator ==(RGBA a, HSVAByte b) => a.Equals(b);
public static bool operator !=(RGBA a, HSVAByte b) => a.Equals(b);
public static bool operator ==(RGBA a, RGBAByte b) => a.Equals(b);
public static bool operator !=(RGBA a, RGBAByte b) => a.Equals(b);
public static implicit operator RGBA(Float3 val) => new(val.x, val.y, val.z);
public static implicit operator RGBA(Float4 val) => new(val.x, val.y, val.z, val.w);
public static implicit operator RGBA(CMYKA val) => val.ToRGBA();
public static implicit operator RGBA(HSVA val) => val.ToRGBA();
public static implicit operator RGBA(RGBAByte val) => val.ToRGBA();
public static implicit operator RGBA(CMYKAByte val) => val.ToRGBA();
public static implicit operator RGBA(HSVAByte val) => val.ToRGBA();
public static implicit operator RGBA(RGBAByte val) => val.ToRGBA();
public static implicit operator RGBA(Fill<float> val) => new(val);
}

View File

@ -1,6 +1,8 @@
namespace Nerd_STF.Graphics;
public struct RGBAByte : IColorByte, IEquatable<RGBAByte>
public record struct RGBAByte : IAverage<RGBAByte>, IClamp<RGBAByte>, IColorByte<RGBAByte>, IColorPresets<RGBAByte>,
IEquatable<RGBAByte>, IIndexAll<int>, IIndexRangeAll<int>, ILerp<RGBAByte, float>, IMedian<RGBAByte>,
ISplittable<RGBAByte, (byte[] Rs, byte[] Gs, byte[] Bs, byte[] As)>
{
public static RGBAByte Black => new(0, 0, 0);
public static RGBAByte Blue => new(0, 0, 255);
@ -15,7 +17,28 @@ public struct RGBAByte : IColorByte, IEquatable<RGBAByte>
public static RGBAByte White => new(255, 255, 255);
public static RGBAByte Yellow => new(255, 255, 0);
public byte R, G, B, A;
public int R
{
get => p_r;
set => p_r = (byte)Mathf.Clamp(value, byte.MinValue, byte.MaxValue);
}
public int G
{
get => p_g;
set => p_g = (byte)Mathf.Clamp(value, byte.MinValue, byte.MaxValue);
}
public int B
{
get => p_b;
set => p_b = (byte)Mathf.Clamp(value, byte.MinValue, byte.MaxValue);
}
public int A
{
get => p_a;
set => p_a = (byte)Mathf.Clamp(value, byte.MinValue, byte.MaxValue);
}
private byte p_r, p_g, p_b, p_a;
public bool HasBlue => B > 0;
public bool HasGreen => G > 0;
@ -37,7 +60,7 @@ public struct RGBAByte : IColorByte, IEquatable<RGBAByte>
public RGBAByte(Fill<byte> fill) : this(fill(0), fill(1), fill(2), fill(3)) { }
public RGBAByte(Fill<int> fill) : this(fill(0), fill(1), fill(2), fill(3)) { }
public byte this[int index]
public int this[int index]
{
get => index switch
{
@ -71,6 +94,28 @@ public struct RGBAByte : IColorByte, IEquatable<RGBAByte>
}
}
}
public int this[Index index]
{
get => this[index.IsFromEnd ? 4 - index.Value : index.Value];
set => this[index.IsFromEnd ? 4 - index.Value : index.Value] = value;
}
public int[] this[Range range]
{
get
{
int start = range.Start.IsFromEnd ? 4 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 4 - range.End.Value : range.End.Value;
List<int> res = new();
for (int i = start; i < end; i++) res.Add(this[i]);
return res.ToArray();
}
set
{
int start = range.Start.IsFromEnd ? 4 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 4 - range.End.Value : range.End.Value;
for (int i = start; i < end; i++) this[i] = value[i];
}
}
public static RGBAByte Average(params RGBAByte[] vals)
{
@ -83,7 +128,7 @@ public struct RGBAByte : IColorByte, IEquatable<RGBAByte>
Mathf.Clamp(val.G, min.G, max.G),
Mathf.Clamp(val.B, min.B, max.B),
Mathf.Clamp(val.A, min.A, max.A));
public static RGBAByte Lerp(RGBAByte a, RGBAByte b, byte t, bool clamp = true) =>
public static RGBAByte Lerp(RGBAByte a, RGBAByte b, float t, bool clamp = true) =>
new(Mathf.Lerp(a.R, b.R, t, clamp), Mathf.Lerp(a.G, b.G, t, clamp), Mathf.Lerp(a.B, b.B, t, clamp),
Mathf.Lerp(a.A, b.A, t, clamp));
public static RGBAByte LerpSquared(RGBAByte a, RGBAByte b, byte t, bool clamp = true) =>
@ -111,10 +156,10 @@ public struct RGBAByte : IColorByte, IEquatable<RGBAByte>
Bs = new byte[vals.Length], As = new byte[vals.Length];
for (int i = 0; i < vals.Length; i++)
{
Rs[i] = vals[i].R;
Gs[i] = vals[i].G;
Bs[i] = vals[i].B;
As[i] = vals[i].A;
Rs[i] = vals[i].p_r;
Gs[i] = vals[i].p_g;
Bs[i] = vals[i].p_b;
As[i] = vals[i].p_a;
}
return (Rs, Gs, Bs, As);
}
@ -133,29 +178,8 @@ public struct RGBAByte : IColorByte, IEquatable<RGBAByte>
}
public bool Equals(IColor? col) => col != null && Equals(col.ToRGBAByte());
public bool Equals(IColorByte? col) => col != null && Equals(col.ToRGBAByte());
public bool Equals(RGBAByte col) => A == 0 && col.A == 0 || R == col.R && G == col.G && B == col.B && A == col.A;
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null) return base.Equals(obj);
Type t = obj.GetType();
if (t == typeof(RGBAByte)) return Equals((RGBAByte)obj);
else if (t == typeof(CMYKA)) return Equals((IColor)obj);
else if (t == typeof(HSVA)) return Equals((IColor)obj);
else if (t == typeof(IColor)) return Equals((IColor)obj);
else if (t == typeof(RGBA)) return Equals((IColorByte)obj);
else if (t == typeof(CMYKAByte)) return Equals((IColorByte)obj);
else if (t == typeof(HSVAByte)) return Equals((IColorByte)obj);
else if (t == typeof(IColorByte)) return Equals((IColorByte)obj);
return base.Equals(obj);
}
public override int GetHashCode() => R.GetHashCode() ^ G.GetHashCode() ^ B.GetHashCode() ^ A.GetHashCode();
public string ToString(IFormatProvider provider) => "R: " + R.ToString(provider) + " G: " + G.ToString(provider) +
" B: " + B.ToString(provider) + " A: " + A.ToString(provider);
public string ToString(string? provider) => "R: " + R.ToString(provider) + " G: " + G.ToString(provider) +
" B: " + B.ToString(provider) + " A: " + A.ToString(provider);
public override string ToString() => ToString((string?)null);
public override int GetHashCode() => base.GetHashCode();
public RGBA ToRGBA() => new(R / 255f, G / 255f, B / 255f, A / 255f);
public CMYKA ToCMYKA() => ToRGBA().ToCMYKA();
@ -165,27 +189,45 @@ public struct RGBAByte : IColorByte, IEquatable<RGBAByte>
public CMYKAByte ToCMYKAByte() => ToRGBA().ToCMYKAByte();
public HSVAByte ToHSVAByte() => ToRGBA().ToHSVAByte();
public byte[] ToArray() => new[] { R, G, B, A };
public byte[] ToArray() => new[] { p_r, p_g, p_b, p_a };
public int[] ToArrayInt() => new[] { R, G, B, A };
public Fill<byte> ToFill()
{
HSVAByte @this = this;
RGBAByte @this = this;
return i => (byte)@this[i];
}
public Fill<int> ToFillInt()
{
RGBAByte @this = this;
return i => @this[i];
}
public List<byte> ToList() => new() { R, G, B, A };
public List<byte> ToList() => new() { p_r, p_g, p_b, p_a };
public List<int> ToListInt() => new() { R, G, B, A };
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<byte> GetEnumerator()
{
yield return R;
yield return G;
yield return B;
yield return A;
yield return p_r;
yield return p_g;
yield return p_b;
yield return p_a;
}
public object Clone() => new RGBAByte(R, G, B, A);
public Vector3d ToVector() => ((RGBA)this).ToVector();
private bool PrintMembers(StringBuilder builder)
{
builder.Append("R = ");
builder.Append(R);
builder.Append(", G = ");
builder.Append(G);
builder.Append(", B = ");
builder.Append(B);
builder.Append(", A = ");
builder.Append(A);
return true;
}
public static RGBAByte operator +(RGBAByte a, RGBAByte b) => new(a.R + b.R, a.G + b.G, a.B + b.B, a.A + b.A);
public static RGBAByte operator -(RGBAByte c) => new(255 - c.R, 255 - c.G, 255 - c.B, c.A != 255 ? 255 - c.A : 255);
public static RGBAByte operator -(RGBAByte a, RGBAByte b) => new(a.R - b.R, a.G - b.G, a.B - b.B, a.A - b.A);
@ -195,18 +237,16 @@ public struct RGBAByte : IColorByte, IEquatable<RGBAByte>
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, int b) => new(a.R / b, a.G / b, a.B / b, a.A / b);
public static RGBAByte operator /(RGBAByte a, float b) => (a.ToRGBA() / b).ToRGBAByte();
public static bool operator ==(RGBAByte a, RGBA b) => a.Equals((IColor?)b);
public static bool operator !=(RGBAByte a, RGBA b) => !a.Equals((IColor?)b);
public static bool operator ==(RGBAByte a, CMYKA b) => a.Equals((IColor?)b);
public static bool operator !=(RGBAByte a, CMYKA b) => !a.Equals((IColor?)b);
public static bool operator ==(RGBAByte a, HSVA b) => a.Equals((IColor?)b);
public static bool operator !=(RGBAByte a, HSVA b) => !a.Equals((IColor?)b);
public static bool operator ==(RGBAByte a, RGBAByte b) => a.Equals(b);
public static bool operator !=(RGBAByte a, RGBAByte b) => !a.Equals(b);
public static bool operator ==(RGBAByte a, CMYKA b) => a.Equals(b);
public static bool operator !=(RGBAByte a, CMYKA b) => a.Equals(b);
public static bool operator ==(RGBAByte a, HSVA b) => a.Equals(b);
public static bool operator !=(RGBAByte a, HSVA b) => a.Equals(b);
public static bool operator ==(RGBAByte a, RGBA b) => a.Equals(b);
public static bool operator !=(RGBAByte a, RGBA b) => a.Equals(b);
public static bool operator ==(RGBAByte a, CMYKAByte b) => a.Equals(b);
public static bool operator !=(RGBAByte a, CMYKAByte b) => !a.Equals(b);
public static bool operator !=(RGBAByte a, CMYKAByte b) => a.Equals(b);
public static bool operator ==(RGBAByte a, HSVAByte b) => a.Equals(b);
public static bool operator !=(RGBAByte a, HSVAByte b) => !a.Equals(b);
public static bool operator !=(RGBAByte a, HSVAByte b) => a.Equals(b);
public static implicit operator RGBAByte(Int3 val) => new(val.x, val.y, val.z);
public static implicit operator RGBAByte(Int4 val) => new(val.x, val.y, val.z, val.w);

View File

@ -1,6 +0,0 @@
namespace Nerd_STF;
public interface IClosest<T> where T : IEquatable<T>
{
public T ClosestTo(T item);
}

View File

@ -1,6 +0,0 @@
namespace Nerd_STF;
public interface IContainer<T> where T : IEquatable<T>
{
public bool Contains(T item);
}

View File

@ -1,6 +1,6 @@
namespace Nerd_STF;
public interface IEncapsulator<T, TE> : IContainer<TE> where T : IEquatable<T> where TE : IEquatable<TE>
public interface IEncapsulator<T, TE> : IContains<TE> where T : IEquatable<T> where TE : IEquatable<TE>
{
public T Encapsulate(TE val);
}

View File

@ -0,0 +1,6 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IAbsolute<T> where T : IAbsolute<T>
{
public static abstract T Absolute(T val);
}

View File

@ -0,0 +1,6 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IAverage<T> where T : IAverage<T>
{
public static abstract T Average(params T[] vals);
}

View File

@ -0,0 +1,7 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface ICeiling<TSelf> : ICeiling<TSelf, TSelf> where TSelf : ICeiling<TSelf> { }
public interface ICeiling<TSelf, TRound> where TSelf : ICeiling<TSelf, TRound>
{
public static abstract TRound Ceiling(TSelf val);
}

View File

@ -0,0 +1,6 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IClamp<T> where T : IClamp<T>
{
public static abstract T Clamp(T val, T min, T max);
}

View File

@ -0,0 +1,15 @@
using System.Numerics;
namespace Nerd_STF.Mathematics.Abstract;
public interface IClampMagnitude<TSelf, TNumber>
where TSelf : IClampMagnitude<TSelf, TNumber>
where TNumber : INumber<TNumber>
{
public static abstract TSelf ClampMagnitude(TSelf val, TNumber minMag, TNumber maxMag);
}
public interface IClampMagnitude<TSelf> where TSelf : IClampMagnitude<TSelf>
{
public static abstract TSelf ClampMagnitude<TNumber>(TSelf val, TNumber minMag, TNumber maxMag)
where TNumber : INumber<TNumber>;
}

View File

@ -0,0 +1,6 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IClosestTo<T> where T : IEquatable<T>
{
public T ClosestTo(T item);
}

View File

@ -0,0 +1,6 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IContains<T> where T : IEquatable<T>
{
public bool Contains(T item);
}

View File

@ -0,0 +1,8 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface ICross<TSelf> : ICross<TSelf, TSelf>
where TSelf : ICross<TSelf> { }
public interface ICross<TSelf, TOut> where TSelf : ICross<TSelf, TOut>
{
public static abstract TOut Cross(TSelf a, TSelf b, bool normalized = false);
}

View File

@ -0,0 +1,8 @@
using System.Numerics;
namespace Nerd_STF.Mathematics.Abstract;
public interface IDivide<T> : IDivisionOperators<T, T, T> where T : IDivide<T>
{
public static abstract T Divide(T num, params T[] vals);
}

View File

@ -0,0 +1,18 @@
using System.Numerics;
namespace Nerd_STF.Mathematics.Abstract;
public interface IDot<TSelf, TNumber>
where TSelf : IDot<TSelf, TNumber>
where TNumber : INumber<TNumber>
{
public static abstract TNumber Dot(TSelf a, TSelf b);
public static abstract TNumber Dot(TSelf[] vals);
}
public interface IDot<TSelf> where TSelf : IDot<TSelf>
{
public static abstract TNumber Dot<TNumber>(TSelf a, TSelf b)
where TNumber : INumber<TNumber>;
public static abstract TNumber Dot<TNumber>(TSelf[] vals)
where TNumber : INumber<TNumber>;
}

View File

@ -0,0 +1,9 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IFloor<TSelf> : IFloor<TSelf, TSelf>
where TSelf : IFloor<TSelf> { }
public interface IFloor<TSelf, TRound>
where TSelf : IFloor<TSelf, TRound>
{
public static abstract TRound Floor(TSelf val);
}

View File

@ -0,0 +1,9 @@
using System.Runtime.CompilerServices;
namespace Nerd_STF.Mathematics.Abstract;
public interface IFromTuple<TSelf, TTuple> where TSelf : IFromTuple<TSelf, TTuple>
where TTuple : ITuple
{
public static abstract implicit operator TSelf(TTuple val);
}

View File

@ -0,0 +1,3 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IIndexAll<TSub> : IIndexGet<TSub>, IIndexSet<TSub> { }

View File

@ -0,0 +1,7 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IIndexGet<TSub>
{
public TSub this[int index] { get; }
public TSub this[Index index] { get; }
}

View File

@ -0,0 +1,3 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IIndexRangeAll<TSub> : IIndexRangeGet<TSub>, IIndexRangeSet<TSub> { }

View File

@ -0,0 +1,6 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IIndexRangeGet<TSub>
{
public TSub[] this[Range range] { get; }
}

View File

@ -0,0 +1,6 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IIndexRangeSet<TSub>
{
public TSub[] this[Range range] { set; }
}

View File

@ -0,0 +1,7 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IIndexSet<TSub>
{
public TSub this[int index] { set; }
public TSub this[Index index] { set; }
}

View File

@ -0,0 +1,15 @@
using System.Numerics;
namespace Nerd_STF.Mathematics.Abstract;
public interface ILerp<TSelf, TNumber>
where TSelf : ILerp<TSelf, TNumber>
where TNumber : INumber<TNumber>
{
public static abstract TSelf Lerp(TSelf a, TSelf b, TNumber t, bool clamp = true);
}
public interface ILerp<TSelf> where TSelf : ILerp<TSelf>
{
public static abstract TSelf Lerp<TNumber>(TSelf a, TSelf b, TNumber t, bool clamp = true)
where TNumber : INumber<TNumber>;
}

View File

@ -0,0 +1,8 @@
using System.Numerics;
namespace Nerd_STF.Mathematics.Abstract;
public interface IMagnitude<TNumber> where TNumber : INumber<TNumber>
{
public TNumber Magnitude { get; }
}

View File

@ -0,0 +1,8 @@
using System.Numerics;
namespace Nerd_STF.Mathematics.Abstract;
public interface IMathOperators<TSelf> : IAdditionOperators<TSelf, TSelf, TSelf>,
ISubtractionOperators<TSelf, TSelf, TSelf>, IMultiplyOperators<TSelf, TSelf, TSelf>,
IDivisionOperators<TSelf, TSelf, TSelf>
where TSelf : IMathOperators<TSelf> { }

View File

@ -1,14 +1,14 @@
namespace Nerd_STF.Mathematics.Algebra;
namespace Nerd_STF.Mathematics.Abstract;
public interface IMatrix<T> : ICloneable, IEnumerable, IEquatable<T>, IGroup2D<float>
public interface IMatrix<T> : IAbsolute<T>, ICeiling<T>, IClamp<T>, IDivide<T>,
IEquatable<T>, IFloor<T>, IGroup2D<float>, ILerp<T, float>, IProduct<T>, IRound<T>,
ISubtract<T>, ISum<T>
where T : IMatrix<T>
{
public T Adjugate();
public float Determinant();
public T Inverse();
public T? Inverse();
public T Transpose();
public Dictionary<Int2, float> ToDictionary();
}
public interface IMatrix<This, TMinor> : IMatrix<This> where This : IMatrix<This, TMinor>

View File

@ -0,0 +1,9 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IMatrixPresets<T> where T : IMatrix<T>, IMatrixPresets<T>
{
public static abstract T Identity { get; }
public static abstract T One { get; }
public static abstract T SignGrid { get; }
public static abstract T Zero { get; }
}

View File

@ -0,0 +1,6 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IMax<T> where T : IMax<T>, IComparable<T>
{
public static virtual T Max(params T[] vals) => Mathf.Max(vals);
}

View File

@ -0,0 +1,6 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IMedian<T> where T : IMedian<T>
{
public static virtual T Median(params T[] vals) => Mathf.Median(vals);
}

View File

@ -0,0 +1,6 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IMin<T> where T : IMin<T>, IComparable<T>
{
public static virtual T Min(params T[] vals) => Mathf.Min(vals);
}

View File

@ -0,0 +1,7 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IPresets1D<T> where T : IPresets1D<T>
{
public static abstract T One { get; }
public static abstract T Zero { get; }
}

View File

@ -0,0 +1,9 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IPresets2D<T> : IPresets1D<T> where T : IPresets2D<T>
{
public static abstract T Down { get; }
public static abstract T Left { get; }
public static abstract T Right { get; }
public static abstract T Up { get; }
}

View File

@ -0,0 +1,7 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IPresets3D<T> : IPresets2D<T> where T : IPresets3D<T>
{
public static abstract T Back { get; }
public static abstract T Forward { get; }
}

View File

@ -0,0 +1,7 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IPresets4D<T> : IPresets2D<T>, IPresets3D<T> where T : IPresets4D<T>
{
public static abstract T HighW { get; }
public static abstract T LowW { get; }
}

View File

@ -0,0 +1,9 @@
using System.Numerics;
namespace Nerd_STF.Mathematics.Abstract;
public interface IProduct<T> : IMultiplyOperators<T, T, T>
where T : IProduct<T>
{
public static abstract T Product(params T[] vals);
}

View File

@ -0,0 +1,9 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IRound<TSelf> : IRound<TSelf, TSelf>
where TSelf : IRound<TSelf> { }
public interface IRound<TSelf, TRound>
where TSelf : IRound<TSelf, TRound>
{
public static abstract TRound Round(TSelf val);
}

View File

@ -0,0 +1,9 @@
using System.Numerics;
namespace Nerd_STF.Mathematics.Abstract;
public interface IShape2D<TNumber> where TNumber : INumber<TNumber>
{
public TNumber Area { get; }
public TNumber Perimeter { get; }
}

View File

@ -0,0 +1,9 @@
using System.Numerics;
namespace Nerd_STF.Mathematics.Abstract;
public interface IShape3D<TNumber> where TNumber : INumber<TNumber>
{
public TNumber SurfaceArea { get; }
public TNumber Volume { get; }
}

View File

@ -0,0 +1,8 @@
using System.Runtime.CompilerServices;
namespace Nerd_STF.Mathematics.Abstract;
public interface ISplittable<TSelf, TTuple> where TTuple : ITuple
{
public static abstract TTuple SplitArray(params TSelf[] vals);
}

View File

@ -0,0 +1,7 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IStaticMatrix<T> : IAverage<T>, IMatrix<T>, IMedian<T>,
IMatrixPresets<T> where T : IStaticMatrix<T>
{
}

View File

@ -0,0 +1,7 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface ISubdivide<T>
{
public T Subdivide();
public T Subdivide(int iterations);
}

View File

@ -0,0 +1,8 @@
using System.Numerics;
namespace Nerd_STF.Mathematics.Abstract;
public interface ISubtract<T> : ISubtractionOperators<T, T, T> where T : ISubtract<T>
{
public static abstract T Subtract(T num, params T[] vals);
}

View File

@ -0,0 +1,9 @@
using System.Numerics;
namespace Nerd_STF.Mathematics.Abstract;
public interface ISum<T> : IAdditionOperators<T, T, T>
where T : ISum<T>
{
public static abstract T Sum(params T[] vals);
}

View File

@ -0,0 +1,19 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface ITriangulate
{
public static Triangle[] TriangulateAll(params ITriangulate[] triangulatables)
{
List<Triangle> res = new();
foreach (ITriangulate triangulatable in triangulatables) res.AddRange(triangulatable.Triangulate());
return res.ToArray();
}
public static Triangle[] TriangulateAll<T>(params T[] triangulatables) where T : ITriangulate
{
List<Triangle> res = new();
foreach (ITriangulate triangulatable in triangulatables) res.AddRange(triangulatable.Triangulate());
return res.ToArray();
}
public Triangle[] Triangulate();
}

View File

@ -1,8 +1,18 @@
namespace Nerd_STF.Mathematics.Algebra;
using System.Buffers;
public struct Matrix : IMatrix<Matrix, Matrix>
namespace Nerd_STF.Mathematics.Algebra;
public class Matrix : IMatrix<Matrix, Matrix>
{
public static Matrix Identity(Int2 size)
{
if (size.x != size.y) throw new InvalidSizeException("Can only create an identity matrix of a square matrix." +
" You may want to use " + nameof(IdentityIsh) + " instead.");
Matrix m = Zero(size);
for (int i = 0; i < size.x; i++) m[i, i] = 1;
return m;
}
public static Matrix IdentityIsh(Int2 size)
{
Matrix m = Zero(size);
int max = Mathf.Min(size.x, size.y);
@ -27,7 +37,7 @@ public struct Matrix : IMatrix<Matrix, Matrix>
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;
for (int r = 0; r < size.x; r++) for (int c = 0; c < size.y; c++) array[r, c] = all;
}
public Matrix(Int2 size, float[] vals)
{
@ -37,7 +47,7 @@ public struct Matrix : IMatrix<Matrix, Matrix>
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];
for (int r = 0; r < size.x; r++) for (int c = 0; c < size.y; c++) array[r, c] = vals[c + r * size.y];
}
public Matrix(Int2 size, int[] vals)
{
@ -47,45 +57,45 @@ public struct Matrix : IMatrix<Matrix, Matrix>
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];
for (int r = 0; r < size.x; r++) for (int c = 0; c < size.y; c++) array[r, c] = 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);
for (int r = 0; r < size.x; r++) for (int c = 0; c < size.y; c++) array[r, c] = 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);
for (int r = 0; r < size.x; r++) for (int c = 0; c < size.y; c++) array[r, c] = 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];
for (int r = 0; r < size.x; r++) for (int c = 0; c < size.y; c++) array[r, c] = vals[r, c];
}
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];
for (int r = 0; r < size.x; r++) for (int c = 0; c < size.y; c++) array[r, c] = vals[r, c];
}
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);
for (int r = 0; r < size.x; r++) for (int c = 0; c < size.y; c++) array[r, c] = vals(r, c);
}
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);
for (int r = 0; r < size.x; r++) for (int c = 0; c < size.y; c++) array[r, c] = vals(r, c);
}
public float this[int r, int c]
@ -98,6 +108,51 @@ public struct Matrix : IMatrix<Matrix, Matrix>
get => this[index.x, index.y];
set => this[index.x, index.y] = value;
}
public float this[Index r, Index c]
{
get
{
int row = r.IsFromEnd ? Size.x - r.Value : r.Value,
col = c.IsFromEnd ? Size.y - c.Value : c.Value;
return array[row, col];
}
set
{
int row = r.IsFromEnd ? Size.x - r.Value : r.Value,
col = c.IsFromEnd ? Size.y - c.Value : c.Value;
array[row, col] = value;
}
}
public Matrix this[Range rs, Range cs]
{
get
{
int rowStart = rs.Start.IsFromEnd ? Size.x - rs.Start.Value : rs.Start.Value,
rowEnd = rs.End.IsFromEnd ? Size.x - rs.End.Value : rs.End.Value,
colStart = cs.Start.IsFromEnd ? Size.y - cs.Start.Value : cs.Start.Value,
colEnd = cs.End.IsFromEnd ? Size.y - cs.End.Value : cs.End.Value;
Matrix newMatrix = new((rowEnd - rowStart - 1, colEnd - colStart - 1));
for (int r = rowStart; r < rowEnd; r++)
for (int c = colStart; c < colEnd; c++)
newMatrix[r, c] = array[r, c];
return newMatrix;
}
set
{
int rowStart = rs.Start.IsFromEnd ? Size.x - rs.Start.Value : rs.Start.Value,
rowEnd = rs.End.IsFromEnd ? Size.x - rs.End.Value : rs.End.Value,
colStart = cs.Start.IsFromEnd ? Size.y - cs.Start.Value : cs.Start.Value,
colEnd = cs.End.IsFromEnd ? Size.y - cs.End.Value : cs.End.Value;
if (value.Size != (rowEnd - rowStart - 1, colEnd - colStart - 1))
throw new InvalidSizeException("Matrix has invalid size.");
for (int r = rowStart; r < rowEnd; r++)
for (int c = colStart; c < colEnd; c++)
array[r, c] = value[r, c];
}
}
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]));
@ -135,37 +190,38 @@ public struct Matrix : IMatrix<Matrix, Matrix>
public void Apply(Modifier2D modifier)
{
for (int r = 0; r < Size.y; r++) for (int c = 0; c < Size.x; c++)
for (int r = 0; r < Size.x; r++) for (int c = 0; c < Size.y; 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];
float[] vals = new float[Size.x];
for (int i = 0; i < Size.x; 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];
float[] vals = new float[Size.y];
for (int i = 0; i < Size.y; i++) vals[i] = array[row, i];
return vals;
}
public void SetColumn(int column, float[] vals)
{
if (vals.Length < Size.y)
if (vals.Length < Size.x)
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];
for (int i = 0; i < Size.x; i++) array[i, column] = vals[i];
}
public void SetRow(int row, float[] vals)
{
if (vals.Length < Size.x)
if (vals.Length < Size.y)
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];
for (int i = 0; i < Size.y; i++) array[row, i] = vals[i];
}
public Matrix Adjugate()
public Matrix Adjugate() => Cofactor().Transpose();
public Matrix Cofactor()
{
Matrix dets = new(Size);
Matrix[,] minors = Minors();
@ -175,58 +231,44 @@ public struct Matrix : IMatrix<Matrix, Matrix>
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];
if (Size.x <= 0) return 0;
if (Size.x == 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);
for (int i = 0; i < minors.Length; i++) det += array[0, i] * minors[i].Determinant() * (i % 2 == 0 ? 1 : -1);
return det;
}
public Matrix Inverse()
public Matrix? Inverse()
{
float d = Determinant();
if (d == 0) throw new NoInverseException();
return Transpose().Adjugate() / d;
if (d == 0) return null;
return 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;
Matrix[,] minors = new Matrix[Size.x, Size.y];
for (int r = 0; r < Size.x; r++) for (int c = 0; c < Size.y; c++) minors[r, c] = MinorOf(new(r, c));
return minors;
}
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;
Matrix @this = this;
return new(Size, (r, c) => @this[c, r]);
}
public Matrix MinorOf(Int2 index)
{
Matrix @this = this;
return new(@this.Size - Int2.One, delegate (int r, int c)
{
if (r >= index.x) r++;
if (c >= index.y) c++;
return @this[r, c];
});
}
public override bool Equals([NotNullWhen(true)] object? obj)
@ -240,25 +282,18 @@ public struct Matrix : IMatrix<Matrix, Matrix>
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)
public bool Equals(Matrix? other)
{
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;
if (other is null) return false;
return array.Equals(other.array);
}
public string ToString(IFormatProvider provider)
public override int GetHashCode() => array.GetHashCode();
public override string ToString()
{
string res = "";
for (int r = 0; r < Size.y; r++)
for (int r = 0; r < Size.x; r++)
{
for (int c = 0; c < Size.x; c++) res += array[c, r].ToString(provider) + " ";
for (int c = 0; c < Size.y; c++) res += array[r, c] + " ";
res += "\n";
}
return res;
@ -272,14 +307,8 @@ public struct Matrix : IMatrix<Matrix, Matrix>
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[] ToArray() => array.Flatten(new(Size.y, Size.x));
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()
{
@ -289,11 +318,14 @@ public struct Matrix : IMatrix<Matrix, Matrix>
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 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 Matrix operator *(Matrix a, Matrix b)
{
if (a.Size.y != b.Size.x) throw new InvalidSizeException("Incompatible Dimensions.");
return new(new(a.Size.x, b.Size.y), (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);
@ -302,7 +334,12 @@ public struct Matrix : IMatrix<Matrix, Matrix>
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 Matrix operator /(Matrix a, Matrix b)
{
Matrix? bInv = b.Inverse();
if (bInv is null) throw new NoInverseException(b);
return a * bInv;
}
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);

View File

@ -1,6 +1,6 @@
namespace Nerd_STF.Mathematics.Algebra;
public struct Matrix2x2 : IMatrix<Matrix2x2>
public record class Matrix2x2 : IStaticMatrix<Matrix2x2>
{
public static Matrix2x2 Identity => new(new[,]
{
@ -55,11 +55,11 @@ public struct Matrix2x2 : IMatrix<Matrix2x2>
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)
public Matrix2x2(float r1c1, float r1c2, float r2c1, float r2c2)
{
this.r1c1 = r1c1;
this.r2c1 = r2c1;
this.r1c2 = r1c2;
this.r2c1 = r2c1;
this.r2c2 = r2c2;
}
public Matrix2x2(float[] nums) : this(nums[0], nums[1], nums[2], nums[3]) { }
@ -70,17 +70,17 @@ public struct Matrix2x2 : IMatrix<Matrix2x2>
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(Float2 r1, Float2 r2) : this(r1.x, r1.y, r2.x, r2.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 Matrix2x2(IEnumerable<float> r1, IEnumerable<float> r2) : this(r1.ToFill(), r2.ToFill()) { }
public Matrix2x2(IEnumerable<int> r1, IEnumerable<int> r2) : this(r1.ToFill(), r2.ToFill()) { }
public Matrix2x2(Fill<float> r1, Fill<float> r2) : this(r1(0), r1(1), r2(0), r2(1)) { }
public Matrix2x2(Fill<int> r1, Fill<int> r2) : this(r1(0), r1(1), r2(0), r2(1)) { }
public float this[int r, int c]
{
get => ToArray2D()[c, r];
get => ToArray2D()[r, c];
set
{
// Maybe this could be improved?
@ -116,25 +116,67 @@ public struct Matrix2x2 : IMatrix<Matrix2x2>
get => this[index.x, index.y];
set => this[index.x, index.y] = value;
}
public float this[Index r, Index c]
{
get
{
int row = r.IsFromEnd ? 2 - r.Value : r.Value,
col = c.IsFromEnd ? 2 - c.Value : c.Value;
return this[row, col];
}
set
{
int row = r.IsFromEnd ? 2 - r.Value : r.Value,
col = c.IsFromEnd ? 2 - c.Value : c.Value;
this[row, col] = value;
}
}
public float[,] this[Range rs, Range cs]
{
get
{
int rowStart = rs.Start.IsFromEnd ? 2 - rs.Start.Value : rs.Start.Value,
rowEnd = rs.End.IsFromEnd ? 2 - rs.End.Value : rs.End.Value,
colStart = cs.Start.IsFromEnd ? 2 - cs.Start.Value : cs.Start.Value,
colEnd = cs.End.IsFromEnd ? 2 - cs.End.Value : cs.End.Value;
public static Matrix2x2 Absolute(Matrix2x2 val) => new(Mathf.Absolute(val.r1c1), Mathf.Absolute(val.r2c1),
Mathf.Absolute(val.r1c2), Mathf.Absolute(val.r2c2));
float[,] vals = new float[rowEnd - rowStart - 1, colEnd - colStart - 1];
for (int r = rowStart; r < rowEnd; r++)
for (int c = colStart; c < colEnd; c++)
vals[r, c] = this[r, c];
return vals;
}
set
{
int rowStart = rs.Start.IsFromEnd ? 2 - rs.Start.Value : rs.Start.Value,
rowEnd = rs.End.IsFromEnd ? 2 - rs.End.Value : rs.End.Value,
colStart = cs.Start.IsFromEnd ? 2 - cs.Start.Value : cs.Start.Value,
colEnd = cs.End.IsFromEnd ? 2 - cs.End.Value : cs.End.Value;
for (int r = rowStart; r < rowEnd; r++)
for (int c = colStart; c < colEnd; c++)
this[r, c] = value[r, c];
}
}
public static Matrix2x2 Absolute(Matrix2x2 val) => new(Mathf.Absolute(val.r1c1), Mathf.Absolute(val.r1c2),
Mathf.Absolute(val.r2c1), 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 Ceiling(Matrix2x2 val) => new(Mathf.Ceiling(val.r1c1), Mathf.Ceiling(val.r1c2),
Mathf.Ceiling(val.r2c1), 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));
new(Mathf.Clamp(val.r1c1, min.r1c1, max.r1c1), Mathf.Clamp(val.r1c2, min.r1c2, max.r1c2),
Mathf.Clamp(val.r2c1, min.r2c1, max.r2c1), 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 Floor(Matrix2x2 val) => new(Mathf.Floor(val.r1c1), Mathf.Floor(val.r1c2),
Mathf.Floor(val.r2c1), 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));
new(Mathf.Lerp(a.r1c1, b.r1c1, t, clamp), Mathf.Lerp(a.r1c2, b.r1c2, t, clamp),
Mathf.Lerp(a.r2c1, b.r2c1, 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);
@ -148,8 +190,8 @@ public struct Matrix2x2 : IMatrix<Matrix2x2>
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 Round(Matrix2x2 val) => new(Mathf.Round(val.r1c1), Mathf.Round(val.r1c2),
Mathf.Round(val.r2c1), Mathf.Round(val.r2c2));
public static Matrix2x2 Subtract(Matrix2x2 num, params Matrix2x2[] vals)
{
foreach (Matrix2x2 m in vals) num -= m;
@ -162,21 +204,22 @@ public struct Matrix2x2 : IMatrix<Matrix2x2>
return val;
}
public static (float[] r1c1s, float[] r2c1s, float[] r1c2s, float[] r2c2s) SplitArray(params Matrix2x2[] vals)
public static (float[] r1c1s, float[] r1c2s, float[] r2c1s, float[] r2c2s) SplitArray(params Matrix2x2[] vals)
{
float[] r1c1s = new float[vals.Length], r2c1s = new float[vals.Length], r1c2s = new float[vals.Length],
float[] r1c1s = new float[vals.Length], r1c2s = new float[vals.Length], r2c1s = 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;
r2c1s[i] = vals[i].r2c1;
r2c2s[i] = vals[i].r2c2;
}
return (r1c1s, r2c1s, r1c2s, r2c2s);
return (r1c1s, r1c2s, r2c1s, r2c2s);
}
public Matrix2x2 Adjugate()
public Matrix2x2 Adjugate() => Cofactor().Transpose();
public Matrix2x2 Cofactor()
{
Matrix2x2 swapped = new(new[,]
{
@ -186,10 +229,10 @@ public struct Matrix2x2 : IMatrix<Matrix2x2>
return swapped ^ SignGrid;
}
public float Determinant() => r1c1 * r2c2 - r1c2 * r2c1;
public Matrix2x2 Inverse()
public Matrix2x2? Inverse()
{
float d = Determinant();
if (d == 0) throw new NoInverseException();
if (d == 0) return null;
return Transpose().Adjugate() / d;
}
public Matrix2x2 Transpose() => new(new[,]
@ -198,76 +241,65 @@ public struct Matrix2x2 : IMatrix<Matrix2x2>
{ r1c2, r2c2 }
});
public override bool Equals([NotNullWhen(true)] object? obj)
public virtual bool Equals(Matrix2x2? other)
{
if (obj == null || obj.GetType() != typeof(Matrix2x2)) return base.Equals(obj);
return Equals((Matrix2x2)obj);
if (other is null) return false;
return r1c1 == other.r1c1 && r1c2 == other.r1c2 && r2c1 == other.r2c1 && r2c2 == other.r2c2;
}
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());
public override int GetHashCode() => base.GetHashCode();
public override string ToString() => r1c1 + " " + r1c2 + "\n" + r2c1 + " " + r2c2;
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<float> GetEnumerator()
{
yield return r1c1;
yield return r2c1;
yield return r1c2;
yield return r2c1;
yield return r2c2;
}
public float[] ToArray() => new[] { r1c1, r2c1, r1c2, r2c2 };
public float[] ToArray() => new[] { r1c1, r1c2, r2c1, 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 List<float> ToList() => new() { r1c1, r1c2, r2c1, 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 a, Matrix2x2 b) => new(a.r1c1 + b.r1c1, a.r1c2 + b.r1c2,
a.r2c1 + b.r2c1, 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(a.r1c1 - b.r1c1, a.r1c2 - b.r1c2,
a.r2c1 - b.r2c1, a.r2c2 - b.r2c2);
public static Matrix2x2 operator *(Matrix2x2 a, float b) => new(a.r1c1 * b, a.r1c2 * b, a.r2c1 * 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 Float2 operator *(Matrix2x2 a, Float2 b) => (Matrix)a * b;
public static Matrix2x2 operator /(Matrix2x2 a, float b) => new(a.r1c1 / b, a.r1c2 / b, a.r2c1 / b, a.r2c2 / b);
public static Matrix2x2 operator /(Matrix2x2 a, Matrix2x2 b)
{
Matrix2x2? bInv = b.Inverse();
if (bInv is null) throw new NoInverseException(b);
return a * bInv;
}
public static Float2 operator /(Matrix2x2 a, Float2 b) => (Matrix)a / b;
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);
new(a.r1c1 * b.r1c1, a.r1c2 * b.r1c2, a.r2c1 * b.r2c1, a.r2c2 * b.r2c2);
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];
res[c, r] = m.Size.x > r && m.Size.y > c ? m[r, c] : identity[r, c];
return res;
}
public static explicit operator Matrix2x2(Matrix3x3 m) => new(m.r1c1, m.r2c1, m.r1c2, m.r2c2);

View File

@ -1,6 +1,6 @@
namespace Nerd_STF.Mathematics.Algebra;
public struct Matrix3x3 : IMatrix<Matrix3x3, Matrix2x2>
public record class Matrix3x3 : IStaticMatrix<Matrix3x3>
{
public static Matrix3x3 Identity => new(new[,]
{
@ -81,17 +81,17 @@ public struct Matrix3x3 : IMatrix<Matrix3x3, Matrix2x2>
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)
public Matrix3x3(float r1c1, float r1c2, float r1c3, float r2c1,
float r2c2, float r2c3, float r3c1, float r3c2, float r3c3)
{
this.r1c1 = r1c1;
this.r2c1 = r2c1;
this.r3c1 = r3c1;
this.r1c2 = r1c2;
this.r2c2 = r2c2;
this.r3c2 = r3c2;
this.r1c3 = r1c3;
this.r2c1 = r2c1;
this.r2c2 = r2c2;
this.r2c3 = r2c3;
this.r3c1 = r3c1;
this.r3c2 = r3c2;
this.r3c3 = r3c3;
}
public Matrix3x3(float[] nums) : this(nums[0], nums[1], nums[2],
@ -110,21 +110,21 @@ public struct Matrix3x3 : IMatrix<Matrix3x3, Matrix2x2>
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(Float3 r1, Float3 r2, Float3 r3) : this(r1.x, r1.y, r1.z, r2.x, r2.y, r2.z, r3.x, r3.y, r3.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 Matrix3x3(IEnumerable<float> r1, IEnumerable<float> r2, IEnumerable<float> r3)
: this(r1.ToFill(), r2.ToFill(), r3.ToFill()) { }
public Matrix3x3(IEnumerable<int> r1, IEnumerable<int> r2, IEnumerable<int> r3)
: this(r1.ToFill(), r2.ToFill(), r3.ToFill()) { }
public Matrix3x3(Fill<float> r1, Fill<float> r2, Fill<float> r3)
: this(r1(0), r1(1), r1(2), r2(0), r2(1), r2(2), r3(0), r3(1), r3(2)) { }
public Matrix3x3(Fill<int> r1, Fill<int> r2, Fill<int> r3)
: this(r1(0), r1(1), r1(2), r2(0), r2(1), r2(2), r3(0), r3(1), r3(2)) { }
public float this[int r, int c]
{
get => ToArray2D()[c, r];
get => ToArray2D()[r, c];
set
{
// Maybe this could be improved?
@ -180,21 +180,63 @@ public struct Matrix3x3 : IMatrix<Matrix3x3, Matrix2x2>
get => this[index.x, index.y];
set => this[index.x, index.y] = value;
}
public float this[Index r, Index c]
{
get
{
int row = r.IsFromEnd ? 3 - r.Value : r.Value,
col = c.IsFromEnd ? 3 - c.Value : c.Value;
return this[row, col];
}
set
{
int row = r.IsFromEnd ? 3 - r.Value : r.Value,
col = c.IsFromEnd ? 3 - c.Value : c.Value;
this[row, col] = value;
}
}
public float[,] this[Range rs, Range cs]
{
get
{
int rowStart = rs.Start.IsFromEnd ? 3 - rs.Start.Value : rs.Start.Value,
rowEnd = rs.End.IsFromEnd ? 3 - rs.End.Value : rs.End.Value,
colStart = cs.Start.IsFromEnd ? 3 - cs.Start.Value : cs.Start.Value,
colEnd = cs.End.IsFromEnd ? 3 - cs.End.Value : cs.End.Value;
float[,] vals = new float[rowEnd - rowStart - 1, colEnd - colStart - 1];
for (int r = rowStart; r < rowEnd; r++)
for (int c = colStart; c < colEnd; c++)
vals[r, c] = this[r, c];
return vals;
}
set
{
int rowStart = rs.Start.IsFromEnd ? 3 - rs.Start.Value : rs.Start.Value,
rowEnd = rs.End.IsFromEnd ? 3 - rs.End.Value : rs.End.Value,
colStart = cs.Start.IsFromEnd ? 3 - cs.Start.Value : cs.Start.Value,
colEnd = cs.End.IsFromEnd ? 3 - cs.End.Value : cs.End.Value;
for (int r = rowStart; r < rowEnd; r++)
for (int c = colStart; c < colEnd; c++)
this[r, c] = value[r, c];
}
}
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));
new(Mathf.Absolute(val.r1c1), Mathf.Absolute(val.r1c2), Mathf.Absolute(val.r1c3),
Mathf.Absolute(val.r2c1), Mathf.Absolute(val.r2c2), Mathf.Absolute(val.r2c3),
Mathf.Absolute(val.r3c1), Mathf.Absolute(val.r3c2), 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));
new(Mathf.Ceiling(val.r1c1), Mathf.Ceiling(val.r1c2), Mathf.Ceiling(val.r1c3),
Mathf.Ceiling(val.r2c1), Mathf.Ceiling(val.r2c2), Mathf.Ceiling(val.r2c3),
Mathf.Ceiling(val.r3c1), Mathf.Ceiling(val.r3c2), 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),
new(Mathf.Clamp(val.r1c1, min.r1c1, max.r1c1), Mathf.Clamp(val.r1c2, min.r1c2, max.r1c2),
Mathf.Clamp(val.r1c3, min.r1c3, max.r1c3), Mathf.Clamp(val.r2c1, min.r2c1, max.r2c1),
Mathf.Clamp(val.r2c2, min.r2c2, max.r2c2), Mathf.Clamp(val.r2c3, min.r2c3, max.r2c3),
Mathf.Clamp(val.r3c1, min.r3c1, max.r3c1), Mathf.Clamp(val.r3c2, min.r3c2, max.r3c2),
Mathf.Clamp(val.r3c3, min.r3c3, max.r3c3));
public static Matrix3x3 Divide(Matrix3x3 num, params Matrix3x3[] vals)
{
@ -202,14 +244,14 @@ public struct Matrix3x3 : IMatrix<Matrix3x3, Matrix2x2>
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));
new(Mathf.Floor(val.r1c1), Mathf.Floor(val.r1c2), Mathf.Floor(val.r1c3),
Mathf.Floor(val.r2c1), Mathf.Floor(val.r2c2), Mathf.Floor(val.r2c3),
Mathf.Floor(val.r3c1), Mathf.Floor(val.r3c2), 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),
new(Mathf.Lerp(a.r1c1, b.r1c1, t, clamp), Mathf.Lerp(a.r1c2, b.r1c2, t, clamp),
Mathf.Lerp(a.r1c3, b.r1c3, t, clamp), Mathf.Lerp(a.r2c1, b.r2c1, t, clamp),
Mathf.Lerp(a.r2c2, b.r2c2, t, clamp), Mathf.Lerp(a.r2c3, b.r2c3, t, clamp),
Mathf.Lerp(a.r3c1, b.r3c1, t, clamp), Mathf.Lerp(a.r3c2, b.r3c2, t, clamp),
Mathf.Lerp(a.r3c3, b.r3c3, t, clamp));
public static Matrix3x3 Median(params Matrix3x3[] vals)
{
@ -225,9 +267,9 @@ public struct Matrix3x3 : IMatrix<Matrix3x3, Matrix2x2>
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));
new(Mathf.Round(val.r1c1), Mathf.Round(val.r1c2), Mathf.Round(val.r1c3),
Mathf.Round(val.r2c1), Mathf.Round(val.r2c2), Mathf.Round(val.r2c3),
Mathf.Round(val.r3c1), Mathf.Round(val.r3c2), Mathf.Round(val.r3c3));
public static Matrix3x3 Subtract(Matrix3x3 num, params Matrix3x3[] vals)
{
foreach (Matrix3x3 m in vals) num -= m;
@ -240,30 +282,31 @@ public struct Matrix3x3 : IMatrix<Matrix3x3, Matrix2x2>
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)
public static (float[] r1c1s, float[] r1c2s, float[] r1c3s, float[] r2c1s, float[] r2c2s,
float[] r2c3s, float[] r3c1s, float[] r3c2s, 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];
float[] r1c1s = new float[vals.Length], r1c2s = new float[vals.Length], r1c3s = new float[vals.Length],
r2c1s = new float[vals.Length], r2c2s = new float[vals.Length], r2c3s = new float[vals.Length],
r3c1s = new float[vals.Length], r3c2s = 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;
r2c1s[i] = vals[i].r2c1;
r2c2s[i] = vals[i].r2c2;
r2c3s[i] = vals[i].r2c3;
r3c1s[i] = vals[i].r3c1;
r3c2s[i] = vals[i].r3c2;
r3c3s[i] = vals[i].r3c3;
}
return (r1c1s, r2c1s, r3c1s, r1c2s, r2c2s, r3c2s, r1c3s, r2c3s, r3c3s);
return (r1c1s, r1c2s, r1c3s, r2c1s, r2c2s, r2c3s, r3c1s, r3c2s, r3c3s);
}
public Matrix3x3 Adjugate()
public Matrix3x3 Adjugate() => Cofactor().Transpose();
public Matrix3x3 Cofactor()
{
Matrix3x3 dets = new();
Matrix3x3 dets = Zero;
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;
@ -271,20 +314,20 @@ public struct Matrix3x3 : IMatrix<Matrix3x3, Matrix2x2>
public float Determinant()
{
Matrix2x2[,] minors = Minors();
return (r1c1 * minors[0, 0].Determinant()) - (r1c2 * minors[1, 0].Determinant())
+ (r1c3 * minors[2, 0].Determinant());
return (r1c1 * minors[0, 0].Determinant()) - (r1c2 * minors[0, 1].Determinant())
+ (r1c3 * minors[0, 2].Determinant());
}
public Matrix3x3 Inverse()
public Matrix3x3? Inverse()
{
float d = Determinant();
if (d == 0) throw new NoInverseException();
return Transpose().Adjugate() / d;
if (d == 0) return null;
return 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) }
{ new(r2c2, r2c3, r3c2, r3c3), new(r2c1, r2c3, r3c1, r3c3), new(r2c1, r2c2, r3c1, r3c2) },
{ new(r1c2, r1c3, r3c2, r3c3), new(r1c1, r1c3, r3c1, r3c3), new(r1c1, r1c2, r3c1, r3c2) },
{ new(r1c2, r1c3, r2c2, r2c3), new(r1c1, r1c3, r2c1, r2c3), new(r1c1, r1c2, r2c1, r2c2) }
};
public Matrix3x3 Transpose() => new(new[,]
{
@ -293,103 +336,89 @@ public struct Matrix3x3 : IMatrix<Matrix3x3, Matrix2x2>
{ r1c3, r2c3, r3c3 }
});
public override bool Equals([NotNullWhen(true)] object? obj)
public virtual bool Equals(Matrix3x3? other)
{
if (obj == null || obj.GetType() != typeof(Matrix3x3)) return base.Equals(obj);
return Equals((Matrix3x3)obj);
if (other is null) return false;
return r1c1 == other.r1c1 && r1c2 == other.r1c2 && r1c3 == other.r1c3 &&
r2c1 == other.r2c1 && r2c2 == other.r2c2 && r2c3 == other.r2c3 &&
r3c1 == other.r3c1 && r3c2 == other.r3c2 && r3c3 == other.r3c3;
}
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());
public override int GetHashCode() => base.GetHashCode();
public override string ToString() =>
r1c1 + " " + r1c2 + " " + r1c3 + "\n" +
r2c1 + " " + r2c2 + " " + r2c3 + "\n" +
r3c1 + " " + r3c2 + " " + r3c3;
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 r2c1;
yield return r2c2;
yield return r2c3;
yield return r3c1;
yield return r3c2;
yield return r3c3;
}
public float[] ToArray() => new[] { r1c1, r2c1, r3c1, r1c2, r2c2, r3c2, r1c3, r2c3, r3c3 };
public float[] ToArray() => new[] { r1c1, r1c2, r1c3, r2c1, r2c2, r2c3, r3c1, r3c2, 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 List<float> ToList() => new() { r1c1, r1c2, r1c3, r2c1, r2c2, r2c3, r3c1, r3c2, 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();
new(a.r1c1 + b.r1c1, a.r1c2 + b.r1c2, a.r1c3 + b.r1c3,
a.r2c1 + b.r2c1, a.r2c2 + b.r2c2, a.r2c3 + b.r2c3,
a.r3c1 + b.r3c1, a.r3c2 + b.r3c2, 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);
new(a.r1c1 - b.r1c1, a.r1c2 - b.r1c2, a.r1c3 - b.r1c3,
a.r2c1 - b.r2c1, a.r2c2 - b.r2c2, a.r2c3 - b.r2c3,
a.r3c1 - b.r3c1, a.r3c2 - b.r3c2, 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);
new(a.r1c1 * b, a.r1c2 * b, a.r1c3 * b,
a.r2c1 * b, a.r2c2 * b, a.r2c3 * b,
a.r3c1 * b, a.r3c2 * 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 Float3 operator *(Matrix3x3 a, Float3 b) => (Matrix)a * b;
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();
new(a.r1c1 / b, a.r1c2 / b, a.r1c3 / b,
a.r2c1 / b, a.r2c2 / b, a.r2c3 / b,
a.r3c1 / b, a.r3c2 / b, a.r3c3 / b);
public static Matrix3x3 operator /(Matrix3x3 a, Matrix3x3 b)
{
Matrix3x3? bInv = b.Inverse();
if (bInv is null) throw new NoInverseException(b);
return a * bInv;
}
public static Float3 operator /(Matrix3x3 a, Float3 b) => (Matrix)a / b;
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);
new(a.r1c1 * b.r1c1, a.r1c2 * b.r1c2, a.r1c3 * b.r1c3,
a.r2c1 * b.r2c1, a.r2c2 * b.r2c2, a.r2c3 * b.r2c3,
a.r3c1 * b.r3c1, a.r3c2 * b.r3c2, a.r3c3 * b.r3c3);
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];
res[c, r] = m.Size.x > r && m.Size.y > c ? m[r, c] : identity[r, c];
return res;
}
public static implicit operator Matrix3x3(Matrix2x2 m)

View File

@ -1,6 +1,6 @@
namespace Nerd_STF.Mathematics.Algebra;
public struct Matrix4x4 : IMatrix<Matrix4x4, Matrix3x3>
public record class Matrix4x4 : IStaticMatrix<Matrix4x4>
{
public static Matrix4x4 Identity => new(new[,]
{
@ -65,7 +65,7 @@ public struct Matrix4x4 : IMatrix<Matrix4x4, Matrix3x3>
}
public Float4 Row1
{
get => new(r1c1, r1c2, r1c3, r1c3);
get => new(r1c1, r1c2, r1c3, r1c4);
set
{
r1c1 = value.x;
@ -76,7 +76,7 @@ public struct Matrix4x4 : IMatrix<Matrix4x4, Matrix3x3>
}
public Float4 Row2
{
get => new(r2c1, r2c2, r2c3, r2c3);
get => new(r2c1, r2c2, r2c3, r2c4);
set
{
r2c1 = value.x;
@ -87,7 +87,7 @@ public struct Matrix4x4 : IMatrix<Matrix4x4, Matrix3x3>
}
public Float4 Row3
{
get => new(r3c1, r3c2, r3c3, r3c3);
get => new(r3c1, r3c2, r3c3, r3c4);
set
{
r3c1 = value.x;
@ -98,7 +98,7 @@ public struct Matrix4x4 : IMatrix<Matrix4x4, Matrix3x3>
}
public Float4 Row4
{
get => new(r4c1, r4c2, r4c3, r4c3);
get => new(r4c1, r4c2, r4c3, r4c4);
set
{
r4c1 = value.x;
@ -112,8 +112,8 @@ public struct Matrix4x4 : IMatrix<Matrix4x4, Matrix3x3>
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)
public Matrix4x4(float r1c1, float r1c2, float r1c3, float r1c4, float r2c1, float r2c2, float r2c3,
float r2c4, float r3c1, float r3c2, float r3c3, float r3c4, float r4c1, float r4c2, float r4c3, float r4c4)
{
this.r1c1 = r1c1;
this.r2c1 = r2c1;
@ -152,22 +152,22 @@ public struct Matrix4x4 : IMatrix<Matrix4x4, Matrix3x3>
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(Float4 r1, Float4 r2, Float4 r3, Float4 r4) : this(r1.x, r1.y, r1.z,
r1.w, r2.x, r2.y, r2.z, r2.w, r3.x, r3.y, r3.z, r3.w, r4.x, r4.y, r4.z, r4.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 Matrix4x4(IEnumerable<float> r1, IEnumerable<float> r2, IEnumerable<float> r3, IEnumerable<float> r4)
: this(r1.ToFill(), r2.ToFill(), r3.ToFill(), r4.ToFill()) { }
public Matrix4x4(IEnumerable<int> r1, IEnumerable<int> r2, IEnumerable<int> r3, IEnumerable<int> r4)
: this(r1.ToFill(), r2.ToFill(), r3.ToFill(), r4.ToFill()) { }
public Matrix4x4(Fill<float> r1, Fill<float> r2, Fill<float> r3, Fill<float> r4) : this(r1(0), r1(1),
r1(2), r1(3), r2(0), r2(1), r2(2), r2(3), r3(0), r3(1), r3(2), r3(3), r4(0), r4(1), r4(2), r4(3)) { }
public Matrix4x4(Fill<int> r1, Fill<int> r2, Fill<int> r3, Fill<int> r4) : this(r1(0), r1(1),
r1(2), r1(3), r2(0), r2(1), r2(2), r2(3), r3(0), r3(1), r3(2), r3(3), r4(0), r4(1), r4(2), r4(3)) { }
public float this[int r, int c]
{
get => ToArray2D()[c, r];
get => ToArray2D()[r, c];
set
{
// Maybe this could be improved?
@ -251,46 +251,88 @@ public struct Matrix4x4 : IMatrix<Matrix4x4, Matrix3x3>
get => this[index.x, index.y];
set => this[index.x, index.y] = value;
}
public float this[Index r, Index c]
{
get
{
int row = r.IsFromEnd ? 4 - r.Value : r.Value,
col = c.IsFromEnd ? 4 - c.Value : c.Value;
return this[row, col];
}
set
{
int row = r.IsFromEnd ? 4 - r.Value : r.Value,
col = c.IsFromEnd ? 4 - c.Value : c.Value;
this[row, col] = value;
}
}
public float[,] this[Range rs, Range cs]
{
get
{
int rowStart = rs.Start.IsFromEnd ? 4 - rs.Start.Value : rs.Start.Value,
rowEnd = rs.End.IsFromEnd ? 4 - rs.End.Value : rs.End.Value,
colStart = cs.Start.IsFromEnd ? 4 - cs.Start.Value : cs.Start.Value,
colEnd = cs.End.IsFromEnd ? 4 - cs.End.Value : cs.End.Value;
float[,] vals = new float[rowEnd - rowStart - 1, colEnd - colStart - 1];
for (int r = rowStart; r < rowEnd; r++)
for (int c = colStart; c < colEnd; c++)
vals[r, c] = this[r, c];
return vals;
}
set
{
int rowStart = rs.Start.IsFromEnd ? 4 - rs.Start.Value : rs.Start.Value,
rowEnd = rs.End.IsFromEnd ? 4 - rs.End.Value : rs.End.Value,
colStart = cs.Start.IsFromEnd ? 4 - cs.Start.Value : cs.Start.Value,
colEnd = cs.End.IsFromEnd ? 4 - cs.End.Value : cs.End.Value;
for (int r = rowStart; r < rowEnd; r++)
for (int c = colStart; c < colEnd; c++)
this[r, c] = value[r, c];
}
}
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));
new(Mathf.Absolute(val.r1c1), Mathf.Absolute(val.r1c2), Mathf.Absolute(val.r1c3), Mathf.Absolute(val.r1c4),
Mathf.Absolute(val.r2c1), Mathf.Absolute(val.r2c2), Mathf.Absolute(val.r2c3), Mathf.Absolute(val.r2c4),
Mathf.Absolute(val.r3c1), Mathf.Absolute(val.r3c2), Mathf.Absolute(val.r3c3), Mathf.Absolute(val.r3c4),
Mathf.Absolute(val.r4c1), Mathf.Absolute(val.r4c2), Mathf.Absolute(val.r4c3), 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));
new(Mathf.Ceiling(val.r1c1), Mathf.Ceiling(val.r1c2), Mathf.Ceiling(val.r1c3), Mathf.Ceiling(val.r1c4),
Mathf.Ceiling(val.r2c1), Mathf.Ceiling(val.r2c2), Mathf.Ceiling(val.r2c3), Mathf.Ceiling(val.r2c4),
Mathf.Ceiling(val.r3c1), Mathf.Ceiling(val.r3c2), Mathf.Ceiling(val.r3c3), Mathf.Ceiling(val.r3c4),
Mathf.Ceiling(val.r4c1), Mathf.Ceiling(val.r4c2), Mathf.Ceiling(val.r4c3), 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));
new(Mathf.Clamp(val.r1c1, min.r1c1, max.r1c1), Mathf.Clamp(val.r1c2, min.r1c2, max.r1c2),
Mathf.Clamp(val.r1c3, min.r1c3, max.r1c3), Mathf.Clamp(val.r1c4, min.r1c4, max.r1c4),
Mathf.Clamp(val.r2c1, min.r2c1, max.r2c1), Mathf.Clamp(val.r2c2, min.r2c2, max.r2c2),
Mathf.Clamp(val.r2c3, min.r2c3, max.r2c3), Mathf.Clamp(val.r2c4, min.r2c4, max.r2c4),
Mathf.Clamp(val.r3c1, min.r3c1, max.r3c1), Mathf.Clamp(val.r3c2, min.r3c2, max.r3c2),
Mathf.Clamp(val.r3c3, min.r3c3, max.r3c3), Mathf.Clamp(val.r3c4, min.r3c4, max.r3c4),
Mathf.Clamp(val.r4c1, min.r4c1, max.r4c1), Mathf.Clamp(val.r4c2, min.r4c2, max.r4c2),
Mathf.Clamp(val.r4c3, min.r4c3, max.r4c3), 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));
new(Mathf.Floor(val.r1c1), Mathf.Floor(val.r1c2), Mathf.Floor(val.r1c3), Mathf.Floor(val.r1c4),
Mathf.Floor(val.r2c1), Mathf.Floor(val.r2c2), Mathf.Floor(val.r2c3), Mathf.Floor(val.r2c4),
Mathf.Floor(val.r3c1), Mathf.Floor(val.r3c2), Mathf.Floor(val.r3c3), Mathf.Floor(val.r3c4),
Mathf.Floor(val.r4c1), Mathf.Floor(val.r4c2), Mathf.Floor(val.r4c3), 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));
new(Mathf.Lerp(a.r1c1, b.r1c1, t, clamp), Mathf.Lerp(a.r1c2, b.r1c2, t, clamp),
Mathf.Lerp(a.r1c3, b.r1c3, t, clamp), Mathf.Lerp(a.r1c4, b.r1c4, t, clamp),
Mathf.Lerp(a.r2c1, b.r2c1, t, clamp), Mathf.Lerp(a.r2c2, b.r2c2, t, clamp),
Mathf.Lerp(a.r2c3, b.r2c3, t, clamp), Mathf.Lerp(a.r2c4, b.r2c4, t, clamp),
Mathf.Lerp(a.r3c1, b.r3c1, t, clamp), Mathf.Lerp(a.r3c2, b.r3c2, t, clamp),
Mathf.Lerp(a.r3c3, b.r3c3, t, clamp), Mathf.Lerp(a.r3c4, b.r3c4, t, clamp),
Mathf.Lerp(a.r4c1, b.r4c1, t, clamp), Mathf.Lerp(a.r4c2, b.r4c2, t, clamp),
Mathf.Lerp(a.r4c3, b.r4c3, 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);
@ -305,10 +347,10 @@ public struct Matrix4x4 : IMatrix<Matrix4x4, Matrix3x3>
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));
new(Mathf.Round(val.r1c1), Mathf.Round(val.r1c2), Mathf.Round(val.r1c3), Mathf.Round(val.r1c4),
Mathf.Round(val.r2c1), Mathf.Round(val.r2c2), Mathf.Round(val.r2c3), Mathf.Round(val.r2c4),
Mathf.Round(val.r3c1), Mathf.Round(val.r3c2), Mathf.Round(val.r3c3), Mathf.Round(val.r3c4),
Mathf.Round(val.r4c1), Mathf.Round(val.r4c2), Mathf.Round(val.r4c3), Mathf.Round(val.r4c4));
public static Matrix4x4 Subtract(Matrix4x4 num, params Matrix4x4[] vals)
{
foreach (Matrix4x4 m in vals) num -= m;
@ -321,39 +363,43 @@ public struct Matrix4x4 : IMatrix<Matrix4x4, Matrix3x3>
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)
public static (float[] r1c1s, float[] r1c2, float[] r1c3, float[] r1c4, float[] r2c1, float[] r2c2s,
float[] r2c3, float[] r2c4, float[] r3c1, float[] r3c2, float[] r3c3s, float[] r3c4, float[] r4c1,
float[] r4c2, float[] r4c3, 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],
float[] r1c1s = new float[vals.Length], r1c2s = new float[vals.Length], r1c3s = new float[vals.Length],
r1c4s = new float[vals.Length], r2c1s = new float[vals.Length], r2c2s = new float[vals.Length],
r2c3s = new float[vals.Length], r2c4s = new float[vals.Length], r3c1s = new float[vals.Length],
r3c2s = new float[vals.Length], r3c3s = new float[vals.Length], r3c4s = new float[vals.Length],
r4c1s = new float[vals.Length], r4c2s = new float[vals.Length], r4c3s = 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;
r1c4s[i] = vals[i].r1c4;
r2c1s[i] = vals[i].r2c1;
r2c2s[i] = vals[i].r2c2;
r2c3s[i] = vals[i].r2c3;
r2c4s[i] = vals[i].r2c4;
r3c1s[i] = vals[i].r3c1;
r3c2s[i] = vals[i].r3c2;
r3c3s[i] = vals[i].r3c3;
r3c4s[i] = vals[i].r3c4;
r4c1s[i] = vals[i].r4c1;
r4c2s[i] = vals[i].r4c2;
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);
return (r1c1s, r1c2s, r1c3s, r1c4s, r2c1s, r2c2s, r2c3s, r2c4s,
r3c1s, r3c2s, r3c3s, r3c4s, r4c1s, r4c2s, r4c3s, r4c4s);
}
public Matrix4x4 Adjugate()
public Matrix4x4 Adjugate() => Cofactor().Transpose();
public Matrix4x4 Cofactor()
{
Matrix4x4 dets = new();
Matrix4x4 dets = Zero;
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;
@ -361,40 +407,40 @@ public struct Matrix4x4 : IMatrix<Matrix4x4, Matrix3x3>
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());
return (r1c1 * minors[0, 0].Determinant()) - (r1c2 * minors[0, 1].Determinant()) +
(r1c3 * minors[0, 2].Determinant()) - (r1c4 * minors[0, 3].Determinant());
}
public Matrix4x4 Inverse()
public Matrix4x4? Inverse()
{
float d = Determinant();
if (d == 0) throw new NoInverseException();
return Transpose().Adjugate() / d;
if (d == 0) return null;
return 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(r2c2, r2c3, r2c4, r3c2, r3c3, r3c4, r4c2, r4c3, r4c4),
new(r2c1, r2c3, r2c4, r3c1, r3c3, r3c4, r4c1, r4c3, r4c4),
new(r2c1, r2c2, r2c4, r3c1, r3c2, r3c4, r4c1, r4c2, r4c4),
new(r2c1, r2c2, r2c3, r3c1, r3c2, r3c3, r4c1, r4c2, 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, r1c3, r1c4, r3c2, r3c3, r3c4, r4c2, r4c3, r4c4),
new(r1c1, r1c3, r1c4, r3c1, r3c3, r3c4, r4c1, r4c3, r4c4),
new(r1c1, r1c2, r1c4, r3c1, r3c2, r3c4, r4c1, r4c2, r4c4),
new(r1c1, r1c2, r1c3, r3c1, r3c2, r3c3, r4c1, r4c2, 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, r1c3, r1c4, r2c2, r2c3, r2c4, r4c2, r4c3, r4c4),
new(r1c1, r1c3, r1c4, r2c1, r2c3, r2c4, r4c1, r4c3, r4c4),
new(r1c1, r1c2, r1c4, r2c1, r2c2, r2c4, r4c1, r4c2, r4c4),
new(r1c1, r1c2, r1c3, r2c1, r2c2, r2c3, r4c1, r4c2, 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)
new(r1c2, r1c3, r1c4, r2c2, r2c3, r2c4, r3c2, r3c3, r3c4),
new(r1c1, r1c3, r1c4, r2c1, r2c3, r2c4, r3c1, r3c3, r3c4),
new(r1c1, r1c2, r1c4, r2c1, r2c2, r2c4, r3c1, r3c2, r3c4),
new(r1c1, r1c2, r1c3, r2c1, r2c2, r2c3, r3c1, r3c2, r3c3)
}
};
public Matrix4x4 Transpose() => new(new[,]
@ -405,56 +451,39 @@ public struct Matrix4x4 : IMatrix<Matrix4x4, Matrix3x3>
{ r1c4, r2c4, r3c4, r4c4 }
});
public override bool Equals([NotNullWhen(true)] object? obj)
public virtual bool Equals(Matrix4x4? other)
{
if (obj == null || obj.GetType() != typeof(Matrix4x4)) return base.Equals(obj);
return Equals((Matrix4x4)obj);
if (other is null) return false;
return r1c1 == other.r1c1 && r1c2 == other.r1c2 && r1c3 == other.r1c3 && r1c4 == other.r1c4 &&
r2c1 == other.r2c1 && r2c2 == other.r2c2 && r2c3 == other.r2c3 && r2c4 == other.r2c4 &&
r3c1 == other.r3c1 && r3c2 == other.r3c2 && r3c3 == other.r3c3 && r3c4 == other.r3c4 &&
r4c1 == other.r4c1 && r4c2 == other.r4c2 && r4c3 == other.r4c3 && r4c4 == other.r4c4;
}
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());
public override int GetHashCode() => base.GetHashCode();
public override string ToString() =>
r1c1 + " " + r1c2 + " " + r1c3 + " " + r1c4 + "\n" +
r2c1 + " " + r2c2 + " " + r2c3 + " " + r2c4 + "\n" +
r3c1 + " " + r3c2 + " " + r3c3 + " " + r3c4 + "\n" +
r4c1 + " " + r4c2 + " " + r4c3 + " " + r4c4;
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 r2c1;
yield return r2c2;
yield return r2c3;
yield return r2c4;
yield return r3c1;
yield return r3c2;
yield return r3c3;
yield return r3c4;
yield return r4c1;
yield return r4c2;
yield return r4c3;
yield return r4c4;
}
@ -472,13 +501,6 @@ public struct Matrix4x4 : IMatrix<Matrix4x4, Matrix3x3>
{ 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()
{
@ -494,21 +516,21 @@ public struct Matrix4x4 : IMatrix<Matrix4x4, Matrix3x3>
};
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();
new(a.r1c1 + b.r1c1, a.r1c2 + b.r1c2, a.r1c3 + b.r1c3, a.r1c4 + b.r1c4,
a.r2c1 + b.r2c1, a.r2c2 + b.r2c2, a.r2c3 + b.r2c3, a.r2c4 + b.r2c4,
a.r3c1 + b.r3c1, a.r3c2 + b.r3c2, a.r3c3 + b.r3c3, a.r3c4 + b.r3c4,
a.r4c1 + b.r4c1, a.r4c2 + b.r4c2, a.r4c3 + b.r4c3, 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);
new(a.r1c1 - b.r1c1, a.r1c2 - b.r1c2, a.r1c3 - b.r1c3, a.r1c4 - b.r1c4,
a.r2c1 - b.r2c1, a.r2c2 - b.r2c2, a.r2c3 - b.r2c3, a.r2c4 - b.r2c4,
a.r3c1 - b.r3c1, a.r3c2 - b.r3c2, a.r3c3 - b.r3c3, a.r3c4 - b.r3c4,
a.r4c1 - b.r4c1, a.r4c2 - b.r4c2, a.r4c3 - b.r4c3, 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);
new(a.r1c1 * b, a.r1c2 * b, a.r1c3 * b, a.r1c4 * b,
a.r2c1 * b, a.r2c2 * b, a.r2c3 * b, a.r2c4 * b,
a.r3c1 * b, a.r3c2 * b, a.r3c3 * b, a.r3c4 * b,
a.r4c1 * b, a.r4c2 * b, a.r4c3 * 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),
@ -520,25 +542,30 @@ public struct Matrix4x4 : IMatrix<Matrix4x4, Matrix3x3>
{ 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 Float4 operator *(Matrix4x4 a, Float4 b) => (Matrix)a * b;
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();
new(a.r1c1 / b, a.r1c2 / b, a.r1c3 / b, a.r1c4 / b,
a.r2c1 / b, a.r2c2 / b, a.r2c3 / b, a.r2c4 / b,
a.r3c1 / b, a.r3c2 / b, a.r3c3 / b, a.r3c4 / b,
a.r4c1 / b, a.r4c2 / b, a.r4c3 / b, a.r4c4 / b);
public static Matrix4x4 operator /(Matrix4x4 a, Matrix4x4 b)
{
Matrix4x4? bInv = b.Inverse();
if (bInv is null) throw new NoInverseException(b);
return a * bInv;
}
public static Float4 operator /(Matrix4x4 a, Float4 b) => (Matrix)a / b;
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);
new(a.r1c1 * b.r1c1, a.r1c2 * b.r1c2, a.r1c3 * b.r1c3, a.r1c4 * b.r1c4,
a.r2c1 * b.r2c1, a.r2c2 * b.r2c2, a.r2c3 * b.r2c3, a.r2c4 * b.r2c4,
a.r3c1 * b.r3c1, a.r3c2 * b.r3c2, a.r3c3 * b.r3c3, a.r3c4 * b.r3c4,
a.r4c1 * b.r4c1, a.r4c2 * b.r4c2, a.r4c3 * b.r4c3, a.r4c4 * b.r4c4);
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];
res[c, r] = m.Size.x > r && m.Size.y > c ? m[r, c] : identity[r, c];
return res;
}
public static implicit operator Matrix4x4(Matrix2x2 m)

View File

@ -1,6 +1,11 @@
namespace Nerd_STF.Mathematics.Algebra;
public struct Vector2d : ICloneable, IComparable<Vector2d>, IEquatable<Vector2d>
public record struct Vector2d : IAbsolute<Vector2d>, IAverage<Vector2d>,
IClampMagnitude<Vector2d, float>, IComparable<Vector2d>, ICross<Vector2d, Vector3d>,
IDot<Vector2d, float>, IEquatable<Vector2d>, IFromTuple<Vector2d, (Angle angle, float mag)>,
ILerp<Vector2d, float>, IMax<Vector2d>, IMagnitude<float>, IMedian<Vector2d>, IMin<Vector2d>,
IPresets2D<Vector2d>, ISplittable<Vector2d, (Angle[] rots, float[] mags)>, ISubtract<Vector2d>,
ISum<Vector2d>
{
public static Vector2d Down => new(Angle.Down);
public static Vector2d Left => new(Angle.Left);
@ -10,6 +15,12 @@ public struct Vector2d : ICloneable, IComparable<Vector2d>, IEquatable<Vector2d>
public static Vector2d One => new(Angle.Zero);
public static Vector2d Zero => new(Angle.Zero, 0);
public float Magnitude
{
get => magnitude;
set => magnitude = value;
}
public Vector2d Inverse => new(-theta, magnitude);
public Vector2d Normalized => new(theta, 1);
@ -86,7 +97,7 @@ public struct Vector2d : ICloneable, IComparable<Vector2d>, IEquatable<Vector2d>
return val;
}
public static (Angle[] Rots, float[] Mags) SplitArray(params Vector2d[] vals)
public static (Angle[] rots, float[] mags) SplitArray(params Vector2d[] vals)
{
Angle[] rots = new Angle[vals.Length];
float[] mags = new float[vals.Length];
@ -99,21 +110,11 @@ public struct Vector2d : ICloneable, IComparable<Vector2d>, IEquatable<Vector2d>
}
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 override int GetHashCode() => base.GetHashCode();
public override string ToString() => ToString(Angle.Type.Degrees);
public string ToString(Angle.Type outputType) =>
nameof(Vector2d) + " { Mag = " + magnitude + ", Rot = " + theta.ToString(outputType) + " }";
public Float2 ToXYZ() => new Float2(Mathf.Cos(theta), Mathf.Sin(theta)) * magnitude;
@ -124,8 +125,6 @@ public struct Vector2d : ICloneable, IComparable<Vector2d>, IEquatable<Vector2d>
public static Vector2d operator *(Vector2d a, Matrix b) => (Vector2d)((Matrix)a * b);
public static Vector2d operator /(Vector2d a, float b) => new(a.theta, a.magnitude / b);
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;
@ -139,4 +138,5 @@ public struct Vector2d : ICloneable, IComparable<Vector2d>, IEquatable<Vector2d>
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);
public static implicit operator Vector2d((Angle angle, float mag) val) => new(val.angle, val.mag);
}

View File

@ -1,6 +1,10 @@
namespace Nerd_STF.Mathematics.Algebra;
public struct Vector3d : ICloneable, IComparable<Vector3d>, IEquatable<Vector3d>
public record struct Vector3d : IAbsolute<Vector3d>, IAverage<Vector3d>, IClampMagnitude<Vector3d, float>,
IComparable<Vector3d>, ICross<Vector3d>, IDot<Vector3d, float>, IEquatable<Vector3d>,
IFromTuple<Vector3d, (Angle yaw, Angle pitch, float mag)>, IIndexAll<Angle>, IIndexRangeAll<Angle>,
ILerp<Vector3d, float>, IMagnitude<float>, IMax<Vector3d>, IMedian<Vector3d>, IMin<Vector3d>,
IPresets3D<Vector3d>, ISubtract<Vector3d>, ISum<Vector3d>
{
public static Vector3d Back => new(Angle.Zero, Angle.Up);
public static Vector3d Down => new(Angle.Down, Angle.Zero);
@ -12,6 +16,12 @@ public struct Vector3d : ICloneable, IComparable<Vector3d>, IEquatable<Vector3d>
public static Vector3d One => new(Angle.Zero);
public static Vector3d Zero => new(Angle.Zero, 0);
public float Magnitude
{
get => magnitude;
set => magnitude = value;
}
public Vector3d Inverse => new(-yaw, -pitch, magnitude);
public Vector3d Normalized => new(yaw, pitch, 1);
@ -56,6 +66,28 @@ public struct Vector3d : ICloneable, IComparable<Vector3d>, IEquatable<Vector3d>
}
}
}
public Angle this[Index index]
{
get => this[index.IsFromEnd ? 2 - index.Value : index.Value];
set => this[index.IsFromEnd ? 2 - index.Value : index.Value] = value;
}
public Angle[] this[Range range]
{
get
{
int start = range.Start.IsFromEnd ? 2 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 2 - range.End.Value : range.End.Value;
List<Angle> res = new();
for (int i = start; i < end; i++) res.Add(this[i]);
return res.ToArray();
}
set
{
int start = range.Start.IsFromEnd ? 2 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 2 - range.End.Value : range.End.Value;
for (int i = start; i < end; i++) this[i] = value[i];
}
}
public static Vector3d Absolute(Vector3d val) => new(Angle.Absolute(val.yaw), Angle.Absolute(val.pitch),
Mathf.Absolute(val.magnitude));
@ -123,37 +155,31 @@ public struct Vector3d : ICloneable, IComparable<Vector3d>, IEquatable<Vector3d>
return val;
}
public static (Angle[] Thetas, Angle[] Phis, float[] Mags) SplitArray(params Vector3d[] vals)
public static (Angle[] yaws, Angle[] pitches, float[] mags) SplitArray(params Vector3d[] vals)
{
Angle[] yaws = new Angle[vals.Length], pitchs = new Angle[vals.Length];
Angle[] yaws = new Angle[vals.Length], pitches = 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;
pitches[i] = vals[i].pitch;
mags[i] = vals[i].magnitude;
}
return (yaws, pitchs, mags);
return (yaws, pitches, 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 override int GetHashCode() => base.GetHashCode();
public override string ToString() => ToString(Angle.Type.Degrees);
public string ToString(Angle.Type outputType) =>
nameof(Vector3d) + " { Mag = " + magnitude + ", Yaw = " + yaw.ToString(outputType) +
", Pitch = " + pitch.ToString(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 Float3 ToXYZ() => new Float3(Mathf.Sin(pitch) * Mathf.Sin(yaw),
Mathf.Cos(yaw),
Mathf.Cos(pitch) * Mathf.Sin(yaw)) * magnitude;
public static Vector3d operator +(Vector3d a, Vector3d b) => new(a.yaw + b.yaw, a.pitch + b.pitch,
a.magnitude + b.magnitude);
@ -164,8 +190,6 @@ public struct Vector3d : ICloneable, IComparable<Vector3d>, IEquatable<Vector3d>
public static Vector3d operator *(Vector3d a, Matrix b) => (Vector3d)((Matrix)a * b);
public static Vector3d operator /(Vector3d a, float b) => new(a.yaw, a.pitch, a.magnitude / b);
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;
@ -179,4 +203,6 @@ public struct Vector3d : ICloneable, IComparable<Vector3d>, IEquatable<Vector3d>
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);
public static implicit operator Vector3d((Angle yaw, Angle pitch, float mag) val) =>
new(val.yaw, val.pitch, val.mag);
}

View File

@ -1,6 +1,8 @@
namespace Nerd_STF.Mathematics;
public struct Angle : ICloneable, IComparable<Angle>, IEquatable<Angle>
public struct Angle : IAbsolute<Angle>, IAverage<Angle>, IClamp<Angle>, ICloneable,
IComparable<Angle>, IEquatable<Angle>, ILerp<Angle, float>, IMax<Angle>, IMedian<Angle>,
IMin<Angle>, IPresets2D<Angle>
{
public static Angle Down => new(270);
public static Angle Left => new(180);
@ -35,6 +37,8 @@ public struct Angle : ICloneable, IComparable<Angle>, IEquatable<Angle>
}
public Angle Bounded => new(Mathf.AbsoluteMod(p_deg, 360));
public Angle Complimentary => Quarter - this;
public Angle Supplementary => Half - this;
public Angle Reflected => new Angle(-p_deg).Bounded;
private float p_deg;
@ -82,17 +86,7 @@ public struct Angle : ICloneable, IComparable<Angle>, IEquatable<Angle>
public static float[] SplitArray(Type outputType, params Angle[] vals)
{
float[] res = new float[vals.Length];
for (int i = 0; i < vals.Length; i++)
{
res[i] = outputType switch
{
Type.Degrees => vals[i].Degrees,
Type.Gradians => vals[i].Gradians,
Type.Normalized => vals[i].Normalized,
Type.Radians => vals[i].Radians,
_ => throw new ArgumentException("Unknown type.", nameof(outputType)),
};
}
for (int i = 0; i < vals.Length; i++) res[i] = vals[i].ValueFromType(outputType);
return res;
}
@ -104,22 +98,13 @@ public struct Angle : ICloneable, IComparable<Angle>, IEquatable<Angle>
}
public bool Equals(Angle other) => p_deg == other.p_deg;
public override int GetHashCode() => Degrees.GetHashCode() ^ Gradians.GetHashCode() ^ Radians.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(Type outputType) => ToString((string?)null, outputType);
public string ToString(string? provider, Type outputType = Type.Degrees) => outputType switch
public override string ToString() => ToString(Type.Degrees);
public string ToString(Type outputType) => outputType switch
{
Type.Degrees => p_deg.ToString(provider) + "°",
Type.Gradians => Gradians.ToString(provider) + "grad",
Type.Normalized => Normalized.ToString(provider) + "%",
Type.Radians => Radians.ToString(provider) + "rad",
_ => throw new ArgumentException("Unknown type.", nameof(outputType)),
};
public string ToString(IFormatProvider provider, Type outputType = Type.Degrees) => outputType switch
{
Type.Degrees => p_deg.ToString(provider) + "°",
Type.Gradians => Gradians.ToString(provider) + "grad",
Type.Normalized => Normalized.ToString(provider) + "%",
Type.Radians => Radians.ToString(provider) + "rad",
Type.Degrees => p_deg + "°",
Type.Gradians => Gradians + "grad",
Type.Normalized => Normalized + "%",
Type.Radians => Radians + "rad",
_ => throw new ArgumentException("Unknown type.", nameof(outputType)),
};
@ -131,7 +116,7 @@ public struct Angle : ICloneable, IComparable<Angle>, IEquatable<Angle>
Type.Gradians => Gradians,
Type.Normalized => Normalized,
Type.Radians => Radians,
_ => throw new ArgumentException("Unknown type.", nameof(type)),
_ => throw new ArgumentException("Unknown type.", nameof(type))
};
public static Angle operator +(Angle a, Angle b) => new(a.p_deg + b.p_deg);
@ -146,6 +131,8 @@ public struct Angle : ICloneable, IComparable<Angle>, IEquatable<Angle>
public static bool operator >=(Angle a, Angle b) => a == b || a > b;
public static bool operator <=(Angle a, Angle b) => a == b || a < b;
public static implicit operator Angle((float val, Type type) obj) => new(obj.val, obj.type);
public enum Type
{
Degrees,

View File

@ -1,6 +1,12 @@
namespace Nerd_STF.Mathematics;
public struct Float2 : ICloneable, IComparable<Float2>, IEquatable<Float2>, IGroup<float>
public record struct Float2 : IAbsolute<Float2>, IAverage<Float2>, ICeiling<Float2, Int2>,
IClamp<Float2>, IClampMagnitude<Float2, float>, IComparable<Float2>,
ICross<Float2, Float3>, IDivide<Float2>, IDot<Float2, float>, IEquatable<Float2>,
IFloor<Float2, Int2>, IFromTuple<Float2, (float x, float y)>, IGroup<float>,
ILerp<Float2, float>, IMathOperators<Float2>, IMax<Float2>, IMedian<Float2>, IMin<Float2>,
IIndexAll<float>, IIndexRangeAll<float>, IPresets2D<Float2>, IProduct<Float2>, IRound<Float2, Int2>,
ISplittable<Float2, (float[] Xs, float[] Ys)>, ISubtract<Float2>, ISum<Float2>
{
public static Float2 Down => new(0, -1);
public static Float2 Left => new(-1, 0);
@ -16,14 +22,13 @@ public struct Float2 : ICloneable, IComparable<Float2>, IEquatable<Float2>, IGro
public float x, y;
public Float2(float all) : this(all, all) { }
public Float2(Fill<float> fill) : this(fill(0), fill(1)) { }
public Float2(Fill<int> fill) : this(fill(0), fill(1)) { }
public Float2(float x, float y)
{
this.x = x;
this.y = y;
}
public Float2(Fill<float> fill) : this(fill(0), fill(1)) { }
public Float2(Fill<int> fill) : this(fill(0), fill(1)) { }
public float this[int index]
{
get => index switch
@ -48,11 +53,33 @@ public struct Float2 : ICloneable, IComparable<Float2>, IEquatable<Float2>, IGro
}
}
}
public float this[Index index]
{
get => this[index.IsFromEnd ? 2 - index.Value : index.Value];
set => this[index.IsFromEnd ? 2 - index.Value : index.Value] = value;
}
public float[] this[Range range]
{
get
{
int start = range.Start.IsFromEnd ? 2 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 2 - range.End.Value : range.End.Value;
List<float> res = new();
for (int i = start; i < end; i++) res.Add(this[i]);
return res.ToArray();
}
set
{
int start = range.Start.IsFromEnd ? 2 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 2 - range.End.Value : range.End.Value;
for (int i = start; i < end; i++) this[i] = value[i];
}
}
public static Float2 Absolute(Float2 val) =>
new(Mathf.Absolute(val.x), Mathf.Absolute(val.y));
public static Float2 Average(params Float2[] vals) => Sum(vals) / vals.Length;
public static Float2 Ceiling(Float2 val) =>
public static Int2 Ceiling(Float2 val) =>
new(Mathf.Ceiling(val.x), Mathf.Ceiling(val.y));
public static Float2 Clamp(Float2 val, Float2 min, Float2 max) =>
new(Mathf.Clamp(val.x, min.x, max.x),
@ -83,7 +110,7 @@ public struct Float2 : ICloneable, IComparable<Float2>, IEquatable<Float2>, IGro
}
return x + y;
}
public static Float2 Floor(Float2 val) =>
public static Int2 Floor(Float2 val) =>
new(Mathf.Floor(val.x), Mathf.Floor(val.y));
public static Float2 Lerp(Float2 a, Float2 b, float t, bool clamp = true) =>
new(Mathf.Lerp(a.x, b.x, t, clamp), Mathf.Lerp(a.y, b.y, t, clamp));
@ -97,14 +124,14 @@ public struct Float2 : ICloneable, IComparable<Float2>, IEquatable<Float2>, IGro
{
if (vals.Length < 1) return Zero;
Float2 val = vals[0];
foreach (Float2 f in vals) val = f > val ? f : val;
foreach (Float2 f in vals) val = f.Magnitude > val.Magnitude ? f : val;
return val;
}
public static Float2 Min(params Float2[] vals)
{
if (vals.Length < 1) return Zero;
Float2 val = vals[0];
foreach (Float2 f in vals) val = f < val ? f : val;
foreach (Float2 f in vals) val = f.Magnitude < val.Magnitude ? f : val;
return val;
}
public static Float2 Product(params Float2[] vals)
@ -114,8 +141,8 @@ public struct Float2 : ICloneable, IComparable<Float2>, IEquatable<Float2>, IGro
foreach (Float2 f in vals) val *= f;
return val;
}
public static Float2 Round(Float2 val) =>
new(Mathf.Round(val.x), Mathf.Round(val.y));
public static Int2 Round(Float2 val) =>
new(Mathf.RoundInt(val.x), Mathf.RoundInt(val.y));
public static Float2 Subtract(Float2 num, params Float2[] vals) => num - Sum(vals);
public static Float2 Sum(params Float2[] vals)
{
@ -135,21 +162,11 @@ public struct Float2 : ICloneable, IComparable<Float2>, IEquatable<Float2>, IGro
return (Xs, Ys);
}
[Obsolete("This method is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public int CompareTo(Float2 other) => Magnitude.CompareTo(other.Magnitude);
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Float2)) return base.Equals(obj);
return Equals((Float2)obj);
}
public bool Equals(Float2 other) => x == other.x && y == other.y;
public override int GetHashCode() => x.GetHashCode() ^ y.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider);
public string ToString(IFormatProvider provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider);
public object Clone() => new Float2(x, y);
public override int GetHashCode() => base.GetHashCode();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<float> GetEnumerator()
@ -168,20 +185,36 @@ public struct Float2 : ICloneable, IComparable<Float2>, IEquatable<Float2>, IGro
public Vector2d ToVector() => new(Mathf.ArcTan(y / x), Magnitude);
private bool PrintMembers(StringBuilder builder)
{
builder.Append("x = ");
builder.Append(x);
builder.Append(", y = ");
builder.Append(y);
return true;
}
public static Float2 operator +(Float2 a, Float2 b) => new(a.x + b.x, a.y + b.y);
public static Float2 operator -(Float2 d) => new(-d.x, -d.y);
public static Float2 operator -(Float2 a, Float2 b) => new(a.x - b.x, a.y - b.y);
public static Float2 operator *(Float2 a, Float2 b) => new(a.x * b.x, a.y * b.y);
public static Float2 operator *(Float2 a, float b) => new(a.x * b, a.y * b);
public static Float2 operator *(Float2 a, Matrix b) => (Float2)((Matrix)a * b);
public static Quaternion operator *(Float2 a, Quaternion b) => (Quaternion)a * b;
public static Float2 operator /(Float2 a, Float2 b) => new(a.x / b.x, a.y / b.y);
public static Float2 operator /(Float2 a, float b) => new(a.x / b, a.y / b);
public static Float2 operator /(Float2 a, Matrix b) => (Float2)((Matrix)a / b);
public static bool operator ==(Float2 a, Float2 b) => a.Equals(b);
public static bool operator !=(Float2 a, Float2 b) => !a.Equals(b);
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator >(Float2 a, Float2 b) => a.CompareTo(b) > 0;
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator <(Float2 a, Float2 b) => a.CompareTo(b) < 0;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
public static bool operator >=(Float2 a, Float2 b) => a == b || a > b;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
public static bool operator <=(Float2 a, Float2 b) => a == b || a < b;
public static implicit operator Float2(Complex val) => new(val.u, val.i);
@ -196,4 +229,5 @@ public struct Float2 : ICloneable, IComparable<Float2>, IEquatable<Float2>, IGro
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<int> fill) => new(fill);
public static implicit operator Float2((float x, float y) val) => new(val.x, val.y);
}

View File

@ -1,6 +1,14 @@
namespace Nerd_STF.Mathematics;
using System;
public struct Float3 : ICloneable, IComparable<Float3>, IEquatable<Float3>, IGroup<float>
namespace Nerd_STF.Mathematics;
public record struct Float3 : IAbsolute<Float3>, IAverage<Float3>,
ICeiling<Float3, Int3>, IClamp<Float3>, IClampMagnitude<Float3, float>, IComparable<Float3>,
ICross<Float3>, IDivide<Float3>, IDot<Float3, float>, IEquatable<Float3>,
IFloor<Float3, Int3>, IFromTuple<Float3, (float x, float y, float z)>, IGroup<float>,
IIndexAll<float>, IIndexRangeAll<float>, ILerp<Float3, float>, IMathOperators<Float3>, IMax<Float3>,
IMedian<Float3>, IMin<Float3>, IPresets3D<Float3>, IProduct<Float3>, IRound<Float3, Int3>,
ISplittable<Float3, (float[] Xs, float[] Ys, float[] Zs)>, ISubtract<Float3>, ISum<Float3>
{
public static Float3 Back => new(0, 0, -1);
public static Float3 Down => new(0, -1, 0);
@ -61,11 +69,33 @@ public struct Float3 : ICloneable, IComparable<Float3>, IEquatable<Float3>, IGro
}
}
}
public float this[Index index]
{
get => this[index.IsFromEnd ? 3 - index.Value : index.Value];
set => this[index.IsFromEnd ? 3 - index.Value : index.Value] = value;
}
public float[] this[Range range]
{
get
{
int start = range.Start.IsFromEnd ? 3 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 3 - range.End.Value : range.End.Value;
List<float> res = new();
for (int i = start; i < end; i++) res.Add(this[i]);
return res.ToArray();
}
set
{
int start = range.Start.IsFromEnd ? 3 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 3 - range.End.Value : range.End.Value;
for (int i = start; i < end; i++) this[i] = value[i];
}
}
public static Float3 Absolute(Float3 val) =>
new(Mathf.Absolute(val.x), Mathf.Absolute(val.y), Mathf.Absolute(val.z));
public static Float3 Average(params Float3[] vals) => Sum(vals) / vals.Length;
public static Float3 Ceiling(Float3 val) =>
public static Int3 Ceiling(Float3 val) =>
new(Mathf.Ceiling(val.x), Mathf.Ceiling(val.y), Mathf.Ceiling(val.z));
public static Float3 Clamp(Float3 val, Float3 min, Float3 max) =>
new(Mathf.Clamp(val.x, min.x, max.x),
@ -103,7 +133,7 @@ public struct Float3 : ICloneable, IComparable<Float3>, IEquatable<Float3>, IGro
}
return x + y + z;
}
public static Float3 Floor(Float3 val) =>
public static Int3 Floor(Float3 val) =>
new(Mathf.Floor(val.x), Mathf.Floor(val.y), Mathf.Floor(val.z));
public static Float3 Lerp(Float3 a, Float3 b, float t, bool clamp = true) =>
new(Mathf.Lerp(a.x, b.x, t, clamp), Mathf.Lerp(a.y, b.y, t, clamp), Mathf.Lerp(a.z, b.z, t, clamp));
@ -117,14 +147,14 @@ public struct Float3 : ICloneable, IComparable<Float3>, IEquatable<Float3>, IGro
{
if (vals.Length < 1) return Zero;
Float3 val = vals[0];
foreach (Float3 d in vals) val = d > val ? d : val;
foreach (Float3 d in vals) val = d.Magnitude > val.Magnitude ? d : val;
return val;
}
public static Float3 Min(params Float3[] vals)
{
if (vals.Length < 1) return Zero;
Float3 val = vals[0];
foreach (Float3 d in vals) val = d < val ? d : val;
foreach (Float3 d in vals) val = d.Magnitude < val.Magnitude ? d : val;
return val;
}
public static Float3 Product(params Float3[] vals)
@ -134,8 +164,8 @@ public struct Float3 : ICloneable, IComparable<Float3>, IEquatable<Float3>, IGro
foreach (Float3 d in vals) val *= d;
return val;
}
public static Float3 Round(Float3 val) =>
new(Mathf.Round(val.x), Mathf.Round(val.y), Mathf.Round(val.z));
public static Int3 Round(Float3 val) =>
new(Mathf.RoundInt(val.x), Mathf.RoundInt(val.y), Mathf.RoundInt(val.z));
public static Float3 Subtract(Float3 num, params Float3[] vals) => num - Sum(vals);
public static Float3 Sum(params Float3[] vals)
{
@ -156,21 +186,11 @@ public struct Float3 : ICloneable, IComparable<Float3>, IEquatable<Float3>, IGro
return (Xs, Ys, Zs);
}
[Obsolete("This method is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public int CompareTo(Float3 other) => Magnitude.CompareTo(other.Magnitude);
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Float3)) return base.Equals(obj);
return Equals((Float3)obj);
}
public bool Equals(Float3 other) => x == other.x && y == other.y && z == other.z;
public override int GetHashCode() => x.GetHashCode() ^ y.GetHashCode() ^ z.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider) + " Z: " + z.ToString(provider);
public string ToString(IFormatProvider provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider) + " Z: " + z.ToString(provider);
public object Clone() => new Float3(x, y, z);
public override int GetHashCode() => base.GetHashCode();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<float> GetEnumerator()
@ -191,7 +211,21 @@ public struct Float3 : ICloneable, IComparable<Float3>, IEquatable<Float3>, IGro
public Vector3d ToVector()
{
float mag = Magnitude;
return new(Mathf.ArcTan(y / x), Mathf.ArcCos(z / mag), mag);
Float3 normalized = Normalized;
Angle yaw = Mathf.ArcCos(normalized.y);
Angle pitch = Mathf.ArcSin(normalized.x / Mathf.Sin(yaw));
return new(yaw, pitch, mag);
}
private bool PrintMembers(StringBuilder builder)
{
builder.Append("x = ");
builder.Append(x);
builder.Append(", y = ");
builder.Append(y);
builder.Append(", z = ");
builder.Append(z);
return true;
}
public static Float3 operator +(Float3 a, Float3 b) => new(a.x + b.x, a.y + b.y, a.z + b.z);
@ -200,14 +234,21 @@ public struct Float3 : ICloneable, IComparable<Float3>, IEquatable<Float3>, IGro
public static Float3 operator *(Float3 a, Float3 b) => new(a.x * b.x, a.y * b.y, a.z * b.z);
public static Float3 operator *(Float3 a, float b) => new(a.x * b, a.y * b, a.z * b);
public static Float3 operator *(Float3 a, Matrix b) => (Float3)((Matrix)a * b);
public static Quaternion operator *(Float3 a, Quaternion b) => (Quaternion)a * b;
public static Float3 operator /(Float3 a, Float3 b) => new(a.x / b.x, a.y / b.y, a.z / b.z);
public static Float3 operator /(Float3 a, float b) => new(a.x / b, a.y / b, a.z / b);
public static Float3 operator /(Float3 a, Matrix b) => (Float3)((Matrix)a / b);
public static bool operator ==(Float3 a, Float3 b) => a.Equals(b);
public static bool operator !=(Float3 a, Float3 b) => !a.Equals(b);
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator >(Float3 a, Float3 b) => a.CompareTo(b) > 0;
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator <(Float3 a, Float3 b) => a.CompareTo(b) < 0;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
public static bool operator >=(Float3 a, Float3 b) => a == b || a > b;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
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);
@ -226,4 +267,6 @@ public struct Float3 : ICloneable, IComparable<Float3>, IEquatable<Float3>, IGro
public static explicit operator Float3(HSVAByte val) => (Float3)val.ToHSVA();
public static implicit operator Float3(Fill<float> fill) => new(fill);
public static implicit operator Float3(Fill<int> fill) => new(fill);
public static implicit operator Float3((float x, float y, float z) val) =>
new(val.x, val.y, val.z);
}

View File

@ -1,18 +1,25 @@
namespace Nerd_STF.Mathematics;
public struct Float4 : ICloneable, IComparable<Float4>, IEquatable<Float4>, IGroup<float>
public record struct Float4 : IAbsolute<Float4>,
IAverage<Float4>, ICeiling<Float4, Int4>, IClamp<Float4>, IClampMagnitude<Float4, float>,
IComparable<Float4>, IDivide<Float4>, IDot<Float4, float>, IEquatable<Float4>,
IFloor<Float4, Int4>, IFromTuple<Float4, (float x, float y, float z, float w)>,
IGroup<float>, IIndexAll<float>, IIndexRangeAll<float>, ILerp<Float4, float>, IMathOperators<Float4>,
IMax<Float4>, IMedian<Float4>, IMin<Float4>, IPresets4D<Float4>, IProduct<Float4>, IRound<Float4, Int4>,
ISplittable<Float4, (float[] Xs, float[] Ys, float[] Zs, float[] Ws)>, ISubtract<Float4>,
ISum<Float4>
{
public static Float4 Back => new(0, 0, -1, 0);
public static Float4 Down => new(0, -1, 0, 0);
[Obsolete("Field has been replaced by " + nameof(HighW) + ", because it has a better name. " +
"This field will be removed in v2.4.0.", false)]
"This field will be removed in v2.4.0.", false)]
public static Float4 Far => new(0, 0, 0, 1);
public static Float4 Forward => new(0, 0, 1, 0);
public static Float4 HighW => new(0, 0, 0, 1);
public static Float4 Left => new(-1, 0, 0, 0);
public static Float4 LowW => new(0, 0, 0, -1);
[Obsolete("Field has been replaced by " + nameof(LowW) + ", because it has a better name. " +
"This field will be removed in v2.4.0.", false)]
"This field will be removed in v2.4.0.", false)]
public static Float4 Near => new(0, 0, 0, -1);
public static Float4 Right => new(1, 0, 0, 0);
public static Float4 Up => new(0, 1, 0, 0);
@ -84,11 +91,33 @@ public struct Float4 : ICloneable, IComparable<Float4>, IEquatable<Float4>, IGro
}
}
}
public float this[Index index]
{
get => this[index.IsFromEnd ? 4 - index.Value : index.Value];
set => this[index.IsFromEnd ? 4 - index.Value : index.Value] = value;
}
public float[] this[Range range]
{
get
{
int start = range.Start.IsFromEnd ? 4 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 4 - range.End.Value : range.End.Value;
List<float> res = new();
for (int i = start; i < end; i++) res.Add(this[i]);
return res.ToArray();
}
set
{
int start = range.Start.IsFromEnd ? 4 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 4 - range.End.Value : range.End.Value;
for (int i = start; i < end; i++) this[i] = value[i];
}
}
public static Float4 Absolute(Float4 val) =>
new(Mathf.Absolute(val.x), Mathf.Absolute(val.y), Mathf.Absolute(val.z), Mathf.Absolute(val.w));
public static Float4 Average(params Float4[] vals) => Sum(vals) / vals.Length;
public static Float4 Ceiling(Float4 val) =>
public static Int4 Ceiling(Float4 val) =>
new(Mathf.Ceiling(val.x), Mathf.Ceiling(val.y), Mathf.Ceiling(val.z), Mathf.Ceiling(val.w));
public static Float4 Clamp(Float4 val, Float4 min, Float4 max) =>
new(Mathf.Clamp(val.x, min.x, max.x),
@ -121,7 +150,7 @@ public struct Float4 : ICloneable, IComparable<Float4>, IEquatable<Float4>, IGro
}
return x + y + z;
}
public static Float4 Floor(Float4 val) =>
public static Int4 Floor(Float4 val) =>
new(Mathf.Floor(val.x), Mathf.Floor(val.y), Mathf.Floor(val.z), Mathf.Floor(val.w));
public static Float4 Lerp(Float4 a, Float4 b, float t, bool clamp = true) =>
new(Mathf.Lerp(a.x, b.x, t, clamp), Mathf.Lerp(a.y, b.y, t, clamp), Mathf.Lerp(a.z, b.z, t, clamp),
@ -136,18 +165,19 @@ public struct Float4 : ICloneable, IComparable<Float4>, IEquatable<Float4>, IGro
{
if (vals.Length < 1) return Zero;
Float4 val = vals[0];
foreach (Float4 d in vals) val = d > val ? d : val;
foreach (Float4 d in vals) val = d.Magnitude > val.Magnitude ? d : val;
return val;
}
public static Float4 Min(params Float4[] vals)
{
if (vals.Length < 1) return Zero;
Float4 val = vals[0];
foreach (Float4 d in vals) val = d < val ? d : val;
foreach (Float4 d in vals) val = d.Magnitude < val.Magnitude ? d : val;
return val;
}
public static Float4 Round(Float4 val) =>
new(Mathf.Round(val.x), Mathf.Round(val.y), Mathf.Round(val.z), Mathf.Round(val.w));
public static Int4 Round(Float4 val) =>
new(Mathf.RoundInt(val.x), Mathf.RoundInt(val.y), Mathf.RoundInt(val.z),
Mathf.RoundInt(val.w));
public static Float4 Product(params Float4[] vals)
{
if (vals.Length < 1) return Zero;
@ -177,23 +207,11 @@ public struct Float4 : ICloneable, IComparable<Float4>, IEquatable<Float4>, IGro
return (Xs, Ys, Zs, Ws);
}
[Obsolete("This method is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public int CompareTo(Float4 other) => Magnitude.CompareTo(other.Magnitude);
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Float4)) return base.Equals(obj);
return Equals((Float4)obj);
}
public bool Equals(Float4 other) => x == other.x && y == other.y && z == other.z && w == other.w;
public override int GetHashCode() => x.GetHashCode() ^ y.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider) + " Z: " + z.ToString(provider)
+ " W: " + w.ToString(provider);
public string ToString(IFormatProvider provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider) + " Z: " + z.ToString(provider)
+ " W: " + w.ToString(provider);
public object Clone() => new Float4(x, y, z, w);
public override int GetHashCode() => base.GetHashCode();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<float> GetEnumerator()
@ -212,6 +230,19 @@ public struct Float4 : ICloneable, IComparable<Float4>, IEquatable<Float4>, IGro
}
public List<float> ToList() => new() { x, y, z, w };
private bool PrintMembers(StringBuilder builder)
{
builder.Append("x = ");
builder.Append(x);
builder.Append(", y = ");
builder.Append(y);
builder.Append(", z = ");
builder.Append(z);
builder.Append(", w = ");
builder.Append(w);
return true;
}
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 d) => new(-d.x, -d.y, -d.z, -d.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);
@ -221,11 +252,17 @@ 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, 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);
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator >(Float4 a, Float4 b) => a.CompareTo(b) > 0;
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator <(Float4 a, Float4 b) => a.CompareTo(b) < 0;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
public static bool operator >=(Float4 a, Float4 b) => a == b || a > b;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
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);
@ -246,4 +283,6 @@ public struct Float4 : ICloneable, IComparable<Float4>, IEquatable<Float4>, IGro
public static implicit operator Float4(HSVAByte val) => (Float4)val.ToHSVA();
public static implicit operator Float4(Fill<float> fill) => new(fill);
public static implicit operator Float4(Fill<int> fill) => new(fill);
public static implicit operator Float4((float x, float y, float z, float w) vals) =>
new(vals.x, vals.y, vals.z, vals.w);
}

View File

@ -1,6 +1,8 @@
namespace Nerd_STF.Mathematics.Geometry;
public struct Box2D : ICloneable, IContainer<Vert>, IEquatable<Box2D>
public record class Box2D : IAbsolute<Box2D>, IAverage<Box2D>, ICeiling<Box2D>, IClamp<Box2D>, IContains<Vert>,
IEquatable<Box2D>, IFloor<Box2D>, ILerp<Box2D, float>, IMedian<Box2D>, IRound<Box2D>, IShape2D<float>,
ISplittable<Box2D, (Vert[] centers, Float2[] sizes)>
{
public static Box2D Unit => new(Vert.Zero, Float2.One);
@ -10,7 +12,7 @@ public struct Box2D : ICloneable, IContainer<Vert>, IEquatable<Box2D>
set
{
Vert diff = center - value;
size = (Float2)diff.position * 2;
size = (Float2)diff.position * 2f;
}
}
public Vert MinVert
@ -19,12 +21,12 @@ public struct Box2D : ICloneable, IContainer<Vert>, IEquatable<Box2D>
set
{
Vert diff = center + value;
size = (Float2)diff.position * 2;
size = (Float2)diff.position * 2f;
}
}
public float Area => size.x * size.y;
public float Perimeter => size.x * 2 + size.y * 2;
public float Perimeter => 2 * (size.x + size.y);
public Vert center;
public Float2 size;
@ -60,16 +62,8 @@ public struct Box2D : ICloneable, IContainer<Vert>, IEquatable<Box2D>
(Vert[] verts, Float2[] sizes) = SplitArray(vals);
return new(Vert.Median(verts), Float2.Median(sizes));
}
public static Box2D Max(params Box2D[] vals)
{
(Vert[] verts, Float2[] sizes) = SplitArray(vals);
return new(Vert.Max(verts), Float2.Max(sizes));
}
public static Box2D Min(params Box2D[] vals)
{
(Vert[] verts, Float2[] sizes) = SplitArray(vals);
return new(Vert.Min(verts), Float2.Min(sizes));
}
public static Box2D Round(Box2D val) => new(Vert.Round(val.center), Float2.Round(val.size));
public static (Vert[] centers, Float2[] sizes) SplitArray(params Box2D[] vals)
{
Vert[] centers = new Vert[vals.Length];
@ -84,18 +78,12 @@ public struct Box2D : ICloneable, IContainer<Vert>, IEquatable<Box2D>
return (centers, sizes);
}
public override bool Equals([NotNullWhen(true)] object? obj)
public virtual bool Equals(Box2D? other)
{
if (obj == null || obj.GetType() != typeof(Box2D)) return base.Equals(obj);
return Equals((Box2D)obj);
if (other is null) return false;
return center == other.center && size == other.size;
}
public bool Equals(Box2D other) => center == other.center && size == other.size;
public override int GetHashCode() => center.GetHashCode() ^ size.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"Min: " + MinVert.ToString(provider) + " Max: " + MaxVert.ToString(provider);
public string ToString(IFormatProvider provider) =>
"Min: " + MinVert.ToString(provider) + " Max: " + MaxVert.ToString(provider);
public override int GetHashCode() => base.GetHashCode();
public bool Contains(Vert vert)
{
@ -103,7 +91,14 @@ public struct Box2D : ICloneable, IContainer<Vert>, IEquatable<Box2D>
return diff.x <= size.x && diff.y <= size.y;
}
public object Clone() => new Box2D(center, size);
protected virtual bool PrintMembers(StringBuilder builder)
{
builder.Append("Min = ");
builder.Append(MinVert);
builder.Append(", Max = ");
builder.Append(MaxVert);
return true;
}
public static Box2D operator +(Box2D a, Vert b) => new(a.center + b, a.size);
public static Box2D operator +(Box2D a, Float2 b) => new(a.center, a.size + b);
@ -114,8 +109,6 @@ public struct Box2D : ICloneable, IContainer<Vert>, IEquatable<Box2D>
public static Box2D operator *(Box2D a, Float2 b) => new(a.center, a.size * b);
public static Box2D operator /(Box2D a, float b) => new(a.center / b, a.size / b);
public static Box2D operator /(Box2D a, Float2 b) => new(a.center, a.size / b);
public static bool operator ==(Box2D a, Box2D b) => a.Equals(b);
public static bool operator !=(Box2D a, Box2D b) => !a.Equals(b);
public static implicit operator Box2D(Fill<float> fill) => new(fill);
public static explicit operator Box2D(Box3D box) => new(box.center, (Float2)box.size);

View File

@ -1,6 +1,8 @@
namespace Nerd_STF.Mathematics.Geometry;
public struct Box3D : ICloneable, IContainer<Vert>, IEquatable<Box3D>
public record class Box3D : IAbsolute<Box3D>, IAverage<Box3D>, ICeiling<Box3D>, IClamp<Box3D>,
IContains<Vert>, IEquatable<Box3D>, IFloor<Box3D>, ILerp<Box3D, float>, IMedian<Box3D>,
IRound<Box3D>, IShape3D<float>, ISplittable<Box3D, (Vert[] centers, Float3[] sizes)>
{
public static Box3D Unit => new(Vert.Zero, Float3.One);
@ -23,8 +25,9 @@ public struct Box3D : ICloneable, IContainer<Vert>, IEquatable<Box3D>
}
}
public float Area => size.x * size.y * size.z;
public float Perimeter => size.x * 2 + size.y * 2 + size.z * 2;
public float Perimeter => 2 * (size.x + size.y + size.z);
public float SurfaceArea => 2 * (size.x * size.y + size.y * size.z + size.x * size.z);
public float Volume => size.x * size.y * size.z;
public Vert center;
public Float3 size;
@ -50,10 +53,12 @@ public struct Box3D : ICloneable, IContainer<Vert>, IEquatable<Box3D>
(Vert[] centers, Float3[] sizes) = SplitArray(vals);
return new(Vert.Average(centers), Float3.Average(sizes));
}
public static Box3D Ceiling(Box3D val) => new(Vert.Ceiling(val.center), Float3.Ceiling(val.size));
public static Box3D Ceiling(Box3D val) =>
new(Vert.Ceiling(val.center), (Float3)Float3.Ceiling(val.size));
public static Box3D Clamp(Box3D val, Box3D min, Box3D max) =>
new(Vert.Clamp(val.center, min.center, max.center), Float3.Clamp(val.size, min.size, max.size));
public static Box3D Floor(Box3D val) => new(Vert.Floor(val.center), Float3.Floor(val.size));
public static Box3D Floor(Box3D val) =>
new(Vert.Floor(val.center), (Float3)Float3.Floor(val.size));
public static Box3D Lerp(Box3D a, Box3D b, float t, bool clamp = true) =>
new(Vert.Lerp(a.center, b.center, t, clamp), Float3.Lerp(a.size, b.size, t, clamp));
public static Box3D Median(params Box3D[] vals)
@ -61,16 +66,8 @@ public struct Box3D : ICloneable, IContainer<Vert>, IEquatable<Box3D>
(Vert[] verts, Float3[] sizes) = SplitArray(vals);
return new(Vert.Median(verts), Float3.Median(sizes));
}
public static Box3D Max(params Box3D[] vals)
{
(Vert[] verts, Float3[] sizes) = SplitArray(vals);
return new(Vert.Max(verts), Float3.Max(sizes));
}
public static Box3D Min(params Box3D[] vals)
{
(Vert[] verts, Float3[] sizes) = SplitArray(vals);
return new(Vert.Min(verts), Float3.Min(sizes));
}
public static Box3D Round(Box3D val) => new(Vert.Ceiling(val.center), (Float3)Float3.Ceiling(val.size));
public static (Vert[] centers, Float3[] sizes) SplitArray(params Box3D[] vals)
{
Vert[] centers = new Vert[vals.Length];
@ -85,25 +82,27 @@ public struct Box3D : ICloneable, IContainer<Vert>, IEquatable<Box3D>
return (centers, sizes);
}
public override bool Equals([NotNullWhen(true)] object? obj)
public virtual bool Equals(Box3D? other)
{
if (obj == null || obj.GetType() != typeof(Box3D)) return base.Equals(obj);
return Equals((Box3D)obj);
if (other is null) return false;
return center == other.center && size == other.size;
}
public bool Equals(Box3D other) => center == other.center && size == other.size;
public override int GetHashCode() => center.GetHashCode() ^ size.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"Min: " + MinVert.ToString(provider) + " Max: " + MaxVert.ToString(provider);
public string ToString(IFormatProvider provider) =>
"Min: " + MinVert.ToString(provider) + " Max: " + MaxVert.ToString(provider);
public override int GetHashCode() => base.GetHashCode();
public bool Contains(Vert vert)
{
Float3 diff = Float3.Absolute(center - vert);
return diff.x <= size.x && diff.y <= size.y && diff.z <= size.z;
}
public object Clone() => new Box3D(center, size);
protected virtual bool PrintMembers(StringBuilder builder)
{
builder.Append("Min = ");
builder.Append(MinVert);
builder.Append(", Max = ");
builder.Append(MaxVert);
return true;
}
public static Box3D operator +(Box3D a, Vert b) => new(a.center + b, a.size);
public static Box3D operator +(Box3D a, Float3 b) => new(a.center, a.size + b);
@ -114,8 +113,6 @@ public struct Box3D : ICloneable, IContainer<Vert>, IEquatable<Box3D>
public static Box3D operator *(Box3D a, Float3 b) => new(a.center, a.size * b);
public static Box3D operator /(Box3D a, float b) => new(a.center / b, a.size / b);
public static Box3D operator /(Box3D a, Float3 b) => new(a.center, a.size / b);
public static bool operator ==(Box3D a, Box3D b) => a.Equals(b);
public static bool operator !=(Box3D a, Box3D b) => !a.Equals(b);
public static implicit operator Box3D(Fill<float> fill) => new(fill);
public static implicit operator Box3D(Box2D box) => new(box);

View File

@ -1,7 +0,0 @@
namespace Nerd_STF.Mathematics.Geometry;
public interface ISubdividable<T>
{
public T Subdivide();
public T Subdivide(int iterations);
}

View File

@ -1,13 +0,0 @@
namespace Nerd_STF.Mathematics.Geometry;
public interface ITriangulatable
{
public static Triangle[] TriangulateAll(params ITriangulatable[] triangulatables)
{
List<Triangle> res = new();
foreach (ITriangulatable triangulatable in triangulatables) res.AddRange(triangulatable.Triangulate());
return res.ToArray();
}
public Triangle[] Triangulate();
}

View File

@ -1,7 +1,11 @@
namespace Nerd_STF.Mathematics.Geometry;
using Nerd_STF.Mathematics.Abstract;
public struct Line : ICloneable, IClosest<Vert>, IComparable<Line>, IContainer<Vert>, IEquatable<Line>,
IGroup<Vert>, ISubdividable<Line[]>
namespace Nerd_STF.Mathematics.Geometry;
public record class Line : IAbsolute<Line>, IAverage<Line>, ICeiling<Line>, IClamp<Line>, IClosestTo<Vert>,
IComparable<Line>, IContains<Vert>, IEquatable<Line>, IFloor<Line>, IFromTuple<Line, (Vert start, Vert end)>,
IGroup<Vert>, IIndexAll<Vert>, IIndexRangeAll<Vert>, ILerp<Line, float>, IMedian<Line>, IPresets3D<Line>,
IRound<Line>, ISplittable<Line, (Vert[] starts, Vert[] ends)>, ISubdivide<Line[]>
{
public static Line Back => new(Vert.Zero, Vert.Back);
public static Line Down => new(Vert.Zero, Vert.Down);
@ -56,6 +60,28 @@ public struct Line : ICloneable, IClosest<Vert>, IComparable<Line>, IContainer<V
}
}
}
public Vert this[Index index]
{
get => this[index.IsFromEnd ? 2 - index.Value : index.Value];
set => this[index.IsFromEnd ? 2 - index.Value : index.Value] = value;
}
public Vert[] this[Range range]
{
get
{
int start = range.Start.IsFromEnd ? 2 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 2 - range.End.Value : range.End.Value;
List<Vert> res = new();
for (int i = start; i < end; i++) res.Add(this[i]);
return res.ToArray();
}
set
{
int start = range.Start.IsFromEnd ? 2 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 2 - range.End.Value : range.End.Value;
for (int i = start; i < end; i++) this[i] = value[i];
}
}
public static Line Absolute(Line val) => new(Vert.Absolute(val.a), Vert.Absolute(val.b));
public static Line Average(params Line[] vals)
@ -74,16 +100,7 @@ public struct Line : ICloneable, IClosest<Vert>, IComparable<Line>, IContainer<V
(Vert[] starts, Vert[] ends) = SplitArray(vals);
return new(Vert.Median(starts), Vert.Median(ends));
}
public static Line Max(params Line[] vals)
{
(Vert[] starts, Vert[] ends) = SplitArray(vals);
return new(Vert.Max(starts), Vert.Max(ends));
}
public static Line Min(params Line[] vals)
{
(Vert[] starts, Vert[] ends) = SplitArray(vals);
return new(Vert.Min(starts), Vert.Min(ends));
}
public static Line Round(Line val) => new(Vert.Round(val.a), Vert.Round(val.b));
public static (Vert[] starts, Vert[] ends) SplitArray(params Line[] lines)
{
@ -96,22 +113,20 @@ public struct Line : ICloneable, IClosest<Vert>, IComparable<Line>, IContainer<V
return (starts, ends);
}
public override bool Equals([NotNullWhen(true)] object? obj)
public virtual bool Equals(Line? other)
{
if (obj == null || obj.GetType() != typeof(Line)) return base.Equals(obj);
return Equals((Line)obj);
if (other is null) return false;
return a == other.a && b == other.b;
}
public bool Equals(Line other) => a == other.a && b == other.b;
public override int GetHashCode() => a.GetHashCode() ^ b.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"A: " + a.ToString(provider) + " B: " + b.ToString(provider);
public string ToString(IFormatProvider provider) =>
"A: " + a.ToString(provider) + " B: " + b.ToString(provider);
public override int GetHashCode() => base.GetHashCode();
public object Clone() => new Line(a, b);
public int CompareTo(Line line) => Length.CompareTo(line.Length);
[Obsolete("This method is a bit ambiguous. You should instead compare " +
nameof(Length) + "s directly.")]
public int CompareTo(Line? line)
{
if (line is null) return -1;
return Length.CompareTo(line.Length);
}
public bool Contains(Vert vert)
{
@ -173,6 +188,15 @@ public struct Line : ICloneable, IClosest<Vert>, IComparable<Line>, IContainer<V
public List<float> ToFloatList() => new() { a.position.x, a.position.y, a.position.z,
b.position.x, b.position.y, b.position.z };
protected virtual bool PrintMembers(StringBuilder builder)
{
builder.Append("A = ");
builder.Append(a);
builder.Append(", B = ");
builder.Append(b);
return true;
}
public static Line operator +(Line a, Line b) => new(a.a + b.a, a.b + b.b);
public static Line operator +(Line a, Vert b) => new(a.a + b, a.b + b);
public static Line operator -(Line l) => new(-l.a, -l.b);
@ -184,11 +208,17 @@ public struct Line : ICloneable, IClosest<Vert>, IComparable<Line>, IContainer<V
public static Line operator /(Line a, Line b) => new(a.a / b.a, a.b / b.b);
public static Line operator /(Line a, Vert b) => new(a.a / b, a.b / b);
public static Line operator /(Line a, float b) => new(a.a / b, a.b / b);
public static bool operator ==(Line a, Line b) => a.Equals(b);
public static bool operator !=(Line a, Line b) => !a.Equals(b);
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Length) + "s directly.")]
public static bool operator >(Line a, Line b) => a.CompareTo(b) > 0;
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Length) + "s directly.")]
public static bool operator <(Line a, Line b) => a.CompareTo(b) < 0;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Length) + "s directly.")]
public static bool operator >=(Line a, Line b) => a > b || a == b;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Length) + "s directly.")]
public static bool operator <=(Line a, Line b) => a < b || a == b;
public static implicit operator Line(Fill<Vert> fill) => new(fill);
@ -196,4 +226,5 @@ public struct Line : ICloneable, IClosest<Vert>, IComparable<Line>, IContainer<V
public static implicit operator Line(Fill<Int3> fill) => new(fill);
public static implicit operator Line(Fill<float> fill) => new(fill);
public static implicit operator Line(Fill<int> fill) => new(fill);
public static implicit operator Line((Vert start, Vert end) val) => new(val.start, val.end);
}

View File

@ -1,6 +1,7 @@
namespace Nerd_STF.Mathematics.Geometry;
public struct Polygon : ICloneable, IEquatable<Polygon>, IGroup<Vert>, ISubdividable<Polygon>, ITriangulatable
[Obsolete("This struct is a garbage fire. This will be completely redesigned in v2.5.0")]
public struct Polygon : ICloneable, IEquatable<Polygon>, IGroup<Vert>, ISubdivide<Polygon>, ITriangulate
{
public Line[] Lines
{
@ -82,8 +83,8 @@ public struct Polygon : ICloneable, IEquatable<Polygon>, IGroup<Vert>, ISubdivid
while (true)
{
Line? v = fill(i);
if (!v.HasValue) break;
lines.Add(v.Value);
if (v is null) break;
lines.Add(v);
}
this = new(lines.ToArray());
}
@ -189,24 +190,6 @@ public struct Polygon : ICloneable, IEquatable<Polygon>, IGroup<Vert>, ISubdivid
Line[][] lines = new Line[2][] { a.Lines, b.Lines };
Line[] res = new Line[a.Lines.Length];
for (int i = 0; i < res.Length; i++) res[i] = Line.Lerp(lines[0][i], lines[1][i], t, clamp);
return new(res);
}
public static Polygon Max(params Polygon[] vals)
{
if (!CheckVerts(vals)) throw new DifferingVertCountException(nameof(vals), vals);
if (vals.Length < 1) return default;
Line[][] lines = new Line[vals.Length][];
for (int i = 0; i < vals.Length; i++) lines[i] = vals[i].Lines;
Line[] res = new Line[vals[0].Lines.Length];
for (int i = 0; i < res.Length; i++)
{
Line[] row = new Line[vals.Length];
for (int j = 0; j < vals[0].Lines.Length; j++) row[j] = vals[j].Lines[i];
res[i] = Line.Max(row);
}
return new(res);
}
public static Polygon Median(params Polygon[] vals)
@ -225,24 +208,6 @@ public struct Polygon : ICloneable, IEquatable<Polygon>, IGroup<Vert>, ISubdivid
res[i] = Line.Median(row);
}
return new(res);
}
public static Polygon Min(params Polygon[] vals)
{
if (!CheckVerts(vals)) throw new DifferingVertCountException(nameof(vals), vals);
if (vals.Length < 1) return default;
Line[][] lines = new Line[vals.Length][];
for (int i = 0; i < vals.Length; i++) lines[i] = vals[i].Lines;
Line[] res = new Line[vals[0].Lines.Length];
for (int i = 0; i < res.Length; i++)
{
Line[] row = new Line[vals.Length];
for (int j = 0; j < vals[0].Lines.Length; j++) row[j] = vals[j].Lines[i];
res[i] = Line.Min(row);
}
return new(res);
}
@ -265,19 +230,12 @@ public struct Polygon : ICloneable, IEquatable<Polygon>, IGroup<Vert>, ISubdivid
return Lines == other.Lines;
}
public override int GetHashCode() => Lines.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider)
{
string s = "";
for (int i = 0; i < Lines.Length; i++) s += "L" + i + ": " + Lines[i].ToString(provider) + " ";
return s;
}
public string ToString(IFormatProvider provider)
{
string s = "";
for (int i = 0; i < Lines.Length; i++) s += "L" + i + ": " + Lines[i].ToString(provider) + " ";
return s;
}
public override string ToString()
{
string s = "";
for (int i = 0; i < Lines.Length; i++) s += "L" + i + ": " + Lines[i] + " ";
return s;
}
public object Clone() => new Polygon(Lines);
@ -374,13 +332,16 @@ public struct Polygon : ICloneable, IEquatable<Polygon>, IGroup<Vert>, ISubdivid
}
}
if (closest == null) throw new("Unknown error triangulating the polygon.");
if (closest == null)
throw new Nerd_STFException("Unknown error triangulating the polygon.");
if (closest.Value.posB > closest.Value.posA)
closest = (closest.Value.posB, closest.Value.posA, closest.Value.line);
List<Line> partA = new(Lines[closest.Value.posA..(closest.Value.posB - 1)]);
partA.Add(closest.Value.line);
List<Line> partA = new(Lines[closest.Value.posA..(closest.Value.posB - 1)])
{
closest.Value.line
};
Polygon pA = new(partA.ToArray());

View File

@ -1,6 +1,9 @@
namespace Nerd_STF.Mathematics.Geometry;
public struct Quadrilateral : ICloneable, IEquatable<Quadrilateral>, IGroup<Vert>, ITriangulatable
public record class Quadrilateral : IAbsolute<Quadrilateral>, IAverage<Quadrilateral>, ICeiling<Quadrilateral>,
IClamp<Quadrilateral>, IEquatable<Quadrilateral>, IFloor<Quadrilateral>,
IFromTuple<Quadrilateral, (Vert a, Vert b, Vert c, Vert d)>, IGroup<Vert>, IIndexAll<Vert>, IIndexRangeAll<Vert>,
ILerp<Quadrilateral, float>, IRound<Quadrilateral>, IShape2D<float>, ITriangulate
{
public Vert A
{
@ -94,6 +97,7 @@ public struct Quadrilateral : ICloneable, IEquatable<Quadrilateral>, IGroup<Vert
private Vert p_a, p_b, p_c, p_d;
private Line p_ab, p_bc, p_cd, p_da;
[Obsolete("This field doesn't account for the Z-axis. This will be fixed in v2.4.0")]
public float Area
{
get
@ -178,6 +182,28 @@ public struct Quadrilateral : ICloneable, IEquatable<Quadrilateral>, IGroup<Vert
}
}
}
public Vert this[Index index]
{
get => this[index.IsFromEnd ? 4 - index.Value : index.Value];
set => this[index.IsFromEnd ? 4 - index.Value : index.Value] = value;
}
public Vert[] this[Range range]
{
get
{
int start = range.Start.IsFromEnd ? 4 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 4 - range.End.Value : range.End.Value;
List<Vert> res = new();
for (int i = start; i < end; i++) res.Add(this[i]);
return res.ToArray();
}
set
{
int start = range.Start.IsFromEnd ? 4 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 4 - range.End.Value : range.End.Value;
for (int i = start; i < end; i++) this[i] = value[i];
}
}
public static Quadrilateral Absolute(Quadrilateral val) =>
new(Vert.Absolute(val.A), Vert.Absolute(val.B), Vert.Absolute(val.C), Vert.Absolute(val.D));
@ -211,6 +237,8 @@ public struct Quadrilateral : ICloneable, IEquatable<Quadrilateral>, IGroup<Vert
(Vert[] As, Vert[] Bs, Vert[] Cs, Vert[] Ds) = SplitVertArray(vals);
return new(Vert.Min(As), Vert.Min(Bs), Vert.Min(Cs), Vert.Min(Ds));
}
public static Quadrilateral Round(Quadrilateral val) =>
new(Vert.Round(val.A), Vert.Round(val.B), Vert.Round(val.C), Vert.Round(val.D));
public static (Vert[] As, Vert[] Bs, Vert[] Cs, Vert[] Ds) SplitVertArray(params Quadrilateral[] quads)
{
@ -264,20 +292,12 @@ public struct Quadrilateral : ICloneable, IEquatable<Quadrilateral>, IGroup<Vert
}
public static List<float> ToFloatListAll(params Quadrilateral[] quads) => new(ToFloatArrayAll(quads));
public override bool Equals([NotNullWhen(true)] object? obj)
public virtual bool Equals(Quadrilateral? other)
{
if (obj == null || obj.GetType() != typeof(Quadrilateral)) return base.Equals(obj);
return Equals((Quadrilateral)obj);
if (other is null) return false;
return 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;
public override int GetHashCode() => A.GetHashCode() ^ B.GetHashCode() ^ C.GetHashCode() ^ D.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) => "A: " + A.ToString(provider) + " B: " + B.ToString(provider)
+ " C: " + C.ToString(provider) + " D: " + D.ToString(provider);
public string ToString(IFormatProvider provider) => "A: " + A.ToString(provider) + " B: "
+ B.ToString(provider) + " C: " + C.ToString(provider) + " D: " + D.ToString(provider);
public object Clone() => new Quadrilateral(A, B, C, D);
public override int GetHashCode() => base.GetHashCode();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<Vert> GetEnumerator()
@ -308,6 +328,19 @@ public struct Quadrilateral : ICloneable, IEquatable<Quadrilateral>, IGroup<Vert
public Triangle[] Triangulate() => new Line(A, C).Length > new Line(B, D).Length ?
new Triangle[] { new(A, B, C), new(C, D, A) } : new Triangle[] { new(B, C, D), new(D, A, B) };
protected virtual bool PrintMembers(StringBuilder builder)
{
builder.Append("A = ");
builder.Append(A);
builder.Append(", B = ");
builder.Append(B);
builder.Append(", C = ");
builder.Append(C);
builder.Append(", D = ");
builder.Append(D);
return true;
}
public static Quadrilateral operator +(Quadrilateral a, Quadrilateral b) => new(a.A + b.A, a.B + b.B,
a.C + b.C, a.D + b.D);
public static Quadrilateral operator +(Quadrilateral a, Vert b) => new(a.A + b, a.B + b, a.C + b, a.D + b);
@ -323,8 +356,6 @@ public struct Quadrilateral : ICloneable, IEquatable<Quadrilateral>, IGroup<Vert
a.C / b.C, a.D / b.D);
public static Quadrilateral operator /(Quadrilateral a, Vert b) => new(a.A / b, a.B / b, a.C / b, a.D / b);
public static Quadrilateral operator /(Quadrilateral a, float b) => new(a.A / b, a.B / b, a.C / b, a.D / b);
public static bool operator ==(Quadrilateral a, Quadrilateral b) => a.Equals(b);
public static bool operator !=(Quadrilateral a, Quadrilateral b) => !a.Equals(b);
public static implicit operator Quadrilateral(Fill<Vert> fill) => new(fill);
public static implicit operator Quadrilateral(Fill<Float3> fill) => new(fill);
@ -332,6 +363,6 @@ public struct Quadrilateral : ICloneable, IEquatable<Quadrilateral>, IGroup<Vert
public static implicit operator Quadrilateral(Fill<Line> fill) => new(fill);
public static implicit operator Quadrilateral(Fill<float> fill) => new(fill);
public static implicit operator Quadrilateral(Fill<int> fill) => new(fill);
public static explicit operator Quadrilateral(Polygon poly) => new(poly.Lines[0], poly.Lines[1],
poly.Lines[2], poly.Lines[3]);
public static implicit operator Quadrilateral((Vert a, Vert b, Vert c, Vert d) val) =>
new(val.a, val.b, val.c, val.d);
}

View File

@ -1,7 +1,9 @@
namespace Nerd_STF.Mathematics.Geometry;
public struct Sphere : ICloneable, IClosest<Vert>, IComparable<Sphere>, IComparable<float>, IContainer<Vert>,
IEquatable<Sphere>, IEquatable<float>
public record class Sphere : IAverage<Sphere>, ICeiling<Sphere>, IClamp<Sphere>, IClosestTo<Vert>,
IComparable<Sphere>, IComparable<float>, IContains<Vert>, IEquatable<Sphere>, IEquatable<float>, IFloor<Sphere>,
IFromTuple<Sphere, (Vert center, float radius)>, ILerp<Sphere, float>, IMax<Sphere>, IMedian<Sphere>,
IMin<Sphere>, IRound<Sphere>, ISplittable<Sphere, (Vert[] centers, float[] radii)>
{
public static Sphere Unit => new(Vert.Zero, 1);
@ -54,6 +56,7 @@ public struct Sphere : ICloneable, IClosest<Vert>, IComparable<Sphere>, ICompara
(Vert[] centers, float[] radii) = SplitArray(vals);
return new(Vert.Min(centers), Mathf.Min(radii));
}
public static Sphere Round(Sphere val) => new(Vert.Round(val.center), Mathf.Round(val.radius));
public static (Vert[] centers, float[] radii) SplitArray(params Sphere[] spheres)
{
@ -67,31 +70,37 @@ public struct Sphere : ICloneable, IClosest<Vert>, IComparable<Sphere>, ICompara
return (centers, radii);
}
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null) return base.Equals(obj);
Type type = obj.GetType();
if (type == typeof(Sphere)) return Equals((Sphere)obj);
if (type == typeof(float)) return Equals((float)obj);
return base.Equals(obj);
}
[Obsolete("This method is a bit ambiguous. You should instead compare " + nameof(radius) + "es directly. " +
"This method will be removed in Nerd_STF 2.5.0.")]
public bool Equals(float other) => Volume == other;
public bool Equals(Sphere other) => center == other.center && radius == other.radius;
public override int GetHashCode() => center.GetHashCode() ^ radius.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) => "Center: " + center.ToString(provider)
+ " Radius: " + radius.ToString(provider);
public string ToString(IFormatProvider provider) => "Center: " + center.ToString(provider)
+ " Radius: " + radius.ToString(provider);
public virtual bool Equals(Sphere? other)
{
if (other is null) return false;
return center == other.center && radius == other.radius;
}
public override int GetHashCode() => base.GetHashCode();
public object Clone() => new Sphere(center, radius);
public int CompareTo(Sphere sphere) => Volume.CompareTo(sphere.Volume);
public int CompareTo(Sphere? other)
{
if (other is null) return -1;
return Volume.CompareTo(other.Volume);
}
[Obsolete("This method is a bit ambiguous. You should instead compare " + nameof(radius) + "es directly. " +
"This method will be removed in Nerd_STF 2.5.0.")]
public int CompareTo(float volume) => Volume.CompareTo(volume);
public bool Contains(Vert vert) => (center - vert).Magnitude <= radius;
public Vert ClosestTo(Vert vert) => Contains(vert) ? vert : ((vert - center).Normalized * radius) + vert;
public Vert ClosestTo(Vert vert) => Contains(vert) ? vert : ((vert - center).Normalized * radius) + center;
protected virtual bool PrintMembers(StringBuilder builder)
{
builder.Append("Center = ");
builder.Append(builder);
builder.Append(", Radius = ");
builder.Append(radius);
return true;
}
public static Sphere operator +(Sphere a, Sphere b) => new(a.center + b.center, a.radius + b.radius);
public static Sphere operator +(Sphere a, Vert b) => new(a.center + b, a.radius);
@ -103,16 +112,37 @@ public struct Sphere : ICloneable, IClosest<Vert>, IComparable<Sphere>, ICompara
public static Sphere operator *(Sphere a, float b) => new(a.center * b, a.radius * b);
public static Sphere operator /(Sphere a, Sphere b) => new(a.center * b.center, a.radius * b.radius);
public static Sphere operator /(Sphere a, float b) => new(a.center * b, a.radius * b);
public static bool operator ==(Sphere a, Sphere b) => a.Equals(b);
public static bool operator !=(Sphere a, Sphere b) => !a.Equals(b);
[Obsolete("This method is a bit ambiguous. You should instead compare " + nameof(radius) + "es directly. " +
"This method will be removed in Nerd_STF 2.5.0.")]
public static bool operator ==(Sphere a, float b) => a.Equals(b);
[Obsolete("This method is a bit ambiguous. You should instead compare " + nameof(radius) + "es directly. " +
"This method will be removed in Nerd_STF 2.5.0.")]
public static bool operator !=(Sphere a, float b) => !a.Equals(b);
[Obsolete("This method is a bit ambiguous. You should instead compare " + nameof(radius) + "es directly. " +
"This method will be removed in Nerd_STF 2.5.0.")]
public static bool operator >(Sphere a, Sphere b) => a.CompareTo(b) > 0;
[Obsolete("This method is a bit ambiguous. You should instead compare " + nameof(radius) + "es directly. " +
"This method will be removed in Nerd_STF 2.5.0.")]
public static bool operator <(Sphere a, Sphere b) => a.CompareTo(b) < 0;
[Obsolete("This method is a bit ambiguous. You should instead compare " + nameof(radius) + "es directly. " +
"This method will be removed in Nerd_STF 2.5.0.")]
public static bool operator >(Sphere a, float b) => a.CompareTo(b) > 0;
[Obsolete("This method is a bit ambiguous. You should instead compare " + nameof(radius) + "es directly. " +
"This method will be removed in Nerd_STF 2.5.0.")]
public static bool operator <(Sphere a, float b) => a.CompareTo(b) < 0;
[Obsolete("This method is a bit ambiguous. You should instead compare " + nameof(radius) + "es directly. " +
"This method will be removed in Nerd_STF 2.5.0.")]
public static bool operator >=(Sphere a, Sphere b) => a > b || a == b;
[Obsolete("This method is a bit ambiguous. You should instead compare " + nameof(radius) + "es directly. " +
"This method will be removed in Nerd_STF 2.5.0.")]
public static bool operator <=(Sphere a, Sphere b) => a < b || a == b;
[Obsolete("This method is a bit ambiguous. You should instead compare " + nameof(radius) + "es directly. " +
"This method will be removed in Nerd_STF 2.5.0.")]
public static bool operator >=(Sphere a, float b) => a > b || a == b;
[Obsolete("This method is a bit ambiguous. You should instead compare " + nameof(radius) + "es directly. " +
"This method will be removed in Nerd_STF 2.5.0.")]
public static bool operator <=(Sphere a, float b) => a < b || a == b;
public static implicit operator Sphere((Vert center, float radius) val) =>
new(val.center, val.radius);
}

View File

@ -1,6 +1,10 @@
namespace Nerd_STF.Mathematics.Geometry;
using System.Net.Security;
public struct Triangle : ICloneable, IEquatable<Triangle>, IGroup<Vert>
namespace Nerd_STF.Mathematics.Geometry;
public record class Triangle : IAbsolute<Triangle>, IAverage<Triangle>, ICeiling<Triangle>, IClamp<Triangle>,
IEquatable<Triangle>, IFloor<Triangle>, IFromTuple<Triangle, (Vert a, Vert b, Vert c)>, IGroup<Vert>,
IIndexAll<Vert>, IIndexRangeAll<Vert>, ILerp<Triangle, float>, IRound<Triangle>, IShape2D<float>
{
public Vert A
{
@ -72,6 +76,7 @@ public struct Triangle : ICloneable, IEquatable<Triangle>, IGroup<Vert>
private Vert p_a, p_b, p_c;
private Line p_ab, p_bc, p_ca;
[Obsolete("This field doesn't account for the Z-axis. This will be fixed in v2.4.0")]
public float Area => (float)Mathf.Absolute((A.position.x * B.position.y) + (B.position.x * C.position.y) +
(C.position.x * A.position.y) - ((B.position.x * A.position.y) + (C.position.x * B.position.y) +
(A.position.x * C.position.y))) * 0.5f;
@ -141,6 +146,28 @@ public struct Triangle : ICloneable, IEquatable<Triangle>, IGroup<Vert>
}
}
}
public Vert this[Index index]
{
get => this[index.IsFromEnd ? 3 - index.Value : index.Value];
set => this[index.IsFromEnd ? 3 - index.Value : index.Value] = value;
}
public Vert[] this[Range range]
{
get
{
int start = range.Start.IsFromEnd ? 3 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 3 - range.End.Value : range.End.Value;
List<Vert> res = new();
for (int i = start; i < end; i++) res.Add(this[i]);
return res.ToArray();
}
set
{
int start = range.Start.IsFromEnd ? 3 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 3 - range.End.Value : range.End.Value;
for (int i = start; i < end; i++) this[i] = value[i];
}
}
public static Triangle Absolute(Triangle val) =>
new(Vert.Absolute(val.A), Vert.Absolute(val.B), Vert.Absolute(val.C));
@ -172,6 +199,8 @@ public struct Triangle : ICloneable, IEquatable<Triangle>, IGroup<Vert>
(Vert[] As, Vert[] Bs, Vert[] Cs) = SplitVertArray(vals);
return new(Vert.Min(As), Vert.Min(Bs), Vert.Min(Cs));
}
public static Triangle Round(Triangle val) =>
new(Vert.Round(val.A), Vert.Round(val.B), Vert.Round(val.C));
public static (Vert[] As, Vert[] Bs, Vert[] Cs) SplitVertArray(params Triangle[] tris)
{
@ -216,20 +245,12 @@ public struct Triangle : ICloneable, IEquatable<Triangle>, IGroup<Vert>
}
public static List<float> ToFloatListAll(params Triangle[] tris) => new(ToFloatArrayAll(tris));
public override bool Equals([NotNullWhen(true)] object? obj)
public virtual bool Equals(Triangle? other)
{
if (obj == null || obj.GetType() != typeof(Triangle)) return base.Equals(obj);
return Equals((Triangle)obj);
if (other is null) return false;
return A == other.A && B == other.B && C == other.C;
}
public bool Equals(Triangle other) => A == other.A && B == other.B && C == other.C;
public override int GetHashCode() => A.GetHashCode() ^ B.GetHashCode() ^ C.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"A: " + A.ToString(provider) + " B: " + B.ToString(provider) + " C: " + C.ToString(provider);
public string ToString(IFormatProvider provider) =>
"A: " + A.ToString(provider) + " B: " + B.ToString(provider) + " C: " + C.ToString(provider);
public object Clone() => new Triangle(A, B, C);
public override int GetHashCode() => base.GetHashCode();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<Vert> GetEnumerator()
@ -253,6 +274,18 @@ public struct Triangle : ICloneable, IEquatable<Triangle>, IGroup<Vert>
public List<float> ToFloatList() => new() { A.position.x, A.position.y, A.position.z,
B.position.x, B.position.y, B.position.z,
C.position.x, C.position.y, C.position.z };
protected virtual bool PrintMembers(StringBuilder builder)
{
builder.Append("A = ");
builder.Append(A);
builder.Append(", B = ");
builder.Append(B);
builder.Append(", C = ");
builder.Append(C);
return true;
}
public static Triangle operator +(Triangle a, Triangle b) => new(a.A + b.A, a.B + b.B, a.C + b.C);
public static Triangle operator +(Triangle a, Vert b) => new(a.A + b, a.B + b, a.C + b);
public static Triangle operator -(Triangle t) => new(-t.A, -t.B, -t.C);
@ -264,8 +297,6 @@ public struct Triangle : ICloneable, IEquatable<Triangle>, IGroup<Vert>
public static Triangle operator /(Triangle a, Triangle b) => new(a.A / b.A, a.B / b.B, a.C / b.C);
public static Triangle operator /(Triangle a, Vert b) => new(a.A / b, a.B / b, a.C / b);
public static Triangle operator /(Triangle a, float b) => new(a.A / b, a.B / b, a.C / b);
public static bool operator ==(Triangle a, Triangle b) => a.Equals(b);
public static bool operator !=(Triangle a, Triangle b) => !a.Equals(b);
public static implicit operator Triangle(Fill<Vert> fill) => new(fill);
public static implicit operator Triangle(Fill<Float3> fill) => new(fill);
@ -273,5 +304,6 @@ public struct Triangle : ICloneable, IEquatable<Triangle>, IGroup<Vert>
public static implicit operator Triangle(Fill<Line> fill) => new(fill);
public static implicit operator Triangle(Fill<float> fill) => new(fill);
public static implicit operator Triangle(Fill<int> fill) => new(fill);
public static explicit operator Triangle(Polygon poly) => new(poly.Lines[0], poly.Lines[1], poly.Lines[2]);
public static implicit operator Triangle((Vert a, Vert b, Vert c) val) =>
new(val.a, val.b, val.c);
}

View File

@ -13,7 +13,7 @@ public struct Vert : ICloneable, IEquatable<Vert>, IGroup<float>
public static Vert Zero => new(0, 0, 0);
public float Magnitude => position.Magnitude;
public Vert Normalized => new(this / Magnitude);
public Vert Normalized => this / Magnitude;
public Float3 position;
@ -50,6 +50,8 @@ public struct Vert : ICloneable, IEquatable<Vert>, IGroup<float>
Float3.Max(ToFloat3Array(vals));
public static Vert Min(params Vert[] vals) =>
Float3.Min(ToFloat3Array(vals));
public static Vert Round(Vert val) =>
Float3.Round(val);
public static Float3[] ToFloat3Array(params Vert[] vals)
{
@ -66,9 +68,7 @@ public struct Vert : ICloneable, IEquatable<Vert>, IGroup<float>
}
public bool Equals(Vert other) => position == other.position;
public override int GetHashCode() => position.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) => position.ToString(provider);
public string ToString(IFormatProvider provider) => position.ToString(provider);
public override string ToString() => position.ToString();
public object Clone() => new Vert(position);

View File

@ -1,6 +1,10 @@
namespace Nerd_STF.Mathematics;
public struct Int2 : ICloneable, IComparable<Int2>, IEquatable<Int2>, IGroup<int>
public record struct Int2 : IAbsolute<Int2>, IAverage<Int2>, IClamp<Int2>, IClampMagnitude<Int2, int>,
IComparable<Int2>, ICross<Int2, Int3>, IDivide<Int2>, IDot<Int2, int>, IEquatable<Int2>,
IFromTuple<Int2, (int x, int y)>, IGroup<int>, IIndexAll<int>, IIndexRangeAll<int>, ILerp<Int2, float>,
IMathOperators<Int2>, IMax<Int2>, IMedian<Int2>, IMin<Int2>, IPresets2D<Int2>, IProduct<Int2>,
ISplittable<Int2, (int[] Xs, int[] Ys)>, ISubtract<Int2>, ISum<Int2>
{
public static Int2 Down => new(0, -1);
public static Int2 Left => new(-1, 0);
@ -47,6 +51,28 @@ public struct Int2 : ICloneable, IComparable<Int2>, IEquatable<Int2>, IGroup<int
}
}
}
public int this[Index index]
{
get => this[index.IsFromEnd ? 2 - index.Value : index.Value];
set => this[index.IsFromEnd ? 2 - index.Value : index.Value] = value;
}
public int[] this[Range range]
{
get
{
int start = range.Start.IsFromEnd ? 2 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 2 - range.End.Value : range.End.Value;
List<int> res = new();
for (int i = start; i < end; i++) res.Add(this[i]);
return res.ToArray();
}
set
{
int start = range.Start.IsFromEnd ? 2 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 2 - range.End.Value : range.End.Value;
for (int i = start; i < end; i++) this[i] = value[i];
}
}
public static Int2 Absolute(Int2 val) =>
new(Mathf.Absolute(val.x), Mathf.Absolute(val.y));
@ -92,14 +118,14 @@ public struct Int2 : ICloneable, IComparable<Int2>, IEquatable<Int2>, IGroup<int
{
if (vals.Length < 1) return Zero;
Int2 val = vals[0];
foreach (Int2 d in vals) val = d > val ? d : val;
foreach (Int2 d in vals) val = d.Magnitude > val.Magnitude ? d : val;
return val;
}
public static Int2 Min(params Int2[] vals)
{
if (vals.Length < 1) return Zero;
Int2 val = vals[0];
foreach (Int2 d in vals) val = d < val ? d : val;
foreach (Int2 d in vals) val = d.Magnitude < val.Magnitude ? d : val;
return val;
}
public static Int2 Product(params Int2[] vals)
@ -128,21 +154,11 @@ public struct Int2 : ICloneable, IComparable<Int2>, IEquatable<Int2>, IGroup<int
return (Xs, Ys);
}
[Obsolete("This method is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public int CompareTo(Int2 other) => Magnitude.CompareTo(other.Magnitude);
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Int2)) return base.Equals(obj);
return Equals((Int2)obj);
}
public bool Equals(Int2 other) => x == other.x && y == other.y;
public override int GetHashCode() => x.GetHashCode() ^ y.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider);
public string ToString(IFormatProvider provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider);
public object Clone() => new Int2(x, y);
public override int GetHashCode() => base.GetHashCode();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<int> GetEnumerator()
@ -161,6 +177,15 @@ public struct Int2 : ICloneable, IComparable<Int2>, IEquatable<Int2>, IGroup<int
public Vector2d ToVector() => ((Float2)this).ToVector();
private bool PrintMembers(StringBuilder builder)
{
builder.Append("x = ");
builder.Append(x);
builder.Append(", y = ");
builder.Append(y);
return true;
}
public static Int2 operator +(Int2 a, Int2 b) => new(a.x + b.x, a.y + b.y);
public static Int2 operator -(Int2 i) => new(-i.x, -i.y);
public static Int2 operator -(Int2 a, Int2 b) => new(a.x - b.x, a.y - b.y);
@ -173,11 +198,17 @@ public struct Int2 : ICloneable, IComparable<Int2>, IEquatable<Int2>, IGroup<int
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 bool operator ==(Int2 a, Int2 b) => a.Equals(b);
public static bool operator !=(Int2 a, Int2 b) => !a.Equals(b);
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator >(Int2 a, Int2 b) => a.CompareTo(b) > 0;
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator <(Int2 a, Int2 b) => a.CompareTo(b) < 0;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
public static bool operator >=(Int2 a, Int2 b) => a == b || a > b;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
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);
@ -191,4 +222,5 @@ public struct Int2 : ICloneable, IComparable<Int2>, IEquatable<Int2>, IGroup<int
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 implicit operator Int2(Fill<int> fill) => new(fill);
public static implicit operator Int2((int x, int y) val) => new(val.x, val.y);
}

View File

@ -1,6 +1,12 @@
namespace Nerd_STF.Mathematics;
using System.Data;
public struct Int3 : ICloneable, IComparable<Int3>, IEquatable<Int3>, IGroup<int>
namespace Nerd_STF.Mathematics;
public record struct Int3 : IAbsolute<Int3>, IAverage<Int3>, IClamp<Int3>, IClampMagnitude<Int3, int>,
IComparable<Int3>, ICross<Int3>, IDivide<Int3>, IDot<Int3, int>, IEquatable<Int3>,
IFromTuple<Int3, (int x, int y, int z)>, IGroup<int>, IIndexAll<int>, IIndexRangeAll<int>, ILerp<Int3, float>,
IMathOperators<Int3>, IMax<Int3>, IMedian<Int3>, IMin<Int3>, IPresets3D<Int3>, IProduct<Int3>,
ISplittable<Int3, (int[] Xs, int[] Ys, int[] Zs)>, ISubtract<Int3>, ISum<Int3>
{
public static Int3 Back => new(0, 0, -1);
public static Int3 Down => new(0, -1, 0);
@ -60,6 +66,28 @@ public struct Int3 : ICloneable, IComparable<Int3>, IEquatable<Int3>, IGroup<int
}
}
}
public int this[Index index]
{
get => this[index.IsFromEnd ? 3 - index.Value : index.Value];
set => this[index.IsFromEnd ? 3 - index.Value : index.Value] = value;
}
public int[] this[Range range]
{
get
{
int start = range.Start.IsFromEnd ? 3 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 3 - range.End.Value : range.End.Value;
List<int> res = new();
for (int i = start; i < end; i++) res.Add(this[i]);
return res.ToArray();
}
set
{
int start = range.Start.IsFromEnd ? 3 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 3 - range.End.Value : range.End.Value;
for (int i = start; i < end; i++) this[i] = value[i];
}
}
public static Int3 Absolute(Int3 val) =>
new(Mathf.Absolute(val.x), Mathf.Absolute(val.y), Mathf.Absolute(val.z));
@ -112,14 +140,14 @@ public struct Int3 : ICloneable, IComparable<Int3>, IEquatable<Int3>, IGroup<int
{
if (vals.Length < 1) return Zero;
Int3 val = vals[0];
foreach (Int3 d in vals) val = d > val ? d : val;
foreach (Int3 d in vals) val = d.Magnitude > val.Magnitude ? d : val;
return val;
}
public static Int3 Min(params Int3[] vals)
{
if (vals.Length < 1) return Zero;
Int3 val = vals[0];
foreach (Int3 d in vals) val = d < val ? d : val;
foreach (Int3 d in vals) val = d.Magnitude < val.Magnitude ? d : val;
return val;
}
public static Int3 Product(params Int3[] vals)
@ -148,20 +176,13 @@ public struct Int3 : ICloneable, IComparable<Int3>, IEquatable<Int3>, IGroup<int
}
return (Xs, Ys, Zs);
}
[Obsolete("This method is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public int CompareTo(Int3 other) => Magnitude.CompareTo(other.Magnitude);
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Int3)) return base.Equals(obj);
return Equals((Int3)obj);
}
public bool Equals(Int3 other) => x == other.x && y == other.y && z == other.z;
public override int GetHashCode() => x.GetHashCode() ^ y.GetHashCode() ^ z.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider) + " Z: " + z.ToString(provider);
public string ToString(IFormatProvider provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider) + " Z: " + z.ToString(provider);
public object Clone() => new Int3(x, y, z);
public override int GetHashCode() => base.GetHashCode();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<int> GetEnumerator()
{
@ -180,6 +201,17 @@ public struct Int3 : ICloneable, IComparable<Int3>, IEquatable<Int3>, IGroup<int
public Vector3d ToVector() => ((Float3)this).ToVector();
private bool PrintMembers(StringBuilder builder)
{
builder.Append("x = ");
builder.Append(x);
builder.Append(", y = ");
builder.Append(y);
builder.Append(", z = ");
builder.Append(z);
return true;
}
public static Int3 operator +(Int3 a, Int3 b) => new(a.x + b.x, a.y + b.y, a.z + b.z);
public static Int3 operator -(Int3 i) => new(-i.x, -i.y, -i.z);
public static Int3 operator -(Int3 a, Int3 b) => new(a.x - b.x, a.y - b.y, a.z - b.z);
@ -192,11 +224,17 @@ public struct Int3 : ICloneable, IComparable<Int3>, IEquatable<Int3>, IGroup<int
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 bool operator ==(Int3 a, Int3 b) => a.Equals(b);
public static bool operator !=(Int3 a, Int3 b) => !a.Equals(b);
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator >(Int3 a, Int3 b) => a.CompareTo(b) > 0;
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator <(Int3 a, Int3 b) => a.CompareTo(b) < 0;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
public static bool operator >=(Int3 a, Int3 b) => a == b || a > b;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
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);
@ -215,4 +253,6 @@ public struct Int3 : ICloneable, IComparable<Int3>, IEquatable<Int3>, IGroup<int
public static explicit operator Int3(RGBAByte val) => new(val.R, val.G, val.B);
public static explicit operator Int3(HSVAByte val) => new(val.H, val.S, val.V);
public static implicit operator Int3(Fill<int> fill) => new(fill);
public static implicit operator Int3((int x, int y, int z) vals) =>
new(vals.x, vals.y, vals.z);
}

View File

@ -1,8 +1,14 @@
namespace Nerd_STF.Mathematics;
public struct Int4 : ICloneable, IComparable<Int4>, IEquatable<Int4>, IGroup<int>
public record struct Int4 : IAbsolute<Int4>, IAverage<Int4>, IClamp<Int4>, IClampMagnitude<Int4, int>,
IComparable<Int4>, IDivide<Int4>, IDot<Int4, int>, IEquatable<Int4>,
IFromTuple<Int4, (int x, int y, int z, int w)>, IGroup<int>, IIndexAll<int>, IIndexRangeAll<int>,
ILerp<Int4, float>, IMathOperators<Int4>, IMax<Int4>, IMedian<Int4>, IMin<Int4>, IPresets4D<Int4>,
IProduct<Int4>, ISplittable<Int4, (int[] Xs, int[] Ys, int[] Zs, int[] Ws)>, ISubtract<Int4>, ISum<Int4>
{
public static Int4 Back => new(0, 0, -1, 0);
[Obsolete("Field has been replaced by " + nameof(HighW) + ", because it has a better name. " +
"This field will be removed in v2.4.0.", false)]
public static Int4 Deep => new(0, 0, 0, -1);
public static Int4 Down => new(0, -1, 0, 0);
[Obsolete("Field has been replaced by " + nameof(HighW) + ", because it has a better name. " +
@ -81,6 +87,28 @@ public struct Int4 : ICloneable, IComparable<Int4>, IEquatable<Int4>, IGroup<int
}
}
}
public int this[Index index]
{
get => this[index.IsFromEnd ? 4 - index.Value : index.Value];
set => this[index.IsFromEnd ? 4 - index.Value : index.Value] = value;
}
public int[] this[Range range]
{
get
{
int start = range.Start.IsFromEnd ? 4 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 4 - range.End.Value : range.End.Value;
List<int> res = new();
for (int i = start; i < end; i++) res.Add(this[i]);
return res.ToArray();
}
set
{
int start = range.Start.IsFromEnd ? 4 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 4 - range.End.Value : range.End.Value;
for (int i = start; i < end; i++) this[i] = value[i];
}
}
public static Int4 Absolute(Int4 val) =>
new(Mathf.Absolute(val.x), Mathf.Absolute(val.y), Mathf.Absolute(val.z), Mathf.Absolute(val.w));
@ -129,14 +157,14 @@ public struct Int4 : ICloneable, IComparable<Int4>, IEquatable<Int4>, IGroup<int
{
if (vals.Length < 1) return Zero;
Int4 val = vals[0];
foreach (Int4 d in vals) val = d > val ? d : val;
foreach (Int4 d in vals) val = d.Magnitude > val.Magnitude ? d : val;
return val;
}
public static Int4 Min(params Int4[] vals)
{
if (vals.Length < 1) return Zero;
Int4 val = vals[0];
foreach (Int4 d in vals) val = d < val ? d : val;
foreach (Int4 d in vals) val = d.Magnitude < val.Magnitude ? d : val;
return val;
}
public static Int4 Product(params Int4[] vals)
@ -168,23 +196,11 @@ public struct Int4 : ICloneable, IComparable<Int4>, IEquatable<Int4>, IGroup<int
return (Xs, Ys, Zs, Ws);
}
[Obsolete("This method is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public int CompareTo(Int4 other) => Magnitude.CompareTo(other.Magnitude);
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Int4)) return base.Equals(obj);
return Equals((Int4)obj);
}
public bool Equals(Int4 other) => x == other.x && y == other.y && z == other.z && w == other.w;
public override int GetHashCode() => x.GetHashCode() ^ y.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider) + " Z: " + z.ToString(provider)
+ " W: " + w.ToString(provider);
public string ToString(IFormatProvider provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider) + " Z: " + z.ToString(provider)
+ " W: " + w.ToString(provider);
public object Clone() => new Int4(x, y, z, w);
public override int GetHashCode() => base.GetHashCode();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<int> GetEnumerator()
@ -203,6 +219,19 @@ public struct Int4 : ICloneable, IComparable<Int4>, IEquatable<Int4>, IGroup<int
}
public List<int> ToList() => new() { x, y, z, w };
private bool PrintMembers(StringBuilder builder)
{
builder.Append("x = ");
builder.Append(x);
builder.Append(", y = ");
builder.Append(y);
builder.Append(", z = ");
builder.Append(z);
builder.Append(", w = ");
builder.Append(w);
return true;
}
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 d) => new(-d.x, -d.y, -d.z, -d.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);
@ -215,11 +244,17 @@ 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 bool operator ==(Int4 a, Int4 b) => a.Equals(b);
public static bool operator !=(Int4 a, Int4 b) => !a.Equals(b);
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator >(Int4 a, Int4 b) => a.CompareTo(b) > 0;
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator <(Int4 a, Int4 b) => a.CompareTo(b) < 0;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
public static bool operator >=(Int4 a, Int4 b) => a == b || a > b;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
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);
@ -240,4 +275,6 @@ public struct Int4 : ICloneable, IComparable<Int4>, IEquatable<Int4>, IGroup<int
public static explicit operator Int4(CMYKAByte val) => new(val.C, val.M, val.Y, val.K);
public static implicit operator Int4(HSVAByte val) => new(val.H, val.S, val.V, val.A);
public static implicit operator Int4(Fill<int> fill) => new(fill);
public static implicit operator Int4((int x, int y, int z, int w) vals) =>
new(vals.x, vals.y, vals.z, vals.w);
}

View File

@ -105,8 +105,7 @@ public static class Mathf
public static int[] Factors(int val)
{
List<int> factors = new();
factors.Add(1);
List<int> factors = new() { 1 };
for (int i = 2; i < val; i++) if (val % i == 0) factors.Add(i);
factors.Add(val);
return factors.ToArray();
@ -144,14 +143,14 @@ public static class Mathf
if (clamp) v = Clamp(v, Min(a, b), Max(a, b));
return v;
}
public static int Lerp(int a, int b, float value, bool clamp = true) => (int)Lerp((float)a, b, value, clamp);
public static int Lerp(int a, int b, float t, bool clamp = true) => (int)Lerp((float)a, b, t, clamp);
public static Equation MakeEquation(Dictionary<float, float> vals) => delegate (float x)
{
if (vals.Count < 1) throw new UndefinedException();
if (vals.Count == 1) return vals.Values.First();
if (vals.ContainsKey(x)) return vals[x];
if (vals.TryGetValue(x, out float value)) return value;
float? min, max;
if (x < (min = vals.Keys.Min()))
@ -212,9 +211,9 @@ public static class Mathf
foreach (int i in vals) val = i > val ? i : val;
return val;
}
public static T? Max<T>(params T[] vals) where T : IComparable<T>
public static T Max<T>(params T[] vals) where T : IComparable<T>
{
if (vals.Length < 1) return default;
if (vals.Length < 1) return default!;
T val = vals[0];
foreach (T t in vals) val = t.CompareTo(val) > 0 ? t : val;
return val;
@ -254,9 +253,9 @@ public static class Mathf
foreach (int i in vals) val = i < val ? i : val;
return val;
}
public static T? Min<T>(params T[] vals) where T : IComparable<T>
public static T Min<T>(params T[] vals) where T : IComparable<T>
{
if (vals.Length < 1) return default;
if (vals.Length < 1) return default!;
T val = vals[0];
foreach (T t in vals) val = t.CompareTo(val) < 0 ? t : val;
return val;
@ -327,8 +326,7 @@ public static class Mathf
if (pow == 1) return num;
if (pow < 1) return 0;
int val = 1;
int abs = Absolute(pow);
for (int i = 0; i < abs; i++) val = val * num % mod;
for (int i = 0; i < pow; i++) val = val * num % mod;
return val;
}

View File

@ -1,6 +1,10 @@
namespace Nerd_STF.Mathematics.NumberSystems;
public struct Complex : ICloneable, IComparable<Complex>, IEquatable<Complex>, IGroup<float>
public record struct Complex(float u, float i) : IAbsolute<Complex>, IAverage<Complex>, ICeiling<Complex>,
IClampMagnitude<Complex, float>, IComparable<Complex>, IDivide<Complex>, IDot<Complex, float>,
IEquatable<Complex>, IFloor<Complex>, IGroup<float>, IIndexAll<float>, IIndexRangeAll<float>,
ILerp<Complex, float>, IMax<Complex>, IMedian<Complex>, IMin<Complex>, IPresets2D<Complex>, IProduct<Complex>,
IRound<Complex>, ISplittable<Complex, (float[] Us, float[] Is)>, ISum<Complex>
{
public static Complex Down => new(0, -1);
public static Complex Left => new(-1, 0);
@ -15,14 +19,10 @@ public struct Complex : ICloneable, IComparable<Complex>, IEquatable<Complex>, 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 float u = u;
public float i = 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)) { }
@ -50,6 +50,28 @@ public struct Complex : ICloneable, IComparable<Complex>, IEquatable<Complex>, I
}
}
}
public float this[Index index]
{
get => this[index.IsFromEnd ? 2 - index.Value : index.Value];
set => this[index.IsFromEnd ? 2 - index.Value : index.Value] = value;
}
public float[] this[Range range]
{
get
{
int start = range.Start.IsFromEnd ? 2 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 2 - range.End.Value : range.End.Value;
List<float> res = new();
for (int i = start; i < end; i++) res.Add(this[i]);
return res.ToArray();
}
set
{
int start = range.Start.IsFromEnd ? 2 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 2 - range.End.Value : range.End.Value;
for (int i = start; i < end; i++) this[i] = value[i];
}
}
public static Complex Absolute(Complex val) => Float2.Absolute(val);
public static Complex Average(params Complex[] vals)
@ -129,20 +151,8 @@ public struct Complex : ICloneable, IComparable<Complex>, IEquatable<Complex>, I
public Angle GetAngle() => Mathf.ArcTan(i / u);
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 ? " + " : " - ") + Mathf.Absolute(i).ToString(provider) + "i";
public string ToString(IFormatProvider provider) =>
u.ToString(provider) + (i >= 0 ? " + " : " - ") + Mathf.Absolute(i).ToString(provider) + "i";
public object Clone() => new Complex(u, i);
public override int GetHashCode() => base.GetHashCode();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<float> GetEnumerator()
@ -161,6 +171,15 @@ public struct Complex : ICloneable, IComparable<Complex>, IEquatable<Complex>, I
public Vector2d ToVector() => ((Float2)this).ToVector();
private bool PrintMembers(StringBuilder builder)
{
builder.Append(u);
builder.Append(i >= 0 ? " + " : " - ");
builder.Append(Mathf.Absolute(i));
builder.Append('i');
return true;
}
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);
@ -170,11 +189,18 @@ public struct Complex : ICloneable, IComparable<Complex>, IEquatable<Complex>, I
public static Complex operator /(Complex a, Complex b) => a * b.Inverse;
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 Complex operator ~(Complex v) => v.Conjugate;
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator >(Complex a, Complex b) => a.CompareTo(b) > 0;
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator <(Complex a, Complex b) => a.CompareTo(b) < 0;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
public static bool operator >=(Complex a, Complex b) => a == b || a > b;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
public static bool operator <=(Complex a, Complex b) => a == b || a < b;
public static explicit operator Complex(Quaternion val) => new(val.u, val.i);
@ -189,4 +215,5 @@ public struct Complex : ICloneable, IComparable<Complex>, IEquatable<Complex>, I
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);
public static implicit operator Complex((float u, float i) val) => new(val.u, val.i);
}

View File

@ -1,12 +1,23 @@
namespace Nerd_STF.Mathematics.NumberSystems;
public struct Quaternion : ICloneable, IComparable<Quaternion>, IEquatable<Quaternion>, IGroup<float>
public record struct Quaternion(float u, float i, float j, float k) : IAbsolute<Quaternion>, IAverage<Quaternion>,
ICeiling<Quaternion>, IClamp<Quaternion>, IClampMagnitude<Quaternion, float>, IComparable<Quaternion>,
IDivide<Quaternion>, IDot<Quaternion, float>, IEquatable<Quaternion>, IFloor<Quaternion>, IGroup<float>,
IIndexAll<float>, IIndexRangeAll<float>, ILerp<Quaternion, float>, IMax<Quaternion>, IMedian<Quaternion>,
IMin<Quaternion>, IPresets4D<Quaternion>, IProduct<Quaternion>, IRound<Quaternion>,
ISplittable<Quaternion, (float[] Us, float[] Is, float[] Js, float[] Ks)>, ISum<Quaternion>
{
public static Quaternion Back => new(0, 0, -1, 0);
public static Quaternion Down => new(0, -1, 0, 0);
[Obsolete("Field has been replaced by " + nameof(HighW) + ", because it has a better name. " +
"This field will be removed in v2.4.0.", false)]
public static Quaternion Far => new(0, 0, 0, 1);
public static Quaternion Forward => new(0, 0, 1, 0);
public static Quaternion HighW => new(0, 0, 0, 1);
public static Quaternion Left => new(-1, 0, 0, 0);
public static Quaternion LowW => new(0, 0, 0, -1);
[Obsolete("Field has been replaced by " + nameof(LowW) + ", because it has a better name. " +
"This field will be removed in v2.4.0.", false)]
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);
@ -14,26 +25,31 @@ public struct Quaternion : ICloneable, IComparable<Quaternion>, IEquatable<Quate
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 Quaternion Conjugate => new(u, -IJK);
public Quaternion Inverse => Conjugate / (u * u + i * i + j * j + k * 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 Float3 IJK
{
get => new(i, j, k);
set
{
i = value.x;
j = value.y;
k = value.z;
}
}
public float u, i, j, k;
public float u = u;
public float i = i;
public float j = j;
public float k = 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)) { }
@ -71,6 +87,28 @@ public struct Quaternion : ICloneable, IComparable<Quaternion>, IEquatable<Quate
}
}
}
public float this[Index index]
{
get => this[index.IsFromEnd ? 4 - index.Value : index.Value];
set => this[index.IsFromEnd ? 4 - index.Value : index.Value] = value;
}
public float[] this[Range range]
{
get
{
int start = range.Start.IsFromEnd ? 4 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 4 - range.End.Value : range.End.Value;
List<float> res = new();
for (int i = start; i < end; i++) res.Add(this[i]);
return res.ToArray();
}
set
{
int start = range.Start.IsFromEnd ? 4 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 4 - range.End.Value : range.End.Value;
for (int i = start; i < end; i++) this[i] = value[i];
}
}
public static Quaternion Absolute(Quaternion val) => Float4.Absolute(val);
public static Quaternion Average(params Quaternion[] vals)
@ -136,27 +174,31 @@ public struct Quaternion : ICloneable, IComparable<Quaternion>, IEquatable<Quate
return Float4.Sum(floats.ToArray());
}
[Obsolete("This method does not produce the correct output. Please update to a newer release.")]
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 cosYaw, cosPitch, cosRoll, sinYaw, sinPitch, sinRoll,
cosYawCosPitch, sinYawSinPitch, cosYawSinPitch, sinYawCosPitch;
float cosYawCosPitch = cosYaw * cosPitch,
cosYawSinPitch = cosYaw * sinPitch,
sinYawCosPitch = sinYaw * cosPitch,
sinYawSinPitch = sinYaw * sinPitch;
cosYaw = Mathf.Cos(yaw * 0.5f);
cosPitch = Mathf.Cos(pitch * 0.5f);
cosRoll = Mathf.Cos(roll.Value * 0.5f);
sinYaw = Mathf.Sin(yaw * 0.5f);
sinPitch = Mathf.Sin(pitch * 0.5f);
sinRoll = Mathf.Sin(roll.Value * 0.5f);
cosYawCosPitch = cosYaw * cosPitch;
sinYawSinPitch = sinYaw * sinPitch;
cosYawSinPitch = cosYaw * sinPitch;
sinYawCosPitch = sinYaw * cosPitch;
return new(cosYawCosPitch * cosRoll + sinYawSinPitch * sinRoll,
cosYawCosPitch * sinRoll + sinYawSinPitch * cosRoll,
cosYawCosPitch * sinRoll - sinYawSinPitch * cosRoll,
cosYawSinPitch * cosRoll + sinYawCosPitch * sinRoll,
sinYawCosPitch * cosRoll + cosYawSinPitch * sinRoll);
sinYawCosPitch * cosRoll - cosYawSinPitch * sinRoll);
}
[Obsolete("This method does not produce the correct output. Please update to a newer release.")]
public static Quaternion FromAngles(Float3 vals, Angle.Type valType) =>
FromAngles(new(vals.x, valType), new(vals.y, valType), new(vals.z, valType));
[Obsolete("This method does not produce the correct output. Please update to a newer release.")]
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)
@ -174,37 +216,11 @@ public struct Quaternion : ICloneable, IComparable<Quaternion>, IEquatable<Quate
}
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 ? " + " : " - ") + Mathf.Absolute(i).ToString(provider) + "i"
+ (j >= 0 ? " + " : " - ") + Mathf.Absolute(j).ToString(provider) + "j"
+ (k >= 0 ? " + " : " - ") + Mathf.Absolute(k).ToString(provider) + "k";
public string ToString(IFormatProvider provider) => u.ToString(provider)
+ (i >= 0 ? " + " : " - ") + Mathf.Absolute(i).ToString(provider) + "i"
+ (j >= 0 ? " + " : " - ") + Mathf.Absolute(j).ToString(provider) + "j"
+ (k >= 0 ? " + " : " - ") + Mathf.Absolute(k).ToString(provider) + "k";
public override int GetHashCode() => base.GetHashCode();
public object Clone() => new Quaternion(u, i, j, k);
[Obsolete("This method does not produce the correct output. Please update to a newer release.")]
public Angle GetAngle() => Mathf.ArcCos(u) * 2;
[Obsolete("This method does not produce the correct output. Please update to a newer release.")]
public Float3 GetAxis()
{
Float3 axis = IJK;
float mag = Magnitude;
if (mag < 0) return Float3.Zero;
return axis / mag;
}
[Obsolete("This method does not produce the correct output. Please update to a newer release.")]
public Float3 GetAxis() => IJK.Normalized;
public (Angle yaw, Angle pitch, Angle roll) ToAngles()
{
Quaternion doubled = this;
@ -213,42 +229,47 @@ public struct Quaternion : ICloneable, IComparable<Quaternion>, IEquatable<Quate
doubled.j *= j;
doubled.k *= k;
Matrix3x3 rotMatrix = new(new[,]
Matrix3x3 rot = 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 }
{ u * u + i * i - j * j - k * k, 2 * i * j + 2 * k * u, 2 * i * k - 2 * j * u },
{ 2 * i * j - 2 * k * u, u * u - i * i + j * j - k * k, 2 * j * k + 2 * i * u },
{ 2 * k * i + 2 * j * u, 2 * k * j - 2 * i * u, u * u - i * i - j * j + k * k }
});
Angle yaw, pitch, roll;
float r3c1Abs = Mathf.Absolute(rotMatrix.r3c1);
if (r3c1Abs >= 1)
float absr3c1 = Mathf.Absolute(rot.r3c1);
if (absr3c1 >= 1)
{
rotMatrix.r1c2 = 2 * (i * j - u * k);
rotMatrix.r1c3 = 2 * (i * k + u * j);
yaw = Mathf.ArcTan2(-rotMatrix.r1c2, -rotMatrix.r3c1 * rotMatrix.r1c3);
pitch = new(-Constants.HalfPi * rotMatrix.r3c1 / r3c1Abs, Angle.Type.Radians);
yaw = Mathf.ArcTan2(-rot.r1c2, -rot.r3c1 * rot.r1c3);
pitch = new(-Constants.HalfPi * rot.r3c1 / absr3c1, Angle.Type.Radians);
roll = Angle.Zero;
}
else
{
yaw = Mathf.ArcTan2(rotMatrix.r2c1, rotMatrix.r1c1);
pitch = Mathf.ArcSin(-rotMatrix.r3c1);
roll = Mathf.ArcTan2(rotMatrix.r3c2, rotMatrix.r3c3);
yaw = Mathf.ArcTan2(rot.r2c1, rot.r1c1);
pitch = Mathf.ArcSin(-rot.r3c1);
roll = Mathf.ArcTan2(rot.r3c2, rot.r3c3);
}
return (yaw, pitch, roll);
}
[Obsolete("This method does not produce the correct output. Please update to a newer release.")]
public Vector3d ToVector()
public Float3 ToAnglesFloat3(Angle.Type type = Angle.Type.Degrees)
{
(Angle yaw, Angle pitch, _) = ToAngles();
return new(yaw, pitch);
(Angle yaw, Angle pitch, Angle roll) = ToAngles();
return new(yaw.ValueFromType(type), pitch.ValueFromType(type), roll.ValueFromType(type));
}
public Quaternion Rotate(Quaternion other) => other * this * other.Conjugate;
public Quaternion Rotate(Quaternion other) => this * other * Conjugate;
public Float3 Rotate(Float3 other)
{
const float tolerance = 0.001f;
Quaternion res = this * other * Conjugate;
if (Mathf.Absolute(res.u) > tolerance)
throw new MathException("A rotated vector should never have non-zero scalar part.");
return res.IJK;
}
public Vector3d Rotate(Vector3d other) => Rotate(other.ToXYZ()).ToVector();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<float> GetEnumerator()
@ -267,15 +288,30 @@ public struct Quaternion : ICloneable, IComparable<Quaternion>, IEquatable<Quate
}
public List<float> ToList() => new() { u, i, j, k };
private bool PrintMembers(StringBuilder builder)
{
builder.Append(u);
builder.Append(i >= 0 ? " + " : " - ");
builder.Append(Mathf.Absolute(i));
builder.Append('i');
builder.Append(j >= 0 ? " + " : " - ");
builder.Append(Mathf.Absolute(j));
builder.Append('j');
builder.Append(k >= 0 ? " + " : " - ");
builder.Append('k');
builder.Append(Mathf.Absolute(k));
return true;
}
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 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,
j = a * g - b * h + c * e + d * f,
k = a * h + b * g + d * e - c * f;
return new(u, i, j, k);
}
@ -286,11 +322,18 @@ public struct Quaternion : ICloneable, IComparable<Quaternion>, IEquatable<Quate
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 Quaternion operator ~(Quaternion v) => v.Conjugate;
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator >(Quaternion a, Quaternion b) => a.CompareTo(b) > 0;
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator <(Quaternion a, Quaternion b) => a.CompareTo(b) < 0;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
public static bool operator >=(Quaternion a, Quaternion b) => a == b || a > b;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
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);
@ -305,4 +348,6 @@ public struct Quaternion : ICloneable, IComparable<Quaternion>, IEquatable<Quate
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);
public static implicit operator Quaternion((float u, float i, float j, float k) val) =>
new(val.u, val.i, val.j, val.k);
}

View File

@ -33,7 +33,7 @@ public static class Constants
public const float Sqrt3 = 1.7320508076f;
public const float Sqrt5 = 2.2360679775f;
public const float Sqrt10 = 3.16227766017f;
public const float TwelthRoot2 = 1.05946309436f;
public const float TwelfthRoot2 = 1.05946309436f;
public const float Cos0Deg = 1;
public const float Cos30Deg = Sqrt3 / 2;
@ -85,7 +85,7 @@ public static class Constants
public const float ConnectiveConstant = 1.847759065f;
public const float DeVicciTesseractConstant = 1.0074347568f;
public const float EmbreeTrefethenConstant = 0.70258f;
public const float EulerMascheroniConstant = 0.5772156649f;
public const float EulerMascheroniConstant = EulerConstant;
public const float ErdosBorweinConstant = 1.6066951524f;
public const float ErdosTenenbaumFordConstant = 0.8607133205f;
public const float FeigenbaumConstant1 = 4.6692016091f;
@ -108,7 +108,7 @@ public static class Constants
public const float LandauConstant = (0.5f + 0.54326f) / 2;
public const float LandauThirdConstant = (0.5f + 0.7853f) / 2;
public const float LandauRamanujanConstant = 0.7642236535f;
public const float LiebSquareIceConstant = 8 / (3 * Sqrt3);
public const float LiebSquareIceConstant = 8 * Sqrt3 / 9;
public const float LemniscateConstant = 2.6220575542f;
public const float LevyConstant1 = Pi * Pi / (12 * Ln2);
public const float LevyConstant2 = 3.2758229187f;
@ -141,8 +141,8 @@ public static class Constants
public const float FirstNielsenRamanujanConstant = Pi * Pi / 12;
public const float SecondDuBoisRaymondConstant = (E * E - 7) / 2;
public const float SecondFavardConstant = 1.2337005501f;
public const float SecondHermiteConstant = 2 / Sqrt3;
public const float UniversalHyperbolicConstant = 2.2955871493f;
public const float SecondHermiteConstant = 2 * Sqrt3 / 9;
public const float UniversalParabolicConstant = 2.2955871493f;
public const float DottieNumber = 0.7390851332f;
public const float FractalDimensionOfTheApollonianPackingOfCircles = 1.305688f;
@ -152,5 +152,5 @@ public static class Constants
public const float PlasticNumber = 1.3247179572f;
public const float LaplaceLimit = 0.6627434193f;
public const float LogarithmicCapacityOfTheUnitDisk = 0.5901702995f;
public const float RegularPaperfoldingSequence = 0.8507361882f;
public const float RegularPaperfoldingConstant = 0.8507361882f;
}

View File

@ -0,0 +1,4 @@
using System.Reflection;
// Includes assembly configuration that isn't automatically handled by the compiler.
// So far, there is none. There may be some in the future. We will see.

Some files were not shown because too many files have changed in this diff Show More