Source code for ridgeplot._color.utils
"""Common color utilities."""
from __future__ import annotations
from typing import TYPE_CHECKING, cast
import plotly.express as px
import plotly.graph_objects as go
import plotly.io as pio
from ridgeplot._color.css_colors import CSS_NAMED_COLORS
if TYPE_CHECKING:
from collections.abc import Collection
from ridgeplot._types import Color
[docs]
def default_plotly_template() -> go.layout.Template:
return pio.templates[pio.templates.default or "plotly"]
# TODO: Move this in the future to a separate module
# once we add support for color sequences.
[docs]
def infer_default_color_sequence() -> Collection[Color]: # pragma: no cover
return cast(
"Collection[Color]", default_plotly_template().layout.colorway or px.colors.qualitative.D3
)
[docs]
def to_rgb(color: Color) -> str:
if not isinstance(color, (str, tuple)): # type: ignore[reportUnnecessaryIsInstance]
raise TypeError(f"Expected str or tuple for color, got {type(color)} instead.")
if isinstance(color, tuple):
r, g, b = color
rgb = f"rgb({r}, {g}, {b})"
elif color.startswith("#"):
return to_rgb(cast("str", px.colors.hex_to_rgb(color)))
elif color.startswith(("rgb(", "rgba(")):
rgb = color
elif color in CSS_NAMED_COLORS:
return to_rgb(CSS_NAMED_COLORS[color])
else:
raise ValueError(
f"color should be a tuple or a str representation "
f"of a hex or rgb color, got {color!r} instead."
)
px.colors.validate_colors(rgb)
return rgb
[docs]
def unpack_rgb(rgb: str) -> tuple[float, float, float, float] | tuple[float, float, float]:
prefix = rgb.split("(")[0] + "("
values_str = map(str.strip, rgb.removeprefix(prefix).removesuffix(")").split(","))
values_num = tuple(int(v) if v.isdecimal() else float(v) for v in values_str)
return cast("tuple[float, float, float, float] | tuple[float, float, float]", values_num)
[docs]
def apply_alpha(color: Color, alpha: float) -> str:
values = unpack_rgb(to_rgb(color))
return f"rgba({', '.join(map(str, values[:3]))}, {alpha})"
[docs]
def round_color(color: Color, ndigits: int | None = None) -> str:
# TODO: Since this is only used in the `interpolation` module,
# (and should probably *only* be used there) we should
# move this function to that module to improve cohesion.
color = to_rgb(color)
prefix = color.split("(")[0] + "("
values_round = tuple(v if isinstance(v, int) else round(v, ndigits) for v in unpack_rgb(color))
return f"{prefix}{', '.join(map(str, values_round))})"