Merge pull request #12 from That-One-Nerd/v2.2
bruh cant believe i forgot this
This commit is contained in:
commit
494d3b2581
126
Changelog.md
126
Changelog.md
@ -1,14 +1,124 @@
|
||||
# Nerd_STF v2.1.2
|
||||
# Nerd_STF v2.2.0
|
||||
|
||||
This update just replaces instances of `double` with `float` instead.
|
||||
|
||||
I know, this isn't the update you wanted. More stuff coming soon.
|
||||
This update adds many types of graphics-based objects, as well as some math functions and constants.
|
||||
|
||||
```
|
||||
* Nerd_STF
|
||||
= Replace all instances of `double` with `float`
|
||||
+ delegate Fill2D<T>(int, int)
|
||||
* Exceptions
|
||||
+ FileParsingException
|
||||
+ FileType
|
||||
+ Graphics
|
||||
+ ColorChannel
|
||||
+ CMYKA
|
||||
+ CMYKAByte
|
||||
+ HSVA
|
||||
+ HSVAByte
|
||||
+ IColor
|
||||
+ IColorByte
|
||||
+ IlluminationFlags
|
||||
+ IlluminationModel
|
||||
+ Image
|
||||
+ Material
|
||||
+ RGBA
|
||||
+ RGBAByte
|
||||
* Mathematics
|
||||
= Renamed `Double2` to `Float2`
|
||||
= Renamed `Double3` to `Float3`
|
||||
= Renamed `Double4` to `Float4`
|
||||
* Angle
|
||||
+ Normalized
|
||||
= Made fancier `ToString()` formatting
|
||||
* Type
|
||||
+ Normalized
|
||||
+ Constants
|
||||
* Float2
|
||||
+ static SplitArray(params Float2[])
|
||||
= Renamed `static Multiply(params Float2[])` to `Product`
|
||||
* Float3
|
||||
+ static SplitArray(params Float3[])
|
||||
+ explicit operator Float3(RGBA)
|
||||
+ explicit operator Float3(HSVA)
|
||||
+ explicit operator Float3(RGBAByte)
|
||||
+ explicit operator Float3(HSVAByte)
|
||||
= Renamed `static Multiply(params Float3[])` to `Product`
|
||||
* Float4
|
||||
+ static SplitArray(params Float4[])
|
||||
+ implicit operator Float4(RGBA)
|
||||
+ explicit operator Float4(CMYKA)
|
||||
+ implicit operator Float4(HSVA)
|
||||
+ implicit operator Float4(RGBAByte)
|
||||
+ explicit operator Float4(CMYKAByte)
|
||||
+ implicit operator Float4(HSVAByte)
|
||||
= Renamed `static Multiply(params Float4[])` to `Product`
|
||||
* Geometry
|
||||
* Line
|
||||
+ Midpoint
|
||||
= Renamed `ToDoubleArray()` to `ToFloatArray`
|
||||
= Renamed `ToDoubleList()` to `ToFloatList`
|
||||
* Polygon
|
||||
+ Midpoint
|
||||
= Renamed `ToDoubleArray()` to `ToFloatArray`
|
||||
= Renamed `ToDoubleList()` to `ToFloatList`
|
||||
= Renamed `static ToDoubleArrayAll(params Triangle[])` to `ToFloatArrayAll`
|
||||
= Renamed `static ToDoubleListAll(params Triangle[])` to `ToFloatListAll`
|
||||
* Quadrilateral
|
||||
+ Midpoint
|
||||
= Renamed `ToDoubleArray()` to `ToFloatArray`
|
||||
= Renamed `ToDoubleList()` to `ToFloatList`
|
||||
= Renamed `static ToDoubleArrayAll(params Triangle[])` to `ToFloatArrayAll`
|
||||
= Renamed `static ToDoubleListAll(params Triangle[])` to `ToFloatListAll`
|
||||
* Triangle
|
||||
+ Midpoint
|
||||
= Renamed `ToDoubleArray()` to `ToFloatArray`
|
||||
= Renamed `ToDoubleList()` to `ToFloatList`
|
||||
= Renamed `static ToDoubleArrayAll(params Triangle[])` to `ToFloatArrayAll`
|
||||
= Renamed `static ToDoubleListAll(params Triangle[])` to `ToFloatListAll`
|
||||
* Vert
|
||||
= Renamed `static ToDouble3Array(params Vert[])` to `ToFloat3Array`
|
||||
= Renamed `static ToDouble3List(params Vert[])` to `ToFloat3List`
|
||||
* Int2
|
||||
+ static SplitArray(params Int[])
|
||||
= Renamed `static Multiply(params Int2[])` to `Product`
|
||||
* Int3
|
||||
+ static SplitArray(params Int3[])
|
||||
+ explicit operator Int3(RGBA)
|
||||
+ explicit operator Int3(HSVA)
|
||||
+ explicit operator Int3(RGBAByte)
|
||||
+ explicit operator Int3(HSVAByte)
|
||||
= Renamed `static Multiply(params Int3[])` to `Product`
|
||||
* Int4
|
||||
+ static SplitArray(params Int4[])
|
||||
+ explicit operator Int4(RGBA)
|
||||
+ explicit operator Int4(CMYKA)
|
||||
+ explicit operator Int4(HSVA)
|
||||
+ implicit operator Int4(RGBAByte)
|
||||
+ explicit operator Int4(CMYKAByte)
|
||||
+ implicit operator Int4(HSVAByte)
|
||||
= Renamed `static Multiply(params Int4[])` to `Product`
|
||||
* Mathf
|
||||
+ static Combinations(int, int)
|
||||
+ static GreatestCommonFactor(params int[])
|
||||
+ static InverseSqrt(float)
|
||||
+ static LeastCommonMultiple(params int[])
|
||||
+ static Mode<T>(params T[]) where T : IEquatable<T>
|
||||
+ static Permutations(int, int)
|
||||
+ static Pow(float, int)
|
||||
+ static Product(Equation, float, float, float)
|
||||
+ static Sum(Equation, float, float, float)
|
||||
+ static UniqueItems<T>(params T[]) where T : IEquatable<T>
|
||||
+ static ZScore(float, params float[])
|
||||
+ static ZScore(float, float, float)
|
||||
- const RadToDeg
|
||||
- const E
|
||||
- const GoldenRatio
|
||||
- const HalfPi
|
||||
- const Pi
|
||||
- const DegToRad
|
||||
- const Tau
|
||||
= GreatestCommonFactor actually works now
|
||||
= Pow has been fixed
|
||||
= Mode actually works
|
||||
* static Average(params int[])
|
||||
= Replaced its `int` return type with `float`
|
||||
* Miscellaneous
|
||||
* GlobalUsings.cs
|
||||
+ global using Nerd_STF.Graphics;
|
||||
```
|
||||
|
||||
13
Nerd_STF/FileType.cs
Normal file
13
Nerd_STF/FileType.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace Nerd_STF;
|
||||
|
||||
public enum FileType
|
||||
{
|
||||
None = 0,
|
||||
BMP,
|
||||
HEIC,
|
||||
JPEG,
|
||||
MTL,
|
||||
PNG,
|
||||
TIFF,
|
||||
WEBP,
|
||||
}
|
||||
3
Nerd_STF/Fill2D.cs
Normal file
3
Nerd_STF/Fill2D.cs
Normal file
@ -0,0 +1,3 @@
|
||||
namespace Nerd_STF;
|
||||
|
||||
public delegate T Fill2D<T>(int indexX, int indexY);
|
||||
250
Nerd_STF/Graphics/CMYKA.cs
Normal file
250
Nerd_STF/Graphics/CMYKA.cs
Normal file
@ -0,0 +1,250 @@
|
||||
namespace Nerd_STF.Graphics;
|
||||
|
||||
public struct CMYKA : IColor, IEquatable<CMYKA>
|
||||
{
|
||||
public static CMYKA Black => new(0, 0, 0, 1);
|
||||
public static CMYKA Blue => new(1, 1, 0, 0);
|
||||
public static CMYKA Clear => new(0, 0, 0, 0, 0);
|
||||
public static CMYKA Cyan => new(1, 0, 0, 0);
|
||||
public static CMYKA Gray => new(0, 0, 0, 0.5f);
|
||||
public static CMYKA Green => new(1, 0, 1, 0);
|
||||
public static CMYKA Magenta => new(0, 1, 0, 0);
|
||||
public static CMYKA Orange => new(0, 0.5f, 1, 0);
|
||||
public static CMYKA Purple => new(0.5f, 1, 0, 0);
|
||||
public static CMYKA Red => new(0, 1, 1, 0);
|
||||
public static CMYKA White => new(0, 0, 0, 0);
|
||||
public static CMYKA Yellow => new(0, 0, 1, 0);
|
||||
|
||||
public float C
|
||||
{
|
||||
get => p_c;
|
||||
set => p_c = Mathf.Clamp(value, 0, 1);
|
||||
}
|
||||
public float M
|
||||
{
|
||||
get => p_m;
|
||||
set => p_m = Mathf.Clamp(value, 0, 1);
|
||||
}
|
||||
public float Y
|
||||
{
|
||||
get => p_y;
|
||||
set => p_y = Mathf.Clamp(value, 0, 1);
|
||||
}
|
||||
public float K
|
||||
{
|
||||
get => p_k;
|
||||
set => p_k = Mathf.Clamp(value, 0, 1);
|
||||
}
|
||||
public float A
|
||||
{
|
||||
get => p_a;
|
||||
set => p_a = Mathf.Clamp(value, 0, 1);
|
||||
}
|
||||
|
||||
public bool HasCyan => p_c > 0;
|
||||
public bool HasMagenta => p_m > 0;
|
||||
public bool HasYellow => p_y > 0;
|
||||
public bool HasBlack => p_k > 0;
|
||||
public bool IsOpaque => p_a == 1;
|
||||
public bool IsVisible => p_a != 0;
|
||||
|
||||
private float p_c, p_m, p_y, p_k, p_a;
|
||||
|
||||
public CMYKA() : this(0, 0, 0, 0, 1) { }
|
||||
public CMYKA(float all) : this(all, all, all, all, all) { }
|
||||
public CMYKA(float all, float a) : this(all, all, all, all, a) { }
|
||||
public CMYKA(float c, float m, float y, float k) : this(c, m, y, k, 1) { }
|
||||
public CMYKA(float c, float m, float y, float k, float a)
|
||||
{
|
||||
p_c = Mathf.Clamp(c, 0, 1);
|
||||
p_m = Mathf.Clamp(m, 0, 1);
|
||||
p_y = Mathf.Clamp(y, 0, 1);
|
||||
p_k = Mathf.Clamp(k, 0, 1);
|
||||
p_a = Mathf.Clamp(a, 0, 1);
|
||||
}
|
||||
public CMYKA(Fill<float> fill) : this(fill(0), fill(1), fill(2), fill(3), fill(4)) { }
|
||||
|
||||
public float this[int index]
|
||||
{
|
||||
get => index switch
|
||||
{
|
||||
0 => C,
|
||||
1 => M,
|
||||
2 => Y,
|
||||
3 => K,
|
||||
4 => A,
|
||||
_ => throw new IndexOutOfRangeException(nameof(index)),
|
||||
};
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
C = value;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
M = value;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
Y = value;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
K = value;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
A = value;
|
||||
break;
|
||||
|
||||
default: throw new IndexOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static CMYKA Average(params CMYKA[] vals)
|
||||
{
|
||||
CMYKA val = new(0, 0, 0, 0, 0);
|
||||
for (int i = 0; i < vals.Length; i++) val += vals[i];
|
||||
|
||||
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));
|
||||
public static CMYKA LerpSquared(CMYKA a, CMYKA b, float t, bool clamp = true)
|
||||
{
|
||||
CMYKA val = Lerp(a * a, b * b, t, clamp);
|
||||
float C = Mathf.Sqrt(val.C), M = Mathf.Sqrt(val.M), Y = Mathf.Sqrt(val.Y), K = Mathf.Sqrt(val.K), A = Mathf.Sqrt(val.A);
|
||||
return new(C, M, Y, K, A);
|
||||
}
|
||||
public static CMYKA Median(params CMYKA[] vals)
|
||||
{
|
||||
float index = Mathf.Average(0, vals.Length - 1);
|
||||
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 (float[] Cs, float[] Ms, float[] Ys, float[] Ks, float[] As) SplitArray(params CMYKA[] vals)
|
||||
{
|
||||
float[] Cs = new float[vals.Length], Ms = new float[vals.Length],
|
||||
Ys = new float[vals.Length], Ks = new float[vals.Length],
|
||||
As = new float[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;
|
||||
}
|
||||
return (Cs, Ms, Ys, Ks, As);
|
||||
}
|
||||
|
||||
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 false;
|
||||
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 false;
|
||||
}
|
||||
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 RGBA ToRGBA()
|
||||
{
|
||||
float kInv = 1 - K, r = 1 - C, g = 1 - M, b = 1 - Y;
|
||||
return new(r * kInv, g * kInv, b * kInv);
|
||||
}
|
||||
public CMYKA ToCMYKA() => this;
|
||||
public HSVA ToHSVA() => ToRGBA().ToHSVA();
|
||||
|
||||
public RGBAByte ToRGBAByte() => ToRGBA().ToRGBAByte();
|
||||
public CMYKAByte ToCMYKAByte() => new(Mathf.RoundInt(C * 255), Mathf.RoundInt(M * 255), Mathf.RoundInt(Y * 255),
|
||||
Mathf.RoundInt(K * 255), Mathf.RoundInt(A * 255));
|
||||
public HSVAByte ToHSVAByte() => ToRGBA().ToHSVAByte();
|
||||
|
||||
public float[] ToArray() => new[] { C, M, Y, K, A };
|
||||
public List<float> ToList() => new() { C, M, Y, K, A };
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
public IEnumerator<float> GetEnumerator()
|
||||
{
|
||||
yield return C;
|
||||
yield return M;
|
||||
yield return Y;
|
||||
yield return K;
|
||||
yield return A;
|
||||
}
|
||||
|
||||
public object Clone() => new CMYKA(C, M, Y, K, A);
|
||||
|
||||
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);
|
||||
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, 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 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 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(CMYKAByte val) => val.ToCMYKA();
|
||||
public static implicit operator CMYKA(HSVAByte val) => val.ToCMYKA();
|
||||
public static implicit operator CMYKA(Fill<float> val) => new(val);
|
||||
}
|
||||
236
Nerd_STF/Graphics/CMYKAByte.cs
Normal file
236
Nerd_STF/Graphics/CMYKAByte.cs
Normal file
@ -0,0 +1,236 @@
|
||||
namespace Nerd_STF.Graphics;
|
||||
|
||||
public struct CMYKAByte : IColorByte, IEquatable<CMYKAByte>
|
||||
{
|
||||
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 byte C, M, Y, K, A;
|
||||
|
||||
public bool HasCyan => C > 0;
|
||||
public bool HasMagenta => M > 0;
|
||||
public bool HasYellow => Y > 0;
|
||||
public bool HasBlack => K > 0;
|
||||
public bool IsOpaque => A == 255;
|
||||
public bool IsVisible => A != 0;
|
||||
|
||||
public CMYKAByte() : this(0, 0, 0, 0, 255) { }
|
||||
public CMYKAByte(int all) : this(all, all, all, all, all) { }
|
||||
public CMYKAByte(int all, int a) : this(all, all, all, all, a) { }
|
||||
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);
|
||||
}
|
||||
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]
|
||||
{
|
||||
get => index switch
|
||||
{
|
||||
0 => C,
|
||||
1 => M,
|
||||
2 => Y,
|
||||
3 => K,
|
||||
4 => A,
|
||||
_ => throw new IndexOutOfRangeException(nameof(index)),
|
||||
};
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
C = value;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
M = value;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
Y = value;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
K = value;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
A = value;
|
||||
break;
|
||||
|
||||
default: throw new IndexOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static CMYKAByte Average(params CMYKAByte[] vals)
|
||||
{
|
||||
CMYKAByte val = new(0, 0, 0, 0, 0);
|
||||
for (int i = 0; i < vals.Length; i++) val += vals[i];
|
||||
return val / vals.Length;
|
||||
}
|
||||
public static CMYKAByte Clamp(CMYKAByte val, CMYKAByte min, CMYKAByte 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 CMYKAByte Lerp(CMYKAByte a, CMYKAByte 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));
|
||||
public static CMYKAByte LerpSquared(CMYKAByte a, CMYKAByte b, float t, bool clamp = true) => CMYKA.LerpSquared(a.ToCMYKA(), b.ToCMYKA(), t, clamp).ToCMYKAByte();
|
||||
public static CMYKAByte Median(params CMYKAByte[] vals)
|
||||
{
|
||||
float index = Mathf.Average(0, vals.Length - 1);
|
||||
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)
|
||||
{
|
||||
byte[] Cs = new byte[vals.Length], Ms = new byte[vals.Length],
|
||||
Ys = new byte[vals.Length], Ks = new byte[vals.Length],
|
||||
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;
|
||||
}
|
||||
return (Cs, Ms, Ys, Ks, As);
|
||||
}
|
||||
public static (int[] Cs, int[] Ms, int[] Ys, int[] Ks, int[] As) SplitArrayInt(params CMYKAByte[] vals)
|
||||
{
|
||||
int[] Cs = new int[vals.Length], Ms = new int[vals.Length],
|
||||
Ys = new int[vals.Length], Ks = new int[vals.Length],
|
||||
As = new int[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;
|
||||
}
|
||||
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 false;
|
||||
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 false;
|
||||
}
|
||||
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 RGBA ToRGBA() => ToCMYKA().ToRGBA();
|
||||
public CMYKA ToCMYKA() => new(C / 255f, M / 255f, Y / 255f, K / 255f, A / 255f);
|
||||
public HSVA ToHSVA() => ToRGBA().ToHSVA();
|
||||
|
||||
public RGBAByte ToRGBAByte() => ToCMYKA().ToRGBAByte();
|
||||
public CMYKAByte ToCMYKAByte() => this;
|
||||
public HSVAByte ToHSVAByte() => ToRGBA().ToHSVAByte();
|
||||
|
||||
public byte[] ToArray() => new[] { C, M, Y, K, A };
|
||||
public List<byte> ToList() => 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;
|
||||
}
|
||||
|
||||
public object Clone() => new CMYKAByte(C, M, Y, K, A);
|
||||
|
||||
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);
|
||||
public static CMYKAByte operator -(CMYKAByte c) =>
|
||||
new(255 - c.C, 255 - c.M, 255 - c.Y, 255 - c.K, c.A != 255 ? 255 - c.A : 255);
|
||||
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);
|
||||
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);
|
||||
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 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);
|
||||
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, HSVAByte b) => a.Equals(b);
|
||||
public static bool operator !=(CMYKAByte a, HSVAByte 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(CMYKA val) => val.ToCMYKAByte();
|
||||
public static implicit operator CMYKAByte(HSVAByte val) => val.ToCMYKAByte();
|
||||
public static implicit operator CMYKAByte(Fill<byte> val) => new(val);
|
||||
public static implicit operator CMYKAByte(Fill<int> val) => new(val);
|
||||
}
|
||||
19
Nerd_STF/Graphics/ColorChannel.cs
Normal file
19
Nerd_STF/Graphics/ColorChannel.cs
Normal file
@ -0,0 +1,19 @@
|
||||
namespace Nerd_STF.Graphics;
|
||||
|
||||
public enum ColorChannel : byte
|
||||
{
|
||||
Alpha,
|
||||
Black,
|
||||
Blue,
|
||||
Cyan,
|
||||
Green,
|
||||
Hue,
|
||||
Luminance,
|
||||
Magenta,
|
||||
Matte,
|
||||
Red,
|
||||
Saturation,
|
||||
Yellow,
|
||||
Value,
|
||||
ZDepth
|
||||
}
|
||||
256
Nerd_STF/Graphics/HSVA.cs
Normal file
256
Nerd_STF/Graphics/HSVA.cs
Normal file
@ -0,0 +1,256 @@
|
||||
namespace Nerd_STF.Graphics;
|
||||
|
||||
public struct HSVA : IColor, IEquatable<HSVA>
|
||||
{
|
||||
public static HSVA Black => new(Angle.Zero, 0, 0);
|
||||
public static HSVA Blue => new(new Angle(240), 1, 1);
|
||||
public static HSVA Clear => new(Angle.Zero, 0, 0, 0);
|
||||
public static HSVA Cyan => new(new Angle(180), 1, 1);
|
||||
public static HSVA Gray => new(Angle.Zero, 0, 0.5f);
|
||||
public static HSVA Green => new(new Angle(120), 1, 1);
|
||||
public static HSVA Magenta => new(new Angle(300), 1, 1);
|
||||
public static HSVA Orange => new(new Angle(30), 1, 1);
|
||||
public static HSVA Purple => new(new Angle(270), 1, 1);
|
||||
public static HSVA Red => new(Angle.Zero, 1, 1);
|
||||
public static HSVA White => new(Angle.Zero, 0, 1);
|
||||
public static HSVA Yellow => new(new Angle(60), 1, 1);
|
||||
|
||||
public Angle H
|
||||
{
|
||||
get => p_h;
|
||||
set => p_h = value.Bounded;
|
||||
}
|
||||
public float S
|
||||
{
|
||||
get => p_s;
|
||||
set => p_s = Mathf.Clamp(value, 0, 1);
|
||||
}
|
||||
public float V
|
||||
{
|
||||
get => p_v;
|
||||
set => p_v = Mathf.Clamp(value, 0, 1);
|
||||
}
|
||||
public float A
|
||||
{
|
||||
get => p_a;
|
||||
set => p_a = Mathf.Clamp(value, 0, 1);
|
||||
}
|
||||
|
||||
private Angle p_h;
|
||||
private float p_s, p_v, p_a;
|
||||
|
||||
public bool HasColor => p_s != 0 && p_v != 0;
|
||||
public bool IsOpaque => p_a == 1;
|
||||
public bool IsVisible => p_a != 0;
|
||||
|
||||
public HSVA() : this(Angle.Zero, 0, 0, 1) { }
|
||||
public HSVA(Angle h, float s, float v) : this(h, s, v, 1) { }
|
||||
public HSVA(Angle h, float s, float v, float a)
|
||||
{
|
||||
p_h = h.Bounded;
|
||||
p_s = Mathf.Clamp(s, 0, 1);
|
||||
p_v = Mathf.Clamp(v, 0, 1);
|
||||
p_a = Mathf.Clamp(a, 0, 1);
|
||||
}
|
||||
public HSVA(Angle h, Fill<float> fill) : this(h, fill(0), fill(1), fill(2)) { }
|
||||
public HSVA(Angle h, Fill<int> fill) : this(h, fill(0), fill(1), fill(2)) { }
|
||||
public HSVA(float h, float s, float v) : this(new Angle(h, Angle.Type.Normalized), s, v) { }
|
||||
public HSVA(float h, float s, float v, float a) : this(new Angle(h, Angle.Type.Normalized), s, v, a) { }
|
||||
public HSVA(Fill<float> fill) : this(fill(0), fill(1), fill(2), fill(3)) { }
|
||||
public HSVA(Fill<int> fill) : this(fill(0), fill(1), fill(2), fill(3)) { }
|
||||
|
||||
public float this[int index]
|
||||
{
|
||||
get => index switch
|
||||
{
|
||||
0 => H.Normalized,
|
||||
1 => S,
|
||||
2 => V,
|
||||
3 => A,
|
||||
_ => throw new IndexOutOfRangeException(nameof(index)),
|
||||
};
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
H = new(Mathf.Clamp(value, 0, 1), Angle.Type.Normalized);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
S = Mathf.Clamp(value, 0, 1);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
V = Mathf.Clamp(value, 0, 1);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
A = Mathf.Clamp(value, 0, 1);
|
||||
break;
|
||||
|
||||
default: throw new IndexOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static HSVA Average(params HSVA[] vals)
|
||||
{
|
||||
HSVA val = new(Angle.Zero, 0, 0, 0);
|
||||
for (int i = 0; i < vals.Length; i++) val += vals[i];
|
||||
return val / vals.Length;
|
||||
}
|
||||
public static HSVA Ceiling(HSVA val, Angle.Type type) => new(Angle.Ceiling(val.H, type), Mathf.Ceiling(val.S),
|
||||
Mathf.Ceiling(val.V), Mathf.Ceiling(val.A));
|
||||
public static HSVA Clamp(HSVA val, HSVA min, HSVA max) =>
|
||||
new(Angle.Clamp(val.H, min.H, max.H),
|
||||
Mathf.Clamp(val.S, min.S, max.S),
|
||||
Mathf.Clamp(val.V, min.V, max.V),
|
||||
Mathf.Clamp(val.A, min.A, max.A));
|
||||
public static HSVA Floor(HSVA val, Angle.Type type) => new(Angle.Floor(val.H, type), Mathf.Floor(val.S),
|
||||
Mathf.Floor(val.V), Mathf.Floor(val.A));
|
||||
public static HSVA 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));
|
||||
public static HSVA LerpSquared(HSVA a, HSVA b, float t, Angle.Type type = Angle.Type.Normalized,
|
||||
bool clamp = true)
|
||||
{
|
||||
HSVA val = Lerp(a * a, b * b, t, clamp);
|
||||
float H = Mathf.Sqrt(val.H.ValueFromType(type)), S = Mathf.Sqrt(val.S), V = Mathf.Sqrt(val.V),
|
||||
A = Mathf.Sqrt(val.A);
|
||||
return new(new Angle(H, Angle.Type.Normalized), S, V, A);
|
||||
}
|
||||
public static HSVA Median(params HSVA[] vals)
|
||||
{
|
||||
float index = Mathf.Average(0, vals.Length - 1);
|
||||
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 (Angle[] Hs, float[] Ss, float[] Vs, float[] As) SplitArray(params HSVA[] vals)
|
||||
{
|
||||
Angle[] Hs = new Angle[vals.Length];
|
||||
float[] Ss = new float[vals.Length], Vs = new float[vals.Length],
|
||||
As = new float[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;
|
||||
}
|
||||
return (Hs, Ss, Vs, As);
|
||||
}
|
||||
public static (float[] Hs, float[] Ss, float[] Vs, float[] As) SplitArrayNormalized(params HSVA[] vals)
|
||||
{
|
||||
float[] Hs = new float[vals.Length], Ss = new float[vals.Length],
|
||||
Vs = new float[vals.Length], As = new float[vals.Length];
|
||||
for (int i = 0; i < vals.Length; i++)
|
||||
{
|
||||
Hs[i] = vals[i].H.Normalized;
|
||||
Ss[i] = vals[i].S;
|
||||
Vs[i] = vals[i].V;
|
||||
As[i] = vals[i].A;
|
||||
}
|
||||
return (Hs, Ss, Vs, As);
|
||||
}
|
||||
|
||||
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 false;
|
||||
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 false;
|
||||
}
|
||||
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 RGBA ToRGBA()
|
||||
{
|
||||
float d = H.Degrees, c = V * S, x = c * (1 - Mathf.Absolute(d / 60 % 2 - 1)), m = V - c;
|
||||
(float r, float g, float b) vals = (0, 0, 0);
|
||||
if (d < 60) vals = (c, x, 0);
|
||||
else if (d < 120) vals = (x, c, 0);
|
||||
else if (d < 180) vals = (0, c, x);
|
||||
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);
|
||||
}
|
||||
public CMYKA ToCMYKA() => ToRGBA().ToCMYKA();
|
||||
public HSVA ToHSVA() => this;
|
||||
|
||||
public RGBAByte ToRGBAByte() => ToRGBA().ToRGBAByte();
|
||||
public CMYKAByte ToCMYKAByte() => ToRGBA().ToCMYKAByte();
|
||||
public HSVAByte ToHSVAByte() => new(Mathf.RoundInt(H.Normalized * 255), Mathf.RoundInt(S * 255),
|
||||
Mathf.RoundInt(V * 255), Mathf.RoundInt(A * 255));
|
||||
|
||||
public float[] ToArray() => new[] { H.Normalized, S, V, A };
|
||||
public List<float> ToList() => new() { H.Normalized, S, V, A };
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
public IEnumerator<float> GetEnumerator()
|
||||
{
|
||||
yield return H.Normalized;
|
||||
yield return S;
|
||||
yield return V;
|
||||
yield return A;
|
||||
}
|
||||
|
||||
public object Clone() => new HSVA(H, S, V, A);
|
||||
|
||||
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, 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, 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 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 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(Fill<float> val) => new(val);
|
||||
}
|
||||
214
Nerd_STF/Graphics/HSVAByte.cs
Normal file
214
Nerd_STF/Graphics/HSVAByte.cs
Normal file
@ -0,0 +1,214 @@
|
||||
namespace Nerd_STF.Graphics;
|
||||
|
||||
public struct HSVAByte : IColorByte, IEquatable<HSVAByte>
|
||||
{
|
||||
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 byte H, S, V, A;
|
||||
|
||||
public bool HasColor => S != 0 && V != 0;
|
||||
public bool IsOpaque => A == 255;
|
||||
public bool IsVisible => A == 0;
|
||||
|
||||
public HSVAByte() : this(0, 0, 0, 255) { }
|
||||
public HSVAByte(int all) : this(all, all, all, all) { }
|
||||
public HSVAByte(int all, int a) : this(all, all, all, a) { }
|
||||
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);
|
||||
}
|
||||
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]
|
||||
{
|
||||
get => index switch
|
||||
{
|
||||
0 => H,
|
||||
1 => S,
|
||||
2 => V,
|
||||
3 => A,
|
||||
_ => throw new IndexOutOfRangeException(nameof(index)),
|
||||
};
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
H = value;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
S = value;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
V = value;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
A = value;
|
||||
break;
|
||||
|
||||
default: throw new IndexOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static HSVAByte Average(params HSVAByte[] vals)
|
||||
{
|
||||
HSVAByte val = new(0, 0, 0, 0);
|
||||
for (int i = 0; i < vals.Length; i++) val += vals[i];
|
||||
return val / vals.Length;
|
||||
}
|
||||
public static HSVAByte Clamp(HSVAByte val, HSVAByte min, HSVAByte max) =>
|
||||
new(Mathf.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 HSVAByte Lerp(HSVAByte a, HSVAByte b, byte 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 LerpSquared(HSVAByte a, HSVAByte b, byte t, Angle.Type type, bool clamp = true) =>
|
||||
HSVA.LerpSquared(a.ToHSVA(), b.ToHSVA(), t, type, clamp).ToHSVAByte();
|
||||
public static HSVAByte Median(params HSVAByte[] vals)
|
||||
{
|
||||
float index = Mathf.Average(0, vals.Length - 1);
|
||||
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)
|
||||
{
|
||||
byte[] Hs = new byte[vals.Length], Ss = new byte[vals.Length],
|
||||
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;
|
||||
}
|
||||
return (Hs, Ss, Vs, As);
|
||||
}
|
||||
public static (int[] Hs, int[] Ss, int[] Vs, int[] As) SplitArrayInt(params HSVAByte[] vals)
|
||||
{
|
||||
int[] Hs = new int[vals.Length], Ss = new int[vals.Length],
|
||||
Vs = new int[vals.Length], As = new int[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;
|
||||
}
|
||||
return (Hs, Ss, Vs, As);
|
||||
}
|
||||
|
||||
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 false;
|
||||
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 false;
|
||||
}
|
||||
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 RGBA ToRGBA() => ToHSVA().ToRGBA();
|
||||
public CMYKA ToCMYKA() => ToHSVA().ToCMYKA();
|
||||
public HSVA ToHSVA() => new(H / 255f, S / 255f, V / 255f, A / 255f);
|
||||
|
||||
public RGBAByte ToRGBAByte() => ToHSVA().ToRGBAByte();
|
||||
public CMYKAByte ToCMYKAByte() => ToHSVA().ToCMYKAByte();
|
||||
public HSVAByte ToHSVAByte() => this;
|
||||
|
||||
public byte[] ToArray() => new[] { H, S, V, A };
|
||||
public List<byte> ToList() => 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;
|
||||
}
|
||||
|
||||
public object Clone() => new HSVAByte(H, S, V, A);
|
||||
|
||||
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);
|
||||
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, 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 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, 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 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);
|
||||
public static implicit operator HSVAByte(CMYKA val) => val.ToHSVAByte();
|
||||
public static implicit operator HSVAByte(HSVA val) => val.ToHSVAByte();
|
||||
public static implicit operator HSVAByte(RGBA val) => val.ToHSVAByte();
|
||||
public static implicit operator HSVAByte(CMYKAByte val) => val.ToHSVAByte();
|
||||
public static implicit operator HSVAByte(RGBAByte val) => val.ToHSVAByte();
|
||||
public static implicit operator HSVAByte(Fill<byte> val) => new(val);
|
||||
public static implicit operator HSVAByte(Fill<int> val) => new(val);
|
||||
}
|
||||
12
Nerd_STF/Graphics/IColor.cs
Normal file
12
Nerd_STF/Graphics/IColor.cs
Normal file
@ -0,0 +1,12 @@
|
||||
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();
|
||||
}
|
||||
12
Nerd_STF/Graphics/IColorByte.cs
Normal file
12
Nerd_STF/Graphics/IColorByte.cs
Normal file
@ -0,0 +1,12 @@
|
||||
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();
|
||||
}
|
||||
14
Nerd_STF/Graphics/IlluminationFlags.cs
Normal file
14
Nerd_STF/Graphics/IlluminationFlags.cs
Normal file
@ -0,0 +1,14 @@
|
||||
namespace Nerd_STF.Graphics;
|
||||
|
||||
[Flags]
|
||||
public enum IlluminationFlags : byte
|
||||
{
|
||||
Color = 1,
|
||||
Ambient = 2,
|
||||
Highlight = 4,
|
||||
Reflection = 8,
|
||||
Raytrace = 16,
|
||||
Fresnel = 32,
|
||||
Refraction = 64,
|
||||
Glass = 128,
|
||||
}
|
||||
16
Nerd_STF/Graphics/IlluminationModel.cs
Normal file
16
Nerd_STF/Graphics/IlluminationModel.cs
Normal file
@ -0,0 +1,16 @@
|
||||
namespace Nerd_STF.Graphics;
|
||||
|
||||
public enum IlluminationModel : ushort
|
||||
{
|
||||
Mode0 = IlluminationFlags.Color,
|
||||
Mode1 = IlluminationFlags.Color | IlluminationFlags.Ambient,
|
||||
Mode2 = IlluminationFlags.Highlight,
|
||||
Mode3 = IlluminationFlags.Reflection | IlluminationFlags.Raytrace,
|
||||
Mode4 = IlluminationFlags.Glass | IlluminationFlags.Raytrace,
|
||||
Mode5 = IlluminationFlags.Fresnel | IlluminationFlags.Raytrace,
|
||||
Mode6 = IlluminationFlags.Refraction | IlluminationFlags.Raytrace,
|
||||
Mode7 = IlluminationFlags.Refraction | IlluminationFlags.Fresnel | IlluminationFlags.Raytrace,
|
||||
Mode8 = IlluminationFlags.Reflection,
|
||||
Mode9 = IlluminationFlags.Glass,
|
||||
Mode10 = 256,
|
||||
}
|
||||
162
Nerd_STF/Graphics/Image.cs
Normal file
162
Nerd_STF/Graphics/Image.cs
Normal file
@ -0,0 +1,162 @@
|
||||
namespace Nerd_STF.Graphics;
|
||||
|
||||
public struct Image : ICloneable, IEnumerable, IEquatable<Image>
|
||||
{
|
||||
public IColor[,] Pixels { get; init; }
|
||||
public Int2 Size { get; init; }
|
||||
|
||||
public Image(int width, int height)
|
||||
{
|
||||
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] = RGBA.Clear;
|
||||
}
|
||||
public Image(int width, int height, IColor[] cols)
|
||||
{
|
||||
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];
|
||||
}
|
||||
public Image(int width, int height, IColor[,] cols)
|
||||
{
|
||||
Pixels = cols;
|
||||
Size = new(width, height);
|
||||
}
|
||||
public Image(int width, int height, Fill<IColor> 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] = fill(y * width + x);
|
||||
}
|
||||
public Image(int width, int height, Fill2D<IColor> 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] = 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);
|
||||
}
|
||||
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]
|
||||
{
|
||||
get => Pixels[indexX, indexY];
|
||||
set => Pixels[indexX, indexY] = value;
|
||||
}
|
||||
public IColor this[Int2 index]
|
||||
{
|
||||
get => Pixels[index.x, index.y];
|
||||
set => Pixels[index.x, index.y] = value;
|
||||
}
|
||||
|
||||
public static Image FromSingleColor(int width, int height, IColor col) => new(width, height, (x, y) => col);
|
||||
public static Image FromSingleColor(Int2 size, IColor col) => new(size, (x, y) => col);
|
||||
public static Image FromSingleColor(IColor col) => new(1, 1, (x, y) => col);
|
||||
|
||||
public object Clone() => new Image(Size, Pixels);
|
||||
|
||||
public bool Equals(Image other) => Pixels == other.Pixels;
|
||||
public override bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
if (obj == null) return false;
|
||||
if (obj.GetType() == typeof(Image)) return Equals((Image)obj);
|
||||
return false;
|
||||
}
|
||||
public override int GetHashCode() => Pixels.GetHashCode();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
public IEnumerator<IColor> GetEnumerator()
|
||||
{
|
||||
for (int y = 0; y < Size.y; y++) for (int x = 0; x < Size.x; x++) yield return Pixels[x, y];
|
||||
}
|
||||
|
||||
public void ModifyBrightness(float value, bool set = false)
|
||||
{
|
||||
for (int y = 0; y < Size.y; y++) for (int x = 0; x < Size.x; x++)
|
||||
{
|
||||
RGBA col = Pixels[x, y].ToRGBA();
|
||||
col.R = (set ? 0 : col.R) + value;
|
||||
col.G = (set ? 0 : col.G) + value;
|
||||
col.B = (set ? 0 : col.B) + value;
|
||||
Pixels[x, y] = col;
|
||||
}
|
||||
}
|
||||
public void ModifyContrast(float value)
|
||||
{
|
||||
float factor = 259 * (255 + value) / (255 * (259 - value));
|
||||
for (int y = 0; y < Size.y; y++) for (int x = 0; x < Size.x; x++)
|
||||
{
|
||||
RGBA col = Pixels[x, y].ToRGBA();
|
||||
col.R = factor * (col.R - 0.5f) + 0.5f;
|
||||
col.G = factor * (col.G - 0.5f) + 0.5f;
|
||||
col.B = factor * (col.B - 0.5f) + 0.5f;
|
||||
Pixels[x, y] = col;
|
||||
}
|
||||
}
|
||||
public void ModifyHue(Angle value, bool set = false)
|
||||
{
|
||||
for (int y = 0; y < Size.y; y++) for (int x = 0; x < Size.x; x++)
|
||||
{
|
||||
HSVA col = Pixels[x, y].ToHSVA();
|
||||
col.H = (set ? Angle.Zero : col.H) + value;
|
||||
Pixels[x, y] = col;
|
||||
}
|
||||
}
|
||||
public void ModifySaturation(float value, bool set = false)
|
||||
{
|
||||
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;
|
||||
Pixels[x, y] = col;
|
||||
}
|
||||
}
|
||||
|
||||
public void Paint(Int2 min, Int2 max, IColor col)
|
||||
{
|
||||
for (int y = min.y; y <= max.y; y++) for (int x = min.x; x <= max.x; x++) Pixels[x, y] = col;
|
||||
}
|
||||
|
||||
public void Resize(Int2 resolution) => Scale(resolution / (Float2)Size);
|
||||
|
||||
public void Scale(float factor) => Scale(Float2.One * factor);
|
||||
public void Scale(Float2 factor)
|
||||
{
|
||||
Int2 newSize = (Int2)(Size * factor);
|
||||
Image img = new(newSize);
|
||||
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();
|
||||
img[x, y] = col;
|
||||
}
|
||||
this = img;
|
||||
}
|
||||
|
||||
public IColor[] ToArray()
|
||||
{
|
||||
IColor[] vals = new IColor[Pixels.Length];
|
||||
for (int y = 0; y < Size.y; y++) for (int x = 0; x < Size.x; x++) vals[y * Size.x + x] = Pixels[x, y];
|
||||
return vals;
|
||||
}
|
||||
|
||||
public static bool operator ==(Image a, Image b) => a.Equals(b);
|
||||
public static bool operator !=(Image a, Image b) => !a.Equals(b);
|
||||
}
|
||||
190
Nerd_STF/Graphics/Material.cs
Normal file
190
Nerd_STF/Graphics/Material.cs
Normal file
@ -0,0 +1,190 @@
|
||||
namespace Nerd_STF.Graphics;
|
||||
|
||||
public struct Material : ICloneable, IEquatable<Material>
|
||||
{
|
||||
public float Alpha;
|
||||
public float Anisotropy;
|
||||
public float AnisotropyRoughness;
|
||||
public IColor AmbientColor;
|
||||
public float ClearcoatRoughness;
|
||||
public float ClearcoatThickness;
|
||||
public IColor DiffuseColor;
|
||||
public IColor Emissive;
|
||||
public IlluminationModel IllumModel;
|
||||
public float Metallic;
|
||||
public float OpticalDensity;
|
||||
public float Roughness;
|
||||
public float Sheen;
|
||||
public IColor SpecularColor;
|
||||
public float SpecularExponent;
|
||||
|
||||
public (Image Image, TextureConfig Config) AlphaTexture;
|
||||
public (Image Image, TextureConfig Config) AmbientTexture;
|
||||
public (Image Image, TextureConfig Config) DiffuseTexture;
|
||||
public (Image Image, TextureConfig Config) DisplacementTexture;
|
||||
public (Image Image, TextureConfig Config) EmissiveTexture;
|
||||
public (Image Image, TextureConfig Config) MetallicTexture;
|
||||
public (Image Image, TextureConfig Config) NormalTexture;
|
||||
public (Image Image, TextureConfig Config) RoughnessTexture;
|
||||
public (Image Image, TextureConfig Config) SheenTexture;
|
||||
public (Image Image, TextureConfig Config) SpecularTexture;
|
||||
public (Image Image, TextureConfig Config) SpecularHighlightTexture;
|
||||
public (Image Image, TextureConfig Config) StencilTexture;
|
||||
|
||||
public Material()
|
||||
{
|
||||
Alpha = 1;
|
||||
Anisotropy = 0;
|
||||
AnisotropyRoughness = 0;
|
||||
AmbientColor = RGBA.White;
|
||||
ClearcoatRoughness = 0;
|
||||
ClearcoatThickness = 0;
|
||||
DiffuseColor = RGBA.White;
|
||||
Emissive = RGBA.Clear;
|
||||
IllumModel = IlluminationModel.Mode0;
|
||||
Metallic = 0;
|
||||
OpticalDensity = 1.45f;
|
||||
Roughness = 1;
|
||||
Sheen = 0;
|
||||
SpecularColor = RGBA.White;
|
||||
SpecularExponent = 10;
|
||||
|
||||
AlphaTexture = (Image.FromSingleColor(RGBA.White), new());
|
||||
AmbientTexture = (Image.FromSingleColor(RGBA.White), new());
|
||||
DiffuseTexture = (Image.FromSingleColor(RGBA.White), new());
|
||||
DisplacementTexture = (Image.FromSingleColor(RGBA.White), new());
|
||||
EmissiveTexture = (Image.FromSingleColor(RGBA.Clear), new());
|
||||
MetallicTexture = (Image.FromSingleColor(RGBA.Black), new());
|
||||
NormalTexture = (Image.FromSingleColor(new RGBA(0.5f, 0.5f, 1)), new());
|
||||
RoughnessTexture = (Image.FromSingleColor(RGBA.White), new());
|
||||
SheenTexture = (Image.FromSingleColor(RGBA.Black), new());
|
||||
SpecularTexture = (Image.FromSingleColor(RGBA.White), new());
|
||||
SpecularHighlightTexture = (Image.FromSingleColor(RGBA.White), new());
|
||||
StencilTexture = (Image.FromSingleColor(RGBA.White), new());
|
||||
}
|
||||
public Material(Fill<object> fill)
|
||||
{
|
||||
Alpha = (float)fill(0);
|
||||
Anisotropy = (float)fill(1);
|
||||
AnisotropyRoughness = (float)fill(2);
|
||||
AmbientColor = (IColor)fill(3);
|
||||
ClearcoatRoughness = (float)fill(4);
|
||||
ClearcoatThickness = (float)fill(5);
|
||||
DiffuseColor = (IColor)fill(6);
|
||||
Emissive = (IColor)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);
|
||||
SpecularExponent = (float)fill(14);
|
||||
|
||||
AlphaTexture = ((Image, TextureConfig))fill(15);
|
||||
AmbientTexture = ((Image, TextureConfig))fill(16);
|
||||
DiffuseTexture = ((Image, TextureConfig))fill(17);
|
||||
DisplacementTexture = ((Image, TextureConfig))fill(18);
|
||||
EmissiveTexture = ((Image, TextureConfig))fill(19);
|
||||
MetallicTexture = ((Image, TextureConfig))fill(20);
|
||||
NormalTexture = ((Image, TextureConfig))fill(21);
|
||||
RoughnessTexture = ((Image, TextureConfig))fill(22);
|
||||
SheenTexture = ((Image, TextureConfig))fill(23);
|
||||
SpecularTexture = ((Image, TextureConfig))fill(24);
|
||||
SpecularHighlightTexture = ((Image, TextureConfig))fill(25);
|
||||
StencilTexture = ((Image, TextureConfig))fill(26);
|
||||
}
|
||||
public Material(IlluminationModel illum, Fill<float> floats, Fill<IColor> colors, Fill<(Image, TextureConfig)> images)
|
||||
{
|
||||
Alpha = floats(0);
|
||||
Anisotropy = floats(1);
|
||||
AnisotropyRoughness = floats(2);
|
||||
AmbientColor = colors(0);
|
||||
ClearcoatRoughness = floats(3);
|
||||
ClearcoatThickness = floats(4);
|
||||
DiffuseColor = colors(1);
|
||||
Emissive = colors(2);
|
||||
IllumModel = illum;
|
||||
Metallic = floats(5);
|
||||
OpticalDensity = floats(6);
|
||||
Roughness = floats(7);
|
||||
Sheen = floats(8);
|
||||
SpecularColor = colors(3);
|
||||
SpecularExponent = floats(9);
|
||||
|
||||
AlphaTexture = images(0);
|
||||
AmbientTexture = images(1);
|
||||
DiffuseTexture = images(2);
|
||||
DisplacementTexture = images(3);
|
||||
EmissiveTexture = images(4);
|
||||
MetallicTexture = images(5);
|
||||
NormalTexture = images(6);
|
||||
RoughnessTexture = images(7);
|
||||
SheenTexture = images(8);
|
||||
SpecularTexture = images(9);
|
||||
SpecularHighlightTexture = images(10);
|
||||
StencilTexture = images(11);
|
||||
}
|
||||
|
||||
public object Clone() => new Material()
|
||||
{
|
||||
Alpha = Alpha,
|
||||
AmbientColor = AmbientColor,
|
||||
Anisotropy = Anisotropy,
|
||||
AnisotropyRoughness = AnisotropyRoughness,
|
||||
ClearcoatRoughness = ClearcoatRoughness,
|
||||
ClearcoatThickness = ClearcoatThickness,
|
||||
DiffuseColor = DiffuseColor,
|
||||
Emissive = Emissive,
|
||||
IllumModel = IllumModel,
|
||||
Metallic = Metallic,
|
||||
OpticalDensity = OpticalDensity,
|
||||
Roughness = Roughness,
|
||||
Sheen = Sheen,
|
||||
SpecularColor = SpecularColor,
|
||||
SpecularExponent = SpecularExponent,
|
||||
|
||||
AlphaTexture = AlphaTexture,
|
||||
AmbientTexture = AmbientTexture,
|
||||
DiffuseTexture = DiffuseTexture,
|
||||
DisplacementTexture = DisplacementTexture,
|
||||
EmissiveTexture = EmissiveTexture,
|
||||
MetallicTexture = MetallicTexture,
|
||||
NormalTexture = NormalTexture,
|
||||
RoughnessTexture = RoughnessTexture,
|
||||
SheenTexture = SheenTexture,
|
||||
SpecularTexture = SpecularTexture,
|
||||
SpecularHighlightTexture = SpecularHighlightTexture,
|
||||
StencilTexture = StencilTexture
|
||||
};
|
||||
public bool Equals(Material other) => Alpha.Equals(other.Alpha) && AmbientColor.Equals(other.AmbientColor) &&
|
||||
Anisotropy.Equals(other.Anisotropy) && AnisotropyRoughness.Equals(other.AnisotropyRoughness) &&
|
||||
ClearcoatRoughness.Equals(other.ClearcoatRoughness) && ClearcoatThickness.Equals(other.ClearcoatThickness) &&
|
||||
DiffuseColor.Equals(other.DiffuseColor) && Emissive.Equals(other.Emissive) &&
|
||||
IllumModel.Equals(other.IllumModel) && Metallic.Equals(other.Metallic) &&
|
||||
OpticalDensity.Equals(other.OpticalDensity) && Roughness.Equals(other.Roughness) && Sheen.Equals(other.Sheen) &&
|
||||
SpecularColor.Equals(other.SpecularColor) && SpecularExponent.Equals(other.SpecularExponent) &&
|
||||
AlphaTexture.Equals(other.AlphaTexture) && AmbientTexture.Equals(other.AmbientTexture) &&
|
||||
DiffuseTexture.Equals(other.DiffuseTexture) && DisplacementTexture.Equals(other.DisplacementTexture) &&
|
||||
EmissiveTexture.Equals(other.EmissiveTexture) && MetallicTexture.Equals(other.MetallicTexture) &&
|
||||
NormalTexture.Equals(other.NormalTexture) && RoughnessTexture.Equals(other.RoughnessTexture) &&
|
||||
SheenTexture.Equals(other.SheenTexture) && SpecularTexture.Equals(other.SheenTexture) &&
|
||||
SpecularHighlightTexture.Equals(other.SpecularHighlightTexture) && StencilTexture.Equals(other.StencilTexture);
|
||||
public override bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
if (obj == null) return false;
|
||||
if (obj.GetType() == typeof(Material)) return Equals((Material)obj);
|
||||
return false;
|
||||
}
|
||||
public override int GetHashCode() => Alpha.GetHashCode() ^ AmbientColor.GetHashCode() ^ Anisotropy.GetHashCode() ^
|
||||
AnisotropyRoughness.GetHashCode() ^ ClearcoatRoughness.GetHashCode() ^ ClearcoatThickness.GetHashCode() ^
|
||||
DiffuseColor.GetHashCode() ^ Emissive.GetHashCode() ^ IllumModel.GetHashCode() ^ Metallic.GetHashCode() ^
|
||||
OpticalDensity.GetHashCode() ^ Roughness.GetHashCode() ^ Sheen.GetHashCode() ^ SpecularColor.GetHashCode() ^
|
||||
SpecularExponent.GetHashCode() ^ AlphaTexture.GetHashCode() ^ AmbientTexture.GetHashCode() ^
|
||||
DiffuseTexture.GetHashCode() ^ DisplacementTexture.GetHashCode() ^ Emissive.GetHashCode() ^
|
||||
Metallic.GetHashCode() ^ NormalTexture.GetHashCode() ^ RoughnessTexture.GetHashCode() ^
|
||||
SheenTexture.GetHashCode() ^ SpecularTexture.GetHashCode() ^ SpecularHighlightTexture.GetHashCode() ^
|
||||
StencilTexture.GetHashCode();
|
||||
|
||||
public static bool operator ==(Material a, Material b) => a.Equals(b);
|
||||
public static bool operator !=(Material a, Material b) => !a.Equals(b);
|
||||
}
|
||||
249
Nerd_STF/Graphics/RGBA.cs
Normal file
249
Nerd_STF/Graphics/RGBA.cs
Normal file
@ -0,0 +1,249 @@
|
||||
namespace Nerd_STF.Graphics;
|
||||
|
||||
public struct RGBA : IColor, IEquatable<RGBA>
|
||||
{
|
||||
public static RGBA Black => new(0, 0, 0);
|
||||
public static RGBA Blue => new(0, 0, 1);
|
||||
public static RGBA Clear => new(0, 0, 0, 0);
|
||||
public static RGBA Cyan => new(0, 1, 1);
|
||||
public static RGBA Gray => new(0.5f, 0.5f, 0.5f);
|
||||
public static RGBA Green => new(0, 1, 0);
|
||||
public static RGBA Magenta => new(1, 0, 1);
|
||||
public static RGBA Orange => new(1, 0.5f, 0);
|
||||
public static RGBA Purple => new(0.5f, 0, 1);
|
||||
public static RGBA Red => new(1, 0, 0);
|
||||
public static RGBA White => new(1, 1, 1);
|
||||
public static RGBA Yellow => new(1, 1, 0);
|
||||
|
||||
public float R
|
||||
{
|
||||
get => p_r;
|
||||
set => p_r = Mathf.Clamp(value, 0, 1);
|
||||
}
|
||||
public float G
|
||||
{
|
||||
get => p_g;
|
||||
set => p_g = Mathf.Clamp(value, 0, 1);
|
||||
}
|
||||
public float B
|
||||
{
|
||||
get => p_b;
|
||||
set => p_b = Mathf.Clamp(value, 0, 1);
|
||||
}
|
||||
public float A
|
||||
{
|
||||
get => p_a;
|
||||
set => p_a = Mathf.Clamp(value, 0, 1);
|
||||
}
|
||||
|
||||
public bool HasBlue => p_b > 0;
|
||||
public bool HasGreen => p_g > 0;
|
||||
public bool HasRed => p_r > 0;
|
||||
public bool IsOpaque => p_a == 1;
|
||||
public bool IsVisible => p_a != 0;
|
||||
|
||||
private float p_r, p_g, p_b, p_a;
|
||||
|
||||
public RGBA() : this(0, 0, 0, 1) { }
|
||||
public RGBA(float all) : this(all, all, all, all) { }
|
||||
public RGBA(float all, float a) : this(all, all, all, a) { }
|
||||
public RGBA(float r, float g, float b) : this(r, g, b, 1) { }
|
||||
public RGBA(float r, float g, float b, float a)
|
||||
{
|
||||
p_r = Mathf.Clamp(r, 0, 1);
|
||||
p_g = Mathf.Clamp(g, 0, 1);
|
||||
p_b = Mathf.Clamp(b, 0, 1);
|
||||
p_a = Mathf.Clamp(a, 0, 1);
|
||||
}
|
||||
public RGBA(Fill<float> fill) : this(fill(0), fill(1), fill(2), fill(3)) { }
|
||||
|
||||
public float this[int index]
|
||||
{
|
||||
get => index switch
|
||||
{
|
||||
0 => R,
|
||||
1 => G,
|
||||
2 => B,
|
||||
3 => A,
|
||||
_ => throw new IndexOutOfRangeException(nameof(index)),
|
||||
};
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
R = value;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
G = value;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
B = value;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
A = value;
|
||||
break;
|
||||
|
||||
default: throw new IndexOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static RGBA Average(params RGBA[] vals)
|
||||
{
|
||||
RGBA val = new(0, 0, 0, 0);
|
||||
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));
|
||||
public static RGBA LerpSquared(RGBA a, RGBA b, float t, bool clamp = true)
|
||||
{
|
||||
RGBA val = Lerp(a * a, b * b, t, clamp);
|
||||
float R = Mathf.Sqrt(val.R), G = Mathf.Sqrt(val.G), B = Mathf.Sqrt(val.B), A = Mathf.Sqrt(val.A);
|
||||
return new(R, G, B, A);
|
||||
}
|
||||
public static RGBA Median(params RGBA[] vals)
|
||||
{
|
||||
float index = Mathf.Average(0, vals.Length - 1);
|
||||
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 (float[] Rs, float[] Gs, float[] Bs, float[] As) SplitArray(params RGBA[] vals)
|
||||
{
|
||||
float[] Rs = new float[vals.Length], Gs = new float[vals.Length],
|
||||
Bs = new float[vals.Length], As = new float[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;
|
||||
}
|
||||
return (Rs, Gs, Bs, As);
|
||||
}
|
||||
|
||||
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 false;
|
||||
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 false;
|
||||
}
|
||||
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 RGBA ToRGBA() => this;
|
||||
public CMYKA ToCMYKA()
|
||||
{
|
||||
float v = Mathf.Max(R, G, B), k = 1 - v;
|
||||
if (v == 1) return new(0, 0, 0, 1, A);
|
||||
|
||||
float kInv = 1 / v, c = 1 - R - k, m = 1 - G - k, y = 1 - B - k;
|
||||
return new(c * kInv, m * kInv, y * kInv, k, A);
|
||||
}
|
||||
public HSVA ToHSVA()
|
||||
{
|
||||
float cMax = Mathf.Max(R, G, B), cMin = Mathf.Min(R, G, B), delta = cMax - cMin;
|
||||
Angle hue = Angle.Zero;
|
||||
if (delta != 0)
|
||||
{
|
||||
float val = 0;
|
||||
if (cMax == R) val = (G - B) / delta % 6;
|
||||
else if (cMax == G) val = (B - R) / delta + 2;
|
||||
else if (cMax == B) val = (R - G) / delta + 4;
|
||||
hue = new(val * 60);
|
||||
}
|
||||
|
||||
float sat = cMax == 0 ? 0 : delta / cMax;
|
||||
|
||||
return new(hue, sat, cMax, A);
|
||||
}
|
||||
|
||||
public RGBAByte ToRGBAByte() => new(Mathf.RoundInt(R * 255), Mathf.RoundInt(G * 255), Mathf.RoundInt(B * 255),
|
||||
Mathf.RoundInt(A * 255));
|
||||
public CMYKAByte ToCMYKAByte() => ToCMYKA().ToCMYKAByte();
|
||||
public HSVAByte ToHSVAByte() => ToHSVA().ToHSVAByte();
|
||||
|
||||
public float[] ToArray() => new[] { R, G, B, A };
|
||||
public List<float> ToList() => new() { R, G, B, A };
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
public IEnumerator<float> GetEnumerator()
|
||||
{
|
||||
yield return R;
|
||||
yield return G;
|
||||
yield return B;
|
||||
yield return A;
|
||||
}
|
||||
|
||||
public object Clone() => new RGBA(R, G, B, A);
|
||||
|
||||
public static RGBA operator +(RGBA a, RGBA b) => new(a.R + b.R, a.G + b.G, a.B + b.B, a.A + b.A);
|
||||
public static RGBA operator -(RGBA c) => new(1 - c.R, 1 - c.G, 1 - c.B, c.A != 1 ? 1 - c.A : 1);
|
||||
public static RGBA operator -(RGBA a, RGBA b) => new(a.R - b.R, a.G - b.G, a.B - b.B, a.A - b.A);
|
||||
public static RGBA operator *(RGBA a, RGBA b) => new(a.R * b.R, a.G * b.G, a.B * b.B, a.A * b.A);
|
||||
public static RGBA operator *(RGBA 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, 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 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(Fill<float> val) => new(val);
|
||||
}
|
||||
213
Nerd_STF/Graphics/RGBAByte.cs
Normal file
213
Nerd_STF/Graphics/RGBAByte.cs
Normal file
@ -0,0 +1,213 @@
|
||||
namespace Nerd_STF.Graphics;
|
||||
|
||||
public struct RGBAByte : IColorByte, IEquatable<RGBAByte>
|
||||
{
|
||||
public static RGBAByte Black => new(0, 0, 0);
|
||||
public static RGBAByte Blue => new(0, 0, 255);
|
||||
public static RGBAByte Clear => new(0, 0, 0, 0);
|
||||
public static RGBAByte Cyan => new(0, 255, 255);
|
||||
public static RGBAByte Gray => new(127, 127, 127);
|
||||
public static RGBAByte Green => new(0, 255, 0);
|
||||
public static RGBAByte Magenta => new(255, 0, 255);
|
||||
public static RGBAByte Orange => new(255, 127, 0);
|
||||
public static RGBAByte Purple => new(127, 0, 255);
|
||||
public static RGBAByte Red => new(255, 0, 0);
|
||||
public static RGBAByte White => new(255, 255, 255);
|
||||
public static RGBAByte Yellow => new(255, 255, 0);
|
||||
|
||||
public byte R, G, B, A;
|
||||
|
||||
public bool HasBlue => B > 0;
|
||||
public bool HasGreen => G > 0;
|
||||
public bool HasRed => R > 0;
|
||||
public bool IsOpaque => A == 255;
|
||||
public bool IsVisible => A != 0;
|
||||
|
||||
public RGBAByte() : this(0, 0, 0, 255) { }
|
||||
public RGBAByte(int all) : this(all, all, all, all) { }
|
||||
public RGBAByte(int all, int a) : this(all, all, all, a) { }
|
||||
public RGBAByte(int r, int g, int b) : this(r, g, b, 255) { }
|
||||
public RGBAByte(int r, int g, int b, int a)
|
||||
{
|
||||
R = (byte)Mathf.Clamp(r, 0, 255);
|
||||
G = (byte)Mathf.Clamp(g, 0, 255);
|
||||
B = (byte)Mathf.Clamp(b, 0, 255);
|
||||
A = (byte)Mathf.Clamp(a, 0, 255);
|
||||
}
|
||||
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]
|
||||
{
|
||||
get => index switch
|
||||
{
|
||||
0 => R,
|
||||
1 => G,
|
||||
2 => B,
|
||||
3 => A,
|
||||
_ => throw new IndexOutOfRangeException(nameof(index)),
|
||||
};
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
R = value;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
G = value;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
B = value;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
A = value;
|
||||
break;
|
||||
|
||||
default: throw new IndexOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static RGBAByte Average(params RGBAByte[] vals)
|
||||
{
|
||||
RGBAByte val = new(0, 0, 0, 0);
|
||||
for (int i = 0; i < vals.Length; i++) val += vals[i];
|
||||
return val / vals.Length;
|
||||
}
|
||||
public static RGBAByte Clamp(RGBAByte val, RGBAByte min, RGBAByte 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 RGBAByte Lerp(RGBAByte a, RGBAByte b, byte 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) =>
|
||||
RGBA.LerpSquared(a.ToRGBA(), b.ToRGBA(), t, clamp).ToRGBAByte();
|
||||
public static RGBAByte Median(params RGBAByte[] vals)
|
||||
{
|
||||
float index = Mathf.Average(0, vals.Length - 1);
|
||||
RGBAByte valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
|
||||
return Average(valA, valB);
|
||||
}
|
||||
public static RGBAByte Max(params RGBAByte[] vals)
|
||||
{
|
||||
(int[] Rs, int[] Gs, int[] Bs, int[] As) = SplitArrayInt(vals);
|
||||
return new(Mathf.Max(Rs), Mathf.Max(Gs), Mathf.Max(Bs), Mathf.Max(As));
|
||||
}
|
||||
public static RGBAByte Min(params RGBAByte[] vals)
|
||||
{
|
||||
(int[] Rs, int[] Gs, int[] Bs, int[] As) = SplitArrayInt(vals);
|
||||
return new(Mathf.Min(Rs), Mathf.Min(Gs), Mathf.Min(Bs), Mathf.Min(As));
|
||||
}
|
||||
|
||||
public static (byte[] Rs, byte[] Gs, byte[] Bs, byte[] As) SplitArray(params RGBAByte[] vals)
|
||||
{
|
||||
byte[] Rs = new byte[vals.Length], Gs = new byte[vals.Length],
|
||||
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;
|
||||
}
|
||||
return (Rs, Gs, Bs, As);
|
||||
}
|
||||
public static (int[] Rs, int[] Gs, int[] Bs, int[] As) SplitArrayInt(params RGBAByte[] vals)
|
||||
{
|
||||
int[] Rs = new int[vals.Length], Gs = new int[vals.Length],
|
||||
Bs = new int[vals.Length], As = new int[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;
|
||||
}
|
||||
return (Rs, Gs, Bs, As);
|
||||
}
|
||||
|
||||
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 false;
|
||||
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 false;
|
||||
}
|
||||
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 RGBA ToRGBA() => new(R / 255f, G / 255f, B / 255f, A / 255f);
|
||||
public CMYKA ToCMYKA() => ToRGBA().ToCMYKA();
|
||||
public HSVA ToHSVA() => ToRGBA().ToHSVA();
|
||||
|
||||
public RGBAByte ToRGBAByte() => this;
|
||||
public CMYKAByte ToCMYKAByte() => ToRGBA().ToCMYKAByte();
|
||||
public HSVAByte ToHSVAByte() => ToRGBA().ToHSVAByte();
|
||||
|
||||
public byte[] ToArray() => new[] { R, G, B, A };
|
||||
public List<byte> ToList() => 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;
|
||||
}
|
||||
|
||||
public object Clone() => new RGBAByte(R, G, B, A);
|
||||
|
||||
public static RGBAByte operator +(RGBAByte a, RGBAByte b) => new(a.R + b.R, a.G + b.G, a.B + b.B, a.A + b.A);
|
||||
public static RGBAByte operator -(RGBAByte c) => new(255 - c.R, 255 - c.G, 255 - c.B, c.A != 255 ? 255 - c.A : 255);
|
||||
public static RGBAByte operator -(RGBAByte a, RGBAByte b) => new(a.R - b.R, a.G - b.G, a.B - b.B, a.A - b.A);
|
||||
public static RGBAByte operator *(RGBAByte a, RGBAByte b) => new(a.R * b.R, a.G * b.G, a.B * b.B, a.A * b.A);
|
||||
public static RGBAByte operator *(RGBAByte 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 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, 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 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);
|
||||
public static implicit operator RGBAByte(CMYKA val) => val.ToRGBAByte();
|
||||
public static implicit operator RGBAByte(HSVA val) => val.ToRGBAByte();
|
||||
public static implicit operator RGBAByte(RGBA val) => val.ToRGBAByte();
|
||||
public static implicit operator RGBAByte(CMYKAByte val) => val.ToRGBAByte();
|
||||
public static implicit operator RGBAByte(HSVAByte val) => val.ToRGBAByte();
|
||||
public static implicit operator RGBAByte(Fill<byte> val) => new(val);
|
||||
public static implicit operator RGBAByte(Fill<int> val) => new(val);
|
||||
}
|
||||
25
Nerd_STF/Graphics/TextureConfig.cs
Normal file
25
Nerd_STF/Graphics/TextureConfig.cs
Normal file
@ -0,0 +1,25 @@
|
||||
namespace Nerd_STF.Graphics;
|
||||
|
||||
public struct TextureConfig
|
||||
{
|
||||
public (bool U, bool V) BlendUV;
|
||||
public float Boost;
|
||||
public ColorChannel Channel;
|
||||
public bool Clamp;
|
||||
public float NormalStrength;
|
||||
public Float3 Offset;
|
||||
public Float3 Scale;
|
||||
public Float3 Turbulance;
|
||||
|
||||
public TextureConfig()
|
||||
{
|
||||
BlendUV = (true, true);
|
||||
Boost = 0;
|
||||
Channel = ColorChannel.Red;
|
||||
Clamp = false;
|
||||
NormalStrength = 1;
|
||||
Offset = Float3.Zero;
|
||||
Scale = Float3.One;
|
||||
Turbulance = Float3.Zero;
|
||||
}
|
||||
}
|
||||
@ -18,10 +18,15 @@ public struct Angle : ICloneable, IComparable<Angle>, IEquatable<Angle>
|
||||
get => p_deg * 1.11111111111f; // Reciprocal of 9/10 as a constant (10/9)
|
||||
set => p_deg = value * 0.9f;
|
||||
}
|
||||
public float Normalized
|
||||
{
|
||||
get => p_deg / 360;
|
||||
set => p_deg = value * 360;
|
||||
}
|
||||
public float Radians
|
||||
{
|
||||
get => p_deg * Mathf.DegToRad;
|
||||
set => p_deg = value * Mathf.RadToDeg;
|
||||
get => p_deg * Constants.DegToRad;
|
||||
set => p_deg = value * Constants.RadToDeg;
|
||||
}
|
||||
|
||||
public Angle Bounded => new(p_deg % 360);
|
||||
@ -34,23 +39,24 @@ public struct Angle : ICloneable, IComparable<Angle>, IEquatable<Angle>
|
||||
{
|
||||
Type.Degrees => value,
|
||||
Type.Gradians => value * 0.9f,
|
||||
Type.Radians => value * Mathf.RadToDeg,
|
||||
Type.Normalized => value * 360,
|
||||
Type.Radians => value * Constants.RadToDeg,
|
||||
_ => throw new ArgumentException("Unknown type.", nameof(valueType)),
|
||||
};
|
||||
}
|
||||
|
||||
public static Angle Absolute(Angle val) => new(Mathf.Absolute(val.p_deg));
|
||||
public static Angle Average(params Angle[] vals) => new(Mathf.Average(ToDoubles(Type.Degrees, vals)));
|
||||
public static Angle Ceiling(Angle val) => new(Mathf.Ceiling(val.p_deg));
|
||||
public static Angle Average(params Angle[] vals) => new(Mathf.Average(SplitArray(Type.Degrees, vals)));
|
||||
public static Angle Ceiling(Angle val, Type type = Type.Degrees) => new(Mathf.Ceiling(val.ValueFromType(type)));
|
||||
public static Angle Clamp(Angle val, Angle min, Angle max) => new(Mathf.Clamp(val.p_deg, min.p_deg, max.p_deg));
|
||||
public static Angle Floor(Angle val) => new(Mathf.Ceiling(val.p_deg));
|
||||
public static Angle Floor(Angle val, Type type = Type.Degrees) => new(Mathf.Floor(val.ValueFromType(type)));
|
||||
public static Angle Lerp(Angle a, Angle b, float t, bool clamp = true) =>
|
||||
new(Mathf.Lerp(a.p_deg, b.p_deg, t, clamp));
|
||||
public static Angle Max(params Angle[] vals) => new(Mathf.Max(ToDoubles(Type.Degrees, vals)));
|
||||
public static Angle Median(params Angle[] vals) => new(Mathf.Median(ToDoubles(Type.Degrees, vals)));
|
||||
public static Angle Min(params Angle[] vals) => new(Mathf.Min(ToDoubles(Type.Degrees, vals)));
|
||||
public static Angle Max(params Angle[] vals) => new(Mathf.Max(SplitArray(Type.Degrees, vals)));
|
||||
public static Angle Median(params Angle[] vals) => new(Mathf.Median(SplitArray(Type.Degrees, vals)));
|
||||
public static Angle Min(params Angle[] vals) => new(Mathf.Min(SplitArray(Type.Degrees, vals)));
|
||||
|
||||
public static float[] ToDoubles(Type outputType, params Angle[] vals)
|
||||
public static float[] SplitArray(Type outputType, params Angle[] vals)
|
||||
{
|
||||
float[] res = new float[vals.Length];
|
||||
for (int i = 0; i < vals.Length; i++)
|
||||
@ -59,6 +65,7 @@ public struct Angle : ICloneable, IComparable<Angle>, IEquatable<Angle>
|
||||
{
|
||||
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)),
|
||||
};
|
||||
@ -78,21 +85,32 @@ public struct Angle : ICloneable, IComparable<Angle>, IEquatable<Angle>
|
||||
public string ToString(Type outputType) => ToString((string?)null, outputType);
|
||||
public string ToString(string? provider, Type outputType = Type.Degrees) => outputType switch
|
||||
{
|
||||
Type.Degrees => p_deg.ToString(provider),
|
||||
Type.Gradians => Gradians.ToString(provider),
|
||||
Type.Radians => Radians.ToString(provider),
|
||||
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),
|
||||
Type.Radians => Radians.ToString(provider),
|
||||
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 object Clone() => new Angle(p_deg);
|
||||
|
||||
public float ValueFromType(Type type) => type switch
|
||||
{
|
||||
Type.Degrees => Degrees,
|
||||
Type.Gradians => Gradians,
|
||||
Type.Normalized => Normalized,
|
||||
Type.Radians => Radians,
|
||||
_ => throw new ArgumentException("Unknown type.", nameof(type)),
|
||||
};
|
||||
|
||||
public static Angle operator +(Angle a, Angle b) => new(a.p_deg + b.p_deg);
|
||||
public static Angle operator -(Angle a) => new(-a.p_deg);
|
||||
public static Angle operator -(Angle a, Angle b) => new(a.p_deg - b.p_deg);
|
||||
@ -111,6 +129,7 @@ public struct Angle : ICloneable, IComparable<Angle>, IEquatable<Angle>
|
||||
{
|
||||
Degrees,
|
||||
Gradians,
|
||||
Normalized,
|
||||
Radians,
|
||||
}
|
||||
}
|
||||
|
||||
156
Nerd_STF/Mathematics/Constants.cs
Normal file
156
Nerd_STF/Mathematics/Constants.cs
Normal file
@ -0,0 +1,156 @@
|
||||
namespace Nerd_STF.Mathematics;
|
||||
|
||||
public static class Constants
|
||||
{
|
||||
public const float DegToRad = 180 / Pi;
|
||||
public const float HalfPi = Pi / 2;
|
||||
public const float Pi = 3.14159265359f;
|
||||
public const float RadToDeg = Pi / 180;
|
||||
public const float Tau = Pi * 2;
|
||||
|
||||
public const float E = 2.71828182846f;
|
||||
public const float EulerConstant = 0.5772156649f;
|
||||
public const float Ln2 = 0.69314718056f;
|
||||
public const float Ln3 = 1.09861228867f;
|
||||
public const float Ln5 = 1.60943791243f;
|
||||
public const float Ln10 = 2.30258509299f;
|
||||
|
||||
public const float Log2 = 0.301029995664f;
|
||||
public const float Log3 = 0.47712125472f;
|
||||
public const float Log5 = 0.698970004336f;
|
||||
public const float Log10 = 1;
|
||||
|
||||
public const float GoldenAngle = 180 * (3 - Sqrt5);
|
||||
public const float GoldenRatio = (1 + Sqrt5) / 2;
|
||||
public const float SilverRatio = Sqrt2 + 1;
|
||||
public const float SupergoldenRatio = 1.465571232f;
|
||||
|
||||
public const float Cbrt2 = 1.25992104989f;
|
||||
public const float Cbrt3 = 1.44224957031f;
|
||||
public const float Cbrt5 = 1.70997594668f;
|
||||
public const float Cbrt10 = 2.15443469003f;
|
||||
public const float Sqrt2 = 1.4142135624f;
|
||||
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 Cos0Deg = 1;
|
||||
public const float Cos30Deg = Sqrt3 / 2;
|
||||
public const float Cos45Deg = Sqrt2 / 2;
|
||||
public const float Cos60Deg = 0.5f;
|
||||
public const float Cos90Deg = 0;
|
||||
public const float Cot0Deg = float.PositiveInfinity;
|
||||
public const float Cot30Deg = Sqrt3;
|
||||
public const float Cot45Deg = 1;
|
||||
public const float Cot60Deg = Sqrt3 / 3;
|
||||
public const float Cot90Deg = 0;
|
||||
public const float Csc0Deg = float.PositiveInfinity;
|
||||
public const float Csc30Deg = 2;
|
||||
public const float Csc45Deg = Sqrt2;
|
||||
public const float Csc60Deg = 2 * Sqrt3 / 3;
|
||||
public const float Csc90Deg = 1;
|
||||
public const float MagicAngle = 54.7356103172f;
|
||||
public const float Sec0Deg = 1;
|
||||
public const float Sec30Deg = 2 * Sqrt3 / 3;
|
||||
public const float Sec45Deg = Sqrt2;
|
||||
public const float Sec60Deg = 2;
|
||||
public const float Sec90Deg = float.PositiveInfinity;
|
||||
public const float Sin0Deg = 0;
|
||||
public const float Sin30Deg = 0.5f;
|
||||
public const float Sin45Deg = Sqrt2 / 2;
|
||||
public const float Sin60Deg = Sqrt3 / 2;
|
||||
public const float Sin90Deg = 1;
|
||||
public const float Tan0Deg = 0;
|
||||
public const float Tan30Deg = Sqrt3 / 3;
|
||||
public const float Tan45Deg = 1;
|
||||
public const float Tan60Deg = Sqrt3;
|
||||
public const float Tan90Deg = float.PositiveInfinity;
|
||||
|
||||
public const float AperyConstant = 1.2020569031f;
|
||||
public const float ArtinConstant = 0.3739558136f;
|
||||
public const float AsymptoticLebesgueConstant = 0.9894312738f;
|
||||
public const float BackhouseConstant = 1.4560749485f;
|
||||
public const float Base10ChampernowneConstant = 0.1234567891f;
|
||||
public const float BernsteinConstant = 0.2801694990f;
|
||||
public const float BlochConstant = (0.4332f + 0.4719f) / 2;
|
||||
public const float BruijnNewmanConstant = (0 + 0.2f) / 2;
|
||||
public const float BrunConstant = 1.9021605831f;
|
||||
public const float CatalanConstant = 0.9159655941f;
|
||||
public const float CahenConstant = 0.6434105462f;
|
||||
public const float ChaitinConstant = 0.0078749969f;
|
||||
public const float ChvatalSankoffConstant = (0.788071f + 0.826280f) / 2;
|
||||
public const float ConwayConstant = 1.3035772690f;
|
||||
public const float CopelandErdosConstant = 0.2357111317f;
|
||||
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 ErdosBorweinConstant = 1.6066951524f;
|
||||
public const float ErdosTenenbaumFordConstant = 0.8607133205f;
|
||||
public const float FeigenbaumConstant1 = 4.6692016091f;
|
||||
public const float FeigenbaumConstant2 = 2.5029078750f;
|
||||
public const float FellerTornierConstant = 0.6613170494f;
|
||||
public const float FoiasConstant = 1.1874523511f;
|
||||
public const float FransenRobinsonConstant = 2.8077702420f;
|
||||
public const float GaussConstant = 0.8346268416f;
|
||||
public const float GelfondConstant = 23.1406926327f;
|
||||
public const float GelfondSchneiderConstant = 2.6651441426f;
|
||||
public const float GiesekingConstant = 1.0149416064f;
|
||||
public const float GlaisherKinkelinConstant = 1.2824271291f;
|
||||
public const float GolombDickmanConstant = 0.6243299885f;
|
||||
public const float GompertzConstant = 0.5963473623f;
|
||||
public const float HafnerSarnakMcCurleyConstant = 0.3532363718f;
|
||||
public const float HeathBrownMorozConstant = 0.0013176411f;
|
||||
public const float KeplerBouwkampConstant = 0.1149420449f;
|
||||
public const float KhinchinConstant = 2.6854520010f;
|
||||
public const float KomornikLoreti = 1.7872316501f;
|
||||
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 LemniscateConstant = 2.6220575542f;
|
||||
public const float LevyConstant1 = Pi * Pi / (12 * Ln2);
|
||||
public const float LevyConstant2 = 3.2758229187f;
|
||||
public const float LiouvilleConstant = 0.110001f;
|
||||
public const float LochsConstant = 6 * Ln2 * Ln10 / (Pi * Pi);
|
||||
public const float MeisselMertensConstant = 0.2614972128f;
|
||||
public const float MillsConstant = 1.3063778838f;
|
||||
public const float MRBConstant = 0.1878596424f;
|
||||
public const float NivenConstant = 1.7052111401f;
|
||||
public const float OmegaConstant = 0.5671432904f;
|
||||
public const float PorterConstant = 1.4670780794f;
|
||||
public const float PrimeConstant = 0.4146825098f;
|
||||
public const float ProuhetThueMorseConstant = 0.4124540336f;
|
||||
public const float RamanujanConstant = 262_537_412_640_768_743.99999f;
|
||||
public const float RamanujanSoldnerConstant = 1.4513692348f;
|
||||
public const float ReciprocalFibonacciConstant = 3.3598856662f;
|
||||
public const float RobbinsConstant = 0.6617071822f;
|
||||
public const float SalemConstant = 1.1762808182f;
|
||||
public const float SierpinskiConstant = 2.5849817595f;
|
||||
public const float SomosQuadraticRecurranceConstant = 1.6616879496f;
|
||||
public const float StephensConstant = 0.5759599688f;
|
||||
public const float TaniguchiConstant = 0.6782344919f;
|
||||
public const float TribonacciConstant = 1.8392867552f;
|
||||
public const float TwinPrimesConstant = 0.6601618158f;
|
||||
public const float VanDerPauwConstant = Pi / Ln2;
|
||||
public const float ViswanathConstant = 1.1319882487f;
|
||||
public const float WeierstrassConstant = 0.4749493799f;
|
||||
|
||||
public const float FirstContinuedFractionConstant = 0.6977746579f;
|
||||
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 DottieNumber = 0.7390851332f;
|
||||
public const float FractalDimensionOfTheApollonianPackingOfCircles = 1.305688f;
|
||||
public const float FractalDimensionOfTheCanterSet = Log2 / Log3;
|
||||
public const float HausdorffDimensionOfTheSerpinskiTriangle = Log3 / Log2;
|
||||
public const float MagicNumber = 3; // 3 is a magic number.
|
||||
public const float PlasticNumber = 1.3247179572f;
|
||||
public const float LaplaceLimit = 0.6627434193f;
|
||||
public const float LogarithmicCapacityOfTheUnitDisk = 0.5901702995f;
|
||||
public const float RegularPaperfoldingSequence = 0.8507361882f;
|
||||
}
|
||||
@ -111,7 +111,7 @@ public struct Float2 : ICloneable, IComparable<Float2>, IEquatable<Float2>, IGro
|
||||
foreach (Float2 d in vals) val = d < val ? d : val;
|
||||
return val;
|
||||
}
|
||||
public static Float2 Multiply(params Float2[] vals)
|
||||
public static Float2 Product(params Float2[] vals)
|
||||
{
|
||||
if (vals.Length < 1) return Zero;
|
||||
Float2 val = One;
|
||||
@ -130,6 +130,17 @@ public struct Float2 : ICloneable, IComparable<Float2>, IEquatable<Float2>, IGro
|
||||
return val;
|
||||
}
|
||||
|
||||
public static (float[] Xs, float[] Ys) SplitArray(params Float2[] vals)
|
||||
{
|
||||
float[] Xs = new float[vals.Length], Ys = new float[vals.Length];
|
||||
for (int i = 0; i < vals.Length; i++)
|
||||
{
|
||||
Xs[i] = vals[i].x;
|
||||
Ys[i] = vals[i].y;
|
||||
}
|
||||
return (Xs, Ys);
|
||||
}
|
||||
|
||||
public int CompareTo(Float2 other) => Magnitude.CompareTo(other.Magnitude);
|
||||
public override bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
|
||||
@ -131,7 +131,7 @@ public struct Float3 : ICloneable, IComparable<Float3>, IEquatable<Float3>, IGro
|
||||
foreach (Float3 d in vals) val = d < val ? d : val;
|
||||
return val;
|
||||
}
|
||||
public static Float3 Multiply(params Float3[] vals)
|
||||
public static Float3 Product(params Float3[] vals)
|
||||
{
|
||||
if (vals.Length < 1) return Zero;
|
||||
Float3 val = One;
|
||||
@ -150,6 +150,18 @@ public struct Float3 : ICloneable, IComparable<Float3>, IEquatable<Float3>, IGro
|
||||
return val;
|
||||
}
|
||||
|
||||
public static (float[] Xs, float[] Ys, float[] Zs) SplitArray(params Float3[] vals)
|
||||
{
|
||||
float[] Xs = new float[vals.Length], Ys = new float[vals.Length], Zs = new float[vals.Length];
|
||||
for (int i = 0; i < vals.Length; i++)
|
||||
{
|
||||
Xs[i] = vals[i].x;
|
||||
Ys[i] = vals[i].y;
|
||||
Zs[i] = vals[i].z;
|
||||
}
|
||||
return (Xs, Ys, Zs);
|
||||
}
|
||||
|
||||
public int CompareTo(Float3 other) => Magnitude.CompareTo(other.Magnitude);
|
||||
public override bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
@ -197,6 +209,10 @@ public struct Float3 : ICloneable, IComparable<Float3>, IEquatable<Float3>, IGro
|
||||
public static implicit operator Float3(Int3 val) => new(val.x, val.y, val.z);
|
||||
public static explicit operator Float3(Int4 val) => new(val.x, val.y, val.z);
|
||||
public static implicit operator Float3(Vert val) => new(val.position.x, val.position.y, val.position.z);
|
||||
public static explicit operator Float3(RGBA val) => new(val.R, val.G, val.B);
|
||||
public static explicit operator Float3(HSVA val) => new(val.H.Normalized, val.S, val.V);
|
||||
public static explicit operator Float3(RGBAByte val) => (Float3)val.ToRGBA();
|
||||
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);
|
||||
}
|
||||
|
||||
@ -144,7 +144,7 @@ public struct Float4 : ICloneable, IComparable<Float4>, IEquatable<Float4>, IGro
|
||||
foreach (Float4 d in vals) val = d < val ? d : val;
|
||||
return val;
|
||||
}
|
||||
public static Float4 Multiply(params Float4[] vals)
|
||||
public static Float4 Product(params Float4[] vals)
|
||||
{
|
||||
if (vals.Length < 1) return Zero;
|
||||
Float4 val = One;
|
||||
@ -163,6 +163,20 @@ public struct Float4 : ICloneable, IComparable<Float4>, IEquatable<Float4>, IGro
|
||||
return val;
|
||||
}
|
||||
|
||||
public static (float[] Xs, float[] Ys, float[] Zs, float[] Ws) SplitArray(params Float4[] vals)
|
||||
{
|
||||
float[] Xs = new float[vals.Length], Ys = new float[vals.Length], Zs = new float[vals.Length],
|
||||
Ws = new float[vals.Length];
|
||||
for (int i = 0; i < vals.Length; i++)
|
||||
{
|
||||
Xs[i] = vals[i].x;
|
||||
Ys[i] = vals[i].y;
|
||||
Zs[i] = vals[i].z;
|
||||
Ws[i] = vals[i].w;
|
||||
}
|
||||
return (Xs, Ys, Zs, Ws);
|
||||
}
|
||||
|
||||
public int CompareTo(Float4 other) => Magnitude.CompareTo(other.Magnitude);
|
||||
public override bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
@ -213,6 +227,12 @@ public struct Float4 : ICloneable, IComparable<Float4>, IEquatable<Float4>, IGro
|
||||
public static implicit operator Float4(Int3 val) => new(val.x, val.y, val.z, 0);
|
||||
public static implicit operator Float4(Int4 val) => new(val.x, val.y, val.z, val.w);
|
||||
public static implicit operator Float4(Vert val) => new(val.position.x, val.position.y, val.position.z, 0);
|
||||
public static implicit operator Float4(RGBA val) => new(val.R, val.G, val.B, val.A);
|
||||
public static explicit operator Float4(CMYKA val) => new(val.C, val.M, val.Y, val.K);
|
||||
public static explicit operator Float4(HSVA val) => new(val.H.Normalized, val.S, val.V, val.A);
|
||||
public static implicit operator Float4(RGBAByte val) => val.ToRGBA();
|
||||
public static explicit operator Float4(CMYKAByte val) => (Float4)val.ToCMYKA();
|
||||
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);
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ public struct Line : ICloneable, IClosest<Vert>, IComparable<Line>, IContainer<V
|
||||
public static Line Zero => new(Vert.Zero, Vert.Zero);
|
||||
|
||||
public float Length => (b - a).Magnitude;
|
||||
public Vert Midpoint => Vert.Average(a, b);
|
||||
|
||||
public Vert a, b;
|
||||
|
||||
@ -162,9 +163,9 @@ public struct Line : ICloneable, IClosest<Vert>, IComparable<Line>, IContainer<V
|
||||
public Vert[] ToArray() => new Vert[] { a, b };
|
||||
public List<Vert> ToList() => new() { a, b };
|
||||
|
||||
public float[] ToDoubleArray() => new float[] { a.position.x, a.position.y, a.position.z,
|
||||
public float[] ToFloatArray() => new float[] { a.position.x, a.position.y, a.position.z,
|
||||
b.position.x, b.position.y, b.position.z };
|
||||
public List<float> ToDoubleList() => new() { a.position.x, a.position.y, a.position.z,
|
||||
public List<float> ToFloatList() => new() { a.position.x, a.position.y, a.position.z,
|
||||
b.position.x, b.position.y, b.position.z };
|
||||
|
||||
public static Line operator +(Line a, Line b) => new(a.a + b.a, a.b + b.b);
|
||||
|
||||
@ -11,6 +11,7 @@ public struct Polygon : ICloneable, IEquatable<Polygon>, IGroup<Vert>, ISubdivid
|
||||
p_verts = GenerateVerts(value);
|
||||
}
|
||||
}
|
||||
public Vert Midpoint => Vert.Average(Verts);
|
||||
public Vert[] Verts
|
||||
{
|
||||
get => p_verts;
|
||||
@ -132,7 +133,7 @@ public struct Polygon : ICloneable, IEquatable<Polygon>, IGroup<Vert>, ISubdivid
|
||||
List<Vert> parts = new();
|
||||
for (int i = 0; i < vertCount; i++)
|
||||
{
|
||||
float val = Mathf.Tau * i / vertCount;
|
||||
float val = Constants.Tau * i / vertCount;
|
||||
parts.Add(new(Mathf.Cos(val), Mathf.Sin(val)));
|
||||
}
|
||||
return new(parts.ToArray());
|
||||
@ -245,11 +246,11 @@ public struct Polygon : ICloneable, IEquatable<Polygon>, IGroup<Vert>, ISubdivid
|
||||
return new(res);
|
||||
}
|
||||
|
||||
public static float[] ToDoubleArrayAll(params Polygon[] polys) => ToDoubleListAll(polys).ToArray();
|
||||
public static List<float> ToDoubleListAll(params Polygon[] polys)
|
||||
public static float[] ToFloatArrayAll(params Polygon[] polys) => ToFloatListAll(polys).ToArray();
|
||||
public static List<float> ToFloatListAll(params Polygon[] polys)
|
||||
{
|
||||
List<float> vals = new();
|
||||
foreach (Polygon poly in polys) vals.AddRange(poly.ToDoubleArray());
|
||||
foreach (Polygon poly in polys) vals.AddRange(poly.ToFloatArray());
|
||||
return vals;
|
||||
}
|
||||
|
||||
@ -286,7 +287,7 @@ public struct Polygon : ICloneable, IEquatable<Polygon>, IGroup<Vert>, ISubdivid
|
||||
public Vert[] ToArray() => Verts;
|
||||
public List<Vert> ToList() => new(Verts);
|
||||
|
||||
public float[] ToDoubleArray()
|
||||
public float[] ToFloatArray()
|
||||
{
|
||||
float[] vals = new float[Verts.Length * 3];
|
||||
for (int i = 0; i < Verts.Length; i++)
|
||||
@ -298,7 +299,7 @@ public struct Polygon : ICloneable, IEquatable<Polygon>, IGroup<Vert>, ISubdivid
|
||||
}
|
||||
return vals;
|
||||
}
|
||||
public List<float> ToDoubleList() => new(ToDoubleArray());
|
||||
public List<float> ToFloatList() => new(ToFloatArray());
|
||||
|
||||
public Polygon Subdivide()
|
||||
{
|
||||
|
||||
@ -103,6 +103,7 @@ public struct Quadrilateral : ICloneable, IEquatable<Quadrilateral>, IGroup<Vert
|
||||
return val;
|
||||
}
|
||||
}
|
||||
public Vert Midpoint => Vert.Average(A, B, C, D);
|
||||
public float Perimeter => AB.Length + BC.Length + CD.Length + DA.Length;
|
||||
|
||||
public Quadrilateral(Vert a, Vert b, Vert c, Vert d)
|
||||
@ -120,7 +121,6 @@ public struct Quadrilateral : ICloneable, IEquatable<Quadrilateral>, IGroup<Vert
|
||||
{
|
||||
if (ab.a != da.b || ab.b != bc.a || bc.b != cd.a || cd.b != da.a)
|
||||
throw new DisconnectedLinesException(ab, bc, cd, da);
|
||||
|
||||
p_a = ab.a;
|
||||
p_b = bc.a;
|
||||
p_c = cd.a;
|
||||
@ -241,7 +241,7 @@ public struct Quadrilateral : ICloneable, IEquatable<Quadrilateral>, IGroup<Vert
|
||||
return (ab, bc, cd, da);
|
||||
}
|
||||
|
||||
public static float[] ToDoubleArrayAll(params Quadrilateral[] quads)
|
||||
public static float[] ToFloatArrayAll(params Quadrilateral[] quads)
|
||||
{
|
||||
float[] vals = new float[quads.Length * 12];
|
||||
for (int i = 0; i < quads.Length; i++)
|
||||
@ -262,7 +262,7 @@ public struct Quadrilateral : ICloneable, IEquatable<Quadrilateral>, IGroup<Vert
|
||||
}
|
||||
return vals;
|
||||
}
|
||||
public static List<float> ToDoubleListAll(params Quadrilateral[] quads) => new(ToDoubleArrayAll(quads));
|
||||
public static List<float> ToFloatListAll(params Quadrilateral[] quads) => new(ToFloatArrayAll(quads));
|
||||
|
||||
public override bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
@ -291,11 +291,11 @@ public struct Quadrilateral : ICloneable, IEquatable<Quadrilateral>, IGroup<Vert
|
||||
public Vert[] ToArray() => new Vert[] { A, B, C, D };
|
||||
public List<Vert> ToList() => new() { A, B, C, D };
|
||||
|
||||
public float[] ToDoubleArray() => new float[] { A.position.x, A.position.y, A.position.z,
|
||||
public float[] ToFloatArray() => new float[] { A.position.x, A.position.y, A.position.z,
|
||||
B.position.x, B.position.y, B.position.z,
|
||||
C.position.x, C.position.y, C.position.z,
|
||||
D.position.x, D.position.y, D.position.z };
|
||||
public List<float> ToDoubleList() => new() { A.position.x, A.position.y, A.position.z,
|
||||
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,
|
||||
D.position.x, D.position.y, D.position.z };
|
||||
|
||||
@ -8,8 +8,8 @@ public struct Sphere : ICloneable, IClosest<Vert>, IComparable<Sphere>, ICompara
|
||||
public Vert center;
|
||||
public float radius;
|
||||
|
||||
public float SurfaceArea => 4 * Mathf.Pi * radius * radius;
|
||||
public float Volume => 4 / 3 * (Mathf.Pi * radius * radius * radius);
|
||||
public float SurfaceArea => 4 * Constants.Pi * radius * radius;
|
||||
public float Volume => 4 / 3 * (Constants.Pi * radius * radius * radius);
|
||||
|
||||
public static Sphere FromDiameter(Vert a, Vert b) => new(Vert.Average(a, b), (a - b).Magnitude / 2);
|
||||
public static Sphere FromRadius(Vert center, Vert radius) => new(center, (center - radius).Magnitude);
|
||||
|
||||
@ -75,6 +75,7 @@ public struct Triangle : ICloneable, IEquatable<Triangle>, IGroup<Vert>
|
||||
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;
|
||||
public Vert Midpoint => Vert.Average(A, B, C);
|
||||
public float Perimeter => AB.Length + BC.Length + CA.Length;
|
||||
|
||||
public Triangle(Vert a, Vert b, Vert c)
|
||||
@ -195,7 +196,7 @@ public struct Triangle : ICloneable, IEquatable<Triangle>, IGroup<Vert>
|
||||
return (ab, bc, ca);
|
||||
}
|
||||
|
||||
public static float[] ToDoubleArrayAll(params Triangle[] tris)
|
||||
public static float[] ToFloatArrayAll(params Triangle[] tris)
|
||||
{
|
||||
float[] vals = new float[tris.Length * 9];
|
||||
for (int i = 0; i < tris.Length; i++)
|
||||
@ -213,7 +214,7 @@ public struct Triangle : ICloneable, IEquatable<Triangle>, IGroup<Vert>
|
||||
}
|
||||
return vals;
|
||||
}
|
||||
public static List<float> ToDoubleListAll(params Triangle[] tris) => new(ToDoubleArrayAll(tris));
|
||||
public static List<float> ToFloatListAll(params Triangle[] tris) => new(ToFloatArrayAll(tris));
|
||||
|
||||
public override bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
@ -241,10 +242,10 @@ public struct Triangle : ICloneable, IEquatable<Triangle>, IGroup<Vert>
|
||||
public Vert[] ToArray() => new Vert[] { A, B, C };
|
||||
public List<Vert> ToList() => new() { A, B, C };
|
||||
|
||||
public float[] ToDoubleArray() => new float[] { A.position.x, A.position.y, A.position.z,
|
||||
public float[] ToFloatArray() => new float[] { A.position.x, A.position.y, A.position.z,
|
||||
B.position.x, B.position.y, B.position.z,
|
||||
C.position.x, C.position.y, C.position.z };
|
||||
public List<float> ToDoubleList() => new() { A.position.x, A.position.y, A.position.z,
|
||||
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 };
|
||||
public static Triangle operator +(Triangle a, Triangle b) => new(a.A + b.A, a.B + b.B, a.C + b.C);
|
||||
|
||||
@ -31,7 +31,7 @@ public struct Vert : ICloneable, IEquatable<Vert>, IGroup<float>
|
||||
}
|
||||
|
||||
public static Vert Absolute(Vert val) => new(Float3.Absolute(val.position));
|
||||
public static Vert Average(params Vert[] vals) => Float3.Average(ToDouble3Array(vals));
|
||||
public static Vert Average(params Vert[] vals) => Float3.Average(ToFloat3Array(vals));
|
||||
public static Vert Ceiling(Vert val) => new(Float3.Ceiling(val.position));
|
||||
public static Vert Clamp(Vert val, Vert min, Vert max) =>
|
||||
new(Float3.Clamp(val.position, min.position, max.position));
|
||||
@ -40,23 +40,24 @@ public struct Vert : ICloneable, IEquatable<Vert>, IGroup<float>
|
||||
public static Vert Cross(Vert a, Vert b, bool normalized = false) =>
|
||||
new(Float3.Cross(a.position, b.position, normalized));
|
||||
public static float Dot(Vert a, Vert b) => Float3.Dot(a.position, b.position);
|
||||
public static float Dot(params Vert[] vals) => Float3.Dot(ToDouble3Array(vals));
|
||||
public static float Dot(params Vert[] vals) => Float3.Dot(ToFloat3Array(vals));
|
||||
public static Vert Floor(Vert val) => new(Float3.Floor(val.position));
|
||||
public static Vert Lerp(Vert a, Vert b, float t, bool clamp = true) =>
|
||||
new(Float3.Lerp(a.position, b.position, t, clamp));
|
||||
public static Vert Median(params Vert[] vals) =>
|
||||
Float3.Median(ToDouble3Array(vals));
|
||||
Float3.Median(ToFloat3Array(vals));
|
||||
public static Vert Max(params Vert[] vals) =>
|
||||
Float3.Max(ToDouble3Array(vals));
|
||||
Float3.Max(ToFloat3Array(vals));
|
||||
public static Vert Min(params Vert[] vals) =>
|
||||
Float3.Min(ToDouble3Array(vals));
|
||||
public static Float3[] ToDouble3Array(params Vert[] vals)
|
||||
Float3.Min(ToFloat3Array(vals));
|
||||
|
||||
public static Float3[] ToFloat3Array(params Vert[] vals)
|
||||
{
|
||||
Float3[] doubles = new Float3[vals.Length];
|
||||
for (int i = 0; i < vals.Length; i++) doubles[i] = vals[i].position;
|
||||
return doubles;
|
||||
Float3[] floats = new Float3[vals.Length];
|
||||
for (int i = 0; i < vals.Length; i++) floats[i] = vals[i].position;
|
||||
return floats;
|
||||
}
|
||||
public static List<Float3> ToDouble3List(params Vert[] vals) => ToDouble3Array(vals).ToList();
|
||||
public static List<Float3> ToFloat3List(params Vert[] vals) => ToFloat3Array(vals).ToList();
|
||||
|
||||
public override bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
|
||||
@ -88,7 +88,7 @@ public struct Int2 : ICloneable, IComparable<Int2>, IEquatable<Int2>, IGroup<int
|
||||
new(Mathf.Lerp(a.x, b.x, t, clamp), Mathf.Lerp(a.y, b.y, t, clamp));
|
||||
public static Int2 Median(params Int2[] vals)
|
||||
{
|
||||
int index = Mathf.Average(0, vals.Length - 1);
|
||||
float index = Mathf.Average(0, vals.Length - 1);
|
||||
Int2 valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
|
||||
return Average(valA, valB);
|
||||
}
|
||||
@ -106,7 +106,7 @@ public struct Int2 : ICloneable, IComparable<Int2>, IEquatable<Int2>, IGroup<int
|
||||
foreach (Int2 d in vals) val = d < val ? d : val;
|
||||
return val;
|
||||
}
|
||||
public static Int2 Multiply(params Int2[] vals)
|
||||
public static Int2 Product(params Int2[] vals)
|
||||
{
|
||||
if (vals.Length < 1) return Zero;
|
||||
Int2 val = One;
|
||||
@ -125,6 +125,17 @@ public struct Int2 : ICloneable, IComparable<Int2>, IEquatable<Int2>, IGroup<int
|
||||
return val;
|
||||
}
|
||||
|
||||
public static (int[] Xs, int[] Ys) SplitArray(params Int2[] vals)
|
||||
{
|
||||
int[] Xs = new int[vals.Length], Ys = new int[vals.Length];
|
||||
for (int i = 0; i < vals.Length; i++)
|
||||
{
|
||||
Xs[i] = vals[i].x;
|
||||
Ys[i] = vals[i].y;
|
||||
}
|
||||
return (Xs, Ys);
|
||||
}
|
||||
|
||||
public int CompareTo(Int2 other) => Magnitude.CompareTo(other.Magnitude);
|
||||
public override bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
|
||||
@ -108,7 +108,7 @@ public struct Int3 : ICloneable, IComparable<Int3>, IEquatable<Int3>, IGroup<int
|
||||
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));
|
||||
public static Int3 Median(params Int3[] vals)
|
||||
{
|
||||
int index = Mathf.Average(0, vals.Length - 1);
|
||||
float index = Mathf.Average(0, vals.Length - 1);
|
||||
Int3 valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
|
||||
return Average(valA, valB);
|
||||
}
|
||||
@ -126,7 +126,7 @@ public struct Int3 : ICloneable, IComparable<Int3>, IEquatable<Int3>, IGroup<int
|
||||
foreach (Int3 d in vals) val = d < val ? d : val;
|
||||
return val;
|
||||
}
|
||||
public static Int3 Multiply(params Int3[] vals)
|
||||
public static Int3 Product(params Int3[] vals)
|
||||
{
|
||||
if (vals.Length < 1) return Zero;
|
||||
Int3 val = One;
|
||||
@ -144,6 +144,18 @@ public struct Int3 : ICloneable, IComparable<Int3>, IEquatable<Int3>, IGroup<int
|
||||
foreach (Int3 d in vals) val += d;
|
||||
return val;
|
||||
}
|
||||
|
||||
public static (int[] Xs, int[] Ys, int[] Zs) SplitArray(params Int3[] vals)
|
||||
{
|
||||
int[] Xs = new int[vals.Length], Ys = new int[vals.Length], Zs = new int[vals.Length];
|
||||
for (int i = 0; i < vals.Length; i++)
|
||||
{
|
||||
Xs[i] = vals[i].x;
|
||||
Ys[i] = vals[i].y;
|
||||
Zs[i] = vals[i].z;
|
||||
}
|
||||
return (Xs, Ys, Zs);
|
||||
}
|
||||
public int CompareTo(Int3 other) => Magnitude.CompareTo(other.Magnitude);
|
||||
public override bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
@ -193,5 +205,9 @@ public struct Int3 : ICloneable, IComparable<Int3>, IEquatable<Int3>, IGroup<int
|
||||
public static explicit operator Int3(Int4 val) => new(val.x, val.y, val.z);
|
||||
public static explicit operator Int3(Vert val) => new((int)val.position.x, (int)val.position.y,
|
||||
(int)val.position.z);
|
||||
public static explicit operator Int3(RGBA val) => (Int3)val.ToRGBAByte();
|
||||
public static explicit operator Int3(HSVA val) => (Int3)val.ToHSVAByte();
|
||||
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);
|
||||
}
|
||||
|
||||
@ -121,7 +121,7 @@ public struct Int4 : ICloneable, IComparable<Int4>, IEquatable<Int4>, IGroup<int
|
||||
Mathf.Lerp(a.w, b.w, t, clamp));
|
||||
public static Int4 Median(params Int4[] vals)
|
||||
{
|
||||
int index = Mathf.Average(0, vals.Length - 1);
|
||||
float index = Mathf.Average(0, vals.Length - 1);
|
||||
Int4 valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
|
||||
return Average(valA, valB);
|
||||
}
|
||||
@ -139,7 +139,7 @@ public struct Int4 : ICloneable, IComparable<Int4>, IEquatable<Int4>, IGroup<int
|
||||
foreach (Int4 d in vals) val = d < val ? d : val;
|
||||
return val;
|
||||
}
|
||||
public static Int4 Multiply(params Int4[] vals)
|
||||
public static Int4 Product(params Int4[] vals)
|
||||
{
|
||||
if (vals.Length < 1) return Zero;
|
||||
Int4 val = One;
|
||||
@ -158,6 +158,20 @@ public struct Int4 : ICloneable, IComparable<Int4>, IEquatable<Int4>, IGroup<int
|
||||
return val;
|
||||
}
|
||||
|
||||
public static (int[] Xs, int[] Ys, int[] Zs, int[] Ws) SplitArray(params Int4[] vals)
|
||||
{
|
||||
int[] Xs = new int[vals.Length], Ys = new int[vals.Length], Zs = new int[vals.Length],
|
||||
Ws = new int[vals.Length];
|
||||
for (int i = 0; i < vals.Length; i++)
|
||||
{
|
||||
Xs[i] = vals[i].x;
|
||||
Ys[i] = vals[i].y;
|
||||
Zs[i] = vals[i].z;
|
||||
Ws[i] = vals[i].w;
|
||||
}
|
||||
return (Xs, Ys, Zs, Ws);
|
||||
}
|
||||
|
||||
public int CompareTo(Int4 other) => Magnitude.CompareTo(other.Magnitude);
|
||||
public override bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
@ -212,5 +226,11 @@ public struct Int4 : ICloneable, IComparable<Int4>, IEquatable<Int4>, IGroup<int
|
||||
public static implicit operator Int4(Int3 val) => new(val.x, val.y, val.z, 0);
|
||||
public static explicit operator Int4(Vert val) => new((int)val.position.x, (int)val.position.y,
|
||||
(int)val.position.z, 0);
|
||||
public static explicit operator Int4(RGBA val) => val.ToRGBAByte();
|
||||
public static explicit operator Int4(CMYKA val) => (Int4)val.ToCMYKAByte();
|
||||
public static explicit operator Int4(HSVA val) => val.ToHSVAByte();
|
||||
public static implicit operator Int4(RGBAByte val) => new(val.R, val.G, val.B, val.A);
|
||||
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);
|
||||
}
|
||||
|
||||
@ -2,18 +2,10 @@
|
||||
|
||||
public static class Mathf
|
||||
{
|
||||
public const float RadToDeg = 0.0174532925199f; // Pi / 180
|
||||
public const float E = 2.71828182846f;
|
||||
public const float GoldenRatio = 1.61803398875f; // (1 + Sqrt(5)) / 2
|
||||
public const float HalfPi = 1.57079632679f; // Pi / 2
|
||||
public const float Pi = 3.14159265359f;
|
||||
public const float DegToRad = 57.2957795131f; // 180 / Pi
|
||||
public const float Tau = 6.28318530718f; // 2 * Pi
|
||||
|
||||
public static float Absolute(float val) => val < 0 ? -val : val;
|
||||
public static int Absolute(int val) => val < 0 ? -val : val;
|
||||
|
||||
public static float ArcCos(float value) => -ArcSin(value) + HalfPi;
|
||||
public static float ArcCos(float value) => -ArcSin(value) + Constants.HalfPi;
|
||||
|
||||
public static float ArcCot(float value) => ArcCos(value / Sqrt(1 + value * value));
|
||||
|
||||
@ -33,14 +25,14 @@ public static class Mathf
|
||||
return Average(vals.ToArray());
|
||||
}
|
||||
public static float Average(params float[] vals) => Sum(vals) / vals.Length;
|
||||
public static int Average(params int[] vals) => Sum(vals) / vals.Length;
|
||||
public static float Average(params int[] vals) => Sum(vals) / (float)vals.Length;
|
||||
|
||||
public static int Ceiling(float val) => (int)(val + (1 - (val % 1)));
|
||||
|
||||
public static float Clamp(float val, float min, float max)
|
||||
{
|
||||
if (max < min) throw new ArgumentOutOfRangeException(nameof(max),
|
||||
nameof(max) + " must be greater than or equal to " + nameof(min));
|
||||
nameof(max) + " must be greater than or equal to " + nameof(min) + ".");
|
||||
val = val < min ? min : val;
|
||||
val = val > max ? max : val;
|
||||
return val;
|
||||
@ -54,7 +46,11 @@ public static class Mathf
|
||||
return val;
|
||||
}
|
||||
|
||||
public static float Cos(float radians) => Sin(radians + HalfPi);
|
||||
// nCr (n = total, r = size)
|
||||
public static int Combinations(int total, int size) => Factorial(total) /
|
||||
(Factorial(size) * Factorial(total - size));
|
||||
|
||||
public static float Cos(float radians) => Sin(radians + Constants.HalfPi);
|
||||
|
||||
public static float Cot(float radians) => Cos(radians) / Sin(radians);
|
||||
|
||||
@ -89,6 +85,22 @@ public static class Mathf
|
||||
return vals;
|
||||
}
|
||||
|
||||
public static int GreatestCommonFactor(params int[] vals)
|
||||
{
|
||||
int loops = Min(vals);
|
||||
for (int i = loops; i > 0; i--)
|
||||
{
|
||||
bool fit = true;
|
||||
foreach (int v in vals) fit &= v % i == 0;
|
||||
if (fit) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static float InverseSqrt(float val) => 1 / Sqrt(val);
|
||||
|
||||
public static int LeastCommonMultiple(params int[] vals) => Product(vals) / GreatestCommonFactor(vals);
|
||||
|
||||
public static float Lerp(float a, float b, float t, bool clamp = true)
|
||||
{
|
||||
float v = a + t * (b - a);
|
||||
@ -169,27 +181,61 @@ public static class Mathf
|
||||
return val;
|
||||
}
|
||||
|
||||
public static float Multiply(params float[] vals)
|
||||
public static (T value, int occurences) Mode<T>(params T[] vals) where T : IEquatable<T>
|
||||
{
|
||||
if (vals.Length < 1) throw new ArgumentException("List must contain at least 1 element.", nameof(vals));
|
||||
(T value, int occurences) = (vals[0], -1);
|
||||
for (int i = 0; i < vals.Length; i++)
|
||||
{
|
||||
int count = vals.Count(x => x.Equals(vals[i]));
|
||||
if (count > occurences)
|
||||
{
|
||||
value = vals[i];
|
||||
occurences = count;
|
||||
}
|
||||
}
|
||||
return (value, occurences);
|
||||
}
|
||||
|
||||
// nPr (n = total, r = size)
|
||||
public static int Permutations(int total, int size) => Factorial(total) / Factorial(total - size);
|
||||
|
||||
public static float Product(params float[] vals)
|
||||
{
|
||||
if (vals.Length < 1) return 0;
|
||||
float val = 1;
|
||||
foreach (float d in vals) val *= d;
|
||||
return val;
|
||||
}
|
||||
public static int Multiply(params int[] vals)
|
||||
public static int Product(params int[] vals)
|
||||
{
|
||||
if (vals.Length < 1) return 0;
|
||||
int val = 1;
|
||||
foreach (int i in vals) val *= i;
|
||||
return val;
|
||||
}
|
||||
public static float Product(Equation equ, float min, float max, float step = 1)
|
||||
{
|
||||
float total = 1;
|
||||
for (float f = min; f <= max; f += step) total *= equ(f);
|
||||
return total;
|
||||
}
|
||||
|
||||
public static float Power(float num, float pow) => (float)Math.Pow(num, pow);
|
||||
public static float Power(float num, int pow)
|
||||
{
|
||||
if (pow < 0) return 0;
|
||||
float val = 1;
|
||||
for (int i = 0; i < Absolute(pow); i++) val *= num;
|
||||
if (pow < 1) val = 1 / val;
|
||||
return val;
|
||||
}
|
||||
public static int Power(int num, int pow)
|
||||
{
|
||||
if (pow < 0) return 0;
|
||||
int val = 1;
|
||||
for (int i = 0; i < Absolute(pow); i++) val *= num;
|
||||
if (pow < 1) val = 1 / val;
|
||||
return val;
|
||||
}
|
||||
|
||||
@ -214,7 +260,7 @@ public static class Mathf
|
||||
h = -0.000577413f,
|
||||
i = 0.0000613134f,
|
||||
j = -0.00000216852f;
|
||||
float x = radians % Tau;
|
||||
float x = radians % Constants.Tau;
|
||||
|
||||
return
|
||||
a + (b * x) + (c * x * x) + (d * x * x * x) + (e * x * x * x * x) + (f * x * x * x * x * x)
|
||||
@ -247,6 +293,36 @@ public static class Mathf
|
||||
foreach (int i in vals) val += i;
|
||||
return val;
|
||||
}
|
||||
public static float Sum(Equation equ, float min, float max, float step = 1)
|
||||
{
|
||||
float total = 0;
|
||||
for (float f = min; f <= max; f += step) total += equ(f);
|
||||
return total;
|
||||
}
|
||||
|
||||
// Known as stdev
|
||||
public static float StandardDeviation(params float[] vals) => Sqrt(Variance(vals));
|
||||
|
||||
public static float Tan(float radians) => Sin(radians) / Cos(radians);
|
||||
|
||||
public static T[] UniqueItems<T>(params T[] vals) where T : IEquatable<T>
|
||||
{
|
||||
List<T> unique = new();
|
||||
foreach (T item in vals) if (!unique.Any(x => x.Equals(item))) unique.Add(item);
|
||||
return unique.ToArray();
|
||||
}
|
||||
|
||||
public static float Variance(params float[] vals)
|
||||
{
|
||||
float mean = Average(vals), sum = 0;
|
||||
for (int i = 0; i < vals.Length; i++)
|
||||
{
|
||||
float val = vals[i] - mean;
|
||||
sum += val * val;
|
||||
}
|
||||
return sum / vals.Length;
|
||||
}
|
||||
|
||||
public static float ZScore(float val, params float[] vals) => ZScore(val, Average(vals), StandardDeviation(vals));
|
||||
public static float ZScore(float val, float mean, float stdev) => (val - mean) / stdev;
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ global using System.Net.Http;
|
||||
global using System.Threading;
|
||||
global using System.Threading.Tasks;
|
||||
global using Nerd_STF;
|
||||
global using Nerd_STF.Graphics;
|
||||
global using Nerd_STF.Exceptions;
|
||||
global using Nerd_STF.Mathematics;
|
||||
global using Nerd_STF.Mathematics.Geometry;
|
||||
|
||||
Binary file not shown.
@ -7,7 +7,7 @@ Nerd_STF is a C# library that runs on .Net 6.0, and contains added structures an
|
||||
Nerd_STF has recently been remade, completely rebuilding many of its topics.
|
||||
|
||||
## What does it include?
|
||||
Nerd_STF will include math structures as well as other computer science topics. Right now, it is mainly focused on mathematics, but will branch out in the future. It currently contains things like lists of 3 doubles or ints, or `Vert`, `Line`, and `Triangle` classes, that are rich in implementation.
|
||||
Nerd_STF will include math structures as well as other computer science topics. Right now, it is mainly focused on mathematics, but will branch out in the future. It currently contains things like lists of 3 floats or ints, or `Vert`, `Line`, and `Triangle` classes, that are rich in implementation.
|
||||
|
||||
## What about Nerd_STF Versions `2021`?
|
||||
Nerd_STF `2021` used an different version scheme, based on the year, as you might have guessed (it is not the year `2` right now), and while I will be keeping the `2021` versions up, I wouldn't recommend using them, and the code is old code, written by a more naive me. Hell, I wrote an entire `List<T>` class there before I knew of the `System.Collections.Generic.List<T>` class that did literally everything for me already. Oh well. So, keep that in mind when you check out those versions of the library.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user