Source code for dartwork_mpl.helpers.colors

"""Curated palette lookup for dartwork-mpl.

`make_palette` returns a list of dartwork color names sized to the
requested series count and palette kind. The four built-in lists
(categorical, sequential blue at two cardinalities, diverging
red-blue at two cardinalities) are the dartwork-mpl recommended
series colors; they live inside this function so the project's
palette curation stays centralised.

Renamed from ``auto_select_colors`` in 0.5 (``#156`` Round 5):

- ``auto_`` prefix collided with ``auto_layout`` (measure-and-adjust).
- ``select_colors`` was a strong verb for what is structurally a list slice.
- ``palette`` is the existing domain term used by ``list_palettes`` and ``show_palette``.
"""

from __future__ import annotations

from typing import Literal


[docs] def make_palette( n: int, kind: Literal["categorical", "sequential", "diverging"] = "categorical", highlight: int | None = None, ) -> list[str]: """Return ``n`` curated dartwork color names for ``kind`` series. Parameters ---------- n : int Number of series the palette must cover. Colors repeat if ``n`` exceeds the built-in list for ``kind``. kind : {"categorical", "sequential", "diverging"} Palette family. ``categorical`` returns distinct hues; ``sequential`` returns light→dark blues; ``diverging`` returns red↔blue through gray. highlight : int | None If set, the series at this index becomes darker (``oc.*7``) and the rest become lighter (``oc.*3``) to emphasise it. Only effective for the categorical and (loosely) diverging palettes whose names contain a ``"5"`` middle shade. Returns ------- list[str] Length-``n`` list of dartwork color names. Examples -------- >>> make_palette(5) # 5 categorical series >>> make_palette(3, kind="sequential") # 3 sequential blues >>> make_palette(4, highlight=0) # emphasise series 0 """ if kind == "categorical": # Distinct colors for categorical data base_colors = [ "oc.blue5", "oc.red5", "oc.green5", "oc.orange5", "oc.purple5", "oc.teal5", "oc.pink5", "oc.yellow5", ] elif kind == "sequential": # Gradient from light to dark if n <= 5: base_colors = [f"oc.blue{i}" for i in range(3, 8)] else: base_colors = [f"oc.blue{i}" for i in range(1, 10)] elif kind == "diverging": # Red to blue through gray if n <= 5: base_colors = [ "oc.red6", "oc.red4", "oc.gray5", "oc.blue4", "oc.blue6", ] else: base_colors = [ "oc.red7", "oc.red5", "oc.red3", "oc.gray5", "oc.blue3", "oc.blue5", "oc.blue7", ] else: raise ValueError(f"Unknown kind: {kind!r}") # Select colors if n <= len(base_colors): colors = base_colors[:n] else: # Repeat colors if needed colors = base_colors * (n // len(base_colors) + 1) colors = colors[:n] # Apply highlighting if highlight is not None and 0 <= highlight < n: # Make highlighted series darker, others lighter new_colors = [] for i, color in enumerate(colors): if i == highlight: # Keep original or make darker new_colors.append(color.replace("5", "7")) else: # Make lighter new_colors.append(color.replace("5", "3")) colors = new_colors return colors