using System; using System.Collections.Generic; using System.Xml; namespace Otter { /// /// Class that represents a color with red, green, blue, and alpha channels. /// public class Color { #region Static Methods /// /// Interpolate from one Color to another. /// /// The start Color. /// The end Color. /// The amount of completion on the lerp. (0 - 1) /// The interpolated Color. public static Color Lerp(Color from, Color to, float amount) { if (amount <= 0) return new Color(from); if (amount >= 1) return new Color(to); var c = new Color(from); c.R = from.R + (to.R - from.R) * amount; c.G = from.G + (to.G - from.G) * amount; c.B = from.B + (to.B - from.B) * amount; c.A = from.A + (to.A - from.A) * amount; return c; } /// /// Interpolate through a set of Colors. /// /// The amount of completion on the lerp. (0 - 1) /// The Colors to interpolate through. /// The interpolated Color. public static Color Lerp(float amount, params Color[] colors) { if (amount <= 0) return colors[0]; if (amount >= 1) return colors[colors.Length - 1]; int fromIndex = (int)Util.ScaleClamp(amount, 0, 1, 0, colors.Length - 1); int toIndex = fromIndex + 1; float length = 1f / (colors.Length - 1); float lerp = Util.ScaleClamp(amount % length, 0, length, 0, 1); // This is a fix for odd numbered color amounts. When fromIndex was // odd, lerp would evaluate to 1 when it should be 0. if (lerp >= 0.9999f && fromIndex % 2 == 1) { lerp = 0; } return Lerp(colors[fromIndex], colors[toIndex], lerp); } /// /// Return a new color made by mixing multiple colors. /// Mixes the colors evenly. /// /// The colors to be mixed. /// A new color of all the colors mixed together. public static Color Mix(params Color[] colors) { float r = 0, g = 0, b = 0, a = 0; foreach (var c in colors) { r += c.R; g += c.G; b += c.B; a += c.A; } r /= colors.Length; g /= colors.Length; b /= colors.Length; a /= colors.Length; return new Color(r, g, b, a); } /// /// Create a new color from HSV values. /// /// Hue, 0 to 360. /// Saturation, 0 to 1. /// Value, 0 to 1. /// Alpha, 0 to 1. /// A new RGBA color. public static Color FromHSV(float h, float s, float v, float a) { h = h < 0 ? 0 : (h > 1 ? 1 : h); s = s < 0 ? 0 : (s > 1 ? 1 : s); v = v < 0 ? 0 : (v > 1 ? 1 : v); h *= 360; int hi = (int)(h / 60) % 6; float f = (h / 60) - (int)(h / 60); float p = (v * (1 - s)); float q = (v * (1 - f * s)); float t = (v * (1 - (1 - f) * s)); float r, g, b; switch (hi) { case 0: r = v; g = t; b = p; break; case 1: r = q; g = v; b = p; break; case 2: r = p; g = v; b = t; break; case 3: r = p; g = q; b = v; break; case 4: r = t; g = p; b = v; break; case 5: r = v; g = p; b = q; break; default: r = g = b = 0; break; } return new Color(r, g, b, a); } /// /// Store a custom Color. Actually stores a new copy of that Color. /// /// The Color to store. /// The name of the Color. public static void AddCustom(Color color, Enum name) { customColors.Add(Util.EnumValueToString(name), new Color(color)); } /// /// Store a custom Color. Actually stores a new copy of that Color. /// /// The Color to store. /// The name of the Color. public static void AddCustom(string color, Enum name) { AddCustom(new Color(color), name); } /// /// Store a custom Color. Actually stores a new copy of that Color. /// /// The Color to store. /// The name of the Color. public static void AddCustom(UInt32 color, Enum name) { AddCustom(new Color(color), name); } /// /// Store a custom Color. Actually stores a new copy of that Color. /// /// The Color to store. /// The name of the Color. public static void AddCustom(Color color, string name) { customColors.Add(name, new Color(color)); } /// /// Store a custom Color. Actually stores a new copy of that Color. /// /// The Color to store. /// The name of the Color. public static void AddCustom(string color, string name) { AddCustom(new Color(color), name); } /// /// Store a custom Color. Actually stores a new copy of that Color. /// /// The Color to store. /// The name of the Color. public static void AddCustom(UInt32 color, string name) { AddCustom(new Color(color), name); } /// /// Get a stored custom Color. Returns a new copy of it. /// /// The name of the Color stored. /// A new copy of the custom Color. public static Color Custom(Enum name) { return customColors[Util.EnumValueToString(name)].Copy(); } public static Color Custom(string name) { return customColors[name].Copy(); } /// /// Create a shade of gray based on a value 0 to 1. /// /// The level of gray. 0 is black, 1 is white. /// A color of RGB equal to the value input for rgb. public static Color Shade(float rgb) { return new Color(rgb, rgb, rgb); } /// /// Create a shade of gray based on a value 0 to 1. /// /// The level of gray. 0 is black, 1 is white. /// The alpha of the returned Color. /// A color of RGB equal to the value input for rgb with alpha a. public static Color Shade(float rgb, float a) { return new Color(rgb, rgb, rgb, a); } /// /// Create a Color using byte values 0 - 255. /// /// Red /// Green /// Blue /// Alpha /// A new Color. public static Color FromBytes(byte r, byte g, byte b, byte a = 255) { var color = new Color(); color.ByteR = r; color.ByteG = g; color.ByteB = b; color.ByteA = a; return color; } #endregion #region Static Properties public static Color White { get { return new Color(1f, 1f, 1f); } } public static Color Black { get { return new Color(0f, 0f, 0f); } } public static Color Red { get { return new Color(1f, 0f, 0f); } } public static Color Green { get { return new Color(0f, 1f, 0f); } } public static Color Blue { get { return new Color(0f, 0f, 1f); } } public static Color Cyan { get { return new Color(0f, 1f, 1f); } } public static Color Magenta { get { return new Color(1f, 0f, 1f); } } public static Color Yellow { get { return new Color(1f, 1f, 0f); } } public static Color Orange { get { return new Color(1f, 0.5f, 0); } } public static Color Gold { get { return new Color("FFCC00"); } } public static Color None { get { return new Color(0f, 0f, 0f, 0f); } } public static Color Grey { get { return new Color("999999"); } } public static Color Gray { get { return Color.Grey; } } public static Color Random { get { return Rand.Color; } } public static Color RandomAlpha { get { return Rand.ColorAlpha; } } #endregion #region Static Fields static Dictionary customColors = new Dictionary(); #endregion #region Private Fields float r, g, b, a; #endregion #region Public Properties /// /// Red /// public float R { get { return r; } set { r = Util.Clamp(value, 0, 1); if (Graphic != null) { Graphic.NeedsUpdate = true; } } } /// /// Green /// public float G { get { return g; } set { g = Util.Clamp(value, 0, 1); if (Graphic != null) { Graphic.NeedsUpdate = true; } } } /// /// Blue /// public float B { get { return b; } set { b = Util.Clamp(value, 0, 1); if (Graphic != null) { Graphic.NeedsUpdate = true; } } } /// /// Alpha /// public float A { get { return a; } set { a = Util.Clamp(value, 0, 1); if (Graphic != null) { Graphic.NeedsUpdate = true; } } } /// /// The bytes for Red. /// public byte ByteR { set { R = Convert.ToSingle(value) / 255f; } get { return (byte)(R * 255); } } /// /// The bytes for Green. /// public byte ByteG { set { G = Convert.ToSingle(value) / 255f; } get { return (byte)(G * 255); } } /// /// The bytes for Blue. /// public byte ByteB { set { B = Convert.ToSingle(value) / 255f; } get { return (byte)(B * 255); } } /// /// The bytes for Alpha. /// public byte ByteA { set { A = Convert.ToSingle(value) / 255f; } get { return (byte)(A * 255); } } #endregion #region Constructors /// /// Create a new color. /// /// Red, 0 to 1. /// Green, 0 to 1. /// Blue, 0 to 1. /// Alpha, 0 to 1. public Color(float r = 1f, float g = 1f, float b = 1f, float a = 1f) { R = r; G = g; B = b; A = a; } /// /// Create a color by copying the RGBA from another color. /// /// The color to copy. public Color(Color copy) : this(copy.R, copy.G, copy.B, copy.A) { } /// /// Create a new color from an XML element. /// /// An XmlElement that contains attributes R, G, B, and A, from 0 to 255. public Color(XmlElement e) { A = float.Parse(e.Attributes["A"].Value) / 255; R = float.Parse(e.Attributes["R"].Value) / 255; G = float.Parse(e.Attributes["G"].Value) / 255; B = float.Parse(e.Attributes["B"].Value) / 255; } /// /// Return a new color containing the channels from this color. /// /// public Color Copy() { return new Color(R, G, B, A); } /// /// Create a new color from a string. Formats are "RGB", "RGBA", "RRGGBB", and "RRGGBBAA". /// /// A string with a hex representation of each channel. public Color(string hex) { int red = 255, green = 255, blue = 255, alpha = 255; if (hex.Length == 6) { red = Convert.ToInt32(hex.Substring(0, 2), 16); green = Convert.ToInt32(hex.Substring(2, 2), 16); blue = Convert.ToInt32(hex.Substring(4, 2), 16); } else if (hex.Length == 3) { red = Convert.ToInt32(hex.Substring(0, 1) + hex.Substring(0, 1), 16); green = Convert.ToInt32(hex.Substring(1, 1) + hex.Substring(1, 1), 16); blue = Convert.ToInt32(hex.Substring(2, 1) + hex.Substring(2, 1), 16); } else if (hex.Length == 8) { red = Convert.ToInt32(hex.Substring(0, 2), 16); green = Convert.ToInt32(hex.Substring(2, 2), 16); blue = Convert.ToInt32(hex.Substring(4, 2), 16); alpha = Convert.ToInt32(hex.Substring(6, 2), 16); } else if (hex.Length == 4) { red = Convert.ToInt32(hex.Substring(0, 1) + hex.Substring(0, 1), 16); green = Convert.ToInt32(hex.Substring(1, 1) + hex.Substring(1, 1), 16); blue = Convert.ToInt32(hex.Substring(2, 1) + hex.Substring(2, 1), 16); alpha = Convert.ToInt32(hex.Substring(3, 1) + hex.Substring(2, 1), 16); } R = Util.ScaleClamp(red, 0, 255, 0, 1); G = Util.ScaleClamp(green, 0, 255, 0, 1); B = Util.ScaleClamp(blue, 0, 255, 0, 1); A = Util.ScaleClamp(alpha, 0, 255, 0, 1); } /// /// Create a new color from a hex number. Formats are 0xRGB, 0xRRGGBB, 0xRGBA, 0xRRGGBBAA. /// /// A hex number representing a color. public Color(UInt32 hex) : this(hex.ToString("X6")) { } #endregion #region Public Methods public override string ToString() { return "Color [R: " + R.ToString("0.00") + " G: " + G.ToString("0.00") + " B: " + B.ToString("0.00") + " A: " + A.ToString("0.00") + "]"; } /// /// Get a hex string of the Color. /// public string ColorString { get { return ByteR.ToString("X2") + ByteG.ToString("X2") + ByteB.ToString("X2") + ByteA.ToString("X2"); } } public void SetColor(float r, float g, float b, float a) { R = r; G = g; B = b; A = a; } public void SetColor(float r, float g, float b) { SetColor(r, g, b, A); } public void SetColor(Color color) { SetColor(color.R, color.G, color.B, color.A); } #endregion #region Operators public static Color operator *(Color value1, Color value2) { value1 = new Color(value1); // Make new color otherwise this doesn't work properly. value1.R *= value2.R; value1.G *= value2.G; value1.B *= value2.B; value1.A *= value2.A; return value1; } public static Color operator *(Color value1, float value2) { value1 = new Color(value1); value1.R *= value2; value1.G *= value2; value1.B *= value2; return value1; } public static Color operator +(Color value1, Color value2) { value1 = new Color(value1); value1.R += value2.R; value1.G += value2.G; value1.B += value2.B; value1.A += value2.A; return value1; } public static Color operator -(Color value1, Color value2) { value1 = new Color(value1); value1.R -= value2.R; value1.G -= value2.G; value1.B -= value2.B; value1.A -= value2.A; return value1; } public static Color operator /(Color value1, Color value2) { value1 = new Color(value1); value1.R /= value2.R; value1.G /= value2.G; value1.B /= value2.B; value1.A /= value2.A; return value1; } #endregion #region Internal internal Color(SFML.Graphics.Color copy) { ByteR = copy.R; ByteG = copy.G; ByteB = copy.B; ByteA = copy.A; } internal SFML.Graphics.Color SFMLColor { get { return new SFML.Graphics.Color(ByteR, ByteG, ByteB, ByteA); } } internal Graphic Graphic; // Keep track of the graphic so it can be flagged for an update when the color changes #endregion } }