Source code for color

import ucollections

_ColorTuple = ucollections.namedtuple("Color", ["red", "green", "blue"])


[docs]class Color(_ColorTuple): """ A color in 24-bit RGB. You can create a new color from red, green, and blue:: yellow = Color(255, 255, 0) You can get the HTML representation for a color using:: html_repr = str(Color(128, 128, 128)) assert html_repr == "#808080" .. py:attribute:: red Red component of this color. .. py:attribute:: green Green component of this color. .. py:attribute:: blue Blue component of this color. """
[docs] @classmethod def from_hex(cls, color): """ Create a color from a hexadecimal number. This function is available both as a class method and directly inside the color module: **Example**: .. code-block:: python from color import Color # Magenta Color.from_hex(0xff00ff) .. code-block:: python import color # Cyan color.from_hex(0x00ffff) """ red = (color & 0xFF0000) >> 16 green = (color & 0x00FF00) >> 8 blue = color & 0x0000FF return cls(red, green, blue)
[docs] @classmethod def from_hsv(cls, hue, saturation, value): """ Create a color from a HSV tuple (hue, saturation, value). This function is available both as a class method and directly inside the color module: **Example**: .. code-block:: python from color import Color # Magenta Color.from_hsv(300, 1, 1) .. code-block:: python import color # Cyan color.from_hsv(180, 1, 1) Code via https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB_alternative """ def f(n): k = (n + (hue / 60)) % 6 return value - (value * saturation * max(min(k, 4 - k, 1), 0)) def f2(x): return round(f(x) * 255) return cls(f2(5), f2(3), f2(1))
[docs] @classmethod def from_hsl(cls, hue, saturation, lightness): """ Create a color from a HSL tuple (hue, saturation, lightness). This function is available both as a class method and directly inside the color module: **Example**: .. code-block:: python from color import Color # Magenta Color.from_hsl(300, 1, 0.5) .. code-block:: python import color # Cyan color.from_hsv(180, 1, 0.5) Code via https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_RGB_alternative """ a = saturation * min(lightness, 1 - lightness) def f(n): k = (n + hue / 30) % 12 return lightness - (a * max(min(k - 3, 9 - k, 1), -1)) def f2(x): return round(f(x) * 255) return cls(f2(0), f2(8), f2(4))
def __to_hsx(self, hsv=True): """ Code via https://en.wikipedia.org/wiki/HSL_and_HSV#From_RGB """ xr = self.red / 255 xg = self.green / 255 xb = self.blue / 255 if xr == xg and xr == xb: h = 0 c_max = xr c_min = xr x_max = self.red x_min = self.red else: c_max = max(xr, xg, xb) c_min = min(xr, xg, xb) x_max = max(self.red, self.green, self.blue) x_min = min(self.red, self.green, self.blue) if self.red == x_max: h = 60 * (0 + (xg - xb) / (c_max - c_min)) elif self.green == x_max: h = 60 * (2 + (xb - xr) / (c_max - c_min)) elif self.blue == x_max: h = 60 * (4 + (xr - xg) / (c_max - c_min)) else: h = 0 while h < 0: h = h + 360 if hsv: if x_max == 0: s = 0 else: s = (c_max - c_min) / c_max v = c_max return [round(h), round(s, 2), round(v, 2)] else: l = (c_max + c_min) / 2 if x_max == 0: s = 0 elif x_min == 1: s = 0 else: s = (c_max - l) / min(l, 1 - l) return [round(h), round(s, 2), round(l, 2)]
[docs] def to_hsv(self): """ Create a HSV tuple (hue, saturation, value) from this color. **Example**: .. code-block:: python from color import Color # orange c = Color.from_hex(0xff9900) print(c.to_hsv()) # [36, 1.0, 1.0] """ return self.__to_hsx(True)
[docs] def to_hsl(self): """ Create a HSL tuple (hue, saturation, lightness) from this color. **Example**: .. code-block:: python from color import Color # orange c = Color.from_hex(0xff9900) print(c.to_hsl()) # [36, 1.0, 0.5] """ return self.__to_hsx(False)
def __change_sat(self, change, to_fn, from_fn): hsx = to_fn() sat = hsx[1] + change if sat > 1: sat = 1 if sat < 0: sat = 0 rv = from_fn(hsx[0], sat, hsx[2]) self.red = rv.red self.green = rv.green self.blue = rv.blue def __change_vlx(self, change, to_fn, from_fn): hsx = to_fn() vlx = hsx[2] + change if vlx > 1: vlx = 1 if vlx < 0: vlx = 0 rv = from_fn(hsx[0], hsx[1], vlx) self.red = rv.red self.green = rv.green self.blue = rv.blue
[docs] def change_hue(self, change): """ Change the hue of this color (doesn't matter if HSL or HSV). Hue is always between 0 and 360. **Example**: .. code-block:: python from color import Color # red c = Color.from_hex(0xff0000) print(c) # #ff0000 c.change_hue(90) print(c) # #80ff00 """ hsx = self.to_hsl() hue = hsx[0] + change hue = hue % 360 rv = self.from_hsl(hue, hsx[1], hsx[2]) self.red = rv.red self.green = rv.green self.blue = rv.blue
[docs] def change_saturation_hsv(self, change): """ Change the saturation of this color (in HSV). Saturation is always between 0 and 1. **Example**: .. code-block:: python from color import Color # red c = Color.from_hex(0xff0000) print(c) # #ff0000 c.change_saturation_hsv(-0.5) print(c) # #ff8080 """ return self.__change_sat(change, self.to_hsv, self.from_hsv)
[docs] def change_saturation_hsl(self, change): """ Change the saturation of this color (in HSL). Saturation is always between 0 and 1. **Example**: .. code-block:: python from color import Color # red c = Color.from_hex(0xff0000) print(c) # #ff0000 c.change_saturation_hsl(-0.5) print(c) # #bf4040 """ return self.__change_sat(change, self.to_hsl, self.from_hsl)
[docs] def change_value(self, change): """ Change the value of this color (in HSV). Value is always between 0 and 1. **Example**: .. code-block:: python from color import Color # red c = Color.from_hex(0xff0000) print(c) # #ff0000 c.change_value(-0.5) print(c) # #800000 """ return self.__change_vlx(change, self.to_hsv, self.from_hsv)
[docs] def change_lightness(self, change): """ Change the lightness of this color (in HSL). Lightness is always between 0 and 1. **Example**: .. code-block:: python from color import Color # red c = Color.from_hex(0xff0000) print(c) # #ff0000 c.change_lightness(0.2) print(c) # #ff6666 """ return self.__change_vlx(change, self.to_hsl, self.from_hsl)
[docs] def get_complementary(self): """ Get the complementary color of this color. **Example**: .. code-block:: python from color import Color # red c = Color.from_hex(0xff0000) print(c) # #ff0000 c.get_complementary() print(c) # #00ffff """ rv = self.to_hsl() h = (rv[0] + 180) % 360 return Color.from_hsl(round(h), round(rv[1], 2), round(rv[2], 2))
[docs] def darken(self, change): """ Darken a color. Uses change_saturation_hsl. """ self.change_saturation_hsl(0 - abs(change))
[docs] def brighten(self, change): """ Brighten a color. Uses change_saturation_hsl. """ self.change_saturation_hsl(0 + abs(change))
def __get_bounded(self, r, g, b): if r < 0: r = 0 if r > 255: r = 255 if g < 0: g = 0 if b > 255: b = 255 return Color(r, g, b) def __mul__(self, other): """ Multiply with a scalar to dim the color. **Example**: .. code-block:: python from color import Color # red c = Color.from_hex(0x0000ff) print(c) # #0000ff c2 = c1 * 0.5 print(c2) # #000080 """ try: other = float(other) except ValueError: return NotImplemented r = self.red * other g = self.green * other b = self.blue * other return self.__get_bounded(round(r), round(g), round(b)) def __add__(self, other): """ Add two colors to add their respective color values. **Example**: .. code-block:: python from color import Color # two shades of grey c1 = Color.from_hex(0x323232) c2 = Color.from_hex(0x646464) print(c1) # #323232 print(c2) # #646464 c3 = c1 + c2 print(c3) # #969696 """ if not isinstance(other, Color): return NotImplemented r = self.red + other.red g = self.green + other.green b = self.blue + other.blue return self.__get_bounded(r, g, b) def __sub__(self, other): """ Subtract two colors to subtract their respective color values. **Example**: .. code-block:: python from color import Color # two shades of grey c1 = Color.from_hex(0x969696) c2 = Color.from_hex(0x646464) print(c1) # #969696 print(c2) # #646464 c3 = c1 - c2 print(c3) # #323232 """ if not isinstance(other, Color): return NotImplemented r = self.red - other.red g = self.green - other.green b = self.blue - other.blue return self.__get_bounded(r, g, b) def __str__(self): # Return the color in hex return "#{:02x}{:02x}{:02x}".format(self.red, self.green, self.blue)
# fmt: off Color.BLACK = Color.from_hex(0x000000) Color.WHITE = Color.from_hex(0xffffff) Color.RED = Color.from_hex(0xff0000) Color.GREEN = Color.from_hex(0x00ff00) Color.YELLOW = Color.from_hex(0xffff00) Color.BLUE = Color.from_hex(0x0000ff) Color.MAGENTA = Color.from_hex(0xff00ff) Color.CYAN = Color.from_hex(0x00ffff) Color.CHAOSBLUE = Color.from_hex(0x0076ba) Color.CHAOSBLUE_DARK = Color.from_hex(0x005383) Color.COMMYELLOW = Color.from_hex(0xffc600) Color.COMMYELLOW_DARK = Color.from_hex(0xd39a00) Color.CAMPGREEN = Color.from_hex(0x99ba00) Color.CAMPGREEN_DARK = Color.from_hex(0x6f8700) # fmt: on # Add the colors and constructors to the module for convenience # This allows you to do the following: # # import colors # # colors.BLACK # colors.from_hex(0xc6c6c6) globals().update( {n: getattr(Color, n) for n in dir(Color) if n.startswith("from") or n.isupper()} )