How to calculate color based on a range of values in C#?
var colors = new Color[] {
Color.Blue,
Color.Green,
Color.Yellow,
Color.Orange,
Color.Red
};
开发者_如何转开发
var min = 0;
var max = 400;
I'm trying to get the color in between these values based on another number. So for example if I wanted to the color for the value 350, it would be 50% orange and 50% red.
EDIT - Reworded for clarity
The only way I can think of doing it is creating a gradient image in photoshop, then calculating the offset and grabbing the pixel RGB value. However this seems extremely hacky and I would like to do it by some kind of calculation.
Any ideas?
You could use linear interpolation to mix the R, G and B values (and A if you want). Here's some example code for a Windows Form project. Just add a trackbar with range 0 to 400 and wire up the trackbar's scroll event to the handler below:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
byte interpolate(byte a, byte b, double p)
{
return (byte)(a * (1 - p) + b * p);
}
private void trackBar1_Scroll(object sender, EventArgs e)
{
int v = trackBar1.Value;
BackColor = getColor(v);
}
private Color getColor(int v)
{
SortedDictionary<int, Color> d = new SortedDictionary<int, Color>();
d.Add(0, Color.Blue);
d.Add(100, Color.Green);
d.Add(200, Color.Yellow);
d.Add(300, Color.Orange);
d.Add(400, Color.Red);
KeyValuePair<int, Color> kvp_previous = new KeyValuePair<int,Color>(-1, Color.Black);
foreach (KeyValuePair<int, Color> kvp in d)
{
if (kvp.Key > v)
{
double p = (v - kvp_previous.Key) / (double)(kvp.Key - kvp_previous.Key);
Color a = kvp_previous.Value;
Color b = kvp.Value;
Color c = Color.FromArgb(
interpolate(a.R, b.R, p),
interpolate(a.G, b.G, p),
interpolate(a.B, b.B, p));
return c;
}
kvp_previous = kvp;
}
return Color.Black;
}
}
You could also use this idea with HSL colors as suggested by nobugz.
Note: This code is just proof-of-concept. In a real application you would want to create a class to encapsulate the color choosing logic, make it more customizable, and error handling, etc. It's also not optimized for speed. If speed is an important consideration then you should probably use a look-up table instead.
There is one small bug in Mark Byers answer, it needs to be modified to:
if (kvp.Key > v)
{
double p = (v - kvp_previous.Key) / (double)(kvp.Key - kvp_previous.Key);
Color a = kvp_previous.Value;
Color b = kvp.Value;
Color c = Color.FromArgb(
interpolate(a.R, b.R, p),
interpolate(a.G, b.G, p),
interpolate(a.B, b.B, p));
return c;
}
else if (kvp.Key == v)
{
return kvp.Value;
}
Otherwise anything that is equal to one of the input points is returned as Black. (I don't have enough reputation to comment on his answer so as far as I know this is my only recourse)
Another way to generalize solution
/// <summary>
/// Interpolate colors 0.0 - 1.0
/// </summary>
public static Color Interpolate(double percent, params Color[] colors)
{
int left = (int)Math.Floor(percent * (colors.Length - 1));
int right = (int)Math.Ceiling(percent * (colors.Length - 1));
Color colorLeft = colors[left];
Color colorRight = colors[right];
double step = 1.0 / (colors.Length - 1);
double percentRight = (percent - (left * step)) / step;
double percentLeft = 1.0 - percentRight;
Color outputColor = new Color();
outputColor.R = (byte)(colorLeft.R * percentLeft + colorRight.R * percentRight);
outputColor.G = (byte)(colorLeft.G * percentLeft + colorRight.G * percentRight);
outputColor.B = (byte)(colorLeft.B * percentLeft + colorRight.B * percentRight);
outputColor.A = (byte)(colorLeft.A * percentLeft + colorRight.A * percentRight);
return outputColor;
}
Something like this might work for you...though, this is completely untested. The idea is that you calculate what two colors you need, and after that you mix these two based on the percentage value which you can calculate. As said, completely untested.
using System.Convert;
public static Color oddColorFunction(int value)
{
Color colors = new Color[] { Color.Blue, Color.Green, Color.Yellow, Color.Orange, Color.Red };
int min = 0;
int max = 400;
decimal range = max / colors.Length;
Color leftColor = ToInt32(Decimal.Floor(value / range));
Color rightColor = ToInt32(Decimal.Ceiling(value / range));
return mixColors(colors[leftColor], colors[rightColor], ToInt32(Decimal.Round(value % range * 100)));
}
public static mixColors(Color colorA, Color colorB, int percentage)
{
//combine colors here based on percentage
//I'm to lazy to code this :P
}
Color has the properties R, G, and B. You can take your input value, divide it by 100 to get the bottom color (clipping it at 3). Add one to that to get the top color. Then grab R, G, and B from the bottom and top colors, create a weighted average of each based on value % 100, and make a new Color with those values.
精彩评论