mtennekes / cols4all

Colors for all (R package)
https://mtennekes.github.io/cols4all/
324 stars 17 forks source link

color ramp #10

Closed mtennekes closed 1 year ago

mtennekes commented 2 years ago

@zeileis: does colorspace also have a color ramp function that I can use? If seems that the grDevices::colorRamp does not take only takes objects of class color?

Reason I ask is that cols4all currently uses grDevices::colorRamp, but ideally I'd like to use unrounded RGB values (or polarLAB if desired).

zeileis commented 2 years ago

We did not add a color ramp function for two reasons:

My impression is that this should be able to do everything you need? Or do you need functionality for discrete unrounded support points?

mtennekes commented 2 years ago

Thanks!

A few thinks are unclear:

1.

(blues3_9 = hcl.colors(9, "Blues 3"))
#> [1] "#00366C" "#00538E" "#0072B4" "#468FD0" "#79ABE2" "#A1C4F1" "#C3DBFD"
#> [8] "#E1EEFF" "#F9F9F9"
(blues3_9b = colorRampPalette(blues3_9, space = "Lab")(9))
#> [1] "#00356C" "#00528D" "#0072B3" "#468ED0" "#79ABE1" "#A0C4F1" "#C3DAFD"
#> [8] "#E0EDFF" "#F8F8F8"

Why are these not equal to each other? The colors of "Blues 3" are valid in the CIELAB space, so I would have expected that (at least) the exact same colors are returned, but perhaps I overlooked something.

2.

From my (possibly wrong) understanding colorspace is used to create the hcl-based palettes in grDevices (the ones listed with hcl.pals()). Is that true? Can I also extract the colors (for different n) in colorspace? E.g. with something like this?

library(colorspace)
blues3_hcl = hcl_palettes(palette = "Blues 3", n = 9)

as(blues3_hcl, "RGB")
#> Error in as(blues3_hcl, "RGB"): no method or default for coercing "hcl_palettes" to "RGB"

Another slightly off-but still related question:

  1. Is there a theory or empirical foundation behind the L and C curves in a diverging palette, e.g. from "Purple Green":
hcl.colors(11, "Purple Green") |> specplot()

In hcl_wizard I noticed that these curves can be adjusted with the P1 and P2 parameters. Are there any best practices of setting those parameters?

mtennekes commented 2 years ago

@zeileis The questions above are not important for the upcoming CRAN release of cols4all, but I'm still curious:-)

zeileis commented 2 years ago

Apologies, I was on Easter vacation and forgot about this. I'll try to have a closer look in the next days.

zeileis commented 2 years ago

Finally some follow-up:

  1. Yes, theoretically these colors should be identical. It's just an artifact from taking integer (0-255) sRGB coordinates, doing the interpolation, and then implicitly using floor rather than round to go back to integers. Thus, this can sometimes decrease an integer sRGB coordinate by 1. This has nothing to do, though, with how the colors were generated in the first place.
    Illustration: An olive color with integer sRGB coordinates 120, 160, 100:
    x_hex <- rgb(120, 160, 100, maxColorValue = 255)
    x_hex
    ## [1] "#78A064"
    x_rgb <- t(col2rgb(x))
    x_rgb
    ##      red green blue
    ## [1,] 120   160  100
    x_lab <- convertColor(x_rgb/255, from = "sRGB", to = "Lab")
    x_rgb2 <- convertColor(x_lab, from = "Lab", to = "sRGB") * 255
    x_rgb2
    ##          [,1]     [,2]     [,3]
    ## [1,] 120.0005 159.9997 100.0008

    So you end up at almost the same coordinates but not quite. If you were to round to integers you would get exactly the same colors. But this is not what the rgb() function does. Instead the 159.9997 is treated like 159, thus reducing the integer sRGB coordinate by 1 compared to the original.

    rgb(x_rgb2, maxColorValue = 255)
    ## [1] "#789F64"
  2. I'm not sure what you are looking for here exactly. If you can tell me what you want to compute exactly, I can hopefully suggest a way to do so...
    Background: The HCL palettes are designed by a set of parameters that correspond to trajectories in the three HCL dimensions. The final palettes are set up by selecting points along the trajectories and converting them to sRGB and the corresponding hex colors. Where the parameters are stored and which functions are used for the conversion differs between the grDevices and colorspace implementations. The former is much more streamlined while the latter has many more convenience features.
    • grDevices: The parameters are stored in the (unexported) data frame grDevices:::.hcl_colors_parameters. hcl.colors() extracts the parameters, sets up the trajectories, and then calls grDevices::hcl() to compute the hex colors.
    • colorspace: The same parameters are stored in several lists (rather than one data frame), e.g., colorspace:::seqm.pals. The function hcl_palettes() extracts these parameters and returns a classed data frame with print/plot/summary methods (but no other coercion methods at the moment). The different palette functions like sequential_hcl() query these parameters - and optionally tweak them on the fly - and then uses colorspace's coercion functions to compute the hex colors. The tweaked palettes can also be registered as new palettes etc.
  3. I'm not aware of a "theory" of choosing the power parameters for the chroma and luminance dimensions. We had implemented them for the 2009 "Escaping RGBland" paper in CSDA in order to have a simple way of controlling how quickly the palette increases in chroma and luminance. This allows you to obtain palettes that: (a) highlight only the extreme(s) vs. (b) have relatively colorful colors throughout.
    Later on it became clear that these parameters were very helpful for closely approximating palettes from ColorBrewer, CARTO, Crameri, viridis, etc.