Skip to content

Commit 5fe6b05

Browse files
committed
show_cycles and show_cmaps both use colorbars
1 parent 9f59217 commit 5fe6b05

File tree

1 file changed

+109
-128
lines changed

1 file changed

+109
-128
lines changed

proplot/styletools.py

Lines changed: 109 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
]
4040

4141
# Colormap stuff
42-
CMAPS_CATEGORIES = {
42+
CMAPS_TABLE = {
4343
# Assorted origin, but these belong together
4444
'Grayscale': (
4545
'Grays', 'Mono', 'GrayCycle',
@@ -124,11 +124,12 @@
124124
'gist_earth', 'gist_gray', 'gist_heat', 'gist_ncar',
125125
'gist_rainbow', 'gist_stern', 'gist_yarg',
126126
),
127-
'Miscellaneous': (
127+
'Other': (
128128
'binary', 'bwr', 'brg', # appear to be custom matplotlib
129129
'cubehelix', 'wistia', 'CMRmap', # individually released
130130
'seismic', 'terrain', 'nipy_spectral', # origin ambiguous
131-
),
131+
'tab10', 'tab20', 'tab20b', 'tab20c', # merged colormap cycles
132+
)
132133
}
133134
CMAPS_DELETE = (
134135
'binary', 'gist_yarg', 'gist_gray', 'gray', 'bone', 'pink',
@@ -3116,8 +3117,54 @@ def register_fonts():
31163117
fonts[:] = [*fonts_proplot, *fonts_system]
31173118

31183119

3119-
def show_channels(*args, N=100, rgb=True, saturation=True,
3120-
minhue=0, maxsat=1000, axwidth=None, width=100):
3120+
def _draw_bars(cmapdict, length=4.0, width=0.2):
3121+
"""
3122+
Draw colorbars for "colormaps" and "color cycles". This is called by
3123+
`show_cycles` and `show_cmaps`.
3124+
"""
3125+
# Figure
3126+
from . import subplots
3127+
naxs = len(cmapdict) + sum(map(len, cmapdict.values()))
3128+
fig, axs = subplots(
3129+
nrows=naxs, axwidth=length, axheight=width,
3130+
share=0, hspace=0.03,
3131+
)
3132+
iax = -1
3133+
nheads = nbars = 0 # for deciding which axes to plot in
3134+
a = np.linspace(0, 1, 257).reshape(1, -1)
3135+
a = np.vstack((a, a))
3136+
for cat, names in cmapdict.items():
3137+
if not names:
3138+
continue
3139+
nheads += 1
3140+
for imap, name in enumerate(names):
3141+
iax += 1
3142+
if imap + nheads + nbars > naxs:
3143+
break
3144+
ax = axs[iax]
3145+
if imap == 0: # allocate this axes for title
3146+
iax += 1
3147+
ax.set_visible(False)
3148+
ax = axs[iax]
3149+
cmap = mcm.cmap_d[name]
3150+
ax.imshow(
3151+
a, cmap=name, origin='lower', aspect='auto',
3152+
levels=cmap.N
3153+
)
3154+
ax.format(
3155+
ylabel=name,
3156+
ylabel_kw={'rotation': 0, 'ha': 'right', 'va': 'center'},
3157+
xticks='none', yticks='none', # no ticks
3158+
xloc='neither', yloc='neither', # no spines
3159+
title=(cat if imap == 0 else None)
3160+
)
3161+
nbars += len(names)
3162+
3163+
3164+
def show_channels(
3165+
*args, N=100, rgb=True, saturation=True, minhue=0,
3166+
maxsat=500, width=100, axwidth=1.7
3167+
):
31213168
"""
31223169
Show how arbitrary colormap(s) vary with respect to the hue, chroma,
31233170
luminance, HSL saturation, and HPL saturation channels, and optionally
@@ -3149,12 +3196,12 @@ def show_channels(*args, N=100, rgb=True, saturation=True,
31493196
Returns
31503197
-------
31513198
`~proplot.subplots.Figure`
3152-
The figure instance.
3153-
""" # noqa
3199+
The figure.
3200+
"""
31543201
# Figure and plot
31553202
from . import subplots
31563203
if not args:
3157-
args = (rcParams['image.cmap'],)
3204+
raise ValueError(f'At least one positional argument required.')
31583205
array = [[1, 1, 2, 2, 3, 3]]
31593206
labels = ('Hue', 'Chroma', 'Luminance')
31603207
if saturation:
@@ -3165,7 +3212,7 @@ def show_channels(*args, N=100, rgb=True, saturation=True,
31653212
labels += ('Red', 'Green', 'Blue')
31663213
fig, axs = subplots(
31673214
array=array, span=False, share=1,
3168-
aspect=1, axwidth=axwidth, axpad='1em',
3215+
axwidth=axwidth, axpad='1em',
31693216
)
31703217
# Iterate through colormaps
31713218
mc, ms, mp = 0, 0, 0
@@ -3260,7 +3307,7 @@ def show_colorspaces(luminance=None, saturation=None, hue=None, axwidth=2):
32603307
Returns
32613308
-------
32623309
`~proplot.subplots.Figure`
3263-
The figure instance.
3310+
The figure.
32643311
"""
32653312
# Get colorspace properties
32663313
hues = np.linspace(0, 360, 361)
@@ -3319,14 +3366,14 @@ def show_colorspaces(luminance=None, saturation=None, hue=None, axwidth=2):
33193366
return fig
33203367

33213368

3322-
def show_colors(nbreak=17, minsat=20):
3369+
def show_colors(nhues=17, minsat=20):
33233370
"""
3324-
Visualize the registered color names in two figures. Adapted from
3325-
`this example <https://matplotlib.org/examples/color/named_colors.html>`_.
3371+
Generate tables of the registered color names. Adapted from
3372+
`this example <https://matplotlib.org/examples/color/named_colors.html>`__.
33263373
33273374
Parameters
33283375
----------
3329-
nbreak : int, optional
3376+
nhues : int, optional
33303377
The number of breaks between hues for grouping "like colors" in the
33313378
color table.
33323379
minsat : float, optional
@@ -3336,16 +3383,16 @@ def show_colors(nbreak=17, minsat=20):
33363383
Returns
33373384
-------
33383385
figs : list of `~proplot.subplots.Figure`
3339-
The figure instances.
3386+
The figure.
33403387
"""
33413388
# Test used to "categories" colors
3342-
breakpoints = np.linspace(0, 360, nbreak)
3389+
breakpoints = np.linspace(0, 360, nhues)
33433390
def _color_filter(i, hcl): # noqa: E306
33443391
gray = hcl[1] <= minsat
33453392
if i == 0:
33463393
return gray
33473394
color = breakpoints[i - 1] <= hcl[0] < breakpoints[i]
3348-
if i == nbreak - 1:
3395+
if i == nhues - 1:
33493396
color = color or color == breakpoints[i] # endpoint inclusive
33503397
return not gray and color
33513398

@@ -3375,8 +3422,6 @@ def _color_filter(i, hcl): # noqa: E306
33753422
nrows = nrows * 2
33763423
ncols = (ncols + 1) // 2
33773424
names.resize((ncols, nrows))
3378-
names = names.tolist()
3379-
# names = names.reshape((ncols, nrows)).tolist()
33803425

33813426
# Get colors in perceptally uniform space, then group based on hue
33823427
# thresholds
@@ -3393,7 +3438,7 @@ def _color_filter(i, hcl): # noqa: E306
33933438
[pair for pair in hclpairs if _color_filter(i, pair[1])],
33943439
key=lambda x: x[1][2]
33953440
)
3396-
for i in range(nbreak)
3441+
for i in range(nhues)
33973442
]
33983443
names = np.array([
33993444
name for ipairs in hclpairs for name, _ in ipairs
@@ -3434,158 +3479,94 @@ def _color_filter(i, hcl): # noqa: E306
34343479
return figs
34353480

34363481

3437-
def show_cmaps(*args, N=256, length=4.0, width=0.2, unknown='User'):
3482+
def show_cmaps(*args, N=None, unknown='User', **kwargs):
34383483
"""
3439-
Visualize all registered colormaps, or the list of colormap names if
3440-
positional arguments are passed. Adapted from `this example \
3484+
Generate a table of the registered colormaps or the input colormaps.
3485+
Adapted from `this example \
34413486
<http://matplotlib.org/examples/color/colormaps_reference.html>`__.
34423487
34433488
Parameters
34443489
----------
34453490
*args : colormap-spec, optional
3446-
Positional arguments are colormap names or objects. Default is
3447-
all of the registered colormaps.
3491+
Colormap names or objects.
34483492
N : int, optional
3449-
The number of levels in each colorbar.
3493+
The number of levels in each colorbar. Default is
3494+
:rc:`image.lut`.
3495+
unknown : str, optional
3496+
Category name for colormaps that are unknown to ProPlot. The
3497+
default is ``'User'``.
34503498
length : float or str, optional
3451-
The length of each colorbar. Units are interpreted by
3499+
The length of the colorbars. Units are interpreted by
34523500
`~proplot.utils.units`.
34533501
width : float or str, optional
3454-
The width of each colorbar. Units are interpreted by
3502+
The width of the colorbars. Units are interpreted by
34553503
`~proplot.utils.units`.
3456-
unknown : str, optional
3457-
Category name for colormaps that are unknown to ProPlot. The
3458-
default is ``'User'``.
34593504
34603505
Returns
34613506
-------
34623507
`~proplot.subplots.Figure`
3463-
The figure instance.
3508+
The figure.
34643509
"""
34653510
# Have colormaps separated into categories
3511+
N = _notNone(N, rcParams['image.lut'])
34663512
if args:
3467-
imaps = [Colormap(cmap, N=N).name for cmap in args]
3513+
names = [Colormap(cmap, N=N).name for cmap in args]
34683514
else:
3469-
imaps = [
3470-
name for name in mcm.cmap_d.keys() if name not in ('vega', 'greys')
3471-
and name[0] != '_'
3472-
and isinstance(mcm.cmap_d[name], LinearSegmentedColormap)
3515+
names = [
3516+
name for name in mcm.cmap_d.keys() if
3517+
isinstance(mcm.cmap_d[name], LinearSegmentedColormap)
34733518
]
34743519

34753520
# Get dictionary of registered colormaps and their categories
3476-
imaps = [name.lower() for name in imaps]
3477-
cats = {cat: names for cat, names in CMAPS_CATEGORIES.items()}
3478-
cats_plot = {cat: [name for name in names if name.lower() in imaps]
3479-
for cat, names in cats.items()}
3480-
# Distinguish known from unknown (i.e. user) maps, add as a new category
3481-
imaps_known = [name.lower() for cat, names in cats.items()
3482-
for name in names if name.lower() in imaps]
3483-
imaps_unknown = [name for name in imaps if name not in imaps_known]
3484-
# Remove categories with no known maps and put user at start
3485-
cats_plot = {unknown: imaps_unknown, **cats_plot}
3486-
cats_plot = {cat: maps for cat, maps in cats_plot.items() if maps}
3521+
cmapdict = {}
3522+
names_all = list(map(str.lower, names))
3523+
names_known = sum(CMAPS_TABLE.values(), [])
3524+
cmapdict[unknown] = [name for name in names if name not in names_known]
3525+
for cat, names in CMAPS_TABLE.items():
3526+
cmapdict[cat] = [name for name in names if name.lower() in names_all]
34873527

3488-
# Figure
3489-
from . import subplots
3490-
naxs = len(imaps_known) + len(imaps_unknown) + len(cats_plot)
3491-
fig, axs = subplots(
3492-
nrows=naxs, axwidth=length, axheight=width,
3493-
share=0, hspace=0.03,
3494-
)
3495-
iax = -1
3496-
ntitles = nplots = 0 # for deciding which axes to plot in
3497-
a = np.linspace(0, 1, 257).reshape(1, -1)
3498-
a = np.vstack((a, a))
3499-
for cat, names in cats_plot.items():
3500-
# Space for title
3501-
if not names:
3502-
continue
3503-
ntitles += 1
3504-
for imap, name in enumerate(names):
3505-
# Draw colorbar
3506-
iax += 1
3507-
if imap + ntitles + nplots > naxs:
3508-
break
3509-
ax = axs[iax]
3510-
if imap == 0: # allocate this axes for title
3511-
iax += 1
3512-
ax.set_visible(False)
3513-
ax = axs[iax]
3514-
if name not in mcm.cmap_d or name.lower(
3515-
) not in imaps: # i.e. the expected builtin colormap is missing
3516-
ax.set_visible(False) # empty space
3517-
continue
3518-
ax.imshow(a, cmap=name, origin='lower', aspect='auto', levels=N)
3519-
ax.format(ylabel=name,
3520-
ylabel_kw={'rotation': 0, 'ha': 'right', 'va': 'center'},
3521-
xticks='none', yticks='none', # no ticks
3522-
xloc='neither', yloc='neither', # no spines
3523-
title=(cat if imap == 0 else None))
3524-
# Space for plots
3525-
nplots += len(names)
3526-
return fig
3528+
# Return figure of colorbars
3529+
return _draw_bars(cmapdict, **kwargs)
35273530

35283531

3529-
def show_cycles(*args, axwidth=1.5):
3532+
def show_cycles(*args, **kwargs):
35303533
"""
3531-
Visualize all registered color cycles, or the list of cycle names if
3532-
positional arguments are passed.
3534+
Generate a table of registered color cycles or the input color cycles.
35333535
35343536
Parameters
35353537
----------
35363538
*args : colormap-spec, optional
3537-
Positional arguments are cycle names or objects. Default is
3538-
all of the registered colormaps.
3539-
axwidth : str or float, optional
3540-
Average width of each subplot. Units are interpreted by
3539+
Cycle names or objects.
3540+
length : float or str, optional
3541+
The length of the colorbars. Units are interpreted by
3542+
`~proplot.utils.units`.
3543+
width : float or str, optional
3544+
The width of the colorbars. Units are interpreted by
35413545
`~proplot.utils.units`.
35423546
35433547
Returns
35443548
-------
35453549
`~proplot.subplots.Figure`
3546-
The figure instance.
3550+
The figure.
35473551
"""
35483552
# Get the list of cycles
35493553
if args:
3550-
icycles = {
3551-
getattr(cycle, 'name', '_no_name'): Colors(cycle)
3552-
for cycle in args}
3554+
names = [cmap.name for cmap in args]
35533555
else:
3554-
# use global cycles variable
3555-
icycles = {key: mcm.cmap_d[key].colors for key in cycles}
3556-
nrows = len(icycles) // 3 + len(icycles) % 3
3556+
names = [
3557+
name for name in mcm.cmap_d.keys() if
3558+
isinstance(mcm.cmap_d[name], ListedColormap)
3559+
]
35573560

3558-
# Create plot
3559-
from . import subplots
3560-
state = np.random.RandomState(51423)
3561-
fig, axs = subplots(
3562-
ncols=3, nrows=nrows, aspect=1, axwidth=axwidth,
3563-
sharey=False, sharex=False, axpad=0.05
3564-
)
3565-
for i, (ax, (key, cycle)) in enumerate(zip(axs, icycles.items())):
3566-
key = key.lower()
3567-
array = state.rand(20, len(cycle)) - 0.5
3568-
array = array[:, :1] + array.cumsum(axis=0) + np.arange(0, len(cycle))
3569-
for j, color in enumerate(cycle):
3570-
l, = ax.plot(array[:, j], lw=5, ls='-', color=color)
3571-
# make first lines have big zorder
3572-
l.set_zorder(10 + len(cycle) - j)
3573-
title = f'{key}: {len(cycle)} colors'
3574-
ax.set_title(title)
3575-
ax.grid(True)
3576-
for axis in 'xy':
3577-
ax.tick_params(axis=axis,
3578-
which='both', labelbottom=False, labelleft=False,
3579-
bottom=False, top=False, left=False, right=False)
3580-
if axs[i + 1:]:
3581-
axs[i + 1:].set_visible(False)
3582-
return fig
3561+
# Return figure of colorbars
3562+
cmapdict = {'Color cycles': names}
3563+
return _draw_bars(cmapdict, **kwargs)
35833564

35843565

35853566
def show_fonts(*args, size=12, text=None):
35863567
"""
3587-
Visualize the available sans-serif fonts. If a glyph is unavailable,
3588-
it is replaced by the "¤" dummy character.
3568+
Generate a table of fonts. If a glyph for a particular font is unavailable,
3569+
it is replaced with the "¤" dummy character.
35893570
35903571
Parameters
35913572
----------

0 commit comments

Comments
 (0)