Source code for neoscore.core.color

from __future__ import annotations

from typing import Tuple, Union

from typing_extensions import TypeAlias

from neoscore.core.exceptions import ColorBoundsError


[docs]class Color: """An 8-bit int RGBA color."""
[docs] def __init__(self, *args): """ Valid signatures: * Color(css_hex_string) * Color(red, green, blue) * Color(red, green, blue, alpha) """ if len(args) == 1: self._set_with_hex(*args) elif len(args) == 3: self._set_with_rgb(*args) elif len(args) == 4: self._set_with_rgba(*args) else: raise TypeError("Invalid Color init args")
[docs] @classmethod def from_def(cls, color_def: ColorDef) -> Color: if isinstance(color_def, Color): return color_def elif isinstance(color_def, tuple): return Color(*color_def) else: # otherwise color_def must be a str return Color(color_def)
def __repr__(self): return "{}({}, {}, {}, {})".format( type(self).__name__, self.red, self.green, self.blue, self.alpha ) def __eq__(self, other): """Two Colors are considered equal if all of their properties are.""" return ( type(other) == type(self) and self.red == other.red and self.green == other.green and self.blue == other.blue and self.alpha == other.alpha ) def __hash__(self): """Colors with equal properties will have the same hash.""" return 23467817 ^ self.red ^ self.green ^ self.blue ^ self.alpha @property def red(self): """int: The 0-255 value of the red color channel""" return self._red @property def green(self): """int: The 0-255 value of the green color channel""" return self._green @property def blue(self): """int: The 0-255 value of the blue color channel""" return self._blue @property def alpha(self): """int: The 0-255 value of the alpha color channel""" return self._alpha def _set_with_hex(self, hex_value): """Set properties from an #rrggbb hex string Args: hex_value (str): A CSS hex color string, with or without a leading '#' Returns: None """ if hex_value.startswith("#"): hex_value = hex_value[1:] # Short or long form Hex if 3 <= len(hex_value) <= 4: self._red = int(hex_value[0] + (hex_value[0]), 16) self._green = int(hex_value[1] + (hex_value[1]), 16) self._blue = int(hex_value[2] + (hex_value[2]), 16) if len(hex_value) == 4: self._alpha = int(hex_value[3] + (hex_value[3]), 16) else: self._alpha = 255 else: self._red = int(hex_value[0:2], 16) self._green = int(hex_value[2:4], 16) self._blue = int(hex_value[4:6], 16) if len(hex_value) == 8: self._alpha = int(hex_value[6:8], 16) else: self._alpha = 255 self._validate_channel_values() def _set_with_rgb(self, red, green, blue): """Set properties from three 0-255 red, green, and blue values Args: red (int): A 0-255 red channel value green (int): A 0-255 green channel value blue (int): A 0-255 blue channel value Returns: None """ self._red = red self._green = green self._blue = blue self._alpha = 255 self._validate_channel_values() def _set_with_rgba(self, red, green, blue, alpha): """Set properties from four 0-255 red, green, blue, and alpha values Args: red (int): A 0-255 red channel value green (int): A 0-255 green channel value blue (int): A 0-255 blue channel value alpha (int): A 0-255 alpha channel value Returns: None """ self._red = red self._green = green self._blue = blue self._alpha = alpha self._validate_channel_values() def _validate_channel_values(self): for value in [self._red, self._green, self._blue, self._alpha]: if not (0 <= value <= 255): raise ColorBoundsError(value)
ColorDef: TypeAlias = Union[Color, str, Tuple[int, int, int], Tuple[int, int, int, int]] """A ``Color`` or a shorthand hex string or init argument tuple for one. Hex strings may have 3 channels (#RRGGBB) or 4 (#RRGGBBAA). """