/*
* 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;
}
}
}