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/shapes.pyi ADDED
@@ -0,0 +1,272 @@
1
+ """
2
+ Type stubs for shapes module
3
+ High-performance 2D shape rendering using SDF and GPU shaders
4
+ """
5
+
6
+ import moderngl
7
+ import numpy as np
8
+ import numpy.typing as npt
9
+ from typing import Optional, Sequence
10
+ from enum import Enum
11
+ from .types import ContextType, ProgramType, VAOType, VectorType, ColorType, BufferType
12
+ from .color_defs import WHITE, TRANSPARENT
13
+
14
+ class FillMode(Enum):
15
+ """Shape fill mode"""
16
+ FILL: int
17
+ STROKE: int
18
+ FILL_STROKE: int
19
+
20
+ class ShapeLabel:
21
+ """A pre-rendered shape for efficient repeated drawing."""
22
+ ctx: ContextType
23
+ prog: ProgramType
24
+ vbo: BufferType
25
+ vertex_count: int
26
+ shape_type: str
27
+ vao: VAOType
28
+
29
+ def __init__(
30
+ self,
31
+ ctx: ContextType,
32
+ prog: ProgramType,
33
+ vbo: BufferType,
34
+ vertex_count: int,
35
+ shape_type: str = 'line'
36
+ ) -> None: ...
37
+
38
+ def draw(self) -> None:
39
+ """Draw the cached shape."""
40
+ ...
41
+
42
+ class InstancedShapeBatch:
43
+ """High-performance instanced batch for drawing thousands of shapes with minimal CPU overhead."""
44
+ ctx: ContextType
45
+ prog: ProgramType
46
+ shape_type: str
47
+ max_instances: int
48
+ instance_count: int
49
+ floats_per_instance: int
50
+ instance_buffer: BufferType
51
+ quad_vbo: BufferType
52
+ vao: VAOType
53
+ instance_data: list[float]
54
+
55
+ def __init__(
56
+ self,
57
+ ctx: ContextType,
58
+ prog: ProgramType,
59
+ shape_type: str = 'circle',
60
+ max_instances: int = 100000
61
+ ) -> None: ...
62
+
63
+ def add_circle(
64
+ self,
65
+ center: VectorType,
66
+ radius: float,
67
+ color: ColorType = (1.0, 1.0, 1.0, 1.0),
68
+ border_color: ColorType = (0.0, 0.0, 0.0, 0.0),
69
+ border_width: float = 0.0,
70
+ antialiasing: float = 1.0
71
+ ) -> None:
72
+ """Add a circle instance to the batch."""
73
+ ...
74
+
75
+ def add_circles_numpy(
76
+ self,
77
+ centers: npt.NDArray[np.float32],
78
+ radii: npt.NDArray[np.float32],
79
+ colors: npt.NDArray[np.float32],
80
+ border_colors: Optional[npt.NDArray[np.float32]] = None,
81
+ border_widths: Optional[npt.NDArray[np.float32]] = None,
82
+ antialiasing: float = 1.0
83
+ ) -> None:
84
+ """Add multiple circles efficiently using numpy arrays (10-50x faster than loop)."""
85
+ ...
86
+
87
+ def add_rect(
88
+ self,
89
+ center: VectorType,
90
+ size: VectorType,
91
+ color: ColorType = (1.0, 1.0, 1.0, 1.0),
92
+ corner_radius: float = 0.0,
93
+ border_color: ColorType = (0.0, 0.0, 0.0, 0.0),
94
+ border_width: float = 0.0,
95
+ antialiasing: float = 1.0,
96
+ rotation: float = 0.0
97
+ ) -> None:
98
+ """Add a rectangle instance to the batch."""
99
+ ...
100
+
101
+ def add_rects_numpy(
102
+ self,
103
+ centers: npt.NDArray[np.float32],
104
+ sizes: npt.NDArray[np.float32],
105
+ colors: npt.NDArray[np.float32],
106
+ corner_radii: Optional[npt.NDArray[np.float32]] = None,
107
+ border_colors: Optional[npt.NDArray[np.float32]] = None,
108
+ border_widths: Optional[npt.NDArray[np.float32]] = None,
109
+ antialiasing: float = 1.0,
110
+ rotations: Optional[npt.NDArray[np.float32]] = None
111
+ ) -> None:
112
+ """Add multiple rectangles efficiently using numpy arrays."""
113
+ ...
114
+
115
+ def add_line(
116
+ self,
117
+ start: VectorType,
118
+ end: VectorType,
119
+ width: float = 1.0,
120
+ color: ColorType = (1.0, 1.0, 1.0, 1.0)
121
+ ) -> None:
122
+ """Add a line instance to the batch."""
123
+ ...
124
+
125
+ def add_lines_numpy(
126
+ self,
127
+ starts: npt.NDArray[np.float32],
128
+ ends: npt.NDArray[np.float32],
129
+ widths: npt.NDArray[np.float32],
130
+ colors: npt.NDArray[np.float32]
131
+ ) -> None:
132
+ """Add multiple lines efficiently using numpy arrays."""
133
+ ...
134
+
135
+ def flush(self) -> None:
136
+ """Draw all instances in a single draw call."""
137
+ ...
138
+
139
+ def clear(self) -> None:
140
+ """Clear the batch without drawing."""
141
+ ...
142
+
143
+ class ShapeRenderer:
144
+ """
145
+ High-performance 2D shape renderer using SDF (Signed Distance Functions) and GPU shaders.
146
+ Supports immediate mode, cached drawing, and batched rendering.
147
+ """
148
+ ctx: ContextType
149
+ circle_instanced_prog: ProgramType
150
+ rect_instanced_prog: ProgramType
151
+ line_instanced_prog: ProgramType
152
+ circle_prog: ProgramType
153
+ rect_prog: ProgramType
154
+ line_prog: ProgramType
155
+ circle_vbo: BufferType
156
+ rect_vbo: BufferType
157
+ line_vbo: BufferType
158
+ circle_vao: VAOType
159
+ rect_vao: VAOType
160
+ line_vao: VAOType
161
+
162
+ def __init__(self, ctx: ContextType) -> None: ...
163
+
164
+ def draw_circle(
165
+ self,
166
+ center: VectorType,
167
+ radius: float,
168
+ color: ColorType = (1.0, 1.0, 1.0, 1.0),
169
+ rotation: float = 0.0,
170
+ border_color: ColorType = (0.0, 0.0, 0.0, 0.0),
171
+ border_width: float = 0.0,
172
+ antialiasing: float = 1.0
173
+ ) -> None:
174
+ """Draw a circle immediately."""
175
+ ...
176
+
177
+ def create_circle(
178
+ self,
179
+ center: VectorType,
180
+ radius: float,
181
+ color: ColorType = (1.0, 1.0, 1.0, 1.0),
182
+ rotation: float = 0.0,
183
+ border_color: ColorType = (0.0, 0.0, 0.0, 0.0),
184
+ border_width: float = 0.0,
185
+ antialiasing: float = 1.0
186
+ ) -> ShapeLabel:
187
+ """Create a cached circle for repeated drawing."""
188
+ ...
189
+
190
+ def draw_rect(
191
+ self,
192
+ position: VectorType,
193
+ size: VectorType,
194
+ color: ColorType = (1.0, 1.0, 1.0, 1.0),
195
+ rotation: float = 0.0,
196
+ corner_radius: float = 0.0,
197
+ border_color: ColorType = (0.0, 0.0, 0.0, 0.0),
198
+ border_width: float = 0.0,
199
+ antialiasing: float = 1.0
200
+ ) -> None:
201
+ """Draw a rectangle immediately."""
202
+ ...
203
+
204
+ def create_rect(
205
+ self,
206
+ position: VectorType,
207
+ size: VectorType,
208
+ color: ColorType = (1.0, 1.0, 1.0, 1.0),
209
+ rotation: float = 0.0,
210
+ corner_radius: float = 0.0,
211
+ border_color: ColorType = (0.0, 0.0, 0.0, 0.0),
212
+ border_width: float = 0.0,
213
+ antialiasing: float = 1.0
214
+ ) -> ShapeLabel:
215
+ """Create a cached rectangle for repeated drawing."""
216
+ ...
217
+
218
+ def draw_line(
219
+ self,
220
+ start: VectorType,
221
+ end: VectorType,
222
+ width: float = 1.0,
223
+ color: ColorType = (1.0, 1.0, 1.0, 1.0),
224
+ antialiasing: float = 1.0
225
+ ) -> None:
226
+ """Draw a single line segment."""
227
+ ...
228
+
229
+ def draw_lines(
230
+ self,
231
+ points: npt.NDArray[np.float32] | Sequence[VectorType],
232
+ width: float = 1.0,
233
+ color: ColorType | npt.NDArray[np.float32] = (1.0, 1.0, 1.0, 1.0),
234
+ antialiasing: float = 1.0,
235
+ closed: bool = False
236
+ ) -> None:
237
+ """Draw a polyline (connected line segments)."""
238
+ ...
239
+
240
+ def create_line(
241
+ self,
242
+ start: VectorType,
243
+ end: VectorType,
244
+ width: float = 1.0,
245
+ color: ColorType = (1.0, 1.0, 1.0, 1.0),
246
+ antialiasing: float = 1.0
247
+ ) -> ShapeLabel:
248
+ """Create a cached line for repeated drawing."""
249
+ ...
250
+
251
+ def create_lines(
252
+ self,
253
+ points: npt.NDArray[np.float32] | Sequence[VectorType],
254
+ width: float = 1.0,
255
+ color: ColorType | npt.NDArray[np.float32] = (1.0, 1.0, 1.0, 1.0),
256
+ antialiasing: float = 1.0,
257
+ closed: bool = False
258
+ ) -> ShapeLabel:
259
+ """Create a cached polyline for repeated drawing."""
260
+ ...
261
+
262
+ def create_circle_batch(self, max_shapes: int = 10000) -> InstancedShapeBatch:
263
+ """Create a batch for drawing multiple circles efficiently using GPU instancing."""
264
+ ...
265
+
266
+ def create_rect_batch(self, max_shapes: int = 10000) -> InstancedShapeBatch:
267
+ """Create a batch for drawing multiple rectangles efficiently using GPU instancing."""
268
+ ...
269
+
270
+ def create_line_batch(self, max_shapes: int = 10000) -> InstancedShapeBatch:
271
+ """Create a batch for drawing multiple lines efficiently using GPU instancing."""
272
+ ...
e2D/test_colors.py ADDED
@@ -0,0 +1,122 @@
1
+ """
2
+ Test script for new e2D color system
3
+ Demonstrates usage and verifies functionality
4
+ """
5
+
6
+ # Test basic imports - use direct imports to avoid types.py conflict
7
+ import sys
8
+ import os
9
+ sys.path.insert(0, os.path.dirname(__file__))
10
+
11
+ from colors import Color, normalize_color, lerp_colors, gradient
12
+ from color_defs import RED, BLUE, GREEN, ORANGE, get_color, MD_BLUE
13
+
14
+ print("=" * 60)
15
+ print("e2D Color System Test")
16
+ print("=" * 60)
17
+
18
+ # Test 1: Creating colors
19
+ print("\n1. Creating Colors:")
20
+ c1 = Color(1.0, 0.0, 0.0, 1.0) # Red
21
+ print(f" Color(1, 0, 0, 1) = {c1}")
22
+
23
+ c2 = Color.from_rgb255(255, 128, 0) # Orange
24
+ print(f" from_rgb255(255, 128, 0) = {c2}")
25
+
26
+ c3 = Color.from_hex("#00FF00") # Green
27
+ print(f" from_hex('#00FF00') = {c3}")
28
+
29
+ c4 = Color.from_hsv(0.5, 1.0, 1.0) # Cyan
30
+ print(f" from_hsv(0.5, 1, 1) = {c4}")
31
+
32
+ # Test 2: Color operations
33
+ print("\n2. Color Operations:")
34
+ red = Color.red()
35
+ blue = Color.blue()
36
+ print(f" red.lerp(blue, 0.5) = {red.lerp(blue, 0.5)}")
37
+ print(f" red.lighten(0.2) = {red.lighten(0.2)}")
38
+ print(f" blue.darken(0.3) = {blue.darken(0.3)}")
39
+ print(f" red.invert() = {red.invert()}")
40
+
41
+ # Test 3: Color space conversions
42
+ print("\n3. Color Space Conversions:")
43
+ orange = Color.orange()
44
+ print(f" Orange RGB: {orange.to_rgb()}")
45
+ print(f" Orange RGB255: {orange.to_rgb255()}")
46
+ print(f" Orange HSV: {orange.to_hsv()}")
47
+ print(f" Orange HEX: {orange.to_hex()}")
48
+
49
+ # Test 4: Pre-defined colors
50
+ print("\n4. Pre-defined Colors:")
51
+ print(f" RED = {RED}")
52
+ print(f" BLUE = {BLUE}")
53
+ print(f" MD_BLUE = {MD_BLUE}")
54
+ print(f" get_color('orange') = {get_color('orange')}")
55
+
56
+ # Test 5: Utility functions
57
+ print("\n5. Utility Functions:")
58
+ normalized = normalize_color("#FF0000")
59
+ print(f" normalize_color('#FF0000') = {normalized}")
60
+
61
+ lerped = lerp_colors(RED, BLUE, 0.5)
62
+ print(f" lerp_colors(RED, BLUE, 0.5) = {lerped}")
63
+
64
+ grad = gradient([RED, GREEN, BLUE], 5)
65
+ print(f" gradient([RED, GREEN, BLUE], 5):")
66
+ for i, color in enumerate(grad):
67
+ print(f" [{i}] = {color}")
68
+
69
+ # Test 6: Immutability and tuple unpacking
70
+ print("\n6. Immutability & Unpacking:")
71
+ c = Color.red()
72
+ r, g, b, a = c
73
+ print(f" Unpacked red: r={r}, g={g}, b={b}, a={a}")
74
+ print(f" Indexing: c[0]={c[0]}, c[1]={c[1]}, c[2]={c[2]}, c[3]={c[3]}")
75
+
76
+ # Test 7: Operators
77
+ print("\n7. Operators:")
78
+ c1 = Color(0.5, 0.3, 0.2, 1.0)
79
+ c2 = Color(0.2, 0.4, 0.5, 1.0)
80
+ print(f" c1 = {c1}")
81
+ print(f" c2 = {c2}")
82
+ print(f" c1 + c2 = {c1 + c2}")
83
+ print(f" c1 - c2 = {c1 - c2}")
84
+ print(f" c1 * 2.0 = {c1 * 2.0}")
85
+ print(f" c1 / 2.0 = {c1 / 2.0}")
86
+
87
+ # Test 8: GPU compatibility
88
+ print("\n8. GPU Compatibility:")
89
+ c = Color.from_rgba(0.8, 0.4, 0.2, 1.0)
90
+ arr = c.to_array()
91
+ print(f" Color as numpy array: {arr}")
92
+ print(f" Array dtype: {arr.dtype}")
93
+ print(f" Array shape: {arr.shape}")
94
+
95
+ # Test 9: Color manipulation
96
+ print("\n9. Color Manipulation:")
97
+ green = Color.green()
98
+ print(f" Green: {green}")
99
+ print(f" Rotate hue +60°: {green.rotate_hue(60)}")
100
+ print(f" Rotate hue +120°: {green.rotate_hue(120)}")
101
+ print(f" Saturate +0.3: {green.saturate(0.3)}")
102
+ print(f" Desaturate -0.5: {green.desaturate(0.5)}")
103
+ print(f" Grayscale: {green.grayscale()}")
104
+
105
+ # Test 10: with_alpha
106
+ print("\n10. Alpha Manipulation:")
107
+ c = Color.red()
108
+ print(f" Red (full opacity): {c}")
109
+ print(f" Red with 50% alpha: {c.with_alpha(0.5)}")
110
+ print(f" Red transparent: {c.with_alpha(0.0)}")
111
+
112
+ print("\n" + "=" * 60)
113
+ print("All tests completed successfully!")
114
+ print("=" * 60)
115
+
116
+ # Summary
117
+ print("\n✓ Color class is immutable and GPU-optimized")
118
+ print("✓ Default format: RGBA floats (0.0-1.0)")
119
+ print("✓ Full ModernGL/GLTF compatibility")
120
+ print("✓ Rich color operations and conversions")
121
+ print("✓ 80+ pre-defined colors available")
122
+ print("✓ Type-safe with proper type hints")
e2D/text_renderer.py CHANGED
@@ -4,15 +4,18 @@ from PIL import Image, ImageFont
4
4
  from attr import dataclass
5
5
  import numpy as np
6
6
  import moderngl
7
+ from .types import ColorType, VAOType, VectorType, ContextType, ProgramType, BufferType, TextureType
8
+ from .colors import normalize_color
9
+ from .color_defs import WHITE, BLACK
7
10
 
8
11
  @dataclass
9
12
  class TextStyle:
10
13
  font: str = "arial.ttf"
11
14
  font_size: int = 32
12
- color: tuple[float, float, float, float] = (1.0, 1.0, 1.0, 1.0)
13
- bg_color: tuple[float, float, float, float] = (0.0, 0.0, 0.0, 0.9)
14
- bg_margin: float | tuple[float, float, float, float] = 15.0
15
- bg_border_radius: float | tuple[float, float, float, float] = 15.0
15
+ color: ColorType = (1.0, 1.0, 1.0, 1.0)
16
+ bg_color: ColorType = (0.0, 0.0, 0.0, 0.9)
17
+ bg_margin: float | tuple[float, float, float, float] | tuple[float, float] | list[float] = 15.0
18
+ bg_border_radius: float | tuple[float, float, float, float] | tuple[float, float] | list[float] = 15.0
16
19
 
17
20
  class Pivots(Enum):
18
21
  TOP_LEFT = 0
@@ -28,8 +31,19 @@ class Pivots(Enum):
28
31
  DEFAULT_TEXT_STYLE = TextStyle()
29
32
 
30
33
  class TextLabel:
31
- def __init__(self, ctx: moderngl.Context, prog: moderngl.Program, texture: moderngl.Texture, vertices: list,
32
- bg_prog: Optional[moderngl.Program] = None, bg_vertices: Optional[list] = None) -> None:
34
+ ctx: ContextType
35
+ prog: ProgramType
36
+ texture: TextureType
37
+ vertices: list[float]
38
+ vbo: BufferType
39
+ vao: VAOType
40
+ bg_prog: Optional[ProgramType]
41
+ bg_vertices: Optional[list[float]]
42
+ bg_vbo: Optional[BufferType]
43
+ bg_vao: Optional[VAOType]
44
+
45
+ def __init__(self, ctx: ContextType, prog: ProgramType, texture: TextureType, vertices: list[float],
46
+ bg_prog: Optional[ProgramType] = None, bg_vertices: Optional[list[float]] = None) -> None:
33
47
  """
34
48
  A pre-rendered text label for efficient drawing.
35
49
  To generate select a option below:
@@ -75,7 +89,13 @@ class TextRenderer:
75
89
  Renders text using a texture atlas generated from a TTF font via Pillow.
76
90
  Supports multiple fonts and sizes with caching for optimization.
77
91
  """
78
- def __init__(self, ctx: moderngl.Context) -> None:
92
+ ctx: ContextType
93
+ font_cache: dict[tuple[str, int], dict]
94
+ chars: str
95
+ bg_prog: ProgramType
96
+ prog: ProgramType
97
+
98
+ def __init__(self, ctx: ContextType) -> None:
79
99
  self.ctx = ctx
80
100
 
81
101
  # Cache for font atlases: (font_path, font_size) -> {font, char_data, texture}
@@ -281,20 +301,30 @@ class TextRenderer:
281
301
 
282
302
  return total_w, line_height
283
303
 
284
- def _normalize_margin(self, margin: float | tuple[float, float, float, float]) -> tuple[float, float, float, float]:
304
+ def _normalize_margin(self, margin: float | tuple[float, float, float, float] | tuple[float, float] | list[float]) -> tuple[float, float, float, float]:
285
305
  """Normalize margin to (top, right, bottom, left)."""
286
306
  if isinstance(margin, (int, float)):
287
307
  return (margin, margin, margin, margin)
288
- return margin
308
+ elif isinstance(margin, (list, tuple)):
309
+ if len(margin) == 2:
310
+ return (margin[0], margin[1], margin[0], margin[1])
311
+ elif len(margin) == 4:
312
+ return (margin[0], margin[1], margin[2], margin[3])
313
+ return margin[0], margin[1], margin[2], margin[3]
289
314
 
290
- def _normalize_radius(self, radius: float | tuple[float, float, float, float]) -> tuple[float, float, float, float]:
315
+ def _normalize_radius(self, radius: float | tuple[float, float, float, float] | tuple[float, float] | list[float]) -> tuple[float, float, float, float]:
291
316
  """Normalize border radius to (top-left, top-right, bottom-right, bottom-left)."""
292
317
  if isinstance(radius, (int, float)):
293
318
  return (radius, radius, radius, radius)
294
- return radius
319
+ elif isinstance(radius, (list, tuple)):
320
+ if len(radius) == 2:
321
+ return (radius[0], radius[1], radius[0], radius[1])
322
+ elif len(radius) == 4:
323
+ return (radius[0], radius[1], radius[2], radius[3])
324
+ return radius[0], radius[1], radius[2], radius[3]
295
325
 
296
326
  def _generate_background_vertices(self, x: float, y: float, width: float, height: float,
297
- bg_color: tuple, margin: tuple[float, float, float, float],
327
+ bg_color: ColorType, margin: tuple[float, float, float, float],
298
328
  radius: tuple[float, float, float, float]) -> list[float]:
299
329
  """Generate vertices for a rounded rectangle background."""
300
330
  # Apply margin
@@ -321,8 +351,8 @@ class TextRenderer:
321
351
 
322
352
  return vertices
323
353
 
324
- def _generate_vertices(self, text: str, pos: tuple[float, float], scale: float = 1.0,
325
- color: tuple = (1.0, 1.0, 1.0, 1.0), pivot: Pivots = Pivots.TOP_LEFT, char_data: dict = {} ) -> list[float]:
354
+ def _generate_vertices(self, text: str, pos: VectorType, scale: float = 1.0,
355
+ color: ColorType = WHITE, pivot: Pivots | int = Pivots.TOP_LEFT, char_data: dict = {} ) -> list[float]:
326
356
 
327
357
  if not char_data:
328
358
  raise ValueError("char_data is required for _generate_vertices")
@@ -396,7 +426,7 @@ class TextRenderer:
396
426
 
397
427
  return vertices
398
428
 
399
- def draw_text(self, text: str, pos: tuple[float, float], scale: float = 1.0, style: TextStyle = DEFAULT_TEXT_STYLE, pivot: Pivots = Pivots.TOP_LEFT) -> None:
429
+ def draw_text(self, text: str, pos: VectorType, scale: float = 1.0, style: TextStyle = DEFAULT_TEXT_STYLE, pivot: Pivots | int = Pivots.TOP_LEFT) -> None:
400
430
  if not text:
401
431
  return
402
432
 
@@ -451,7 +481,7 @@ class TextRenderer:
451
481
  self.ctx.enable(moderngl.BLEND)
452
482
  self.vao.render(moderngl.TRIANGLES, vertices=len(vertices)//8)
453
483
 
454
- def create_label(self, text: str, x: float, y: float, scale: float = 1.0, style: TextStyle = DEFAULT_TEXT_STYLE, pivot: Pivots = Pivots.TOP_LEFT) -> TextLabel:
484
+ def create_label(self, text: str, x: float, y: float, scale: float = 1.0, style: TextStyle = DEFAULT_TEXT_STYLE, pivot: Pivots | int = Pivots.TOP_LEFT) -> TextLabel:
455
485
  if not text:
456
486
  # Return empty label with default texture
457
487
  font_atlas = self._get_or_create_font_atlas(style.font, style.font_size)
e2D/text_renderer.pyi ADDED
@@ -0,0 +1,118 @@
1
+ """
2
+ Type stubs for text_renderer module
3
+ Text rendering using texture atlases and TTF fonts
4
+ """
5
+
6
+ from enum import Enum
7
+ from typing import Optional
8
+ from .types import ColorType, ContextType, ProgramType, TextureType, VAOType, VectorType, BufferType
9
+ from .color_defs import WHITE
10
+
11
+ class TextStyle:
12
+ """Text styling options"""
13
+ font: str
14
+ font_size: int
15
+ color: ColorType
16
+ bg_color: ColorType
17
+ bg_margin: float | tuple[float, float, float, float]
18
+ bg_border_radius: float | tuple[float, float, float, float]
19
+
20
+ def __init__(
21
+ self,
22
+ font: str = "arial.ttf",
23
+ font_size: int = 32,
24
+ color: ColorType = (1.0, 1.0, 1.0, 1.0),
25
+ bg_color: ColorType = (0.0, 0.0, 0.0, 0.9),
26
+ bg_margin: float | tuple[float, float, float, float] = 15.0,
27
+ bg_border_radius: float | tuple[float, float, float, float] = 15.0
28
+ ) -> None: ...
29
+
30
+ class Pivots(Enum):
31
+ """Text anchor/pivot points"""
32
+ TOP_LEFT: int
33
+ TOP_MIDDLE: int
34
+ TOP_RIGHT: int
35
+ LEFT: int
36
+ CENTER: int
37
+ RIGHT: int
38
+ BOTTOM_LEFT: int
39
+ BOTTOM_MIDDLE: int
40
+ BOTTOM_RIGHT: int
41
+
42
+ DEFAULT_TEXT_STYLE: TextStyle
43
+
44
+ class TextLabel:
45
+ """A pre-rendered text label for efficient drawing."""
46
+ ctx: ContextType
47
+ prog: ProgramType
48
+ texture: TextureType
49
+ vertices: list[float]
50
+ vbo: BufferType
51
+ vao: VAOType
52
+ bg_prog: Optional[ProgramType]
53
+ bg_vertices: Optional[list[float]]
54
+ bg_vbo: Optional[BufferType]
55
+ bg_vao: Optional[VAOType]
56
+
57
+ def __init__(
58
+ self,
59
+ ctx: ContextType,
60
+ prog: ProgramType,
61
+ texture: TextureType,
62
+ vertices: list[float],
63
+ bg_prog: Optional[ProgramType] = None,
64
+ bg_vertices: Optional[list[float]] = None
65
+ ) -> None: ...
66
+
67
+ def draw(self) -> None:
68
+ """Draw the text label"""
69
+ ...
70
+
71
+ class TextRenderer:
72
+ """
73
+ Renders text using a texture atlas generated from a TTF font via Pillow.
74
+ Supports multiple fonts and sizes with caching for optimization.
75
+ """
76
+ ctx: ContextType
77
+ font_cache: dict[tuple[str, int], dict]
78
+ chars: str
79
+ bg_prog: ProgramType
80
+ bg_vbo: BufferType
81
+ bg_vao: VAOType
82
+ prog: ProgramType
83
+ vbo: BufferType
84
+ vao: VAOType
85
+
86
+ def __init__(self, ctx: ContextType) -> None: ...
87
+
88
+ def get_text_width(
89
+ self,
90
+ text: str,
91
+ scale: float = 1.0,
92
+ style: TextStyle = DEFAULT_TEXT_STYLE
93
+ ) -> float:
94
+ """Calculate the width of the text."""
95
+ ...
96
+
97
+ def draw_text(
98
+ self,
99
+ text: str,
100
+ pos: VectorType,
101
+ scale: float = 1.0,
102
+ style: TextStyle = DEFAULT_TEXT_STYLE,
103
+ pivot: Pivots | int = Pivots.TOP_LEFT
104
+ ) -> None:
105
+ """Draw text immediately."""
106
+ ...
107
+
108
+ def create_label(
109
+ self,
110
+ text: str,
111
+ x: float,
112
+ y: float,
113
+ scale: float = 1.0,
114
+ style: TextStyle = DEFAULT_TEXT_STYLE,
115
+ pivot: Pivots | int = Pivots.TOP_LEFT
116
+ ) -> TextLabel:
117
+ """Create a cached text label for repeated drawing."""
118
+ ...
e2D/types.py ADDED
@@ -0,0 +1,58 @@
1
+ """
2
+ Type definitions for e2D library
3
+ Provides common type aliases and type hints used throughout the library
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ # Simple type aliases without imports to avoid circular dependencies
9
+ # These are used for type hints only
10
+
11
+ # Type alias for vector-like objects
12
+ # At runtime, accepts: Vector2D instances, tuples of 2 numbers, or sequences of 2 numbers
13
+ # Use this for ALL vector/position/size parameters for maximum flexibility
14
+ VectorType = object # Union at type-check time via .pyi file
15
+
16
+ # Color type (RGBA values between 0.0 and 1.0)
17
+ ColorType = tuple[float, float, float, float]
18
+
19
+ # Numeric types
20
+ Number = int | float
21
+ IntVec2 = tuple[int, int]
22
+ FloatVec2 = tuple[float, float]
23
+ NumVec2 = tuple[Number, Number]
24
+
25
+ # Array-like types for numpy and buffers
26
+ pArray = object # list | tuple at runtime
27
+ ArrayLike = object # numpy array or similar at runtime
28
+
29
+ # Shader and GPU resource types (forward declarations at runtime)
30
+ ContextType = object # moderngl.Context
31
+ ProgramType = object # moderngl.Program
32
+ ComputeShaderType = object # moderngl.ComputeShader
33
+ BufferType = object # moderngl.Buffer
34
+ VAOType = object # moderngl.VertexArray
35
+ TextureType = object # moderngl.Texture
36
+
37
+ ProgramAttrType = object # moderngl.Uniform | moderngl.UniformBlock | moderngl.Attribute | moderngl.Varying
38
+ UniformType = object # moderngl.Uniform
39
+ UniformBlockType = object # moderngl.UniformBlock
40
+
41
+ # Window and input types
42
+ WindowType = object # glfw window
43
+
44
+ __all__ = [
45
+ 'VectorType',
46
+ 'ColorType',
47
+ 'Number',
48
+ 'IntVec2',
49
+ 'FloatVec2',
50
+ 'NumVec2',
51
+ 'ArrayLike',
52
+ 'ContextType',
53
+ 'ProgramType',
54
+ 'BufferType',
55
+ 'VAOType',
56
+ 'TextureType',
57
+ 'WindowType',
58
+ ]