feat: add opacity to colours

This commit is contained in:
backwardspy 2023-04-25 21:48:52 +01:00 committed by backwardspy (cl)
parent 3520cd1ceb
commit 4f9dae019c
2 changed files with 54 additions and 9 deletions

View File

@ -15,29 +15,48 @@ class Colour:
red: int
green: int
blue: int
alpha: int = 255
@property
def rgb(self) -> Tuple[int, int, int]:
"""Get the colour as a 3-tuple of red, green, and blue."""
return self.red, self.green, self.blue
@property
def rgba(self) -> Tuple[int, int, int, int]:
"""Get the colour as a 4-tuple of red, green, blue, and alpha."""
return self.red, self.green, self.blue, self.alpha
@property
def hex(self) -> str:
"""Get the colour as a lowercase hex string."""
if self.alpha < 255:
return f"{self.red:02x}{self.green:02x}{self.blue:02x}{self.alpha:02x}"
return f"{self.red:02x}{self.green:02x}{self.blue:02x}"
def __eq__(self, other: Any) -> bool:
if not isinstance(other, Colour):
raise ValueError("Cannot check equality with non-colour types.")
return self.hex == other.hex
@classmethod
def from_hex(cls, hex_string: str) -> Colour:
"""Create a color from hex string."""
if len(hex_string) != 6:
raise ValueError("Hex string must be 6 characters long.")
match = re.match(r"([\da-fA-F]{2})" * 3, hex_string)
"""Create a colour from hex string."""
if len(hex_string) not in (6, 8):
raise ValueError("Hex string must be 6 or 8 characters long.")
num_groups = 3 if len(hex_string) == 6 else 4
match = re.match(r"([\da-fA-F]{2})" * num_groups, hex_string)
if match is None:
raise ValueError("Hex string have an invalid format.")
hex_r, hex_g, hex_b = match.groups()
return Colour(*(int(col, 16) for col in (hex_r, hex_g, hex_b)))
raise ValueError("Hex string has an invalid format.")
components = (int(col, 16) for col in match.groups())
return Colour(*components)
def opacity(self, opacity: float) -> Colour:
"""Return a new colour with the given opacity."""
if not 0 <= opacity <= 1:
raise ValueError("Opacity must be between 0 and 1.")
return Colour(self.red, self.green, self.blue, int(opacity * 255))

View File

@ -7,14 +7,30 @@ def test_colour_to_rgb():
assert Colour(12, 123, 234).rgb == (12, 123, 234)
def test_colour_to_hex():
def test_colour_to_rgba():
assert Colour(12, 123, 234, 35).rgba == (12, 123, 234, 35)
def test_colour_to_rgba_default():
assert Colour(12, 123, 234).rgba == (12, 123, 234, 255)
def test_rgb_colour_to_hex():
assert Colour(0x12, 0xEB, 0x77).hex == "12eb77"
def test_hex_to_color():
def test_rgba_colour_to_hex():
assert Colour(0x12, 0xEB, 0x77, 0x35).hex == "12eb7735"
def test_hex_to_colour():
assert Colour.from_hex("12eb77") == Colour(0x12, 0xEB, 0x77)
def test_hex_to_colour_with_alpha():
assert Colour.from_hex("12eb7735") == Colour(0x12, 0xEB, 0x77, 0x35)
def test_invalid_hex():
for invalid_value in ("1234567", "12345", "Z00000", "ABCDEG", "0F7CBJ"):
with pytest.raises(ValueError):
@ -27,3 +43,13 @@ def test_equality():
with pytest.raises(ValueError):
assert Colour(0x12, 0xEB, 0x77) == 42
def test_opacity():
colour = Colour(0x12, 0xEB, 0x77).opacity(0.5)
assert colour == Colour(0x12, 0xEB, 0x77, 0x7F)
def test_opacity_invalid():
with pytest.raises(ValueError):
Colour(0x12, 0xEB, 0x77).opacity(1.5)