Source code for svgen.color.hsl

"""
svgen - Common interfaces for hsl colors. See also:

        https://www.w3schools.com/colors/colors_hsl.asp
"""

# built-in
from typing import NamedTuple

# internal
from svgen.cartesian.angle import DegreePrimitive, Rotatable
from svgen.color.alpha import DEFAULT, Alpha
from svgen.color.numbers import parse_ctor


[docs] class PercentPrimitive(int, Rotatable): """A class for integer percentages.""" def __new__(cls, val: int) -> "PercentPrimitive": """Create a new percentage value.""" return super().__new__(cls, min(max(val, 0), 100)) def __str__(self) -> str: """Get this percentage as a string.""" return f"{int(self)}%" @property def ratio(self) -> float: """Get this percentage as a ratio between 0 and 1.""" return float(self) / 100.0
[docs] def arc(self, count: int = 1, divisor: int = 2) -> "PercentPrimitive": """Rotate this angle around the circle.""" new_val = self + round(count * (100 / divisor)) while new_val > 100: new_val -= 100 return PercentPrimitive(new_val)
[docs] class Hsl(NamedTuple): """A definition of an hsl color.""" hue: DegreePrimitive saturation: PercentPrimitive lightness: PercentPrimitive alpha: Alpha = DEFAULT
[docs] def animate( self, hue: int = None, saturation: float = None, lightness: float = None, alpha: float = None, delta: bool = False, ) -> "Hsl": """Manipulate this color into a new one.""" if hue is None: hue = self.hue elif delta: hue = self.hue + hue if saturation is None: saturation = self.saturation.ratio elif delta: saturation = self.saturation.ratio + saturation if lightness is None: lightness = self.lightness.ratio elif delta: lightness = self.lightness.ratio + lightness if alpha is None: alpha = self.alpha elif delta: alpha = self.alpha + alpha return hsla(hue, saturation, lightness, alpha)
[docs] def arc( self, hue_count: int = 1, hue_divisor: int = 1, saturation_count: int = 1, saturation_divisor: int = 1, lightness_count: int = 1, lightness_divisor: int = 1, ) -> "Hsl": """Rotate this angle around the circle.""" return Hsl( self.hue.arc(count=hue_count, divisor=hue_divisor), self.saturation.arc( count=saturation_count, divisor=saturation_divisor ), self.lightness.arc( count=lightness_count, divisor=lightness_divisor ), self.alpha, )
def __str__(self) -> str: """Convert this hsl color to a string.""" if self.alpha == DEFAULT: return f"hsl({self.hue}, {self.saturation}, {self.lightness})" return ( f"hsla({self.hue}, {self.saturation}, " f"{self.lightness}, {self.alpha})" )
[docs] @staticmethod def from_ctor(value: str) -> "Hsl": """Get an hsl color from a constructor string.""" colors = parse_ctor(value, "hsl", "a") assert len(colors) == 3 or len(colors) == 4 # Parse the alpha value if present. alpha = DEFAULT if len(colors) == 4: alpha = Alpha(float(colors[3])) colors[1] = colors[1].replace("%", "") colors[2] = colors[2].replace("%", "") return Hsl( DegreePrimitive(int(colors[0])), PercentPrimitive(int(colors[1])), PercentPrimitive(int(colors[2])), alpha, )
[docs] def hsl(hue: int, saturation: float, lightness: float) -> Hsl: """Create a new hsl color.""" return Hsl( DegreePrimitive(hue), PercentPrimitive(round(saturation * 100.0)), PercentPrimitive(round(lightness * 100.0)), )
[docs] def hsla(hue: int, saturation: float, lightness: float, alpha: float) -> Hsl: """Create a new hsl color with a transparency value.""" return Hsl( DegreePrimitive(hue), PercentPrimitive(round(saturation * 100.0)), PercentPrimitive(round(lightness * 100.0)), Alpha(alpha), )