/* * By Joe Bowers, http://www.joe-bowers.com * * */ using System; using System.Collections.Generic; using System.Collections; using System.Linq; using System.Drawing; namespace Spectrum { /// /// Generates and associates a "Spectrum" of colors with /// each element of a list of keys, each color having a constant saturation and luminance /// but varying hue, such that that the author thinks look /// those colors look good in graphs. /// public class Spectrum : IEnumerable> { private IDictionary Colors; private double Saturation; private double Luminance; /// A collection of stuff, each element of the collection will have an associated color public Spectrum(IEnumerable collection) : this(collection, 0.5, 0.7) { } /// /// /// /// A collection, each element of which will have an associated color /// A constant saturation for use /// public Spectrum(IEnumerable collection, double saturation, double luminance) { Saturation = saturation; Luminance = luminance; InitColors(collection); } /// /// Thrown when was not provided to the constructor of this object. /// /// Thrown when is null. /// /// The color associated with the argument. The color will always be the same for the same argument. public Color Item(TKey key) { return Colors[key]; } /// Returns one KeyValuePair for each key provided in the constructor, /// with a distinct color as the .Value member of the pair. public IEnumerator> GetEnumerator() { return Colors.GetEnumerator(); } IEnumerator System.Collections.IEnumerable.GetEnumerator() { return Colors.GetEnumerator(); } /************************************************************************/ private void InitColors(IEnumerable keys) { int count = keys.Count(); int interleave = 3; double phase = 360.0 / interleave; double slice = 360.0 / count; var hues = Enumerable.Range(0, count) .Select(ix => ((ix % interleave) * phase) + (ix / interleave) * slice); var colorlist = hues.Select(hue => HSVToColor(hue, Saturation, Luminance)); var pairs = keys.Zip(colorlist, (k, v) => new KeyValuePair(k, v)); Colors = new Dictionary(); foreach( var k_v in pairs ) { Colors.Add(k_v.Key, k_v.Value); } } private static Color ColorFromDoubles(double rd, double gd, double bd) { byte r = (byte)(rd * 255); byte g = (byte)(gd * 255); byte b = (byte)(bd * 255); return Color.FromArgb(0xFF, r, g, b); } // we expect h in [0. 360], s in [0, 1] and v in [0,1] private static Color HSVToColor(double h, double s, double v) { double over_60 = h / 60.0; double f = over_60 - Math.Floor(over_60); double p = v * (1.0 - s); double q = v * (1.0 - (f * s)); double t = v * (1.0 - ((1.0 - f) * s)); Color ret = Color.FromArgb(0xFF, 0, 0, 0); if (over_60 < 1.0) { ret = ColorFromDoubles(v, t, p); } else if (over_60 < 2.0) { ret = ColorFromDoubles(q, v, p); } else if (over_60 < 3.0) { ret = ColorFromDoubles(p, v, t); } else if (over_60 < 4.0) { ret = ColorFromDoubles(p, q, v); } else if (over_60 < 5.0) { ret = ColorFromDoubles(t, p, v); } else { ret = ColorFromDoubles(v, p, q); } return ret; } } }