"""Apply the plotstyle rcParams theme. Text (titles, axis labels, legends, tick labels, annotations — the entirety of every string) is always rendered through a real LaTeX toolchain, using Latin Modern Sans as a modern sans-serif LaTeX font (including math mode, via `sfmath` — otherwise tick numbers fall back to a serif math font even with `\\familydefault` set to sans). `fontenc`'s T1 encoding is loaded too — without it, some plain ASCII characters (e.g. "|") render as the wrong glyph under OT1, LaTeX's default. This requires a working local `latex`/`dvipng` install; there is no mathtext fallback. Note: matplotlib's usetex rendering rasterizes each Text artist as a single greyscale glyph mask via dvipng and then tints the *whole* thing with that artist's one `color` — any in-source `\\color`/`\\textcolor` command is ignored. So two differently-colored spans (e.g. a black title next to a grey subtitle) can't live in one Text object; `figures.new_figure()`'s title/params handling only varies font size (`\\small`) between lines, not color, for exactly this reason. """ from __future__ import annotations from cycler import cycler from importlib import resources import matplotlib.pyplot as plt from .colors import CATEGORICAL _LINESTYLES = ["-", "--", "-.", ":"] _LATEX_PREAMBLE = ( r"\usepackage[T1]{fontenc}\usepackage{amsmath}\usepackage{lmodern}\usepackage{sfmath}" r"\renewcommand{\familydefault}{\sfdefault}" ) def use(cycle_linestyles: bool = False) -> None: """Apply the plotstyle theme to matplotlib's global rcParams. Call once at the top of a plotting script, before creating any figures. By default `axes.prop_cycle` only cycles color (all lines solid) — pass `cycle_linestyles=True` to also cycle through a repeating linestyle sequence, so series stay distinguishable even if color is lost (grayscale printing, projector glare, color-vision deficiency). """ style_path = resources.files("plotstyle").joinpath("assets", "plotstyle.mplstyle") plt.style.use(str(style_path)) if cycle_linestyles: n = len(CATEGORICAL) linestyles = (_LINESTYLES * (n // len(_LINESTYLES) + 1))[:n] plt.rcParams["axes.prop_cycle"] = cycler(color=CATEGORICAL) + cycler(linestyle=linestyles) else: plt.rcParams["axes.prop_cycle"] = cycler(color=CATEGORICAL) plt.rcParams["text.usetex"] = True plt.rcParams["text.latex.preamble"] = _LATEX_PREAMBLE def reset() -> None: """Restore matplotlib defaults (useful between tests/notebook cells).""" plt.rcdefaults()