When generating graphs and showing different sets of data it usually a good idea to difference the sets by color. So one line is red and the next is green and so on. The problem is then that when the number of datasets is unknown one needs to randomly generate these colors and often they end up very close to each other (green, light green for example).

Any ideas on how this could be solved and how it would be possibler to generate distinctly different colors?

I'd be great if any examples (feel free to just discuss the problem and solution with out examples if you find that easier) were in C# and RGB based colors.

The Hard Way

If you don't know how many colours you are going to need, the code below will generate up to 896 colours using this pattern. (896 = 256 * 7 / 2) 256 is the colour space per channel, we have 7 patterns and we stop before we get to colours separated by only 1 colour value.

I've probably made harder work of this code than I needed to. First, there is an intensity generator which starts at 255, then generates the values as per the pattern described above. The pattern generator just loops through the seven colour patterns.

I don't completely follow the example. Can anyone provide a C# example for this?
–
McBainUKFeb 9 '10 at 15:23

Hope that code sample helps - there's probably a much cleaner way of walking the intensity value tree, but this was a first stab which worked well enough. Cheers.
–
Sam MeldrumFeb 10 '10 at 10:39

5

This is the most under rated answer ever - thank you!
–
Adam RackisJan 10 '12 at 23:09

2

Note that this algorithm will produce some color pairs that are VERY similar (particularly in very dark or light, low-saturation regions). It does a good job of starting in regions of high saturation and lightness, but misses a lot of subtle colors that are still visually distinct.
–
PhrogzFeb 22 '12 at 22:40

1

I ended up doing something simliar in Javascript -- there seems to be a mental crutch/limiting reagent building on rgb. If we'd had four 256-choice color channels, would we write formulas with (* n) more colors? Even so, @Phrogz 's & @dean 's critiques would still stand (& is why I searched SO for a better answer). There has to be a way to grab distinctly different shades at each intensity step. Phrogz's answer, below is on the right track, but not quickly accessible to peons like myself if I want hundreds of colors by some int counter.
–
ruffinAug 21 '12 at 15:36

To implement a variation list where by your colors go, 255 then use all possibilities of that up, then add 0 and all RGB patterns with those two values. Then add 128 and all RGB combinations with those. Then 64. Then 192. Etc.

This will produce patterns of that type infinitely (2^24) into the future. However, after a hundred or so spots you likely won't see much of a difference between a color with 0 or 32 in the blue's place.

You might be better off normalizing this into a different color space. LAB color space for example with the L,A,B values normalized and converted. So the distinctness of the color is pushed through something more akin to the human eye.

getElement() reverses the endian of an 8 bit number, and starts counting from -1 rather than 0 (masking with 255). So it goes 255,0,127,192,64,... as the number grows it moves less and less significant bits, subdividing the number.

getPattern() determines what the most significant element in the pattern should be (it's the cube root). Then proceeds to break down the 3N²+3N+1 different patterns that involve that most significant element.

Read left to right, top to bottom. 729 colors (9³). So all the patterns up to n = 9. You'll notice the speed at which they start to clash. There's only so many WRGBCYMK variations. And this solution, while clever basically only does different shades of primary colors.

Much of the clashing is due to green and how similar most greens look to most people. The demand that each be maximally different at start rather than just different enough to not be the same color. And basic flaws in the idea resulting in primary colors patterns, and identical hues.

Using CIELab2000 Color Space and Distance Routine to randomly select and try 10k different colors and find the maximally-distant minimum-distance from previous colors, (pretty much the definition of the request) avoids clashing longer than the above solution:

Which could be just called a static list for the Easy Way. It took an hour and a half to generate 729 entries:

Using brute force to (testing all 16,777,216 RGB colors through CIELab Delta2000 / Starting with black) produces a series. Which starts to clash at around 26 but could make it to 30 or 40 with visual inspection and manual dropping (which can't be done with a computer). So doing the absolute maximum one can programmatically only makes a couple dozen distinct colors. A discrete list is your best bet. You will get more discrete colors with a list than you would programmatically. The easy way is the best solution, start mixing and matching with other ways to alter your data than color.

I think the HSV (or HSL) space has more opportunities here. If you don't mind the extra conversion, it's pretty easy to go through all the colors by just rotating the Hue value. If that's not enough, you can change the Saturation/Value/Lightness values and go through the rotation again. Or, you can always shift the Hue values or change your "stepping" angle and rotate more times.

There's a flaw in the previous RGB solutions. They don't take advantage of the whole color space since they use a color value and 0 for the channels:

#006600
#330000
#FF00FF

Instead they should be using all the possible color values to generate mixed colors that can have up to 3 different values across the color channels:

#336600
#FF0066
#33FF66

Using the full color space you can generate more distinct colors. For example, if you have 4 values per channel, then 4*4*4=64 colors can be generated. With the other scheme, only 4*7+1=29 colors can be generated.

If you want N colors, then the number of values per channel required is: ceil(cube_root(N))

With that, you can then determine the possible (0-255 range) values (python):

total = 0
for red in values:
for green in values:
for blue in values:
if total <= N:
print color(red, green, blue)
total += 1

Nested loops will work, but are not recommended since it will favor the blue channel and the resulting colors will not have enough red (N will most likely be less than the number of all possible color values).

You can create a better algorithm for the loops where each channel is treated equally and more distinct color values are favored over small ones.

I have a solution, but didn't want to post it since it isn't the easiest to understand or efficient. But, you can view the solution if you really want to.

You could also think of the color space as all combinations of three numbers from 0 to 255, inclusive. That's the base-255 representation of a number between 0 and 255^3, forced to have three decimal places (add zeros on to the end if need be.)

So to generate x number of colors, you'd calculate x evenly spaced percentages, 0 to 100. Get numbers by multiplying those percentages by 255^3, convert those numbers to base 255, and add zeros as previously mentioned.

You might also have to do something to bring the range in a little bit, to avoid having white and black, if you want. Those numbers aren't actually a smooth color scale, but they'll generate separate colors if you don't have too many.

I'm almost curious enough to do the math and calculate how long it would be on average before this algorithm goes into an infinite loop when there are no further solutions possible.
–
TatarizeMay 25 at 8:32

Hm. As oddly enough your answer says that any r value too close to the other R value will cause regeneration it's less than 12 at best. Though it oddly would call the colors Red and Blue too close because both have a green of 0 which is within 20. I mean literally your example says: colorcodehex.com/be7864colorcodehex.com/b4c81e Are too close together and should be regenerated.
–
TatarizeMay 25 at 8:40