diff --git a/Source/ConformalDecals/ConformalDecals.csproj b/Source/ConformalDecals/ConformalDecals.csproj index 29fc765..d778ae1 100644 --- a/Source/ConformalDecals/ConformalDecals.csproj +++ b/Source/ConformalDecals/ConformalDecals.csproj @@ -93,6 +93,7 @@ + diff --git a/Source/ConformalDecals/Util/ColorHSL.cs b/Source/ConformalDecals/Util/ColorHSL.cs new file mode 100644 index 0000000..06436f7 --- /dev/null +++ b/Source/ConformalDecals/Util/ColorHSL.cs @@ -0,0 +1,126 @@ +using System; +using System.Globalization; +using UnityEngine; + +namespace ConformalDecals.Util { + public struct ColorHSL : IEquatable { + public float h; + public float s; + public float l; + public float a; + + public ColorHSL(float h, float s = 1, float l = 0.5f, float a = 1) { + this.h = h; + this.s = s; + this.l = l; + this.a = a; + } + + public override string ToString() { + return $"HSLA({(object) this.h:F3}, {(object) this.s:F3}, {(object) this.l:F3}, {(object) this.a:F3})"; + } + + public string ToString(string format) { + return + "HSLA(" + + $"{this.h.ToString(format, CultureInfo.InvariantCulture.NumberFormat)}, " + + $"{this.s.ToString(format, CultureInfo.InvariantCulture.NumberFormat)}, " + + $"{this.l.ToString(format, CultureInfo.InvariantCulture.NumberFormat)}, " + + $"{this.a.ToString(format, CultureInfo.InvariantCulture.NumberFormat)})"; + } + + public bool Equals(ColorHSL other) { + return (this.h.Equals(other.h) && this.s.Equals(other.s) && this.l.Equals(other.l) && this.a.Equals(other.a)); + } + + public bool Equals(Color other) { + var rgb = HSL2RGB(this); + return rgb.Equals(other); + } + + public override bool Equals(object obj) { + if (obj is ColorHSL otherHSL) return Equals(otherHSL); + if (obj is Color otherRGB) return Equals(otherRGB); + + return false; + } + + public override int GetHashCode() { + return ((Vector4) this).GetHashCode(); + } + + public static bool operator ==(ColorHSL lhs, ColorHSL rhs) { + return lhs.Equals(rhs); + } + + public static bool operator !=(ColorHSL lhs, ColorHSL rhs) { + return !(lhs == rhs); + } + + public static implicit operator Vector4(ColorHSL c) { + return new Vector4(c.h, c.s, c.l, c.a); + } + + public static implicit operator ColorHSL(Vector4 v) { + return new ColorHSL(v.x, v.y, v.z, v.w); + } + + public static implicit operator ColorHSL(Color rgb) { + return RGB2HSL(rgb); + } + + public static implicit operator Color(ColorHSL hsl) { + return HSL2RGB(hsl); + } + + public static Color HSL2RGB(ColorHSL hsl) { + float a = hsl.s * Mathf.Min(hsl.l, 1 - hsl.l); + + float Component(int n) { + float k = (n + hsl.h * 12) % 12; + return hsl.l - a * Mathf.Max(-1, Mathf.Min(k - 3, Mathf.Min(9 - k, 1))); + } + + return new Color(Component(0), Component(8), Component(4), hsl.a); + } + + public static ColorHSL RGB2HSL(Color rgb) { + float h = 0; + float s = 0; + float l = 0; + + if (rgb.r >= rgb.g && rgb.r >= rgb.b) { + float xMin = Mathf.Min(rgb.g, rgb.b); + + l = (rgb.r + xMin) / 2; + s = (rgb.r - l) / Mathf.Min(l, 1 - l); + + float c = rgb.r - xMin; + if (c > Mathf.Epsilon) h = (rgb.g - rgb.b) / (6 * c); + + } + else if (rgb.g >= rgb.r && rgb.g >= rgb.b) { + float xMin = Mathf.Min(rgb.r, rgb.b); + + l = (rgb.g + xMin) / 2; + s = (rgb.g - l) / Mathf.Min(l, 1 - l); + + float c = rgb.g - xMin; + if (c > Mathf.Epsilon) h = (2 + ((rgb.b - rgb.r) / c)) / 6; + + } + else if (rgb.b >= rgb.r && rgb.b >= rgb.g) { + float xMin = Mathf.Min(rgb.r, rgb.g); + + l = (rgb.b + xMin) / 2; + s = (rgb.b - l) / Mathf.Min(l, 1 - l); + + float c = rgb.g - xMin; + if (c > Mathf.Epsilon) h = (4 + ((rgb.r - rgb.g) / c)) / 6; + + } + + return new ColorHSL(h, s, l, rgb.a); + } + } +} \ No newline at end of file