proplot-dev / proplot

🎨 A succinct matplotlib wrapper for making beautiful, publication-quality graphics
https://proplot.readthedocs.io
MIT License
1.09k stars 100 forks source link

Perceptually uniform cyclic colormaps #70

Open lukelbd opened 4 years ago

lukelbd commented 4 years ago

We are considering packing proplot with some custom cyclic colormaps. Below is an example that I'm toying with.

@bradyrx It turns out that luminance + chroma as sine curves in quadrature give you uniform, colorblind-friendly single hue maps! I tried making a cyclic multihue map this way but it turns out to be way harder. HSL and HPL give weird zig-zags in chroma/luminance and in HCL, it's almost impossible to "thread the needle" between valid values (i.e. values that translate to non-impossible RGB colors).

Feel free to play with this example:

import proplot as plot
import numpy as np
space = 'hcl'
hue = 'medium blue'
reverse = False
luminance_shift = 0
saturation_shift = np.pi/2
luminance_offset = 0
saturation_offset = 0
luminance_scale = 80
saturation_scale = 60
luminance = lambda x: luminance_offset + 50 + luminance_scale*np.sin(x*2*np.pi + luminance_shift)/2
saturation = lambda x: saturation_offset + 50 + saturation_scale*np.sin(x*2*np.pi + saturation_shift)/2
cmap = plot.PerceptuallyUniformColormap.from_hsl('_cyclic',
    # hue=(hue, hue + 360)[::1 - 2*reverse], # cyclic hues!
    hue=hue, # constant hue!
    saturation=saturation, luminance=luminance,
    cyclic=True, space=space)
cmap = cmap.shifted(180) # optionally shift
f = plot.show_channels(cmap, rgb=False, axwidth=1.5)
f, ax = plot.subplots()
ax.pcolormesh(np.ones((30,30)).cumsum(axis=0) % 10, cmap=cmap, colorbar='b')
ax.format(suptitle='Cyclic colormap test')

image

image

lukelbd commented 4 years ago

Here's a red one.

space = 'hcl'
hue = 'brick red'

image

image

lukelbd commented 4 years ago

Throwing in some hue wiggles makes it a bit more interesting (might use something close to this one!).

import proplot as plot
import numpy as np
space = 'hcl'
hue = 'brick red'
reverse = False
luminance_shift = 0
saturation_shift = np.pi/2
hue_offset = 20
luminance_offset = 0
saturation_offset = 0
hue_scale = 50
luminance_scale = 60
saturation_scale = 60
hue = lambda x: hue_offset + hue_scale*np.sin(x*2*np.pi)/2
luminance = lambda x: luminance_offset + 50 + luminance_scale*np.sin(x*2*np.pi + luminance_shift)/2
saturation = lambda x: saturation_offset + 50 + saturation_scale*np.sin(x*2*np.pi + saturation_shift)/2
cmap = plot.PerceptuallyUniformColormap.from_hsl('_cyclic',
    # hue=(hue, hue + 360)[::1 - 2*reverse], # cyclic hues!
    hue=hue, # constant hue!
    saturation=saturation, luminance=luminance,
    cyclic=True, space=space)
cmap = cmap.shifted(180) # optionally shift
f = plot.show_channels(cmap, rgb=False, axwidth=1.5)
f, ax = plot.subplots()
ax.pcolormesh(np.ones((30,30)).cumsum(axis=0) % 10, cmap=cmap, colorbar='b')
ax.format(suptitle='Cyclic colormap test')

image

image

bradyrx commented 4 years ago

This looks great! @jessica-tomaszewski