e2D 2.0.0__cp313-cp313-win_amd64.whl → 2.0.2__cp313-cp313-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- e2D/__init__.py +199 -72
- e2D/__init__.pyi +145 -0
- e2D/ccolors.c +34514 -0
- e2D/ccolors.cp313-win_amd64.pyd +0 -0
- e2D/ccolors.pyi +51 -0
- e2D/ccolors.pyx +350 -0
- e2D/color_defs.py +238 -0
- e2D/colors.py +380 -0
- e2D/colors.pyi +104 -0
- e2D/commons.py +38 -10
- e2D/commons.pyi +79 -0
- e2D/cvectors.c +152 -152
- e2D/cvectors.cp313-win_amd64.pyd +0 -0
- e2D/cvectors.pyi +243 -0
- e2D/devices.py +19 -6
- e2D/devices.pyi +65 -0
- e2D/plots.py +55 -29
- e2D/plots.pyi +238 -0
- e2D/shapes.py +81 -44
- e2D/shapes.pyi +272 -0
- e2D/test_colors.py +122 -0
- e2D/text_renderer.py +46 -16
- e2D/text_renderer.pyi +118 -0
- e2D/types.py +58 -0
- e2D/types.pyi +61 -0
- e2D/vectors.py +153 -61
- e2D/vectors.pyi +106 -0
- e2D/winrec.py +275 -0
- e2D/winrec.pyi +87 -0
- {e2d-2.0.0.dist-info → e2d-2.0.2.dist-info}/METADATA +95 -26
- e2d-2.0.2.dist-info/RECORD +46 -0
- {e2d-2.0.0.dist-info → e2d-2.0.2.dist-info}/WHEEL +1 -1
- e2d-2.0.0.dist-info/RECORD +0 -26
- {e2d-2.0.0.dist-info → e2d-2.0.2.dist-info}/licenses/LICENSE +0 -0
- {e2d-2.0.0.dist-info → e2d-2.0.2.dist-info}/top_level.txt +0 -0
e2D/colors.py
ADDED
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Modern color utilities for e2D - GPU-optimized with RGBA float defaults
|
|
3
|
+
Designed for ModernGL and GLTF compatibility (0.0-1.0 range)
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
from typing import Union, Sequence, overload, TYPE_CHECKING
|
|
8
|
+
import numpy as np
|
|
9
|
+
from numpy.typing import NDArray
|
|
10
|
+
from colorsys import hsv_to_rgb, hls_to_rgb, rgb_to_hsv, rgb_to_hls
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from .types import ColorType
|
|
14
|
+
else:
|
|
15
|
+
ColorType = tuple[float, float, float, float]
|
|
16
|
+
|
|
17
|
+
# Type aliases for flexibility
|
|
18
|
+
ColorInput = Union[ColorType, int, float, str, Sequence[float]]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Color:
|
|
22
|
+
"""
|
|
23
|
+
Immutable RGBA color optimized for GPU rendering.
|
|
24
|
+
Default: RGBA floats in range [0.0, 1.0]
|
|
25
|
+
"""
|
|
26
|
+
__slots__ = ('_r', '_g', '_b', '_a')
|
|
27
|
+
|
|
28
|
+
def __init__(self, r: float = 0.0, g: float = 0.0, b: float = 0.0, a: float = 1.0) -> None:
|
|
29
|
+
"""Create color with RGBA float values (0.0-1.0)"""
|
|
30
|
+
self._r = float(r)
|
|
31
|
+
self._g = float(g)
|
|
32
|
+
self._b = float(b)
|
|
33
|
+
self._a = float(a)
|
|
34
|
+
|
|
35
|
+
# Properties for immutability
|
|
36
|
+
@property
|
|
37
|
+
def r(self) -> float:
|
|
38
|
+
return self._r
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def g(self) -> float:
|
|
42
|
+
return self._g
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def b(self) -> float:
|
|
46
|
+
return self._b
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def a(self) -> float:
|
|
50
|
+
return self._a
|
|
51
|
+
|
|
52
|
+
# Constructors for different color spaces
|
|
53
|
+
@classmethod
|
|
54
|
+
def from_rgba(cls, r: float, g: float, b: float, a: float = 1.0) -> 'Color':
|
|
55
|
+
"""Create from RGBA floats (0.0-1.0)"""
|
|
56
|
+
return cls(r, g, b, a)
|
|
57
|
+
|
|
58
|
+
@classmethod
|
|
59
|
+
def from_rgb(cls, r: float, g: float, b: float) -> 'Color':
|
|
60
|
+
"""Create from RGB floats (0.0-1.0), alpha defaults to 1.0"""
|
|
61
|
+
return cls(r, g, b, 1.0)
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def from_rgba255(cls, r: int, g: int, b: int, a: int = 255) -> 'Color':
|
|
65
|
+
"""Create from RGBA integers (0-255)"""
|
|
66
|
+
return cls(r / 255.0, g / 255.0, b / 255.0, a / 255.0)
|
|
67
|
+
|
|
68
|
+
@classmethod
|
|
69
|
+
def from_rgb255(cls, r: int, g: int, b: int) -> 'Color':
|
|
70
|
+
"""Create from RGB integers (0-255)"""
|
|
71
|
+
return cls(r / 255.0, g / 255.0, b / 255.0, 1.0)
|
|
72
|
+
|
|
73
|
+
@classmethod
|
|
74
|
+
def from_hex(cls, hex_str: str) -> 'Color':
|
|
75
|
+
"""Create from hex string: '#RRGGBB' or '#RRGGBBAA' or 'RRGGBB'"""
|
|
76
|
+
hex_str = hex_str.lstrip('#')
|
|
77
|
+
if len(hex_str) == 6:
|
|
78
|
+
r, g, b = int(hex_str[0:2], 16), int(hex_str[2:4], 16), int(hex_str[4:6], 16)
|
|
79
|
+
return cls.from_rgb255(r, g, b)
|
|
80
|
+
elif len(hex_str) == 8:
|
|
81
|
+
r, g, b, a = int(hex_str[0:2], 16), int(hex_str[2:4], 16), int(hex_str[4:6], 16), int(hex_str[6:8], 16)
|
|
82
|
+
return cls.from_rgba255(r, g, b, a)
|
|
83
|
+
raise ValueError(f"Invalid hex color: {hex_str}")
|
|
84
|
+
|
|
85
|
+
@classmethod
|
|
86
|
+
def from_hsv(cls, h: float, s: float, v: float, a: float = 1.0) -> 'Color':
|
|
87
|
+
"""Create from HSV (h: 0-1, s: 0-1, v: 0-1)"""
|
|
88
|
+
r, g, b = hsv_to_rgb(h, s, v)
|
|
89
|
+
return cls(r, g, b, a)
|
|
90
|
+
|
|
91
|
+
@classmethod
|
|
92
|
+
def from_hls(cls, h: float, l: float, s: float, a: float = 1.0) -> 'Color':
|
|
93
|
+
"""Create from HLS (h: 0-1, l: 0-1, s: 0-1)"""
|
|
94
|
+
r, g, b = hls_to_rgb(h, l, s)
|
|
95
|
+
return cls(r, g, b, a)
|
|
96
|
+
|
|
97
|
+
@classmethod
|
|
98
|
+
def from_gray(cls, gray: float, a: float = 1.0) -> 'Color':
|
|
99
|
+
"""Create grayscale color"""
|
|
100
|
+
return cls(gray, gray, gray, a)
|
|
101
|
+
|
|
102
|
+
# Conversion methods
|
|
103
|
+
def to_rgba(self) -> tuple[float, float, float, float]:
|
|
104
|
+
"""Convert to RGBA tuple (0.0-1.0)"""
|
|
105
|
+
return (self._r, self._g, self._b, self._a)
|
|
106
|
+
|
|
107
|
+
def to_rgb(self) -> tuple[float, float, float]:
|
|
108
|
+
"""Convert to RGB tuple (0.0-1.0)"""
|
|
109
|
+
return (self._r, self._g, self._b)
|
|
110
|
+
|
|
111
|
+
def to_rgba255(self) -> tuple[int, int, int, int]:
|
|
112
|
+
"""Convert to RGBA integers (0-255)"""
|
|
113
|
+
return (int(self._r * 255), int(self._g * 255), int(self._b * 255), int(self._a * 255))
|
|
114
|
+
|
|
115
|
+
def to_rgb255(self) -> tuple[int, int, int]:
|
|
116
|
+
"""Convert to RGB integers (0-255)"""
|
|
117
|
+
return (int(self._r * 255), int(self._g * 255), int(self._b * 255))
|
|
118
|
+
|
|
119
|
+
def to_hex(self, include_alpha: bool = False) -> str:
|
|
120
|
+
"""Convert to hex string"""
|
|
121
|
+
r, g, b, a = self.to_rgba255()
|
|
122
|
+
if include_alpha:
|
|
123
|
+
return f"#{r:02x}{g:02x}{b:02x}{a:02x}"
|
|
124
|
+
return f"#{r:02x}{g:02x}{b:02x}"
|
|
125
|
+
|
|
126
|
+
def to_hsv(self) -> tuple[float, float, float]:
|
|
127
|
+
"""Convert to HSV (0.0-1.0)"""
|
|
128
|
+
return rgb_to_hsv(self._r, self._g, self._b)
|
|
129
|
+
|
|
130
|
+
def to_hls(self) -> tuple[float, float, float]:
|
|
131
|
+
"""Convert to HLS (0.0-1.0)"""
|
|
132
|
+
return rgb_to_hls(self._r, self._g, self._b)
|
|
133
|
+
|
|
134
|
+
def to_array(self) -> NDArray[np.float32]:
|
|
135
|
+
"""Convert to numpy array for GPU"""
|
|
136
|
+
return np.array([self._r, self._g, self._b, self._a], dtype=np.float32)
|
|
137
|
+
|
|
138
|
+
# Color operations
|
|
139
|
+
def with_alpha(self, a: float) -> 'Color':
|
|
140
|
+
"""Return new color with different alpha"""
|
|
141
|
+
return Color(self._r, self._g, self._b, a)
|
|
142
|
+
|
|
143
|
+
def lerp(self, other: 'Color', t: float) -> 'Color':
|
|
144
|
+
"""Linear interpolation between colors"""
|
|
145
|
+
return Color(
|
|
146
|
+
self._r + (other._r - self._r) * t,
|
|
147
|
+
self._g + (other._g - self._g) * t,
|
|
148
|
+
self._b + (other._b - self._b) * t,
|
|
149
|
+
self._a + (other._a - self._a) * t
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
def multiply(self, factor: float) -> 'Color':
|
|
153
|
+
"""Multiply RGB by factor (preserves alpha)"""
|
|
154
|
+
return Color(
|
|
155
|
+
min(self._r * factor, 1.0),
|
|
156
|
+
min(self._g * factor, 1.0),
|
|
157
|
+
min(self._b * factor, 1.0),
|
|
158
|
+
self._a
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
def lighten(self, amount: float = 0.1) -> 'Color':
|
|
162
|
+
"""Lighten color by amount"""
|
|
163
|
+
return self.lerp(Color.white(), amount)
|
|
164
|
+
|
|
165
|
+
def darken(self, amount: float = 0.1) -> 'Color':
|
|
166
|
+
"""Darken color by amount"""
|
|
167
|
+
return self.lerp(Color.black(), amount)
|
|
168
|
+
|
|
169
|
+
def saturate(self, amount: float = 0.1) -> 'Color':
|
|
170
|
+
"""Increase saturation"""
|
|
171
|
+
h, l, s = self.to_hls()
|
|
172
|
+
return Color.from_hls(h, l, min(s + amount, 1.0), self._a)
|
|
173
|
+
|
|
174
|
+
def desaturate(self, amount: float = 0.1) -> 'Color':
|
|
175
|
+
"""Decrease saturation"""
|
|
176
|
+
h, l, s = self.to_hls()
|
|
177
|
+
return Color.from_hls(h, l, max(s - amount, 0.0), self._a)
|
|
178
|
+
|
|
179
|
+
def rotate_hue(self, degrees: float) -> 'Color':
|
|
180
|
+
"""Rotate hue by degrees (0-360)"""
|
|
181
|
+
h, s, v = self.to_hsv()
|
|
182
|
+
h = (h + degrees / 360.0) % 1.0
|
|
183
|
+
return Color.from_hsv(h, s, v, self._a)
|
|
184
|
+
|
|
185
|
+
def invert(self) -> 'Color':
|
|
186
|
+
"""Invert RGB (preserves alpha)"""
|
|
187
|
+
return Color(1.0 - self._r, 1.0 - self._g, 1.0 - self._b, self._a)
|
|
188
|
+
|
|
189
|
+
def grayscale(self) -> 'Color':
|
|
190
|
+
"""Convert to grayscale using luminance"""
|
|
191
|
+
gray = 0.2989 * self._r + 0.5870 * self._g + 0.1140 * self._b
|
|
192
|
+
return Color(gray, gray, gray, self._a)
|
|
193
|
+
|
|
194
|
+
# Operators
|
|
195
|
+
def __add__(self, other: 'Color') -> 'Color':
|
|
196
|
+
"""Add colors (clamped to 1.0)"""
|
|
197
|
+
return Color(
|
|
198
|
+
min(self._r + other._r, 1.0),
|
|
199
|
+
min(self._g + other._g, 1.0),
|
|
200
|
+
min(self._b + other._b, 1.0),
|
|
201
|
+
min(self._a + other._a, 1.0)
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
def __sub__(self, other: 'Color') -> 'Color':
|
|
205
|
+
"""Subtract colors (clamped to 0.0)"""
|
|
206
|
+
return Color(
|
|
207
|
+
max(self._r - other._r, 0.0),
|
|
208
|
+
max(self._g - other._g, 0.0),
|
|
209
|
+
max(self._b - other._b, 0.0),
|
|
210
|
+
max(self._a - other._a, 0.0)
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
def __mul__(self, factor: float) -> 'Color':
|
|
214
|
+
"""Multiply by scalar"""
|
|
215
|
+
return Color(
|
|
216
|
+
min(self._r * factor, 1.0),
|
|
217
|
+
min(self._g * factor, 1.0),
|
|
218
|
+
min(self._b * factor, 1.0),
|
|
219
|
+
self._a
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
def __rmul__(self, factor: float) -> 'Color':
|
|
223
|
+
"""Right multiply by scalar"""
|
|
224
|
+
return self.__mul__(factor)
|
|
225
|
+
|
|
226
|
+
def __truediv__(self, factor: float) -> 'Color':
|
|
227
|
+
"""Divide by scalar"""
|
|
228
|
+
return Color(
|
|
229
|
+
self._r / factor,
|
|
230
|
+
self._g / factor,
|
|
231
|
+
self._b / factor,
|
|
232
|
+
self._a
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
def __eq__(self, other: object) -> bool:
|
|
236
|
+
if not isinstance(other, Color):
|
|
237
|
+
return False
|
|
238
|
+
return (self._r == other._r and self._g == other._g and
|
|
239
|
+
self._b == other._b and self._a == other._a)
|
|
240
|
+
|
|
241
|
+
def __hash__(self) -> int:
|
|
242
|
+
return hash((self._r, self._g, self._b, self._a))
|
|
243
|
+
|
|
244
|
+
def __repr__(self) -> str:
|
|
245
|
+
return f"Color(r={self._r:.3f}, g={self._g:.3f}, b={self._b:.3f}, a={self._a:.3f})"
|
|
246
|
+
|
|
247
|
+
def __str__(self) -> str:
|
|
248
|
+
return self.to_hex(include_alpha=True)
|
|
249
|
+
|
|
250
|
+
def __iter__(self):
|
|
251
|
+
"""Support tuple unpacking: r, g, b, a = color"""
|
|
252
|
+
return iter((self._r, self._g, self._b, self._a))
|
|
253
|
+
|
|
254
|
+
def __getitem__(self, idx: int) -> float:
|
|
255
|
+
"""Support indexing: color[0] -> r"""
|
|
256
|
+
if idx == 0:
|
|
257
|
+
return self._r
|
|
258
|
+
elif idx == 1:
|
|
259
|
+
return self._g
|
|
260
|
+
elif idx == 2:
|
|
261
|
+
return self._b
|
|
262
|
+
elif idx == 3:
|
|
263
|
+
return self._a
|
|
264
|
+
raise IndexError("Color index out of range (0-3)")
|
|
265
|
+
|
|
266
|
+
# Pre-defined colors as class methods
|
|
267
|
+
@classmethod
|
|
268
|
+
def transparent(cls) -> 'Color':
|
|
269
|
+
return cls(0.0, 0.0, 0.0, 0.0)
|
|
270
|
+
|
|
271
|
+
@classmethod
|
|
272
|
+
def white(cls) -> 'Color':
|
|
273
|
+
return cls(1.0, 1.0, 1.0, 1.0)
|
|
274
|
+
|
|
275
|
+
@classmethod
|
|
276
|
+
def black(cls) -> 'Color':
|
|
277
|
+
return cls(0.0, 0.0, 0.0, 1.0)
|
|
278
|
+
|
|
279
|
+
@classmethod
|
|
280
|
+
def red(cls) -> 'Color':
|
|
281
|
+
return cls(1.0, 0.0, 0.0, 1.0)
|
|
282
|
+
|
|
283
|
+
@classmethod
|
|
284
|
+
def green(cls) -> 'Color':
|
|
285
|
+
return cls(0.0, 1.0, 0.0, 1.0)
|
|
286
|
+
|
|
287
|
+
@classmethod
|
|
288
|
+
def blue(cls) -> 'Color':
|
|
289
|
+
return cls(0.0, 0.0, 1.0, 1.0)
|
|
290
|
+
|
|
291
|
+
@classmethod
|
|
292
|
+
def cyan(cls) -> 'Color':
|
|
293
|
+
return cls(0.0, 1.0, 1.0, 1.0)
|
|
294
|
+
|
|
295
|
+
@classmethod
|
|
296
|
+
def magenta(cls) -> 'Color':
|
|
297
|
+
return cls(1.0, 0.0, 1.0, 1.0)
|
|
298
|
+
|
|
299
|
+
@classmethod
|
|
300
|
+
def yellow(cls) -> 'Color':
|
|
301
|
+
return cls(1.0, 1.0, 0.0, 1.0)
|
|
302
|
+
|
|
303
|
+
@classmethod
|
|
304
|
+
def orange(cls) -> 'Color':
|
|
305
|
+
return cls(1.0, 0.647, 0.0, 1.0)
|
|
306
|
+
|
|
307
|
+
@classmethod
|
|
308
|
+
def purple(cls) -> 'Color':
|
|
309
|
+
return cls(0.5, 0.0, 0.5, 1.0)
|
|
310
|
+
|
|
311
|
+
@classmethod
|
|
312
|
+
def pink(cls) -> 'Color':
|
|
313
|
+
return cls(1.0, 0.753, 0.796, 1.0)
|
|
314
|
+
|
|
315
|
+
@classmethod
|
|
316
|
+
def gray(cls, level: float = 0.5) -> 'Color':
|
|
317
|
+
return cls(level, level, level, 1.0)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
# Utility functions for batch operations
|
|
321
|
+
def normalize_color(color: ColorInput) -> ColorType:
|
|
322
|
+
"""
|
|
323
|
+
Normalize various color inputs to ColorType (RGBA float tuple).
|
|
324
|
+
Supports: Color objects, tuples, lists, hex strings, single floats
|
|
325
|
+
"""
|
|
326
|
+
if isinstance(color, Color):
|
|
327
|
+
return color.to_rgba()
|
|
328
|
+
elif isinstance(color, str):
|
|
329
|
+
return Color.from_hex(color).to_rgba()
|
|
330
|
+
elif isinstance(color, (int, float)):
|
|
331
|
+
v = float(color)
|
|
332
|
+
return (v, v, v, 1.0)
|
|
333
|
+
elif isinstance(color, (tuple, list)):
|
|
334
|
+
if len(color) == 3:
|
|
335
|
+
return (float(color[0]), float(color[1]), float(color[2]), 1.0)
|
|
336
|
+
elif len(color) == 4:
|
|
337
|
+
return (float(color[0]), float(color[1]), float(color[2]), float(color[3]))
|
|
338
|
+
raise ValueError(f"Color tuple must have 3 or 4 elements, got {len(color)}")
|
|
339
|
+
raise TypeError(f"Cannot convert {type(color)} to color")
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def lerp_colors(color1: ColorInput, color2: ColorInput, t: float) -> ColorType:
|
|
343
|
+
"""Interpolate between two colors"""
|
|
344
|
+
c1 = normalize_color(color1)
|
|
345
|
+
c2 = normalize_color(color2)
|
|
346
|
+
return (
|
|
347
|
+
c1[0] + (c2[0] - c1[0]) * t,
|
|
348
|
+
c1[1] + (c2[1] - c1[1]) * t,
|
|
349
|
+
c1[2] + (c2[2] - c1[2]) * t,
|
|
350
|
+
c1[3] + (c2[3] - c1[3]) * t
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def gradient(colors: list[ColorInput], steps: int) -> list[ColorType]:
|
|
355
|
+
"""Generate gradient between multiple colors"""
|
|
356
|
+
if len(colors) < 2:
|
|
357
|
+
raise ValueError("Need at least 2 colors for gradient")
|
|
358
|
+
|
|
359
|
+
normalized = [normalize_color(c) for c in colors]
|
|
360
|
+
result: list[ColorType] = []
|
|
361
|
+
|
|
362
|
+
segments = len(normalized) - 1
|
|
363
|
+
steps_per_segment = steps // segments
|
|
364
|
+
|
|
365
|
+
for i in range(segments):
|
|
366
|
+
for j in range(steps_per_segment):
|
|
367
|
+
t = j / steps_per_segment
|
|
368
|
+
result.append(lerp_colors(normalized[i], normalized[i + 1], t))
|
|
369
|
+
|
|
370
|
+
result.append(normalized[-1])
|
|
371
|
+
return result
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def batch_colors_to_array(colors: Sequence[ColorInput]) -> NDArray[np.float32]:
|
|
375
|
+
"""Convert list of colors to numpy array for GPU"""
|
|
376
|
+
result = np.empty((len(colors), 4), dtype=np.float32)
|
|
377
|
+
for i, color in enumerate(colors):
|
|
378
|
+
c = normalize_color(color)
|
|
379
|
+
result[i] = c
|
|
380
|
+
return result
|
e2D/colors.pyi
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Type stubs for colors module"""
|
|
2
|
+
|
|
3
|
+
from typing import Union, Sequence, overload
|
|
4
|
+
import numpy as np
|
|
5
|
+
from numpy.typing import NDArray
|
|
6
|
+
from .types import ColorType
|
|
7
|
+
|
|
8
|
+
ColorInput = Union[ColorType, int, float, str, Sequence[float]]
|
|
9
|
+
|
|
10
|
+
class Color:
|
|
11
|
+
"""Immutable RGBA color optimized for GPU rendering (0.0-1.0 range)"""
|
|
12
|
+
__slots__ = ('_r', '_g', '_b', '_a')
|
|
13
|
+
|
|
14
|
+
def __init__(self, r: float = 0.0, g: float = 0.0, b: float = 0.0, a: float = 1.0) -> None: ...
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def r(self) -> float: ...
|
|
18
|
+
@property
|
|
19
|
+
def g(self) -> float: ...
|
|
20
|
+
@property
|
|
21
|
+
def b(self) -> float: ...
|
|
22
|
+
@property
|
|
23
|
+
def a(self) -> float: ...
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def from_rgba(cls, r: float, g: float, b: float, a: float = 1.0) -> Color: ...
|
|
27
|
+
@classmethod
|
|
28
|
+
def from_rgb(cls, r: float, g: float, b: float) -> Color: ...
|
|
29
|
+
@classmethod
|
|
30
|
+
def from_rgba255(cls, r: int, g: int, b: int, a: int = 255) -> Color: ...
|
|
31
|
+
@classmethod
|
|
32
|
+
def from_rgb255(cls, r: int, g: int, b: int) -> Color: ...
|
|
33
|
+
@classmethod
|
|
34
|
+
def from_hex(cls, hex_str: str) -> Color: ...
|
|
35
|
+
@classmethod
|
|
36
|
+
def from_hsv(cls, h: float, s: float, v: float, a: float = 1.0) -> Color: ...
|
|
37
|
+
@classmethod
|
|
38
|
+
def from_hls(cls, h: float, l: float, s: float, a: float = 1.0) -> Color: ...
|
|
39
|
+
@classmethod
|
|
40
|
+
def from_gray(cls, gray: float, a: float = 1.0) -> Color: ...
|
|
41
|
+
|
|
42
|
+
def to_rgba(self) -> tuple[float, float, float, float]: ...
|
|
43
|
+
def to_rgb(self) -> tuple[float, float, float]: ...
|
|
44
|
+
def to_rgba255(self) -> tuple[int, int, int, int]: ...
|
|
45
|
+
def to_rgb255(self) -> tuple[int, int, int]: ...
|
|
46
|
+
def to_hex(self, include_alpha: bool = False) -> str: ...
|
|
47
|
+
def to_hsv(self) -> tuple[float, float, float]: ...
|
|
48
|
+
def to_hls(self) -> tuple[float, float, float]: ...
|
|
49
|
+
def to_array(self) -> NDArray[np.float32]: ...
|
|
50
|
+
|
|
51
|
+
def with_alpha(self, a: float) -> Color: ...
|
|
52
|
+
def lerp(self, other: Color, t: float) -> Color: ...
|
|
53
|
+
def multiply(self, factor: float) -> Color: ...
|
|
54
|
+
def lighten(self, amount: float = 0.1) -> Color: ...
|
|
55
|
+
def darken(self, amount: float = 0.1) -> Color: ...
|
|
56
|
+
def saturate(self, amount: float = 0.1) -> Color: ...
|
|
57
|
+
def desaturate(self, amount: float = 0.1) -> Color: ...
|
|
58
|
+
def rotate_hue(self, degrees: float) -> Color: ...
|
|
59
|
+
def invert(self) -> Color: ...
|
|
60
|
+
def grayscale(self) -> Color: ...
|
|
61
|
+
|
|
62
|
+
def __add__(self, other: Color) -> Color: ...
|
|
63
|
+
def __sub__(self, other: Color) -> Color: ...
|
|
64
|
+
def __mul__(self, factor: float) -> Color: ...
|
|
65
|
+
def __rmul__(self, factor: float) -> Color: ...
|
|
66
|
+
def __truediv__(self, factor: float) -> Color: ...
|
|
67
|
+
def __eq__(self, other: object) -> bool: ...
|
|
68
|
+
def __hash__(self) -> int: ...
|
|
69
|
+
def __repr__(self) -> str: ...
|
|
70
|
+
def __str__(self) -> str: ...
|
|
71
|
+
def __iter__(self): ...
|
|
72
|
+
def __getitem__(self, idx: int) -> float: ...
|
|
73
|
+
|
|
74
|
+
@classmethod
|
|
75
|
+
def transparent(cls) -> Color: ...
|
|
76
|
+
@classmethod
|
|
77
|
+
def white(cls) -> Color: ...
|
|
78
|
+
@classmethod
|
|
79
|
+
def black(cls) -> Color: ...
|
|
80
|
+
@classmethod
|
|
81
|
+
def red(cls) -> Color: ...
|
|
82
|
+
@classmethod
|
|
83
|
+
def green(cls) -> Color: ...
|
|
84
|
+
@classmethod
|
|
85
|
+
def blue(cls) -> Color: ...
|
|
86
|
+
@classmethod
|
|
87
|
+
def cyan(cls) -> Color: ...
|
|
88
|
+
@classmethod
|
|
89
|
+
def magenta(cls) -> Color: ...
|
|
90
|
+
@classmethod
|
|
91
|
+
def yellow(cls) -> Color: ...
|
|
92
|
+
@classmethod
|
|
93
|
+
def orange(cls) -> Color: ...
|
|
94
|
+
@classmethod
|
|
95
|
+
def purple(cls) -> Color: ...
|
|
96
|
+
@classmethod
|
|
97
|
+
def pink(cls) -> Color: ...
|
|
98
|
+
@classmethod
|
|
99
|
+
def gray(cls, level: float = 0.5) -> Color: ...
|
|
100
|
+
|
|
101
|
+
def normalize_color(color: ColorInput) -> ColorType: ...
|
|
102
|
+
def lerp_colors(color1: ColorInput, color2: ColorInput, t: float) -> ColorType: ...
|
|
103
|
+
def gradient(colors: list[ColorInput], steps: int) -> list[ColorType]: ...
|
|
104
|
+
def batch_colors_to_array(colors: Sequence[ColorInput]) -> NDArray[np.float32]: ...
|
e2D/commons.py
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import moderngl
|
|
2
1
|
import numpy as np
|
|
2
|
+
from .types import ComputeShaderType, Number, ProgramAttrType, ProgramType, UniformBlockType, UniformType, pArray
|
|
3
3
|
|
|
4
|
-
def _is_array_uniform(u:
|
|
4
|
+
def _is_array_uniform(u: UniformType) -> bool:
|
|
5
5
|
return u.array_length > 1
|
|
6
6
|
|
|
7
|
-
def get_pattr(prog_id:str|
|
|
8
|
-
if isinstance(prog_id,
|
|
7
|
+
def get_pattr(prog_id: str | ProgramType, name: str, *, programs: dict[str, ProgramType] = {}) -> ProgramAttrType:
|
|
8
|
+
if isinstance(prog_id, ProgramType):
|
|
9
9
|
return prog_id[name]
|
|
10
10
|
if prog_id not in programs:
|
|
11
11
|
raise ValueError(f"Program with id '{prog_id}' does not exist.")
|
|
12
12
|
return programs[prog_id][name]
|
|
13
13
|
|
|
14
|
-
def get_uniform(prog_id:str|
|
|
14
|
+
def get_uniform(prog_id: str | ProgramType | ComputeShaderType, name: str, *, compute_shaders: dict[str, ComputeShaderType] = {}, programs: dict[str, ProgramType] = {}) -> UniformType:
|
|
15
15
|
"""Get a uniform from a program or compute shader with proper typing.
|
|
16
16
|
|
|
17
17
|
This is a type-safe alternative to program[name] which returns a union type.
|
|
@@ -26,22 +26,50 @@ def get_uniform(prog_id:str|moderngl.Program|moderngl.ComputeShader, name:str, *
|
|
|
26
26
|
raise ValueError(f"Program or compute shader with id '{prog_id}' does not exist.")
|
|
27
27
|
|
|
28
28
|
attr = prog_id[name]
|
|
29
|
-
if not isinstance(attr,
|
|
29
|
+
if not isinstance(attr, UniformType):
|
|
30
30
|
raise TypeError(f"'{name}' is {type(attr).__name__}, not a Uniform")
|
|
31
31
|
return attr
|
|
32
32
|
|
|
33
|
-
def
|
|
33
|
+
def get_uniform_block(prog_id: str | ProgramType, name: str, *, programs: dict[str, ProgramType] = {}) -> UniformBlockType:
|
|
34
|
+
"""Get a uniform block from a program with proper typing.
|
|
35
|
+
|
|
36
|
+
This is a type-safe alternative to program[name] which returns a union type.
|
|
37
|
+
Raises TypeError if the attribute is not a UniformBlock.
|
|
38
|
+
"""
|
|
39
|
+
if isinstance(prog_id, str):
|
|
40
|
+
if prog_id not in programs:
|
|
41
|
+
raise ValueError(f"Program with id '{prog_id}' does not exist.")
|
|
42
|
+
prog_id = programs[prog_id]
|
|
43
|
+
|
|
44
|
+
attr = prog_id[name]
|
|
45
|
+
if not isinstance(attr, UniformBlockType):
|
|
46
|
+
raise TypeError(f"'{name}' is {type(attr).__name__}, not a UniformBlock")
|
|
47
|
+
return attr
|
|
48
|
+
|
|
49
|
+
def set_uniform_block_binding(prog_id: str | ProgramType, name: str, binding: int, *, programs: dict[str, ProgramType] = {}) -> None:
|
|
50
|
+
"""Set the binding point for a uniform block.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
prog_id: Program or program ID string
|
|
54
|
+
name: Name of the uniform block
|
|
55
|
+
binding: Binding point (0-based integer)
|
|
56
|
+
programs: Dictionary of programs if using string ID
|
|
57
|
+
"""
|
|
58
|
+
block = get_uniform_block(prog_id, name, programs=programs)
|
|
59
|
+
block.binding = binding
|
|
60
|
+
|
|
61
|
+
def get_pattr_value(prog_id: str | ProgramType, name: str, *, programs: dict[str, ProgramType] = {}) -> Number | pArray:
|
|
34
62
|
attr = get_pattr(prog_id, name, programs=programs)
|
|
35
|
-
if not isinstance(attr,
|
|
63
|
+
if not isinstance(attr, UniformType):
|
|
36
64
|
raise TypeError(f"'{name}' is {type(attr).__name__}, not a Uniform")
|
|
37
65
|
if _is_array_uniform(attr):
|
|
38
66
|
raise TypeError(f"Uniform '{name}' is an array; cannot use .value")
|
|
39
67
|
return attr.value
|
|
40
68
|
|
|
41
|
-
def set_pattr_value(prog_id:str|
|
|
69
|
+
def set_pattr_value(prog_id: str | ProgramType, name: str, value: Number | pArray, *, programs: dict[str, ProgramType] = {}, force_write: bool = False) -> None:
|
|
42
70
|
attr = get_pattr(prog_id, name, programs=programs)
|
|
43
71
|
|
|
44
|
-
if not isinstance(attr,
|
|
72
|
+
if not isinstance(attr, UniformType):
|
|
45
73
|
raise TypeError(f"'{name}' is {type(attr).__name__}, not a Uniform")
|
|
46
74
|
|
|
47
75
|
use_write = force_write or _is_array_uniform(attr)
|
e2D/commons.pyi
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Type stubs for commons module
|
|
3
|
+
Common utility functions for moderngl programs
|
|
4
|
+
"""
|
|
5
|
+
from .types import ComputeShaderType, Number, ProgramAttrType, ProgramType, UniformBlockType, UniformType, pArray
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_pattr(
|
|
9
|
+
prog_id: str | ProgramType,
|
|
10
|
+
name: str,
|
|
11
|
+
*,
|
|
12
|
+
programs: dict[str, ProgramType] = {}
|
|
13
|
+
) -> ProgramAttrType:
|
|
14
|
+
"""Get a program attribute by name"""
|
|
15
|
+
...
|
|
16
|
+
|
|
17
|
+
def get_uniform(
|
|
18
|
+
prog_id: str | ProgramType | ComputeShaderType,
|
|
19
|
+
name: str,
|
|
20
|
+
*,
|
|
21
|
+
compute_shaders: dict[str, ComputeShaderType] = {},
|
|
22
|
+
programs: dict[str, ProgramType] = {}
|
|
23
|
+
) -> UniformType:
|
|
24
|
+
"""Get a uniform from a program or compute shader with proper typing.
|
|
25
|
+
|
|
26
|
+
This is a type-safe alternative to program[name] which returns a union type.
|
|
27
|
+
Raises TypeError if the attribute is not a Uniform.
|
|
28
|
+
"""
|
|
29
|
+
...
|
|
30
|
+
|
|
31
|
+
def get_uniform_block(
|
|
32
|
+
prog_id: str | ProgramType,
|
|
33
|
+
name: str,
|
|
34
|
+
*,
|
|
35
|
+
programs: dict[str, ProgramType] = {}
|
|
36
|
+
) -> UniformBlockType:
|
|
37
|
+
"""Get a uniform block from a program with proper typing.
|
|
38
|
+
|
|
39
|
+
This is a type-safe alternative to program[name] which returns a union type.
|
|
40
|
+
Raises TypeError if the attribute is not a UniformBlock.
|
|
41
|
+
"""
|
|
42
|
+
...
|
|
43
|
+
|
|
44
|
+
def set_uniform_block_binding(
|
|
45
|
+
prog_id: str | ProgramType,
|
|
46
|
+
name: str,
|
|
47
|
+
binding: int,
|
|
48
|
+
*,
|
|
49
|
+
programs: dict[str, ProgramType] = {}
|
|
50
|
+
) -> None:
|
|
51
|
+
"""Set the binding point for a uniform block.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
prog_id: Program or program ID string
|
|
55
|
+
name: Name of the uniform block
|
|
56
|
+
binding: Binding point (0-based integer)
|
|
57
|
+
programs: Dictionary of programs if using string ID
|
|
58
|
+
"""
|
|
59
|
+
...
|
|
60
|
+
|
|
61
|
+
def get_pattr_value(
|
|
62
|
+
prog_id: str | ProgramType,
|
|
63
|
+
name: str,
|
|
64
|
+
*,
|
|
65
|
+
programs: dict[str, ProgramType] = {}
|
|
66
|
+
) -> Number | pArray:
|
|
67
|
+
"""Get the value of a program uniform"""
|
|
68
|
+
...
|
|
69
|
+
|
|
70
|
+
def set_pattr_value(
|
|
71
|
+
prog_id: str | ProgramType,
|
|
72
|
+
name: str,
|
|
73
|
+
value: Number | pArray,
|
|
74
|
+
*,
|
|
75
|
+
programs: dict[str, ProgramType] = {},
|
|
76
|
+
force_write: bool = False
|
|
77
|
+
) -> None:
|
|
78
|
+
"""Set the value of a program uniform"""
|
|
79
|
+
...
|