How do I generate contrasting colors?
My application contains a line graph which can display 20 or more datasets at once, but typically it will be displaying less than 5. I'd like each dataset to have a unique color.
Currently I'm doing this:
setHsl(i * 255.0 / session->getNumDataSets(), 255, 128);
It works, but has the downside that two consecutive datasets will have very similar colors. I'd like it so that each color generated will be as contrasting as possible against all previous colors generated.
What is a better way to generate the colors?
Here is my second attempt:
double pos = 0;
if (wheel.size() == 0)
{
wheel.append(0.0);
wheel.append(1.0);
}
else
{
double gap = 0;
double hi = 0;
double lo = 0;
for (int i = 0; i < wheel.size() - 1; i++)
{
double g = wheel[i + 1] - wheel[i];
if (g > gap)
{
gap = g;
lo = wheel[i];
hi = wheel[i + 1];
}
}
pos = (hi - lo) / 2.0 + lo开发者_StackOverflow中文版;
wheel.append(pos);
qSort(wheel);
}
QColor c;
c.setHsl(pos * 255.0, 255, 128);
return c.toRgb();
My idea is that the first color, is position 0 on the color wheel. Then for every following color, I go around the color wheel, looking for the biggest gap between colors, and once I've found it, I insert a new color into that gap. It seems to work better, but still isn't perfect, since once the gaps become small, consecutive colors become similar again.
I know you are asking for a way to computationally generate the colors for a graph, but you may want to consider designing a custom palette and looking up colors from a pre-stored table.
This has the advantage that you can craft a selection of colors that are more easy to distinguished for color blind users. You can use a tool like VisCheck to see how your graph would appear to color blind users.
If you may have more than 20 data sets I imagine it may be very hard to distinguish by color alone, but colors can be reused in combination with another differentiator, such as dashed lines.
You could do something like:
int n = session->getNumDataSets();
setHsl( (((i%3) * n/3)+(i/3)) * 255.0 / n, 255, 128);
I'd have to double-check my math, but basically it rotates through thirds of the spectrum.
Don't use HSL. It's flaws are why colors that "look different" numerically in HSL (or just as bad, HSV) space end up looking similar in your eye. Instead use YUV (aka YCbCr). The basic idea of YUV, if you're not familiar, is to make a linear transformation into coordinates where the first axis is the perceptual intensity, something like:
Y = 0.3*R + 0.6*G + 0.1*B
(usually more fine-tuned coefficients are used, but these are simple ones that give you a rough idea of the weights)
The second and third axes are then simply chosen as two vectors in the plane orthogonal to the Y axis, often roughly the "red" and "blue" directions.
Note that because colors are limited to a cube in RGB coordinates, once you choose a Y value, this puts a bound on the possible U and V values that stay inside the box. At Y=max, U and V must be zero. So simply choose some Y value of intermediate brightness where you have a lot of play in U and V, and then choose N evenly-spaced vectors in polar coordinates in the UV plane.
I use the following function in Java for creating equally spaced out colours for different datasets on java generated graphs. Its short and sweet:
/**
* This function splits the red-green-blue colour wheel into n equally spaced divisions
* and returns the colour from a particular division.
*
* @param index The index of the colour to return, the range is 0 - (count-1)
* @param count The number of divisions to split the HSV colour wheel into
* @return A java.awt.Color object containing the colour.
* @author HughesR
*/
public static Color getSpacedOutColour(int index, int count) {
final float saturation = 0.95f; // Saturation
final float brightness = 0.8f; // Brightness
float hue = (float)index / (float)count;
return Color.getHSBColor(hue, saturation, brightness);
}
精彩评论