Tested HSV and untested CMYK types. This beta will concern colors.
This commit is contained in:
parent
866326863b
commit
e070aa097f
365
Nerd_STF/Graphics/ColorCMYK.cs
Normal file
365
Nerd_STF/Graphics/ColorCMYK.cs
Normal file
@ -0,0 +1,365 @@
|
||||
using Nerd_STF.Mathematics;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Nerd_STF.Graphics
|
||||
{
|
||||
public struct ColorCMYK : IColor<ColorCMYK>,
|
||||
IEnumerable<double>
|
||||
#if CS11_OR_GREATER
|
||||
,IFromTuple<ColorCMYK, (double, double, double, double)>,
|
||||
IFromTuple<ColorCMYK, (double, double, double, double, double)>,
|
||||
ISplittable<ColorCMYK, (double[] Cs, double[] Ms, double[] Ys, double[] Ks, double[] As)>
|
||||
#endif
|
||||
{
|
||||
public static int ChannelCount => 5;
|
||||
|
||||
public static ColorCMYK Black => new ColorCMYK(0 , 0 , 0 , 1 , 1);
|
||||
public static ColorCMYK Blue => new ColorCMYK(1 , 1 , 0 , 0 , 1);
|
||||
public static ColorCMYK Clear => new ColorCMYK(0 , 0 , 0 , 0 , 0);
|
||||
public static ColorCMYK Cyan => new ColorCMYK(1 , 0 , 0 , 0 , 1);
|
||||
public static ColorCMYK Gray => new ColorCMYK(0 , 0 , 0 , 0.5, 1);
|
||||
public static ColorCMYK Green => new ColorCMYK(1 , 0 , 1 , 0 , 1);
|
||||
public static ColorCMYK Magenta => new ColorCMYK(0 , 1 , 0 , 0 , 1);
|
||||
public static ColorCMYK Orange => new ColorCMYK(0 , 0.5, 1 , 0 , 1);
|
||||
public static ColorCMYK Purple => new ColorCMYK(0.5, 1 , 0 , 0 , 1);
|
||||
public static ColorCMYK Red => new ColorCMYK(0 , 1 , 1 , 0 , 1);
|
||||
public static ColorCMYK White => new ColorCMYK(0 , 0 , 0 , 0 , 1);
|
||||
public static ColorCMYK Yellow => new ColorCMYK(0 , 0 , 1 , 0 , 1);
|
||||
|
||||
public double c, m, y, k, a;
|
||||
|
||||
public ColorCMYK(double cyan, double magenta, double yellow, double black)
|
||||
{
|
||||
c = cyan;
|
||||
m = magenta;
|
||||
y = yellow;
|
||||
k = black;
|
||||
a = 1;
|
||||
}
|
||||
public ColorCMYK(double cyan, double magenta, double yellow, double black, double alpha)
|
||||
{
|
||||
c = cyan;
|
||||
m = magenta;
|
||||
y = yellow;
|
||||
k = black;
|
||||
a = alpha;
|
||||
}
|
||||
public ColorCMYK(IEnumerable<double> nums)
|
||||
{
|
||||
c = 0;
|
||||
m = 0;
|
||||
y = 0;
|
||||
k = 0;
|
||||
a = 1;
|
||||
|
||||
int index = 0;
|
||||
foreach (double item in nums)
|
||||
{
|
||||
this[index] = item;
|
||||
index++;
|
||||
if (index == 5) break;
|
||||
}
|
||||
}
|
||||
public ColorCMYK(Fill<double> fill)
|
||||
{
|
||||
c = fill(0);
|
||||
m = fill(1);
|
||||
y = fill(2);
|
||||
k = fill(3);
|
||||
a = fill(4);
|
||||
}
|
||||
|
||||
public double this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: return c;
|
||||
case 1: return m;
|
||||
case 2: return y;
|
||||
case 3: return k;
|
||||
case 4: return a;
|
||||
default: throw new ArgumentOutOfRangeException(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 ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
public ListTuple<double> this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
double[] items = new double[key.Length];
|
||||
for (int i = 0; i < key.Length; i++)
|
||||
{
|
||||
char c = key[i];
|
||||
switch (c)
|
||||
{
|
||||
case 'c': items[i] = c; break;
|
||||
case 'm': items[i] = m; break;
|
||||
case 'y': items[i] = y; break;
|
||||
case 'k': items[i] = k; break;
|
||||
case 'a': items[i] = a; break;
|
||||
default: throw new ArgumentException("Invalid key.", nameof(key));
|
||||
}
|
||||
}
|
||||
return new ListTuple<double>(items);
|
||||
}
|
||||
set
|
||||
{
|
||||
IEnumerator<double> stepper = value.GetEnumerator();
|
||||
for (int i = 0; i < key.Length; i++)
|
||||
{
|
||||
char c = key[i];
|
||||
stepper.MoveNext();
|
||||
switch (c)
|
||||
{
|
||||
case 'c': this.c = stepper.Current; break;
|
||||
case 'm': m = stepper.Current; break;
|
||||
case 'y': y = stepper.Current; break;
|
||||
case 'k': k = stepper.Current; break;
|
||||
case 'a': a = stepper.Current; break;
|
||||
default: throw new ArgumentException("Invalid key.", nameof(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ColorCMYK Average(double gamma, IEnumerable<ColorCMYK> colors)
|
||||
{
|
||||
double avgC = 0, avgM = 0, avgY = 0, avgK = 0, avgA = 0;
|
||||
int count = 0;
|
||||
foreach (ColorCMYK color in colors)
|
||||
{
|
||||
double correctC = MathE.Pow(color.c, gamma),
|
||||
correctM = MathE.Pow(color.m, gamma),
|
||||
correctY = MathE.Pow(color.y, gamma),
|
||||
correctK = MathE.Pow(color.k, gamma);
|
||||
// Gamma doesn't apply to the alpha channel.
|
||||
|
||||
avgC += correctC;
|
||||
avgM += correctM;
|
||||
avgY += correctY;
|
||||
avgK += correctK;
|
||||
avgA += color.a;
|
||||
count++;
|
||||
}
|
||||
avgC /= count;
|
||||
avgM /= count;
|
||||
avgY /= count;
|
||||
avgK /= count;
|
||||
avgA /= count;
|
||||
double invGamma = 1 / gamma;
|
||||
return new ColorCMYK(MathE.Pow(avgC, invGamma),
|
||||
MathE.Pow(avgM, invGamma),
|
||||
MathE.Pow(avgY, invGamma),
|
||||
MathE.Pow(avgK, invGamma),
|
||||
avgA);
|
||||
}
|
||||
#if CS11_OR_GREATER
|
||||
static ColorCMYK IColor<ColorCMYK>.Average(IEnumerable<ColorCMYK> colors) => Average(1, colors);
|
||||
#endif
|
||||
public static ColorCMYK Clamp(ColorCMYK color, ColorCMYK min, ColorCMYK max) =>
|
||||
new ColorCMYK(MathE.Clamp(color.c, min.c, max.c),
|
||||
MathE.Clamp(color.m, min.m, max.m),
|
||||
MathE.Clamp(color.y, min.y, max.y),
|
||||
MathE.Clamp(color.k, min.k, max.k),
|
||||
MathE.Clamp(color.a, min.a, max.a));
|
||||
public static ColorCMYK Lerp(double gamma, ColorCMYK a, ColorCMYK b, double t, bool clamp = true)
|
||||
{
|
||||
double aCorrectedC = MathE.Pow(a.c, gamma), bCorrectedC = MathE.Pow(b.c, gamma),
|
||||
aCorrectedM = MathE.Pow(a.m, gamma), bCorrectedM = MathE.Pow(b.m, gamma),
|
||||
aCorrectedY = MathE.Pow(a.y, gamma), bCorrectedY = MathE.Pow(b.y, gamma),
|
||||
aCorrectedK = MathE.Pow(a.k, gamma), bCorrectedK = MathE.Pow(b.k, gamma);
|
||||
|
||||
double newC = MathE.Lerp(aCorrectedC, bCorrectedC, t, clamp),
|
||||
newM = MathE.Lerp(aCorrectedM, bCorrectedM, t, clamp),
|
||||
newY = MathE.Lerp(aCorrectedY, bCorrectedY, t, clamp),
|
||||
newK = MathE.Lerp(aCorrectedK, bCorrectedK, t, clamp),
|
||||
newA = MathE.Lerp(a.a, b.a, t, clamp);
|
||||
|
||||
double invGamma = 1 / gamma;
|
||||
return new ColorCMYK(MathE.Pow(newC, invGamma),
|
||||
MathE.Pow(newM, invGamma),
|
||||
MathE.Pow(newY, invGamma),
|
||||
MathE.Pow(newK, invGamma),
|
||||
newA);
|
||||
}
|
||||
#if CS11_OR_GREATER
|
||||
static ColorCMYK IInterpolable<ColorCMYK>.Lerp(ColorCMYK a, ColorCMYK b, double t, bool clamp) => Lerp(1, a, b, t, clamp);
|
||||
#endif
|
||||
public static ColorCMYK Product(IEnumerable<ColorCMYK> colors)
|
||||
{
|
||||
bool any = false;
|
||||
ColorCMYK result = new ColorCMYK(1, 1, 1, 1, 1);
|
||||
foreach (ColorCMYK color in colors)
|
||||
{
|
||||
any = true;
|
||||
result *= color;
|
||||
}
|
||||
return any ? result : Black;
|
||||
}
|
||||
public static ColorCMYK Sum(IEnumerable<ColorCMYK> colors)
|
||||
{
|
||||
bool any = false;
|
||||
ColorCMYK result = new ColorCMYK(0, 0, 0, 0);
|
||||
foreach (ColorCMYK color in colors)
|
||||
{
|
||||
any = true;
|
||||
result += color;
|
||||
}
|
||||
return any ? result : Black;
|
||||
}
|
||||
public static (double[] Cs, double[] Ms, double[] Ys, double[] Ks, double[] As) SplitArray(IEnumerable<ColorCMYK> colors)
|
||||
{
|
||||
int count = colors.Count();
|
||||
double[] Cs = new double[count], Ms = new double[count], Ys = new double[count], Ks = new double[count], As = new double[count];
|
||||
int index = 0;
|
||||
foreach (ColorCMYK c in colors)
|
||||
{
|
||||
Cs[index] = c.c;
|
||||
Ms[index] = c.m;
|
||||
Ys[index] = c.y;
|
||||
Ks[index] = c.k;
|
||||
As[index] = c.a;
|
||||
index++;
|
||||
}
|
||||
return (Cs, Ms, Ys, Ks, As);
|
||||
}
|
||||
|
||||
public Dictionary<ColorChannel, double> GetChannels() => new Dictionary<ColorChannel, double>()
|
||||
{
|
||||
{ ColorChannel.Cyan, c },
|
||||
{ ColorChannel.Magenta, m },
|
||||
{ ColorChannel.Yellow, y },
|
||||
{ ColorChannel.Key, k },
|
||||
{ ColorChannel.Alpha, a }
|
||||
};
|
||||
|
||||
public TColor AsColor<TColor>() where TColor : struct, IColor<TColor>
|
||||
{
|
||||
Type type = typeof(TColor);
|
||||
if (type == typeof(ColorRGB)) return (TColor)(object)AsRgb();
|
||||
else if (type == typeof(ColorHSV)) return (TColor)(object)AsHsv();
|
||||
else if (type == typeof(ColorCMYK)) return (TColor)(object)this;
|
||||
else throw new InvalidCastException();
|
||||
}
|
||||
public ColorRGB AsRgb()
|
||||
{
|
||||
// Thanks https://www.rapidtables.com/convert/color/cmyk-to-rgb.html
|
||||
double diffK = 1 - k;
|
||||
return new ColorRGB((1 - c) * diffK,
|
||||
(1 - m) * diffK,
|
||||
(1 - y) * diffK);
|
||||
}
|
||||
public ColorHSV AsHsv()
|
||||
{
|
||||
// Inlined version of AsRgb().AsHsv()
|
||||
double diffK = 1 - k;
|
||||
double r = (1 - c) * diffK, g = (1 - m) * diffK, b = (1 - y) * diffK;
|
||||
double[] items = new double[] { r, g, b };
|
||||
double cMax = MathE.Max(items), cMin = MathE.Min(items), delta = cMax - cMin;
|
||||
Angle h;
|
||||
|
||||
if (delta == 0) h = Angle.Zero;
|
||||
else if (cMax == r) h = Angle.FromDegrees(60 * MathE.ModAbs((g - b) / delta, 6));
|
||||
else if (cMax == g) h = Angle.FromDegrees(60 * ((b - r) / delta + 2));
|
||||
else if (cMax == b) h = Angle.FromDegrees(60 * ((r - g) / delta + 4));
|
||||
else h = Angle.Zero;
|
||||
|
||||
double s = cMax == 0 ? 0 : delta / cMax;
|
||||
return new ColorHSV(h, s, cMax);
|
||||
}
|
||||
public ColorCMYK AsCmyk() => this;
|
||||
|
||||
public string HexCode() => AsRgb().HexCode();
|
||||
|
||||
public IEnumerator<double> GetEnumerator()
|
||||
{
|
||||
yield return c;
|
||||
yield return m;
|
||||
yield return y;
|
||||
yield return k;
|
||||
yield return a;
|
||||
}
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
public void Deconstruct(out double c, out double m, out double y, out double k)
|
||||
{
|
||||
c = this.c;
|
||||
m = this.m;
|
||||
y = this.y;
|
||||
k = this.k;
|
||||
}
|
||||
public void Deconstruct(out double c, out double m, out double y, out double k, out double a)
|
||||
{
|
||||
c = this.c;
|
||||
m = this.m;
|
||||
y = this.y;
|
||||
k = this.k;
|
||||
a = this.a;
|
||||
}
|
||||
|
||||
public bool Equals(ColorCMYK other)
|
||||
{
|
||||
if (a <= 0 && other.a <= 0) return true;
|
||||
else if (k >= 1 && other.k >= 1) return true;
|
||||
else return c == other.c && m == other.m && y == other.y && k == other.k && a == other.a;
|
||||
}
|
||||
public bool Equals(IColor other) => Equals(other.AsCmyk());
|
||||
#if CS8_OR_GREATER
|
||||
public override bool Equals(object? other)
|
||||
#else
|
||||
public override bool Equals(object other)
|
||||
#endif
|
||||
{
|
||||
if (other is IColor color) return Equals(color.AsRgb());
|
||||
else return false;
|
||||
}
|
||||
public override int GetHashCode() => base.GetHashCode();
|
||||
public override string ToString() => $"{{ c={c:0.00}, m={m:0.00}, y={y:0.00}, k={k:0.00}, a={a:0.00} }}";
|
||||
|
||||
public double[] ToArray() => new double[] { c, m, y, k, a };
|
||||
public Fill<double> ToFill()
|
||||
{
|
||||
ColorCMYK copy = this;
|
||||
return i => copy[i];
|
||||
}
|
||||
public List<double> ToList() => new List<double> { c, m, y, k, a };
|
||||
|
||||
public static ColorCMYK operator +(ColorCMYK a, ColorCMYK b) => new ColorCMYK(a.c + b.c, a.m + b.m, a.y + b.y, a.k + b.k, 1 - (1 - a.a) * (1 - b.a));
|
||||
public static ColorCMYK operator *(ColorCMYK a, ColorCMYK b) => new ColorCMYK(a.c * b.c, a.m * b.m, a.y * b.y, a.k * b.k, a.a * b.a);
|
||||
public static ColorCMYK operator *(ColorCMYK a, double b) => new ColorCMYK(a.c * b, a.m * b, a.y * b, a.k * b, a.a);
|
||||
public static bool operator ==(ColorCMYK a, IColor b) => a.Equals(b.AsCmyk());
|
||||
public static bool operator !=(ColorCMYK a, IColor b) => !a.Equals(b.AsCmyk());
|
||||
public static bool operator ==(ColorCMYK a, ColorCMYK b) => a.Equals(b);
|
||||
public static bool operator !=(ColorCMYK a, ColorCMYK b) => !a.Equals(b);
|
||||
|
||||
public static implicit operator ColorCMYK(ListTuple<double> tuple)
|
||||
{
|
||||
if (tuple.Length == 4) return new ColorCMYK(tuple[0], tuple[1], tuple[2], tuple[3]);
|
||||
else if (tuple.Length == 5) return new ColorCMYK(tuple[0], tuple[1], tuple[2], tuple[3], tuple[4]);
|
||||
else throw new InvalidCastException();
|
||||
}
|
||||
public static implicit operator ColorCMYK((double, double, double, double) tuple) => new ColorCMYK(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4);
|
||||
public static implicit operator ColorCMYK((double, double, double, double, double) tuple) => new ColorCMYK(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4, tuple.Item5);
|
||||
|
||||
public static implicit operator ListTuple<double>(ColorCMYK color) => new ListTuple<double>(color.c, color.m, color.y, color.k, color.a);
|
||||
public static implicit operator ValueTuple<double, double, double, double>(ColorCMYK color) => (color.c, color.m, color.y, color.k);
|
||||
public static implicit operator ValueTuple<double, double, double, double, double>(ColorCMYK color) => (color.c, color.m, color.y, color.k, color.a);
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,13 @@
|
||||
Red,
|
||||
Green,
|
||||
Blue,
|
||||
Alpha
|
||||
Alpha,
|
||||
Hue,
|
||||
Saturation,
|
||||
Value,
|
||||
Cyan,
|
||||
Magenta,
|
||||
Yellow,
|
||||
Key
|
||||
}
|
||||
}
|
||||
|
||||
350
Nerd_STF/Graphics/ColorHSV.cs
Normal file
350
Nerd_STF/Graphics/ColorHSV.cs
Normal file
@ -0,0 +1,350 @@
|
||||
using Nerd_STF.Helpers;
|
||||
using Nerd_STF.Mathematics;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Nerd_STF.Graphics
|
||||
{
|
||||
public struct ColorHSV : IColor<ColorHSV>,
|
||||
IEnumerable<double>
|
||||
#if CS11_OR_GREATER
|
||||
,IFromTuple<ColorHSV, (Angle, double, double)>,
|
||||
IFromTuple<ColorHSV, (double, double, double)>,
|
||||
IFromTuple<ColorHSV, (Angle, double, double, double)>,
|
||||
IFromTuple<ColorHSV, (double, double, double, double)>,
|
||||
ISplittable<ColorHSV, (Angle[] Hs, double[] Ss, double[] Vs, double[] As)>
|
||||
#endif
|
||||
{
|
||||
public static int ChannelCount => 4;
|
||||
|
||||
public static ColorHSV Black => new ColorHSV(Angle.FromDegrees( 0), 0, 0 , 1);
|
||||
public static ColorHSV Blue => new ColorHSV(Angle.FromDegrees(240), 1, 1 , 1);
|
||||
public static ColorHSV Clear => new ColorHSV(Angle.FromDegrees( 0), 0, 0 , 0);
|
||||
public static ColorHSV Cyan => new ColorHSV(Angle.FromDegrees(180), 1, 1 , 1);
|
||||
public static ColorHSV Gray => new ColorHSV(Angle.FromDegrees( 0), 0, 0.5, 1);
|
||||
public static ColorHSV Green => new ColorHSV(Angle.FromDegrees(120), 1, 1 , 1);
|
||||
public static ColorHSV Magenta => new ColorHSV(Angle.FromDegrees(300), 1, 1 , 1);
|
||||
public static ColorHSV Orange => new ColorHSV(Angle.FromDegrees( 30), 1, 1 , 1);
|
||||
public static ColorHSV Purple => new ColorHSV(Angle.FromDegrees(270), 1, 1 , 1);
|
||||
public static ColorHSV Red => new ColorHSV(Angle.FromDegrees( 0), 1, 1 , 1);
|
||||
public static ColorHSV White => new ColorHSV(Angle.FromDegrees( 0), 0, 1 , 1);
|
||||
public static ColorHSV Yellow => new ColorHSV(Angle.FromDegrees( 60), 0, 1 , 1);
|
||||
|
||||
public Angle h;
|
||||
public double s, v, a;
|
||||
|
||||
public ColorHSV(Angle hue)
|
||||
{
|
||||
h = hue.Normalized;
|
||||
s = 1;
|
||||
v = 1;
|
||||
a = 1;
|
||||
}
|
||||
public ColorHSV(double hue)
|
||||
{
|
||||
h = Angle.FromRevolutions(hue);
|
||||
s = 1;
|
||||
v = 1;
|
||||
a = 1;
|
||||
}
|
||||
public ColorHSV(Angle hue, double saturation, double value)
|
||||
{
|
||||
h = hue.Normalized;
|
||||
s = saturation;
|
||||
v = value;
|
||||
a = 1;
|
||||
}
|
||||
public ColorHSV(double hue, double saturation, double value)
|
||||
{
|
||||
h = Angle.FromRevolutions(hue);
|
||||
s = saturation;
|
||||
v = value;
|
||||
a = 1;
|
||||
}
|
||||
public ColorHSV(Angle hue, double saturation, double value, double alpha)
|
||||
{
|
||||
h = hue.Normalized;
|
||||
s = saturation;
|
||||
v = value;
|
||||
a = alpha;
|
||||
}
|
||||
public ColorHSV(double hue, double saturation, double value, double alpha)
|
||||
{
|
||||
h = Angle.FromRevolutions(hue);
|
||||
s = saturation;
|
||||
v = value;
|
||||
a = alpha;
|
||||
}
|
||||
public ColorHSV(IEnumerable<double> nums)
|
||||
{
|
||||
h = Angle.Zero;
|
||||
s = 0;
|
||||
v = 0;
|
||||
a = 1;
|
||||
|
||||
int index = 0;
|
||||
foreach (double item in nums)
|
||||
{
|
||||
this[index] = item;
|
||||
index++;
|
||||
if (index == 4) break;
|
||||
}
|
||||
}
|
||||
public ColorHSV(Fill<double> fill)
|
||||
{
|
||||
h = Angle.FromRevolutions(fill(0) % 1);
|
||||
s = fill(1);
|
||||
v = fill(2);
|
||||
a = fill(3);
|
||||
}
|
||||
|
||||
public double this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: return h.Revolutions;
|
||||
case 1: return s;
|
||||
case 2: return v;
|
||||
case 3: return a;
|
||||
default: throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: h = Angle.FromRevolutions(value); break;
|
||||
case 1: s = value; break;
|
||||
case 2: v = value; break;
|
||||
case 3: a = value; break;
|
||||
default: throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
public ListTuple<double> this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
double[] items = new double[key.Length];
|
||||
for (int i = 0; i < key.Length; i++)
|
||||
{
|
||||
char c = key[i];
|
||||
switch (c)
|
||||
{
|
||||
case 'h': items[i] = h.Revolutions; break;
|
||||
case 'H': items[i] = h.Degrees; break;
|
||||
case 's': items[i] = s; break;
|
||||
case 'v': items[i] = v; break;
|
||||
case 'a': items[i] = a; break;
|
||||
default: throw new ArgumentException("Invalid key.", nameof(key));
|
||||
}
|
||||
}
|
||||
return new ListTuple<double>(items);
|
||||
}
|
||||
set
|
||||
{
|
||||
IEnumerator<double> stepper = value.GetEnumerator();
|
||||
for (int i = 0; i < key.Length; i++)
|
||||
{
|
||||
char c = key[i];
|
||||
stepper.MoveNext();
|
||||
switch (c)
|
||||
{
|
||||
case 'h': h = Angle.FromRevolutions(stepper.Current); break;
|
||||
case 'H': h = Angle.FromDegrees(stepper.Current); break;
|
||||
case 's': s = stepper.Current; break;
|
||||
case 'v': v = stepper.Current; break;
|
||||
case 'a': a = stepper.Current; break;
|
||||
default: throw new ArgumentException("Invalid key.", nameof(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ColorHSV Average(IEnumerable<ColorHSV> colors)
|
||||
{
|
||||
Angle avgH = Angle.Zero;
|
||||
double avgS = 0, avgV = 0, avgA = 0;
|
||||
int count = 0;
|
||||
foreach (ColorHSV color in colors)
|
||||
{
|
||||
avgH += color.h;
|
||||
avgS += color.s;
|
||||
avgV += color.v;
|
||||
avgA += color.a;
|
||||
}
|
||||
return new ColorHSV(avgH / count, avgS / count, avgV / count, avgA / count);
|
||||
}
|
||||
public static ColorHSV Clamp(ColorHSV color, ColorHSV min, ColorHSV max) =>
|
||||
new ColorHSV(Angle.Clamp(color.h, min.h, max.h),
|
||||
MathE.Clamp(color.s, min.s, max.s),
|
||||
MathE.Clamp(color.v, min.v, max.v),
|
||||
MathE.Clamp(color.a, min.a, max.a));
|
||||
public static ColorHSV Lerp(ColorHSV a, ColorHSV b, double t, bool clamp = true) =>
|
||||
new ColorHSV(Angle.Lerp(a.h, b.h, t, clamp),
|
||||
MathE.Lerp(a.s, b.s, t, clamp),
|
||||
MathE.Lerp(a.v, b.v, t, clamp),
|
||||
MathE.Lerp(a.a, b.a, t, clamp));
|
||||
public static ColorHSV Product(IEnumerable<ColorHSV> colors)
|
||||
{
|
||||
bool any = false;
|
||||
ColorHSV result = new ColorHSV(Angle.Full, 1, 1, 1);
|
||||
foreach (ColorHSV color in colors)
|
||||
{
|
||||
any = true;
|
||||
result *= color;
|
||||
}
|
||||
return any ? result : Black;
|
||||
}
|
||||
public static ColorHSV Sum(IEnumerable<ColorHSV> colors)
|
||||
{
|
||||
bool any = false;
|
||||
ColorHSV result = new ColorHSV(Angle.Zero, 0, 0, 0);
|
||||
foreach (ColorHSV color in colors)
|
||||
{
|
||||
any = true;
|
||||
result += color;
|
||||
}
|
||||
return any ? result : Black;
|
||||
}
|
||||
public static (Angle[] Hs, double[] Ss, double[] Vs, double[] As) SplitArray(IEnumerable<ColorHSV> colors)
|
||||
{
|
||||
int count = colors.Count();
|
||||
Angle[] Hs = new Angle[count];
|
||||
double[] Ss = new double[count], Vs = new double[count], As = new double[count];
|
||||
int index = 0;
|
||||
foreach (ColorHSV c in colors)
|
||||
{
|
||||
Hs[index] = c.h;
|
||||
Ss[index] = c.s;
|
||||
Vs[index] = c.v;
|
||||
As[index] = c.a;
|
||||
index++;
|
||||
}
|
||||
return (Hs, Ss, Vs, As);
|
||||
}
|
||||
|
||||
public Dictionary<ColorChannel, double> GetChannels() => new Dictionary<ColorChannel, double>()
|
||||
{
|
||||
{ ColorChannel.Hue, h.Revolutions },
|
||||
{ ColorChannel.Saturation, s },
|
||||
{ ColorChannel.Value, v },
|
||||
{ ColorChannel.Alpha, a }
|
||||
};
|
||||
|
||||
public TColor AsColor<TColor>() where TColor : struct, IColor<TColor>
|
||||
{
|
||||
Type type = typeof(TColor);
|
||||
if (type == typeof(ColorRGB)) return (TColor)(object)AsRgb();
|
||||
else if (type == typeof(ColorHSV)) return (TColor)(object)this;
|
||||
else if (type == typeof(ColorCMYK)) return (TColor)(object)AsCmyk();
|
||||
else throw new InvalidCastException();
|
||||
}
|
||||
public ColorRGB AsRgb()
|
||||
{
|
||||
// Thanks https://www.rapidtables.com/convert/color/hsv-to-rgb.html
|
||||
double H = h.Normalized.Degrees,
|
||||
c = v * s,
|
||||
x = c * (1 - MathE.Abs(MathE.ModAbs(H / 60, 2) - 1)),
|
||||
m = v - c;
|
||||
|
||||
ColorRGB color;
|
||||
if (H < 60) color = new ColorRGB(c, x, 0);
|
||||
else if (H < 120) color = new ColorRGB(x, c, 0);
|
||||
else if (H < 180) color = new ColorRGB(0, c, x);
|
||||
else if (H < 240) color = new ColorRGB(0, x, c);
|
||||
else if (H < 300) color = new ColorRGB(x, 0, c);
|
||||
else if (H < 360) color = new ColorRGB(c, 0, x);
|
||||
else throw new ArgumentOutOfRangeException();
|
||||
|
||||
color.r += m;
|
||||
color.g += m;
|
||||
color.b += m;
|
||||
return color;
|
||||
}
|
||||
public ColorHSV AsHsv() => this;
|
||||
public ColorCMYK AsCmyk() => AsRgb().AsCmyk();
|
||||
|
||||
public string HexCode() => AsRgb().HexCode();
|
||||
|
||||
public IEnumerator<double> GetEnumerator()
|
||||
{
|
||||
yield return h.Revolutions;
|
||||
yield return s;
|
||||
yield return v;
|
||||
yield return a;
|
||||
}
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
public void Deconstruct(out Angle h, out double s, out double v)
|
||||
{
|
||||
h = this.h;
|
||||
s = this.s;
|
||||
v = this.v;
|
||||
}
|
||||
public void Deconstruct(out Angle h, out double s, out double v, out double a)
|
||||
{
|
||||
h = this.h;
|
||||
s = this.s;
|
||||
v = this.v;
|
||||
a = this.a;
|
||||
}
|
||||
|
||||
public bool Equals(ColorHSV other)
|
||||
{
|
||||
if (a <= 0 && other.a <= 0) return true;
|
||||
else if (v <= 0 && other.v <= 0) return true;
|
||||
else if (s <= 0 && other.s <= 0) return true;
|
||||
else return h == other.h && s == other.s && v == other.v && a == other.a;
|
||||
}
|
||||
public bool Equals(IColor other) => Equals(other.AsHsv());
|
||||
#if CS8_OR_GREATER
|
||||
public override bool Equals(object? other)
|
||||
#else
|
||||
public override bool Equals(object other)
|
||||
#endif
|
||||
{
|
||||
if (other is IColor color) return Equals(color.AsHsv());
|
||||
else return false;
|
||||
}
|
||||
public override int GetHashCode() => base.GetHashCode();
|
||||
public override string ToString() => $"{{ h={h.Degrees:0}°, s={s:0.00}, v={v:0.00}, a={a:0.00} }}";
|
||||
|
||||
public double[] ToArray() => new double[] { h.Revolutions, s, v, a };
|
||||
public Fill<double> ToFill()
|
||||
{
|
||||
ColorHSV copy = this;
|
||||
return i => copy[i];
|
||||
}
|
||||
public List<double> ToList() => new List<double> { h.Revolutions, s, v, a };
|
||||
|
||||
public static ColorHSV operator +(ColorHSV a, ColorHSV b) => new ColorHSV(a.h + b.h, a.s + b.s, a.v + b.v, 1 - (1 - a.a) * (1 - b.a));
|
||||
public static ColorHSV operator *(ColorHSV a, ColorHSV b) => new ColorHSV(Angle.FromRevolutions(a.h.Revolutions * b.h.Revolutions), a.s * b.s, a.v * b.v, a.a * b.a);
|
||||
public static ColorHSV operator *(ColorHSV a, double b) => new ColorHSV(a.h, a.s * b, a.v, a.a);
|
||||
public static bool operator ==(ColorHSV a, IColor b) => a.Equals(b.AsHsv());
|
||||
public static bool operator !=(ColorHSV a, IColor b) => !a.Equals(b.AsHsv());
|
||||
public static bool operator ==(ColorHSV a, ColorHSV b) => a.Equals(b);
|
||||
public static bool operator !=(ColorHSV a, ColorHSV b) => !a.Equals(b);
|
||||
|
||||
public static implicit operator ColorHSV(ListTuple<double> tuple)
|
||||
{
|
||||
if (tuple.Length == 3) return new ColorHSV(tuple[0], tuple[1], tuple[2]);
|
||||
else if (tuple.Length == 4) return new ColorHSV(tuple[0], tuple[1], tuple[2], tuple[3]);
|
||||
else throw new InvalidCastException();
|
||||
}
|
||||
public static implicit operator ColorHSV((Angle, double, double) tuple) => new ColorHSV(tuple.Item1, tuple.Item2, tuple.Item3);
|
||||
public static implicit operator ColorHSV((double, double, double) tuple) => new ColorHSV(tuple.Item1, tuple.Item2, tuple.Item3);
|
||||
public static implicit operator ColorHSV((Angle, double, double, double) tuple) => new ColorHSV(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4);
|
||||
public static implicit operator ColorHSV((double, double, double, double) tuple) => new ColorHSV(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4);
|
||||
|
||||
public static implicit operator ListTuple<double>(ColorHSV color) => new ListTuple<double>(color.h.Revolutions, color.s, color.v, color.a);
|
||||
public static implicit operator ValueTuple<Angle, double, double>(ColorHSV color) => (color.h, color.s, color.v);
|
||||
public static implicit operator ValueTuple<double, double, double>(ColorHSV color) => (color.h.Revolutions, color.s, color.v);
|
||||
public static implicit operator ValueTuple<Angle, double, double, double>(ColorHSV color) => (color.h, color.s, color.v, color.a);
|
||||
public static implicit operator ValueTuple<double, double, double, double>(ColorHSV color) => (color.h.Revolutions, color.s, color.v, color.a);
|
||||
}
|
||||
}
|
||||
@ -7,7 +7,8 @@ using System.Linq;
|
||||
|
||||
namespace Nerd_STF.Graphics
|
||||
{
|
||||
public struct ColorRGB : IColor<ColorRGB>
|
||||
public struct ColorRGB : IColor<ColorRGB>,
|
||||
INumberGroup<ColorRGB, double>
|
||||
#if CS11_OR_GREATER
|
||||
,IFromTuple<ColorRGB, (double, double, double)>,
|
||||
IFromTuple<ColorRGB, (double, double, double, double)>,
|
||||
@ -33,19 +34,19 @@ namespace Nerd_STF.Graphics
|
||||
|
||||
public double r, g, b, a;
|
||||
|
||||
public ColorRGB(double r, double g, double b)
|
||||
public ColorRGB(double red, double green, double blue)
|
||||
{
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
r = red;
|
||||
g = green;
|
||||
b = blue;
|
||||
a = 1;
|
||||
}
|
||||
public ColorRGB(double r, double g, double b, double a)
|
||||
public ColorRGB(double red, double green, double blue, double alpha)
|
||||
{
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = a;
|
||||
r = red;
|
||||
g = green;
|
||||
b = blue;
|
||||
a = alpha;
|
||||
}
|
||||
public ColorRGB(IEnumerable<double> nums)
|
||||
{
|
||||
@ -160,6 +161,9 @@ namespace Nerd_STF.Graphics
|
||||
MathE.Pow(avgB, invGamma),
|
||||
avgA);
|
||||
}
|
||||
#if CS11_OR_GREATER
|
||||
static ColorRGB IColor<ColorRGB>.Average(IEnumerable<ColorRGB> colors) => Average(1, colors);
|
||||
#endif
|
||||
public static ColorRGB Clamp(ColorRGB color, ColorRGB min, ColorRGB max) =>
|
||||
new ColorRGB(MathE.Clamp(color.r, min.r, max.r),
|
||||
MathE.Clamp(color.g, min.g, max.g),
|
||||
@ -206,12 +210,9 @@ namespace Nerd_STF.Graphics
|
||||
}
|
||||
public static ColorRGB Lerp(double gamma, ColorRGB a, ColorRGB b, double t, bool clamp = true)
|
||||
{
|
||||
double aCorrectedR = MathE.Pow(a.r, gamma),
|
||||
aCorrectedG = MathE.Pow(a.g, gamma),
|
||||
aCorrectedB = MathE.Pow(a.b, gamma),
|
||||
bCorrectedR = MathE.Pow(b.r, gamma),
|
||||
bCorrectedG = MathE.Pow(b.g, gamma),
|
||||
bCorrectedB = MathE.Pow(b.b, gamma);
|
||||
double aCorrectedR = MathE.Pow(a.r, gamma), bCorrectedR = MathE.Pow(b.r, gamma),
|
||||
aCorrectedG = MathE.Pow(a.g, gamma), bCorrectedG = MathE.Pow(b.g, gamma),
|
||||
aCorrectedB = MathE.Pow(a.b, gamma), bCorrectedB = MathE.Pow(b.b, gamma);
|
||||
// Gamma doesn't apply to the alpha channel.
|
||||
|
||||
double newR = MathE.Lerp(aCorrectedR, bCorrectedR, t, clamp),
|
||||
@ -242,7 +243,7 @@ namespace Nerd_STF.Graphics
|
||||
public static ColorRGB Sum(IEnumerable<ColorRGB> colors)
|
||||
{
|
||||
bool any = false;
|
||||
ColorRGB result = new ColorRGB(0, 0, 0, 1);
|
||||
ColorRGB result = new ColorRGB(0, 0, 0, 0);
|
||||
foreach (ColorRGB color in colors)
|
||||
{
|
||||
any = true;
|
||||
@ -278,9 +279,37 @@ namespace Nerd_STF.Graphics
|
||||
{
|
||||
Type type = typeof(TColor);
|
||||
if (type == typeof(ColorRGB)) return (TColor)(object)this;
|
||||
else if (type == typeof(ColorHSV)) return (TColor)(object)AsHsv();
|
||||
else if (type == typeof(ColorCMYK)) return (TColor)(object)AsCmyk();
|
||||
else throw new InvalidCastException();
|
||||
}
|
||||
public ColorRGB AsRgb() => this;
|
||||
public ColorHSV AsHsv()
|
||||
{
|
||||
// Thanks https://www.rapidtables.com/convert/color/rgb-to-hsv.html
|
||||
double cMax = MathE.Max(this), cMin = MathE.Min(this), delta = cMax - cMin;
|
||||
Angle h;
|
||||
|
||||
if (delta == 0) h = Angle.Zero;
|
||||
else if (cMax == r) h = Angle.FromDegrees(60 * MathE.ModAbs((g - b) / delta, 6));
|
||||
else if (cMax == g) h = Angle.FromDegrees(60 * ((b - r) / delta + 2));
|
||||
else if (cMax == b) h = Angle.FromDegrees(60 * ((r - g) / delta + 4));
|
||||
else h = Angle.Zero;
|
||||
|
||||
double s = cMax == 0 ? 0 : delta / cMax;
|
||||
return new ColorHSV(h, s, cMax);
|
||||
}
|
||||
public ColorCMYK AsCmyk()
|
||||
{
|
||||
// Thanks https://www.rapidtables.com/convert/color/rgb-to-cmyk.html
|
||||
double diffK = MathE.Max(this), invDiffK = 1 / diffK;
|
||||
return new ColorCMYK((diffK - r) * invDiffK,
|
||||
(diffK - g) * invDiffK,
|
||||
(diffK - b) * invDiffK,
|
||||
1 - diffK);
|
||||
}
|
||||
|
||||
public string HexCode() => $"#{(int)(r * 255):X2}{(int)(g * 255):X2}{(int)(b * 255):X2}";
|
||||
|
||||
public IEnumerator<double> GetEnumerator()
|
||||
{
|
||||
@ -307,8 +336,8 @@ namespace Nerd_STF.Graphics
|
||||
|
||||
public bool Equals(ColorRGB other)
|
||||
{
|
||||
if (a <= 0 && b <= 0) return true;
|
||||
else return r == other.r && g == other.g && b == other.b;
|
||||
if (a <= 0 && other.a <= 0) return true;
|
||||
else return r == other.r && g == other.g && b == other.b && a == other.a;
|
||||
}
|
||||
public bool Equals(IColor other) => Equals(other.AsRgb());
|
||||
#if CS8_OR_GREATER
|
||||
@ -329,13 +358,11 @@ namespace Nerd_STF.Graphics
|
||||
ColorRGB copy = this;
|
||||
return i => copy[i];
|
||||
}
|
||||
public List<double> ToList() => new List<double>() { r, g, b, a };
|
||||
public List<double> ToList() => new List<double> { r, g, b, a };
|
||||
|
||||
public static ColorRGB operator +(ColorRGB a) => a;
|
||||
public static ColorRGB operator +(ColorRGB a, ColorRGB b) => new ColorRGB(a.r + b.r, a.g + b.g, a.b + b.b, 1 - (1 - a.a) * (1 - b.b));
|
||||
public static ColorRGB operator -(ColorRGB a) => new ColorRGB(1 - a.r, 1 - a.g, 1 - a.b);
|
||||
public static ColorRGB operator +(ColorRGB a, ColorRGB b) => new ColorRGB(a.r + b.r, a.g + b.g, a.b + b.b, 1 - (1 - a.a) * (1 - b.a));
|
||||
public static ColorRGB operator *(ColorRGB a, ColorRGB b) => new ColorRGB(a.r * b.r, a.g * b.g, a.b * b.b, a.a * b.a);
|
||||
public static ColorRGB operator *(ColorRGB a, double b) => new ColorRGB(a.r * b, a.g * b, a.b * b);
|
||||
public static ColorRGB operator *(ColorRGB a, double b) => new ColorRGB(a.r * b, a.g * b, a.b * b, a.a);
|
||||
public static bool operator ==(ColorRGB a, IColor b) => a.Equals(b.AsRgb());
|
||||
public static bool operator !=(ColorRGB a, IColor b) => !a.Equals(b.AsRgb());
|
||||
public static bool operator ==(ColorRGB a, ColorRGB b) => a.Equals(b);
|
||||
|
||||
@ -10,24 +10,27 @@ namespace Nerd_STF.Graphics
|
||||
|
||||
TColor AsColor<TColor>() where TColor : struct, IColor<TColor>;
|
||||
ColorRGB AsRgb();
|
||||
ColorHSV AsHsv();
|
||||
ColorCMYK AsCmyk();
|
||||
|
||||
string HexCode();
|
||||
|
||||
bool Equals(IColor other);
|
||||
}
|
||||
|
||||
public interface IColor<TSelf> : IColor,
|
||||
IEquatable<TSelf>,
|
||||
INumberGroup<TSelf, double>
|
||||
IEquatable<TSelf>
|
||||
#if CS11_OR_GREATER
|
||||
,IColorPresets<TSelf>
|
||||
,IColorOperators<TSelf>,
|
||||
IColorPresets<TSelf>,
|
||||
IInterpolable<TSelf>
|
||||
#endif
|
||||
where TSelf : struct, IColor<TSelf>
|
||||
{
|
||||
#if CS11_OR_GREATER
|
||||
static abstract int ChannelCount { get; }
|
||||
|
||||
// TODO: Do all color formats have a gamma value?
|
||||
static abstract TSelf Average(double gamma, IEnumerable<TSelf> colors);
|
||||
static abstract TSelf Lerp(double gamma, TSelf a, TSelf b, double t, bool clamp = true);
|
||||
static abstract TSelf Average(IEnumerable<TSelf> colors);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
14
Nerd_STF/Graphics/IColorOperators.cs
Normal file
14
Nerd_STF/Graphics/IColorOperators.cs
Normal file
@ -0,0 +1,14 @@
|
||||
#if CS11_OR_GREATER
|
||||
namespace Nerd_STF.Graphics
|
||||
{
|
||||
public interface IColorOperators<TSelf> where TSelf : IColorOperators<TSelf>
|
||||
{
|
||||
static abstract TSelf operator +(TSelf a, TSelf b);
|
||||
static abstract TSelf operator *(TSelf a, double b);
|
||||
static abstract bool operator ==(TSelf a, IColor b);
|
||||
static abstract bool operator !=(TSelf a, IColor b);
|
||||
static abstract bool operator ==(TSelf a, TSelf b);
|
||||
static abstract bool operator !=(TSelf a, TSelf b);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -1,11 +1,7 @@
|
||||
#if CS11_OR_GREATER
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Nerd_STF.Graphics
|
||||
{
|
||||
public interface IColorPresets<TSelf> where TSelf : struct, IColor<TSelf>, IColorPresets<TSelf>
|
||||
public interface IColorPresets<TSelf> where TSelf : IColorPresets<TSelf>
|
||||
{
|
||||
static abstract TSelf Black { get; }
|
||||
static abstract TSelf Blue { get; }
|
||||
|
||||
@ -4,6 +4,14 @@ namespace Nerd_STF.Helpers
|
||||
{
|
||||
internal static class TargetHelper
|
||||
{
|
||||
public static void WriteLine(string content)
|
||||
{
|
||||
#if NETSTANDARD1_1
|
||||
#else
|
||||
Console.WriteLine(content);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static T[] EmptyArray<T>()
|
||||
{
|
||||
#if NETSTANDARD1_1
|
||||
|
||||
@ -86,6 +86,12 @@ namespace Nerd_STF
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
public Fill<T> ToFill()
|
||||
{
|
||||
T[] items = this.items;
|
||||
return i => items[i];
|
||||
}
|
||||
|
||||
public static bool operator ==(ListTuple<T> a, ListTuple<T> b) => a.Equals(b);
|
||||
public static bool operator !=(ListTuple<T> a, ListTuple<T> b) => !a.Equals(b);
|
||||
|
||||
@ -105,7 +111,7 @@ namespace Nerd_STF
|
||||
public static implicit operator ListTuple<T>((T, T, T, T, T) tuple) => new ListTuple<T>(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4, tuple.Item5);
|
||||
public static implicit operator ListTuple<T>((T, T, T, T, T, T) tuple) => new ListTuple<T>(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4, tuple.Item5, tuple.Item6);
|
||||
public static implicit operator ListTuple<T>((T, T, T, T, T, T, T) tuple) => new ListTuple<T>(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4, tuple.Item5, tuple.Item6, tuple.Item7);
|
||||
public static implicit operator ListTuple<T>(T[] array) => new ListTuple<T>(array);
|
||||
public static implicit operator ListTuple<T>(T[] array) => new ListTuple<T>(array)
|
||||
|
||||
public struct Enumerator : IEnumerator<T>
|
||||
{
|
||||
|
||||
@ -9,6 +9,7 @@ namespace Nerd_STF.Mathematics
|
||||
IEquatable<Angle>
|
||||
#if CS11_OR_GREATER
|
||||
,IFromTuple<Angle, (double, Angle.Units)>,
|
||||
IInterpolable<Angle>,
|
||||
IPresets2d<Angle>
|
||||
#endif
|
||||
{
|
||||
@ -38,6 +39,11 @@ namespace Nerd_STF.Mathematics
|
||||
|
||||
private readonly double revTheta;
|
||||
|
||||
public static Angle FromDegrees(double deg) => new Angle(deg, Units.Degrees);
|
||||
public static Angle FromRadians(double rad) => new Angle(rad, Units.Radians);
|
||||
public static Angle FromGradians(double grade) => new Angle(grade, Units.Gradians);
|
||||
public static Angle FromRevolutions(double turns) => new Angle(turns, Units.Revolutions);
|
||||
|
||||
public Angle(double theta, Units unit)
|
||||
{
|
||||
switch (unit)
|
||||
@ -127,6 +133,8 @@ namespace Nerd_STF.Mathematics
|
||||
}
|
||||
public static Angle Clamp(Angle value, Angle min, Angle max) =>
|
||||
new Angle(MathE.Clamp(value.revTheta, min.revTheta, max.revTheta));
|
||||
public static Angle Lerp(Angle a, Angle b, double t, bool clamp = true) =>
|
||||
new Angle(MathE.Lerp(a.revTheta, b.revTheta, t, clamp));
|
||||
public static Angle Max(IEnumerable<Angle> values) => Max(false, values);
|
||||
public static Angle Max(bool normalize, IEnumerable<Angle> values)
|
||||
{
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using Nerd_STF.Exceptions;
|
||||
using Nerd_STF.Graphics;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
@ -127,7 +128,7 @@ namespace Nerd_STF.Mathematics
|
||||
total += val;
|
||||
count++;
|
||||
}
|
||||
return total;
|
||||
return total / count;
|
||||
}
|
||||
public static Int3 Ceiling(Float3 val) =>
|
||||
new Int3(MathE.Ceiling(val.x),
|
||||
@ -314,6 +315,7 @@ namespace Nerd_STF.Mathematics
|
||||
public static bool operator ==(Float3 a, Float3 b) => a.Equals(b);
|
||||
public static bool operator !=(Float3 a, Float3 b) => !a.Equals(b);
|
||||
|
||||
public static explicit operator Float3(ColorRGB color) => new Float3(color.r, color.g, color.b);
|
||||
public static implicit operator Float3(Float2 floats) => new Float3(floats.x, floats.y, 0);
|
||||
public static explicit operator Float3(Float4 floats) => new Float3(floats.x, floats.y, floats.z);
|
||||
public static implicit operator Float3(Int2 ints) => new Float3(ints.x, ints.y, 0);
|
||||
|
||||
@ -4,6 +4,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Nerd_STF.Exceptions;
|
||||
using Nerd_STF.Graphics;
|
||||
|
||||
namespace Nerd_STF.Mathematics
|
||||
{
|
||||
@ -136,7 +137,7 @@ namespace Nerd_STF.Mathematics
|
||||
total += val;
|
||||
count++;
|
||||
}
|
||||
return total;
|
||||
return total / count;
|
||||
}
|
||||
public static Int4 Ceiling(Float4 val) =>
|
||||
new Int4(MathE.Ceiling(val.w),
|
||||
@ -336,6 +337,7 @@ namespace Nerd_STF.Mathematics
|
||||
public static bool operator ==(Float4 a, Float4 b) => a.Equals(b);
|
||||
public static bool operator !=(Float4 a, Float4 b) => !a.Equals(b);
|
||||
|
||||
public static explicit operator Float4(ColorRGB color) => new Float4(color.a, color.r, color.g, color.b);
|
||||
public static implicit operator Float4(Int2 ints) => new Float4(0, ints.x, ints.y, 0);
|
||||
public static implicit operator Float4(Int3 ints) => new Float4(0, ints.x, ints.y, ints.z);
|
||||
public static implicit operator Float4(Int4 ints) => new Float4(ints.w, ints.x, ints.y, ints.z);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user