views:

1846

answers:

6

I'm generating some charts that need a hex string for the colors.

Example:

<dataseries name="ford" color="FF00FF" />

I'm creating these dynamically, so I would like to generate the hex code for each dataseries randomly.

What is the best way to do this?

+20  A: 

Easiest way is to use String.Format and use the hexadecimal format for the argument.

var random = new Random();
var color = String.Format("#{0:X6}", random.Next(0x1000000)); // = "#A197B9"
Samuel
+1 That's so awesomely simple, clear, and fast.
Michael Haren
I know, I first stared thinking about using an array and picking elements, then I remember that String.Format can do hexadecimal.
Samuel
Correct me if I'm wrong, but with that code, you will never generate the value 0xFFFFFF since the integer you pass to the Next method is the non-inclusive upper bound. So you would need random.Next(0x1000000) in order for 0xFFFFFF to be returned.
Chris Dunaway
@Chris - you are correct, it's an exclusive upper-bound
John Rasch
One thing that is interesting; if I put a breakpoint when I set the color, different colors are produced each time through my loop. If I just let it run, the asp page gives me all the same colors.
rahkim
@rahkim: See John Rasch's answer.
Samuel
It does help when we read all the answers! Thanks.
rahkim
Can someone edit the title of this post to something more meaningful please?
Dead account
this is plain clever, I'm being jealous when I see stuff like this :)
dr. evil
@rahkim: That's because the generator is seeded with the clock time. In a loop, each Random is generated so fast that they all use the same seed. When you break, enough time passes to use a new seed. Try reusing the Random objects throughout the loop.
James
+2  A: 
Random rand = new Random(); // specify a seed
int r = rand.Next(0x1000000); 
Console.WriteLine("#{0:X6}", r);
Chris Doggett
This doesn't handle low values. 0x00FFFF turns into #FFFF which is not the same as #00FFFF.
Samuel
+9  A: 

IMO, purely random colours may not be preferable as you need colours that are distinguishable in human eyes.

What about presetting a number of colours and picking them randomly?

Perhaps you could find better answers in some open source charting libraries.

Canton
This would be the preferred method, but maybe the OP has other reasons for completely random colours.
Samuel
I thought about this since some colors may blend together. I wasnt sure how many to preset though since I could have a very high number of series.
rahkim
A hybrid of preset and random may help. Carefully preset about 10 nice looking colors. Go for random if more than that. In general untrained eyes start to get confused in an overly colored chart, so what exact colors used are not really important
Canton
Good advice. thx.
rahkim
+6  A: 

Samuel's answer is the best way to do this, just make sure that if you're generating the colors inside a loop that you don't instantiate a new Random object each time because new Random() seeds the generator using the system clock. Your loop is going to run faster than the clock can tick, so you'll end up generating several of the same colors over and over because random is being seeded with the same value.

It should look something like this:

int numColors = 10;
var colors = new List<string>();
var random = new Random(); // Make sure this is out of the loop!
for (int i = 0; i < numColors; i++) 
{
    colors.Add(String.Format("#{0:X6}", random.Next(0x1000000)));
}

instead of:

int numColors = 10;
var colors = new List<string>();
for (int i = 0; i < numColors; i++) 
{
    var random = new Random(); // Don't put this here!
    colors.Add(String.Format("#{0:X6}", random.Next(0x1000000)));
}
John Rasch
Nice mind reading!
Michael Haren
This explains why rahkim gets random colours when using a breakpoint. lol
Samuel
This didnt even cross my mind. Are you adding the colors to a list for a reason? Just good practice, or is there something that Im missing?
rahkim
This was just an example since I'm not sure how he's assigning them
John Rasch
+7  A: 

A good way of generating a nice set of colours is to define them using fixed saturation and brightness and vary the hue.

  1. Set saturation and brightness to something you like, say 50% saturation and 90% brightness.
  2. Now divide the 360 degrees of hue by the number of distinct colours you want.
  3. Pick colours from HSV using that interval for hue, and the fixed S and V.

This gives you a nice set of colours, which all look like they came from the same 'set' -- all pastel, or all intense, or all off-white, whatever. And it's pretty easy to code if you've got Color.FromHSV().

It probably stops working once you get too many colours though, they'll be indistinguishable. But you'll probably get that problem anyway.

In pseudo code:

Sat = 0.5 * 255 //assuming we want range 0-255...
Brightness = 0.9 * 255
NumberOfColours = 7
HueSeparation = 360 / 7
Colors = []
for (i = 0 ; i< NumberOfColours; i++)
     Colors.Add(Color.FromHSV(HueSeparation * i, Sat, Brightness)

or

n = 7
Colors = [Color.FromHSV(x*360/n, 128, 230) for x in range(n)]

(I do like list comprehensions...)

Greg
Neat idea. I wish I could put it into code. ;-)
rahkim
+2  A: 

I noticed that you (Rahkim) commented on Greg's post that you wish you could put his idea (keeping the saturation and value constant, and just varying the hue...a good idea) into code. You can! Or, rather, someone already has for you!

I found this blog post on Converting HSV to RGB colour using C#, and I'm sure there are more out there. You'll probably end up with a nicer suite of colors this way than by picking them totally randomly.

Additionally, of course, this method makes it simple to get a nice set of colors...since the Hue goes from 0-359, you could do something like set your Hue something like this:

Hue = (PreviousHue + 50) % 360;

(I picked 50 since it doesn't go into 360 evenly, so if you go beyond 360 you won't start repeating hues instantly. You'd have to toy around with the value to get an ideal separation depending on how many different colors you're expecting.)

This way you don't have to worry about the case where the code is randomly picking two colors that are super close to one another when there is still lots of unused "hue" space.

Beska
I like this. It gives me some flexibility.
rahkim