using System; using System.Globalization; using Microsoft.Xna.Framework; namespace MonoGame.Extended.Particles { /// /// An immutable data structure representing a 24bit color composed of separate hue, saturation and lightness channels. /// //[Serializable] public struct HslColor : IEquatable, IComparable { /// /// Gets the value of the hue channel in degrees. /// public readonly float H; /// /// Gets the value of the saturation channel. /// public readonly float S; /// /// Gets the value of the lightness channel. /// public readonly float L; private static float NormalizeHue(float h) { if (h < 0) return h + 360*((int) (h/360) + 1); return h%360; } /// /// Initializes a new instance of the structure. /// /// The value of the hue channel. /// The value of the saturation channel. /// The value of the lightness channel. public HslColor(float h, float s, float l) : this() { // normalize the hue H = NormalizeHue(h); S = MathHelper.Clamp(s, 0f, 1f); L = MathHelper.Clamp(l, 0f, 1f); } /// /// Copies the individual channels of the color to the specified memory location. /// /// The memory location to copy the axis to. public void CopyTo(out HslColor destination) { destination = new HslColor(H, S, L); } /// /// Destructures the color, exposing the individual channels. /// public void Destructure(out float h, out float s, out float l) { h = H; s = S; l = L; } /// /// Exposes the individual channels of the color to the specified matching function. /// /// The function which matches the individual channels of the color. /// /// Thrown if the value passed to the parameter is null. /// public void Match(Action callback) { if (callback == null) throw new ArgumentNullException(nameof(callback)); callback(H, S, L); } /// /// Exposes the individual channels of the color to the specified mapping function and returns the /// result; /// /// The type being mapped to. /// /// A function which maps the color channels to an instance of . /// /// /// The result of the function when passed the individual X and Y components. /// /// /// Thrown if the value passed to the parameter is null. /// public T Map(Func map) { if (map == null) throw new ArgumentNullException(nameof(map)); return map(H, S, L); } public static HslColor operator +(HslColor a, HslColor b) { return new HslColor(a.H + b.H, a.S + b.S, a.L + b.L); } public int CompareTo(HslColor other) { // ReSharper disable ImpureMethodCallOnReadonlyValueField return H.CompareTo(other.H)*100 + S.CompareTo(other.S)*10 + L.CompareTo(L); // ReSharper restore ImpureMethodCallOnReadonlyValueField } /// /// Determines whether the specified is equal to this instance. /// /// The to compare with this instance. /// /// true if the specified is equal to this instance; otherwise, false. /// public override bool Equals(object obj) { if (obj is HslColor) return Equals((HslColor) obj); return base.Equals(obj); } /// /// Determines whether the specified is equal to this instance. /// /// The to compare with this instance. /// /// true if the specified is equal to this instance; otherwise, false. /// public bool Equals(HslColor value) { // ReSharper disable ImpureMethodCallOnReadonlyValueField return H.Equals(value.H) && S.Equals(value.S) && L.Equals(value.L); // ReSharper restore ImpureMethodCallOnReadonlyValueField } /// /// Returns a hash code for this instance. /// /// /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. /// public override int GetHashCode() { return H.GetHashCode() ^ S.GetHashCode() ^ L.GetHashCode(); } /// /// Returns a that represents this instance. /// /// /// A that represents this instance. /// public override string ToString() { return string.Format(CultureInfo.InvariantCulture, "H:{0:N1}° S:{1:N1} L:{2:N1}", H, 100*S, 100*L); } public static HslColor Parse(string s) { var hsl = s.Split(','); var hue = float.Parse(hsl[0].TrimEnd('°'), CultureInfo.InvariantCulture.NumberFormat); var sat = float.Parse(hsl[1], CultureInfo.InvariantCulture.NumberFormat); var lig = float.Parse(hsl[2], CultureInfo.InvariantCulture.NumberFormat); return new HslColor(hue, sat, lig); } /// /// Implements the operator ==. /// /// The lvalue. /// The rvalue. /// /// true if the lvalue is equal to the rvalue; otherwise, false. /// public static bool operator ==(HslColor x, HslColor y) { return x.Equals(y); } /// /// Implements the operator !=. /// /// The lvalue. /// The rvalue. /// /// true if the lvalue is not equal to the rvalue; otherwise, false. /// public static bool operator !=(HslColor x, HslColor y) { return !x.Equals(y); } public static HslColor operator -(HslColor a, HslColor b) { return new HslColor(a.H - b.H, a.S - b.S, a.L - b.L); } public static HslColor Lerp(HslColor c1, HslColor c2, float t) { // loop around if c2.H < c1.H var h2 = c2.H >= c1.H ? c2.H : c2.H + 360; return new HslColor( c1.H + t*(h2 - c1.H), c1.S + t*(c2.S - c1.S), c1.L + t*(c2.L - c2.L)); } } }