Source code for dartwork_mpl.scale

"""Font and line-width scaling helpers relative to rcParams defaults.

Provides ``fs``, ``fw``, and ``lw`` helper functions that add or
subtract fixed offsets from the current matplotlib style defaults.
"""

from __future__ import annotations

__all__ = ["dpi", "fs", "fw", "lw"]

import matplotlib.pyplot as plt

_WEIGHT_MAP: dict[str, int] = {
    "ultralight": 100,
    "light": 200,
    "normal": 400,
    "regular": 400,
    "book": 400,
    "medium": 500,
    "roman": 500,
    "semibold": 600,
    "demibold": 600,
    "demi": 600,
    "bold": 700,
    "heavy": 800,
    "extra bold": 800,
    "black": 900,
}


[docs] def fs(n: int | float) -> float: """Return the base font size plus *n*. Parameters ---------- n : int | float Offset to add to ``rcParams['font.size']``. Positive values increase, negative values decrease. Returns ------- float Scaled font size. """ return float(plt.rcParams["font.size"]) + float(n)
[docs] def fw(n: int | float) -> int: """Return the base font weight plus 100 * *n*. String weight names (e.g., ``'normal'``, ``'bold'``) are automatically converted to their numeric equivalents (e.g., 400, 700) before computation. Parameters ---------- n : int | float Number of weight steps to add (each step = 100). For example, n=1 selects one step bolder than the base weight. Fractional steps are allowed but the result is rounded to an int, since matplotlib font weights are integers (0-1000). Returns ------- int Computed numeric font weight (always an ``int`` — a fractional ``n`` is rounded, so the ``-> int`` contract holds). """ base = plt.rcParams["font.weight"] if isinstance(base, str): base = _WEIGHT_MAP.get(base.lower(), 400) return round(int(base) + 100 * n)
[docs] def lw(n: int | float) -> float: """Return the base line width plus *n*. Parameters ---------- n : int | float Offset to add to ``rcParams['lines.linewidth']``. Positive values thicken, negative values thin. Returns ------- float Scaled line width. """ return float(plt.rcParams["lines.linewidth"]) + float(n)
# Default DPI ladder step. The 50-DPI step matches the gap between # matplotlib's screen (~100) and print (~300) defaults closely enough # to feel natural while still distinguishing the rungs. _DPI_STEP: float = 50.0 def dpi(n: int | float = 0) -> float: """Return the base save DPI plus ``n`` ladder steps. The base value is whatever ``rcParams['savefig.dpi']`` resolves to. The dartwork-mpl presets ship a ``300`` default — print-grade and a sensible export resolution for nearly every use case. Each step of ``n`` adds 50 DPI, matching the natural gap between the screen and print rungs of matplotlib's defaults. Use it the same way you use :func:`fs` / :func:`fw` / :func:`lw`: pass an integer offset that tracks the active preset instead of hardcoding a fixed number that drifts the moment the preset changes. Parameters ---------- n : int | float, optional Offset in steps of 50 DPI. ``0`` (the default) returns the active preset's DPI verbatim; ``+1`` is one rung up, ``-1`` one rung down. Fractional values are allowed. Returns ------- float Scaled DPI. Always positive; the lower clamp is 1 DPI so a very negative ``n`` cannot produce an invalid value for ``savefig.dpi``. Examples -------- >>> import dartwork_mpl as dm >>> dm.style.use("scientific") # savefig.dpi == 300 >>> dm.dpi() # the preset base, verbatim 300.0 >>> dm.dpi(1) # one rung up 350.0 >>> dm.dpi(-1) # one rung down 250.0 Pair with :func:`dartwork_mpl.save_formats`: >>> dm.save_formats(fig, "out", dpi=dm.dpi(1)) """ raw = plt.rcParams.get("savefig.dpi", 100) if isinstance(raw, str): # matplotlib accepts the literal "figure" sentinel — fall back # to figure.dpi in that case so the ladder still tracks the # preset. if raw == "figure": base = float(plt.rcParams.get("figure.dpi", 100)) else: base = float(raw) else: base = float(raw) return max(1.0, base + _DPI_STEP * float(n))