dtolnay / request-for-implementation

Crates that don't exist, but should

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Minimal API for generating color scheme for a chart that needs n colors

dtolnay opened this issue · comments

In cargo tally I need to display a plot with some number of lines on it. I wish there were a crate where I could call:

pub fn get_color_scheme(size: u32) -> Vec<HexColor>;

and get back n different colors in a cohesive color scheme suitable for a chart.

For up to 64 colors, a suitable permutation of the old X11 RGB.txt "Crayola Crayon" colors is not a bad way to go. (If you need more than 64 distinct colors, results are likely to be sketchy anyhow.)

I tend to prefer a deterministic scheme with a predefined order rather than the randomization scheme specced in the IWantHue link above. That way the distribution can be hand-tuned. I have used @dtolnay's scheme from cargo-tally with some success, but I prefer to use darker and lighter pastels of each hue and spin things so that adjacent colors are more distinct.

This brings up an interesting issue: sometimes you want nearby color indices to be as distinct as possible, sometimes you want them to be distinct but related. Should the API provide/reflect this?

What about something like ColorBrewer?

Interesting! It's got some great ideas in it. It's not obvious what algorithms etc it's using. It only goes up to 12 colors or so in the current implementation. It seems pretty tuned for maps.

I suspect one would have to figure out what's going on there, and then build a crate that incorporated that learning. It would be a great project.

commented

A popular R implementation can be found here https://cran.r-project.org/web/packages/RColorBrewer/index.html. It's implemented as predefined sets of colors, but often used in R in combination with functionality that does interpolation (grDevices::colorRamp)

Cool. Documentation about how it works anyplace?

The canonical ColorBrewer implementation is written in JavaScript and available on github here: axismaps/colorbrewer, however the implementation I'm familiar with comes with d3 (also JS). It's very much map-centric as Dr. Brewer is a cartographer at Penn State. The CMYK and RGB specs are available here.

Personally I think that more than twelve colors for a chart seems a bit much, but GraphIQ's design lead made an interesting post on their blog summarizing various ideas of how to generate color sets for data visualizations (unfortunately it's hosted on Medium so it's awkward to read).

That said, how about some bikeshedding? Is there interest in CMYK and HSV support as well? 16-bit RGB?

I'm thinking instead of a HexColor struct an enum like:

enum Color {
  RGB(u8, u8, u8),
  CMYK(u8, u8, u8, u8),
  HSV(u8, u8, u8),
}

would be a good idea assuming ColorBrewer were the starting point as these are already defined.

And then maybe a trait that defines functions for each color space that take an argument indicating number of colors desired and returns a Color enum? Maybe metadata functions as well for the attributes listed on the site (e.g. safe for colorblind folks)?

In the back of my head I'm thinking a proper charting library (even if backed by Cairo) modeled after d3 would be really awesome (both for server side rendering and client side w/ WASM).

Hey everyone.
I'm still new to Rust and this is my first published lib (though it's not on crates.io yet because I don't know if something this small should pollute their archives).
I am open to criticism and appreciate any feedback you can give a newbie like me.

I went through a few iterations on how to generate a random color palette but I'm not at all familiar with color science so I ran into a few dead ends.
The current implementation actually uses the HSV color space to generate random hues by simply shifting the hue of each color in the palette by 85° plus a little extra for each iteration.

Because everything internally is calculated using floats (f32) and I don't like robbing precision from the users the user-facing Color struct only outputs RGB in floats from 0 to 1 (not the traditional 0 to 255).

Anyway here's the code: https://github.com/BrandtM/colourado

Sweet! Would it be possible to add screenshots of color schemes of various sizes to the readme? It is hard to tell from the code what they might look like.

Maybe also, if you're interested: add a little companion binary that pops up a window showing n lines or boxes with your color scheme (or writes out a png file if that is easier). If you stick a file with a main function into examples/preview.rs then Cargo will pick it up like this:

cargo run --example preview 4
                            ^ number of colors

Oh wow thanks.
I did actually test this before but I wasn't aware of this functionality.
Should be done in a couple of minutes.

@dtolnay alright it's done.

Nicely done. Once you get it published to crates.io, I'll close out this issue and mark it off the list with a link to your crate.

For cargo tally I might want some brighter colors that are easier to differentiate on a line graph, but we can sort that out on the issue tracker. Thanks!

Regarding the other folks' suggestions earlier in the thread, I would love to see those take form as well! Let me know if any of those get turned into a library and I can link to them as well.

This crate looks like a great start: a nice place to work on the API and color generation schemes. Thanks for taking the lead @BrandtM !

Thanks for your encouragement!
I had some minor troubles but nothing that I couldn't fix easily but now it's published to crates.io
https://crates.io/crates/colourado

Hope I am not too late. Using colorgrad we can do something like this easily. There is lot of preset gradients, and we can create custom gradient too.

let grad = colorgrad::sinebow();
let hex_colors = Vec::new();

for c in grad.colors(35) {
    hex_colors.push(c.to_hex_string());
}