Source code for wyvern.colors

# MIT License

# Copyright (c) 2023 Sarthak

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from __future__ import annotations

import colorsys
import random
import re
import typing

__all__: tuple[str, ...] = ("Color", "Colour")


[docs]@typing.final class Color: """ Class representing a color in the RGB color space. Alias name Colour exists for convenience. Attributes ---------- value : int The value of the color. This is a 24-bit integer, where the first 8 bits are the red value, the next 8 bits are the green value, and the last 8 bits are the blue value. """ __slots__: tuple[str, ...] = ("value",) RGB_REGEX: re.Pattern[str] = re.compile(r"rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)") HSL_REGEX: re.Pattern[str] = re.compile(r"hsl\((\d{1,3}), (\d{1,3})%, (\d{1,3})%\)") HSV_REGEX: re.Pattern[str] = re.compile(r"hsv\((\d{1,3}), (\d{1,3})%, (\d{1,3})%\)") HEX_REGEX: re.Pattern[str] = re.compile(r"#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})") def __init__(self, value: int) -> None: self.value = value def __repr__(self) -> str: return f"Color({self.value})" def __eq__(self, other: object) -> bool: return isinstance(other, Color) and self.value == other.value def __hash__(self) -> int: return hash(self.value) def __ne__(self, other: object) -> bool: return self is not other
[docs] @classmethod def from_hex(cls, hex_value: str) -> Color: """ Creates a Color object from a hex value. Parameters ---------- hex_value: str The hex value to use. Returns ------- Color A Color object. Examples -------- >>> Color.from_hex('#ff0000') Color(16776960) >>> Color.from_hex('#00ff00') Color(255) >>> Color.from_hex('#0000ff') Color(0) """ if match := cls.HEX_REGEX.match(hex_value): hex_value = match.group(1) if len(hex_value) == 3: hex_value = "".join(c * 2 for c in hex_value) return cls(int(hex_value, 16)) raise ValueError(f"Invalid hex value: {hex_value}")
[docs] @classmethod def from_rgb(cls, r: int, g: int, b: int) -> Color: """ Creates a Color object from RGB values. Parameters ---------- r: int The red value. g: int The green value. b: int The blue value. Returns ------- Color A Color object. Examples -------- >>> Color.from_rgb(255, 0, 0) Color(16711680) >>> Color.from_rgb(0, 255, 0) Color(65280) >>> Color.from_rgb(0, 0, 255) Color(255) """ return cls((r << 16) + (g << 8) + b)
[docs] @classmethod def from_hsv(cls, h: float, s: float, v: float) -> Color: """ Creates a Color object from HSV values. Parameters ---------- h: float The hue value. s: float The saturation value. v: float The value in HSV color space. Returns ------- Color A Color object. Examples -------- >>> Color.from_hsv(0, 1, 1) Color(16711680) >>> Color.from_hsv(120, 1, 1) Color(16711680) >>> Color.from_hsv(240, 1, 1) Color(16711680) """ return cls.from_rgb(*[int(round(c * 255)) for c in colorsys.hsv_to_rgb(h, s, v)])
[docs] @classmethod def from_hsl(cls, h: float, s: float, l: float) -> Color: # noqa: E741 """ Creates a Color object from HSL values. Parameters ---------- h: float The hue value. s: float The saturation value. l: float The lightness value. Returns ------- Color A Color object. Examples -------- >>> Color.from_hsl(0, 1, 0.5) Color(16711680) >>> Color.from_hsl(120, 1, 0.5) Color(16711680) >>> Color.from_hsl(240, 1, 0.5) Color(16711680) """ return cls.from_rgb(*[int(round(c * 255)) for c in colorsys.hls_to_rgb(h, l, s)])
[docs] @classmethod def from_random(cls) -> Color: """ Creates a Color object from a random color. Randomly generates a color in the RGB color space. Returns ------- Color A Color object. """ return cls.from_rgb(*[random.randint(0, 255) for _ in range(3)])
[docs] @classmethod def from_string(cls, string: str) -> Color: """ Creates a Color object from a string. Parameters ---------- string: str The string to use. Returns ------- Color A Color object. Examples -------- >>> Color.from_string('rgb(255, 0, 0)') Color(16711680) >>> Color.from_string('hsl(0, 100%, 50%)') Color(-80727249750) >>> Color.from_string('hsv(0, 100%, 100%)') Color(1022371500) >>> Color.from_string('#ff0000') Color(16776960) """ if string.startswith("#"): return cls.from_hex(string) elif match := cls.RGB_REGEX.match(string): return cls.from_rgb(*[int(c) for c in match.groups()]) elif match := cls.HSL_REGEX.match(string): return cls.from_hsl(*[float(c) for c in match.groups()]) elif match := cls.HSV_REGEX.match(string): return cls.from_hsv(*[float(c) for c in match.groups()]) raise ValueError(f"Invalid color string: {string}")
[docs] @classmethod def default(cls) -> Color: """ Creates a Color object from the default color. This is ``0x000000``. (Black) Examples -------- >>> Color.default() Color(0) """ return cls(0x000000)
@property def hex(self) -> str: """The hex value of the color.""" return f"#{self.value:06x}" @property def rgb(self) -> tuple[int, int, int]: """The RGB values of the color.""" return (self.value >> 16) & 0xFF, (self.value >> 8) & 0xFF, self.value & 0xFF @property def hsv(self) -> tuple[float, float, float]: """The HSV values of the color.""" return colorsys.rgb_to_hsv(*(c / 255 for c in self.rgb)) @property def hsl(self) -> tuple[float, float, float]: """The HSL values of the color.""" return colorsys.rgb_to_hls(*(c / 255 for c in self.rgb)) @property def r(self) -> int: """The red value of the color.""" return self.rgb[0] @property def g(self) -> int: """The green value of the color.""" return self.rgb[1] @property def b(self) -> int: """The blue value of the color.""" return self.rgb[2]
[docs] @classmethod def red(cls) -> Color: """Creates a Color object from the red color. This is ``0xff0000``. (Red)""" return cls(0xFF0000)
[docs] @classmethod def green(cls) -> Color: """Creates a Color object from the green color. This is ``0x00ff00``. (Green)""" return cls(0x00FF00)
[docs] @classmethod def blue(cls) -> Color: """Creates a Color object from the blue color. This is ``0x0000ff``. (Blue)""" return cls(0x0000FF)
[docs] @classmethod def yellow(cls) -> Color: """Creates a Color object from the yellow color. This is ``0xffff00``. (Yellow)""" return cls(0xFFFF00)
[docs] @classmethod def cyan(cls) -> Color: """Creates a Color object from the cyan color. This is ``0x00ffff``. (Cyan)""" return cls(0x00FFFF)
[docs] @classmethod def magenta(cls) -> Color: """Creates a Color object from the magenta color. This is ``0xff00ff``. (Magenta)""" return cls(0xFF00FF)
[docs] @classmethod def black(cls) -> Color: """Creates a Color object from the black color. This is ``0x000000``. (Black)""" return cls(0x000000)
[docs] @classmethod def white(cls) -> Color: """Creates a Color object from the white color. This is ``0xffffff``. (White)""" return cls(0xFFFFFF)
[docs] @classmethod def gray(cls) -> Color: """Creates a Color object from the gray color. This is ``0x808080``. (Gray)""" return cls(0x808080)
[docs] @classmethod def grey(cls) -> Color: """Creates a Color object from the grey color. This is ``0x808080``. (Grey)""" return cls(0x808080)
[docs] @classmethod def orange(cls) -> Color: """Creates a Color object from the orange color. This is ``0xffa500``. (Orange)""" return cls(0xFFA500)
[docs] @classmethod def purple(cls) -> Color: """Creates a Color object from the purple color. This is ``#800080``. (Purple)""" return cls(0x800080)
[docs] @classmethod def brown(cls) -> Color: """Creates a Color object from the brown color. This is ``#a52a2a``. (Brown)""" return cls(0xA52A2A)
[docs] @classmethod def silver(cls) -> Color: """Creates a Color object from the silver color. This is ``#c0c0c0``. (Silver)""" return cls(0xC0C0C0)
[docs] @classmethod def aqua(cls) -> Color: """Creates a Color object from the aqua color. This is ``#00ffff``. (Aqua)""" return cls(0x00FFFF)
Colour = Color """An alias for the :class:`Color` class."""