What follows is the translation of Java code I found here, in the VisualizeCoords class. It returns an array reference holding the given number of array references. Each RGB tuple has a value from 0 to 1 -- simply multiply by 255 to get the "normal" value.
sub get_distinct_colors { use POSIX 'ceil'; my $n = shift; my $discrete = ceil($n ** (1/3)); my @vals = map 1 - (($_-1) / $discrete), 1 .. $discrete; my @colors; my ($r, $g, $b) = (0,0,0); for my $i (1 .. $n) { push @colors, [@vals[$r,$g,$b]]; if (++$b == $discrete) { if (++$g == $discrete) { $r = ($r + 1) % $discrete; $g = 0; } $b = 0; } } return \@colors; }
_____________________________________________________
Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart

Replies are listed 'Best First'.
Re: Generating Visually Distinct Colors
by Roy Johnson (Monsignor) on Mar 22, 2005 at 04:52 UTC
    Nicely translated. I wanted to make it read a little nicer, and came up with this equivalent code:
    sub my_gdc { use POSIX 'ceil'; my $n = shift; my $discrete = ceil($n ** (1/3)); my @colors; GENERATE: for my $r (0..$discrete-1) { for my $g (0..$discrete-1) { for my $b (0..$discrete-1) { push @colors, [map {1-$_/$discrete} $r, $g, $b]; last GENERATE if @colors >= $n; } } } \@colors; }
    and then I wanted to use a single loop and came up with this:
    sub my_gdc1 { use POSIX 'ceil'; my $n = shift; my $discrete = ceil($n ** (1/3)); my @colors = ([1,1,1]); for my $i (1..$n-1) { push @colors, [map {1-($_%$discrete)/$discrete} $i/($discrete**2), $i/$discrete, $i]; } \@colors; }

    Caution: Contents may have been coded under pressure.
Re: Generating Visually Distinct Colors
by iblech (Friar) on Mar 22, 2005 at 15:36 UTC

    Perl 6! :) (code slightly adapted to run under Pugs)

    #!/usr/bin/perl6 use v6; # ceil() not yet implemented in Pugs sub ceil ($n) { int($n) + ($n > int($n) ?? 1 :: 0) } my $n = 10; my $discrete = ceil($n ** (1/3)); my $r = any 0..$discrete-1; my $g = any 0..$discrete-1; my $b = any 0..$discrete-1; # Now $r,$g,$b are Junctions containing all the possible values of # red/green/blue. $r = int( (1-$r/$discrete) * 255 ); $g = int( (1-$g/$discrete) * 255 ); $b = int( (1-$b/$discrete) * 255 ); # $color is a Junction containing all colors as "(red, green, blue)". my $color = "($r, $g, $b)"; # Finally, print $color. for $color -> $x { say $x }

    Update: Hand-rolled ceil was wrong, fixed (thanks japhy!).

      Um, what's with that ceil() function? int($x+2)? I'd feel much safer with
      sub ceil (Num $x) { $x == int($x) ?? $x :: int($x+1) }
      I'm pretty sure that's the appropriate P6 code.
      _____________________________________________________
      Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
      How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart
        In Perl 5, ceil is in package POSIX and it is (together with floor) the recommended way to do a unilateral round towards an integer. For what I can remember, the use of int for this is discouraged even in Perl (5) documentation, due to the fact that its behaviour is not consistent between positive and negative numbers (it only cuts decimals away).

        I don't know if this has been changed in Perl 6, but I think that I'll stick to the library ceil anyway.

        Flavio

        -- Don't fool yourself.
      Does that output $n colors, or $n rounded to the next higher cube?

      Caution: Contents may have been coded under pressure.
Re: Generating Visually Distinct Colors
by sfink (Deacon) on Mar 22, 2005 at 18:07 UTC
    I implemented something like this for an application I wrote once. The algorithm you're implementing chooses N evenly-spaced 3D grid points in the RGB color space. I found that this didn't give a terribly good result, because our eyes are far from being equally sensitive to changes in red, green, and blue at different points in the color space.

    Anyone know of a better algorithm? As a first cut, probably using HSV space would be better, but you wouldn't want to sample it evenly -- you'd probably use a whole bunch of gradations in hue, but only a few in saturation and value.

    I probably just ought to spend some quality time with Google. Given how important this is for things like MPEG encoding, I'm sure somebody's come up with a color space that approximates an even spreading of perceptual differences. Well, "approximates" it for "most" people, or something.

    Then you'd just have to implement a "red/green colorblind" mode that a user could select, with a very different sampling space...

      The YUV colorspace is a good choice. It happens to be the color schema used in JPEG (and I presume in MPEG).

      Caution: Contents may have been coded under pressure.
Re: Generating Visually Distinct Colors
by cosimo (Hermit) on Mar 23, 2005 at 12:18 UTC
    I usually use something like this to generate colors for sections of histogram or pie charts.
    sub getColor { my $index = shift; # Numerical index (integer from 0 to .. +.) my @color; # Color components my @hue_matrix = ( # # R G B Component to modify(0=R, 1=G, 2=B) # | | | | # V V V V Add or subtract the offset # | from (r,g,b) triplet # V [ 255, 0, 0, 2, 1 ], # Sector 1 R -> R+B [ 255, 0, 255, 0, -1 ], # Sector 2 R+B -> B [ 0, 0, 255, 1, 1 ], # ... B -> Cyan [ 0, 255, 255, 2, -1 ], # ... Cyan-> G [ 0, 255, 0, 0, 1 ], # ... ... [ 255, 255, 0, 1, -1 ], [ 255, 0, 0, 2, 1 ] ); # Select a "spectrum sector", according to my hue matrix my $sector = $hue_matrix[ int($index / 42) % @hue_matrix ]; # Calculate an offset to be applied to starting color my $offset = ($index % 42) * 6; @color = @$sector[0..2]; # Modify selected component to generate a "continuous gradient" $color[$sector->[3]] += ($sector->[4] > 0 ? $offset : -$offset); return(@color); } # Generate visually distinct colors... print '<HTML><BODY>', "\n"; # How much distinct you decide with this factor my $distinct_factor = 10; for( 1 .. 30 ) { printf( "<TABLE BGCOLOR=#%02X%02X%02X><TR><TD>" . '&nbsp;' x 20 . "</T +D></TR></TABLE>\n", getColor($distinct_factor * $_) ); } print '</BODY></HTML>'; # Or you can choose getColor() parameter at random...

    The final result is:

                                            

    ... that of course is not perfect, but adjusting the $distinct_factor can provide satisfying results.

    Update: Sorry, it was missing the last piece of code...

Re: Generating Visually Distinct Colors
by iblech (Friar) on Mar 23, 2005 at 13:39 UTC

    japhy, you might find Coloring IRC logs nicely interesting, too.

    This is the algorithm my IRC bot iblechbot uses, too (screenshot).

    First, a raw IRC log is read to find out which nicks were online (and talked) at the same time. This information is used to build a interference graph. Then, using the graph, all nicks get distict colors.

    Because of the interference graph, the colors used are reduced to a minimum:

    • 10:00 - A joins - color: 1/3 # notice: three colors needed
    • 10:03 - B joins - color: 2/3
    • 10:05 - C joins - color: 3/3
    • (Time passes...)
    • 20:00 - D joins - color: 1/2 # notice: only two colors used here
    • 20:02 - E joins - color: 2/2

    Using the colornum/total_num_of_colors information, the actual color pair (foreground, background) is built: