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