Source code for pyfli.plot_style
"""
plot_style.py
-------------
Shared line/marker color palette and outside-axes legend placement for the
plotting functions in :mod:`pyfli.analysis` and :mod:`pyfli.data_vnp`.
Every categorical palette in those two packages is built from `dark_palette()`
(or is `DARK_PALETTE` itself), so retheming every plot that uses one means
editing `VMAX`/`SMIN` (or a call's `base=` seaborn palette name) here rather
than touching each plotting function.
"""
import colorsys
from typing import Any
import seaborn as sns
VMAX = 0.55
SMIN = 0.55
LEGEND_FONTSIZE = 8
[docs]
def darken_color(
color: tuple[float, ...], vmax: float = VMAX, smin: float = SMIN
) -> tuple[float, ...]:
"""Cap an RGB(A) color's brightness and floor its saturation so it reads as dark."""
r, g, b = color[:3]
h, s, v = colorsys.rgb_to_hsv(r, g, b)
r2, g2, b2 = colorsys.hsv_to_rgb(h, max(s, smin), min(v, vmax))
return (r2, g2, b2, color[3]) if len(color) == 4 else (r2, g2, b2)
[docs]
def dark_palette(
n_colors: int, base: str = "colorblind", vmax: float = VMAX, smin: float = SMIN
) -> list[tuple[float, ...]]:
"""Return `n_colors` dark, mutually distinguishable colors derived from a
seaborn base palette (any name/list accepted by `seaborn.color_palette`)."""
return [
darken_color(c, vmax=vmax, smin=smin)
for c in sns.color_palette(base, n_colors=n_colors)
]
DARK_PALETTE: list[tuple[float, ...]] = dark_palette(10)
[docs]
def legend_outside(
ax: Any,
loc: str = "upper left",
bbox_to_anchor: tuple[float, float] = (1.02, 1.0),
borderaxespad: float = 0.0,
**kwargs: Any,
) -> Any:
"""Draw ax's legend outside the axes box (to the right, by default) instead
of overlapping the plotted data."""
kwargs.setdefault("frameon", False)
kwargs.setdefault("fontsize", LEGEND_FONTSIZE)
return ax.legend(
loc=loc, bbox_to_anchor=bbox_to_anchor, borderaxespad=borderaxespad, **kwargs
)