Source code for scanpex.pl.preferences

from typing import Tuple

import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import LinearSegmentedColormap

# Global configuration for font and path simplification
mpl.rcParams.update({"pdf.fonttype": 42, "ps.fonttype": 42, "path.simplify": True})


# Standard arguments for saving figures
kwarg_savefig = {
    "facecolor": "white",
    "dpi": 600,
    "bbox_inches": "tight",
    "pad_inches": 0.05,
    "transparent": True,
}

# Standard arguments for saving transparent figures
kwarg_save_transparent_fig = {
    "dpi": 600,
    "bbox_inches": "tight",
    "pad_inches": 0.05,
    "transparent": True,
}


[docs] def level_cmap( color: tuple = None, grayscale: float = 0.2, gamma: float = 2, name: str = None, ): """ Create a custom linear segmented colormap transitioning from grey to a color. This function generates a colormap that starts at a specific shade of grey and interpolates linearly to the target `color`. Parameters ---------- color : tuple of float, optional The target RGBA color to transition to. By default None, assigning a specific purple-blue blend. grayscale : float, optional The intensity of the starting grey color (passed to `plt.cm.Greys`). 0.0 is black, 1.0 is white. By default 0.2. gamma : float, optional Gamma correction factor for the colormap. By default 2. name : str, optional The name of the colormap. If None, generates a name based on the color. By default None. Returns ------- matplotlib.colors.LinearSegmentedColormap The generated colormap object. """ color = ( tuple((3 * np.array(plt.cm.Purples(0.6)) + 2.5 * np.array([0, 0, 1, 1])) / 5) if color is None else color ) cdict = { "red": [ (x, y0, y1) for x, y0, y1 in zip( np.linspace(0, 1, 256), np.linspace(plt.cm.Greys(grayscale)[0], color[0], 256), np.linspace(plt.cm.Greys(grayscale)[0], color[0], 256), ) ], "green": [ (x, y0, y1) for x, y0, y1 in zip( np.linspace(0, 1, 256), np.linspace(plt.cm.Greys(grayscale)[1], color[1], 256), np.linspace(plt.cm.Greys(grayscale)[1], color[1], 256), ) ], "blue": [ (x, y0, y1) for x, y0, y1 in zip( np.linspace(0, 1, 256), np.linspace(plt.cm.Greys(grayscale)[2], color[2], 256), np.linspace(plt.cm.Greys(grayscale)[2], color[2], 256), ) ], } name = name if name is not None else f"level_cmap_{color}" return LinearSegmentedColormap(name, cdict, gamma=gamma)
# Create a default instance for Seurat-like visualizations seurat = level_cmap(name="seurat")
[docs] def rgba2gray(r: float, g: float, b: float, a: float) -> Tuple[float]: """ Calculate the luminance of an RGBA color weighted by its alpha channel. Uses the standard luminance formula (0.299R + 0.587G + 0.114B) adapted to the provided weights. Parameters ---------- r, g, b : float Red, Green, and Blue components (0.0 to 1.0). a : float Alpha component (transparency). Returns ------- float The weighted luminance value. """ return a * (0.299 * r + 0.578 * g + 0.114 * b)
[docs] def textcolor(c, thresh=0.4, dark=".2", light="1") -> str: """ Determine whether text should be light or dark based on background brightness. Useful for ensuring text readability on varying background colors (e.g., in heatmaps or bar plots). Parameters ---------- c : str or tuple The background color. Can be a string representation of a float (e.g., "0.5" for grayscale) or an RGBA tuple. thresh : float, optional The luminance threshold. If the calculated luminance of `c` is greater than this value, the background is considered "light". By default 0.4. dark : str, optional The color to return if the background is light. By default ".2" (dark grey). light : str, optional The color to return if the background is dark. By default "1" (white). Returns ------- str The color code for the text (`dark` or `light`). """ if isinstance(c, str): return dark if float(c) > thresh else light else: return dark if rgba2gray(*c) > thresh else light