Source code for boxes.Color

from dataclasses import dataclass
from random import randint
from typing import Final, ClassVar, Self
from typing import Annotated


[docs] @dataclass(frozen=True, slots=True) class RGB: red: Annotated[int, "0–255"] green: Annotated[int, "0–255"] blue: Annotated[int, "0–255"] def __post_init__(self) -> None: """Validate value range while runtime.""" if not (0 <= self.red <= 255): raise ValueError("red must be 0–255") if not (0 <= self.green <= 255): raise ValueError("green must be 0–255") if not (0 <= self.blue <= 255): raise ValueError("blue must be 0–255")
[docs] @classmethod def random(cls) -> Self: return cls( red=randint(0, 255), green=randint(0, 255), blue=randint(0, 255), )
[docs] def as_css(self) -> str: return f"rgb({self.red:.0f},{self.green:.0f},{self.blue:.0f})"
[docs] def as_hex(self) -> str: return f"#{self.red:02X}{self.green:02X}{self.blue:02X}"
[docs] def as_float_tuple(self) -> tuple[Annotated[float, "0.0–1.0"], Annotated[float, "0.0–1.0"], Annotated[float, "0.0–1.0"]]: return self.red / 255.0, self.green / 255.0, self.blue / 255.0
[docs] class ColorPalette: BLACK: Final[RGB] = RGB(0, 0, 0) BLUE: Final[RGB] = RGB(0, 0, 255) CYAN: Final[RGB] = RGB(0, 255, 255) GREEN: Final[RGB] = RGB(0, 255, 0) MAGENTA: Final[RGB] = RGB(255, 0, 255) RED: Final[RGB] = RGB(255, 0, 0) WHITE: Final[RGB] = RGB(255, 255, 255) YELLOW: Final[RGB] = RGB(255, 255, 0)
[docs] class ColorMode: # TODO: Make this configurable OUTER_CUT: ClassVar[RGB] = ColorPalette.BLACK INNER_CUT: ClassVar[RGB] = ColorPalette.BLUE ANNOTATIONS: ClassVar[RGB] = ColorPalette.RED ETCHING: ClassVar[RGB] = ColorPalette.GREEN ETCHING_DEEP: ClassVar[RGB] = ColorPalette.CYAN
[docs] class Color(ColorPalette, ColorMode): """ Keep old behavior for existing generators. """