Skip to content

Commit ebb48e9

Browse files
committed
0.9.0 - color animation interfaces
1 parent 3236f82 commit ebb48e9

File tree

13 files changed

+170
-21
lines changed

13 files changed

+170
-21
lines changed

.github/workflows/python-package.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ jobs:
7878
7979
- run: |
8080
mk python-release owner=libre-embedded \
81-
repo=svgen version=0.8.8
81+
repo=svgen version=0.9.0
8282
if: |
8383
matrix.python-version == '3.12'
8484
&& matrix.system == 'ubuntu-latest'

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
=====================================
33
generator=datazen
44
version=3.2.3
5-
hash=4e19b7fd794cee4f663c3486ed81a7ae
5+
hash=8900a7ca18bc8f76384982687cb33826
66
=====================================
77
-->
88

9-
# svgen ([0.8.8](https://pypi.org/project/svgen/))
9+
# svgen ([0.9.0](https://pypi.org/project/svgen/))
1010

1111
[![python](https://img.shields.io/pypi/pyversions/svgen.svg)](https://pypi.org/project/svgen/)
1212
![Build Status](https://github.com/libre-embedded/svgen/workflows/Python%20Package/badge.svg)

local/variables/package.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
22
major: 0
3-
minor: 8
4-
patch: 8
3+
minor: 9
4+
patch: 0
55
entry: svgen

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta:__legacy__"
44

55
[project]
66
name = "svgen"
7-
version = "0.8.8"
7+
version = "0.9.0"
88
description = "A tool for working with scalable vector graphics."
99
readme = "README.md"
1010
requires-python = ">=3.12"

svgen/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# =====================================
22
# generator=datazen
33
# version=3.2.3
4-
# hash=d7ef29596b2bc69d2f78eb6f58e41e51
4+
# hash=1ced247e5dcefc85302a056ef8fe63cd
55
# =====================================
66

77
"""
@@ -10,4 +10,4 @@
1010

1111
DESCRIPTION = "A tool for working with scalable vector graphics."
1212
PKG_NAME = "svgen"
13-
VERSION = "0.8.8"
13+
VERSION = "0.9.0"

svgen/color/__init__.py

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"""
44

55
# built-in
6-
from typing import NamedTuple, Union
6+
from typing import NamedTuple, TypeVar, Union
77

88
# internal
99
from svgen.color.conversion import hsl_to_rgb, rgb_to_hsl
@@ -166,6 +166,7 @@
166166
CSS_COLORS["slategrey"] = CSS_COLORS["slategray"]
167167

168168
Colorlike = Union[str, Hsl, Rgb, "Color"]
169+
T = TypeVar("T", bound="Color")
169170

170171

171172
class Color(NamedTuple):
@@ -180,17 +181,17 @@ def __eq__(self, other) -> bool:
180181
return bool(self.rgb == other.rgb or self.hsl == other.hsl)
181182

182183
@classmethod
183-
def from_rgb(cls, color: Rgb) -> "Color":
184+
def from_rgb(cls: type[T], color: Rgb) -> T:
184185
"""Create a color from an rgb object."""
185186
return cls(color, rgb_to_hsl(color))
186187

187188
@classmethod
188-
def from_hex(cls, value: str) -> "Color":
189+
def from_hex(cls: type[T], value: str) -> T:
189190
"""Create a color from a hex value."""
190191
return cls.from_rgb(Rgb.from_hex(value))
191192

192193
@classmethod
193-
def from_hsl(cls, color: Hsl) -> "Color":
194+
def from_hsl(cls: type[T], color: Hsl) -> T:
194195
"""Create a color from an hsl object."""
195196
return cls(hsl_to_rgb(color), color)
196197

@@ -199,7 +200,7 @@ def hsl_arc(self, **kwargs) -> "Color":
199200
return self.from_hsl(self.hsl.arc(**kwargs))
200201

201202
@classmethod
202-
def from_ctor(cls, value: str) -> "Color":
203+
def from_ctor(cls: type[T], value: str) -> T:
203204
"""Create a color from an hsl or rgb constructor string."""
204205

205206
# Check if this is a canonical color.
@@ -216,16 +217,47 @@ def from_ctor(cls, value: str) -> "Color":
216217
return cls.from_hex(value)
217218

218219
@classmethod
219-
def create(cls, value: Colorlike) -> "Color":
220+
def create(cls: type[T], value: Colorlike) -> T:
220221
"""Create a color from a variety of possible sources."""
221-
if isinstance(value, Color):
222-
return value
222+
223+
if isinstance(value, (cls, Color)):
224+
return value # type: ignore
223225
if isinstance(value, Hsl):
224226
return cls.from_hsl(value)
225227
if isinstance(value, Rgb):
226228
return cls.from_rgb(value)
227229
return cls.from_ctor(value)
228230

231+
def animate(
232+
self: T,
233+
hue: int = None,
234+
saturation: float = None,
235+
lightness: float = None,
236+
red: int = None,
237+
green: int = None,
238+
blue: int = None,
239+
alpha: float = None,
240+
delta: bool = False,
241+
) -> T:
242+
"""Animate this color based on HSL properties."""
243+
244+
if red is not None or blue is not None or green is not None:
245+
return self.from_rgb(
246+
self.rgb.animate(
247+
red=red, green=green, blue=blue, alpha=alpha, delta=delta
248+
)
249+
)
250+
251+
return self.from_hsl(
252+
self.hsl.animate(
253+
hue=hue,
254+
saturation=saturation,
255+
lightness=lightness,
256+
alpha=alpha,
257+
delta=delta,
258+
)
259+
)
260+
229261
def __str__(self) -> str:
230262
"""Convert this color to a hex string."""
231263
return str(self.rgb)

svgen/color/hsl.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,38 @@ class Hsl(NamedTuple):
4747
lightness: PercentPrimitive
4848
alpha: Alpha = DEFAULT
4949

50+
def animate(
51+
self,
52+
hue: int = None,
53+
saturation: float = None,
54+
lightness: float = None,
55+
alpha: float = None,
56+
delta: bool = False,
57+
) -> "Hsl":
58+
"""Manipulate this color into a new one."""
59+
60+
if hue is None:
61+
hue = self.hue
62+
elif delta:
63+
hue = self.hue + hue
64+
65+
if saturation is None:
66+
saturation = self.saturation.ratio
67+
elif delta:
68+
saturation = self.saturation.ratio + saturation
69+
70+
if lightness is None:
71+
lightness = self.lightness.ratio
72+
elif delta:
73+
lightness = self.lightness.ratio + lightness
74+
75+
if alpha is None:
76+
alpha = self.alpha
77+
elif delta:
78+
alpha = self.alpha + alpha
79+
80+
return hsla(hue, saturation, lightness, alpha)
81+
5082
def arc(
5183
self,
5284
hue_count: int = 1,

svgen/color/numbers.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ def css_number_to_ratio(val: float | int | str) -> float:
3737
float_val = float(val) / 255.0
3838

3939
# Normalize the result to a ratio between 0.0 and 1.0.
40-
float_val = max(float_val, 0.0)
41-
return min(float_val, 1.0)
40+
return min(max(float_val, 0.0), 1.0)
4241

4342

4443
def parse_ctor(

svgen/color/resolve.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,27 @@
22
A module for resolving colors.
33
"""
44

5+
# built-in
6+
from contextlib import suppress
7+
58
# internal
69
from svgen.color import Color, Colorlike
710
from svgen.color.theme.manager import THEMES, ColorThemeManager
811

912

10-
def get_color(key: Colorlike, manager: ColorThemeManager = None) -> Color:
13+
def get_color(
14+
key: Colorlike = "black", manager: ColorThemeManager = None, **kwargs
15+
) -> Color:
1116
"""Resolve a color using the theme manager."""
17+
1218
if manager is None:
1319
manager = THEMES
14-
return manager[key].color
20+
21+
color = manager[kwargs.get("color", key)].color
22+
with suppress(KeyError):
23+
del kwargs["color"]
24+
25+
if kwargs:
26+
color = color.animate(**kwargs)
27+
28+
return color

svgen/color/rgb.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,38 @@ class Rgb(NamedTuple):
4040
blue: RgbPrimitive
4141
alpha: Alpha = DEFAULT
4242

43+
def animate(
44+
self,
45+
red: int = None,
46+
green: int = None,
47+
blue: int = None,
48+
alpha: float = None,
49+
delta: bool = False,
50+
) -> "Rgb":
51+
"""Animate a color."""
52+
53+
if red is None:
54+
red = self.red
55+
elif delta:
56+
red = self.red + red
57+
58+
if green is None:
59+
green = self.green
60+
elif delta:
61+
green = self.green + green
62+
63+
if blue is None:
64+
blue = self.blue
65+
elif delta:
66+
blue = self.blue + blue
67+
68+
if alpha is None:
69+
alpha = self.alpha
70+
elif delta:
71+
alpha = self.alpha + alpha
72+
73+
return rgba(red, green, blue, alpha)
74+
4375
def __str__(self) -> str:
4476
"""Get this color as a hex string."""
4577

0 commit comments

Comments
 (0)