Commit 9f40f534 authored by wink's avatar wink
Browse files

feat(color): more methods for color manipulation

parent 4f99f17c
Pipeline #1334 passed with stage
in 2 minutes and 12 seconds
......@@ -127,6 +127,360 @@ class Color(_ColorTuple):
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)]
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)
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
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
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)
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)
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)
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_value(-0.25)
print(c)
# #800000
"""
return self.__change_vlx(change, self.to_hsl, self.from_hsl)
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))
def darken(self, change):
"""
Darken a color. Uses change_saturation_hsl.
"""
self.change_saturation_hsl(0 - abs(change))
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(
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment