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/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: moderngl.Uniform) -> bool:
4
+ def _is_array_uniform(u: UniformType) -> bool:
5
5
  return u.array_length > 1
6
6
 
7
- def get_pattr(prog_id:str|moderngl.Program, name:str, *, programs: dict[str, moderngl.Program]= {}) -> moderngl.Uniform | moderngl.UniformBlock | moderngl.Attribute | moderngl.Varying:
8
- if isinstance(prog_id, moderngl.Program):
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|moderngl.Program|moderngl.ComputeShader, name:str, *, compute_shaders: dict[str, moderngl.ComputeShader]= {}, programs: dict[str, moderngl.Program]= {}) -> moderngl.Uniform:
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, moderngl.Uniform):
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 get_pattr_value(prog_id:str|moderngl.Program, name:str, *, programs: dict[str, moderngl.Program]= {}) -> int|float|tuple|list:
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, moderngl.Uniform):
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|moderngl.Program, name: str, value, *, programs: dict[str, moderngl.Program]= {}, force_write: bool= False) -> None:
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, moderngl.Uniform):
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
+ ...