"""Color, palette, and colormap exploration tools.
Provides utility functions for listing and visualizing the discrete
palettes and colormaps available in dartwork-mpl.
This module consolidates all asset exploration and visualization functions,
including those previously in the asset_viz module.
"""
from __future__ import annotations
import re
import matplotlib.colors as mcolors
import matplotlib.pyplot as plt
# Import asset visualization functions (to be fully integrated in future version)
from .asset_viz import (
classify_colormap,
plot_colormaps,
plot_colors,
plot_fonts,
)
__all__ = [
"list_palettes",
"list_colormaps",
"show_palette",
"classify_colormap",
"plot_colormaps",
"plot_colors",
"plot_fonts",
]
def _get_all_colors() -> list[str]:
from .color._loader import ensure_loaded
ensure_loaded()
return list(mcolors.get_named_colors_mapping().keys())
[docs]
def list_palettes() -> list[str]:
"""List all available discrete color palettes.
Returns
-------
list[str]
Sorted list of palette names (e.g., 'dc.vivid', 'oc.blue').
"""
colors: list[str] = _get_all_colors()
palettes: set[str] = set()
# match prefix.name + digits
pattern: re.Pattern[str] = re.compile(
r"^([a-z]+)\.([a-z]+(?:\-[a-z]+)?)\d+$"
)
for c in colors:
match = pattern.match(c)
if match:
palettes.add(f"{match.group(1)}.{match.group(2)}")
return sorted(palettes)
[docs]
def list_colormaps(include_reversed: bool = False) -> list[str]:
"""List all registered dartwork colormaps.
Parameters
----------
include_reversed : bool, optional
Whether to include reversed colormaps (names ending with '_r').
Default is False.
Returns
-------
list[str]
Sorted list of registered colormap names.
"""
from .cmap import ensure_loaded
ensure_loaded()
cmaps: list[str] = [c for c in plt.colormaps() if c.startswith("dc.")]
if not include_reversed:
cmaps = [c for c in cmaps if not c.endswith("_r")]
return sorted(cmaps)
[docs]
def show_palette(palette_name: str) -> None:
"""Visually display the colors of a specific discrete palette.
Renders all shades in the specified palette as a row of color
swatches. Useful for previewing colors in Jupyter notebooks.
Parameters
----------
palette_name : str
Name of the palette to visualize (e.g., 'dc.acid', 'oc.gray').
Raises
------
ValueError
If the palette name does not exist or contains no numbered
color entries.
"""
colors: list[str] = _get_all_colors()
# find all colors that start with palette_name followed by a number
pattern: re.Pattern[str] = re.compile(rf"^{re.escape(palette_name)}(\d+)$")
palette_colors: list[tuple[int, str]] = []
for c in colors:
match = pattern.match(c)
if match:
palette_colors.append((int(match.group(1)), c))
if not palette_colors:
raise ValueError(
f"Palette '{palette_name}' not found or has no numbered shades."
)
palette_colors.sort(key=lambda x: x[0])
color_names: list[str] = [c[1] for c in palette_colors]
n: int = len(color_names)
fig, ax = plt.subplots(figsize=(n * 0.8, 1.2))
for i, cname in enumerate(color_names):
ax.add_patch(
plt.Rectangle((i, 0), 1, 1, facecolor=cname, edgecolor="none")
)
# Simple contrast heuristic: lighter text for darker shades (index >= 5 usually)
shade_idx = palette_colors[i][0]
text_color = "white" if shade_idx >= 5 else "black"
ax.text(
i + 0.5,
0.5,
str(shade_idx),
color=text_color,
ha="center",
va="center",
fontsize=11,
fontweight="bold",
)
ax.set_xlim(0, n)
ax.set_ylim(0, 1)
ax.set_xticks([])
ax.set_yticks([])
ax.set_title(palette_name, loc="left", pad=10, fontweight="bold")
for spine in ax.spines.values():
spine.set_visible(False)
plt.tight_layout()
plt.show()