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));
}
}
}