13 ms·
I've grappled with this problem multiple times in data-vis programming. A related problem is, once you have a uniform color space, how do you color code a list
by _greim_ 9y ago
I've grappled with this problem multiple times in data-vis programming.
A related problem is, once you have a uniform color space, how do you color code a list of things such that:
A) Color are maximally distinguishable from each other, i.e. maximally separated on the color wheel.
B) Colors are stable as items are added and removed, to avoid confusion caused by the color for an item shifting as you update the display.
The two criteria are at odds, unfortunately. The compromise I came up used PHI, since it's essentially a radial distribution problem, which PHI excels at. Something like:
(PHI * idx * 360) % 360
- yorwba 9y agoAdd to that the requirement of being distinguishable even for colorblind people. Whenever I choose colors for a chart or something, I try to find colors that work even with red/green blindness; but usually I give up and just use different textures or marker shapes and hope that they look distinctly enough even without color.
- jacobolus 9y agoThe best way to make colors distinguishable (for the color blind and everyone else) is to use lightness contrast.
- twic 9y agoPHI as in ~1.618? What is radial distribution, and why is phi particularly good for it?
- _greim_ 9y agoYes. It's basically the idea that as you plot points around the circle using this formula, at every iteration you'll have close-to-even spacing between points, no matter how many points you've plotted, without redistributing already-plotted points. It doesn't matter if you plot three points or three million, they'll be fairly evenly distributed. As to why phi behaves this way, maybe a mathematician can explain it better, but one thing I read is in some sense it's the "most" irrational number there is, so it has this property.
- kmill 9y agoSuppose you're generating positions mod 1, so f(i)=fmod(phi * i,1.0) is the position for index i. The distance between f(i) and f(j) is basically fmod(phi * (i-j),1.0), with the complication that mod wraps around... Anyway, if you do the correct wrap-around distance, the worst-case distance ends up being about approximating phi with i-j in the denominator, and the distance (i-j) * phi is away from being an integer is apparently just about 1/((i-j) * sqrt(5)) for phi (phi is what makes Hurwitz's theorem's bound tight-ish). In particular, with n points on a circle, the values will all be at least about 360/(n * sqrt(5)) degrees from each other. Of course, 360/n would be ideal, but it is not so bad being about 0.44x the best case. (Don't do anything safety critical with this analysis. I just spent a little time looking into it and probably got something wrong.)
- thaumasiotes 9y agoThis is the same solution plants use for distributing branches around their stem. (They repeat, but the separation between branches is closely related to phi.) I don't have a proof for why phi is good for this (although see kmill's comment), but the fact that all plants use it suggests it is in fact a good solution.
- _greim_ 9y agoThat's actually where I got the idea!
- wereHamster 9y agoYou plot the visible gammut of your favourite color space (RGB, CIE LUV, CIE LAB, preferably one that is perceptually uniform) in 3D space. You get either a cube (RGB) or something that resembles a skewed cube (CIE LUV/LAB). Then take the corners which are farthest apart and place a line between them, or place a line between any other two points which are sufficiently apart from each other. You sample however many points you need along that line and that is your color scale. Now one variation is instead of a straight line, you draw a spiral along it. And you got yourself a CUBEHELIX color scale (https://www.mrao.cam.ac.uk/~dag/CUBEHELIX/ https://www.mrao.cam.ac.uk/~dag/CUBEHELIX/). Though CUBEHLELIX works inside a RGB cube and not a CIE LUV/LAB mesh, but the principle could be applied to other color spaces, too. One thing to watch out for when working with these non-linear color spaces which have a weird shape in 3D space is how to deal with colors outside of the gammut. Simple clamping only works in RGB, in CIE LUV/LAB it's not that simple, because for any given L the range of UV/AB varies. And often times clamping is not the most (perceptually-)accurate solution.
- psychometry 9y agoThe problem I encountered is that once if subset your color space to include only color within ranges of, e.g., lightness and saturation, the transformed (perceptually uniform) color space is some weird, non-convex shape. Packing spheres into that transformed space is difficult.
- WorldMaker 9y agoTo domain switch, slightly: it's essentially a hash bucketing problem in a different guise at that point. The color space and visually distinguishable steps defines your hash bucket count (and kind of an equivalent to a memory limit), and maximally distributing your hashes across the available buckets remains roughly the same problem and you want a relatively well distributed, stable hash for basically the same reasons (color locality versus memory locality). The perceptual color problem is an interesting "bandwidth" limit on available hash buckets.
- akx 9y agoRidiculousfish, of Hex Fiend and fishshell fame, also has this nifty trick up his sleeve: http://ridiculousfish.com/blog/posts/colors.html http://ridiculousfish.com/blog/posts/colors.html