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 CHANGED
@@ -6,9 +6,9 @@ Copyright (c) 2025 Riccardo Mariani
6
6
  MIT License
7
7
  """
8
8
 
9
- __version__ = "2.0.0"
9
+ __version__ = "2.0.2"
10
10
  __author__ = "Riccardo Mariani"
11
- __email__ = "ricomari2006@gmail.com"
11
+ __email__ = "riccardo.mariani@emptyhead.dev"
12
12
 
13
13
  import numpy as np
14
14
  import moderngl
@@ -16,6 +16,12 @@ import glfw
16
16
  import time
17
17
  import os
18
18
 
19
+ # Import type definitions
20
+ from .types import (
21
+ ComputeShaderType, ProgramAttrType, UniformType, VectorType, ColorType, Number,
22
+ ContextType, ProgramType, BufferType, WindowType, pArray
23
+ )
24
+
19
25
  # Import original e2D modules
20
26
  from .text_renderer import DEFAULT_TEXT_STYLE, Pivots, TextRenderer, TextLabel, TextStyle
21
27
  from .shapes import ShapeRenderer, ShapeLabel, InstancedShapeBatch, FillMode
@@ -24,48 +30,43 @@ from .commons import get_pattr, get_pattr_value, set_pattr_value, get_uniform
24
30
 
25
31
  from typing import Optional
26
32
 
27
- # Import optimized Vector2D
33
+ # Import color utilities
34
+ from .colors import Color, normalize_color, lerp_colors, gradient, batch_colors_to_array
35
+ from .color_defs import (
36
+ # Basic colors
37
+ TRANSPARENT, WHITE, BLACK, RED, GREEN, BLUE, CYAN, MAGENTA, YELLOW,
38
+ # Extended colors (most common)
39
+ ORANGE, PURPLE, PINK, GRAY50,
40
+ # Lookup functions
41
+ COLOR_NAMES, get_color, has_color,
42
+ )
43
+
44
+ # Try to import Cython-optimized color operations (optional batch utilities)
28
45
  try:
29
- from .cvectors import (
30
- Vector2D,
31
- batch_add_inplace,
32
- batch_scale_inplace,
33
- batch_normalize_inplace,
34
- vectors_to_array,
35
- array_to_vectors,
36
- )
37
- _VECTOR_COMPILED = True
46
+ from . import ccolors # type: ignore[import-not-found]
47
+ _COLOR_COMPILED = True
38
48
  except ImportError:
39
- # Fallback to pure Python implementation
40
- import warnings
41
- warnings.warn(
42
- "Vector2D compiled extension not available. "
43
- "Install Cython and rebuild for optimal performance.",
44
- RuntimeWarning
45
- )
46
- from .vectors import (
47
- Vector2D,
48
- batch_add_inplace,
49
- batch_scale_inplace,
50
- batch_normalize_inplace,
51
- vectors_to_array,
52
- array_to_vectors,
53
- )
54
- _VECTOR_COMPILED = False
49
+ _COLOR_COMPILED = False
55
50
 
56
- # Import vector utilities
51
+ # Import Vector2D - vectors.py handles Cython/Python fallback internally
57
52
  from .vectors import (
53
+ Vector2D,
58
54
  V2,
59
- Vec2,
60
55
  CommonVectors,
56
+ batch_add_inplace,
57
+ batch_scale_inplace,
58
+ batch_normalize_inplace,
59
+ vectors_to_array,
60
+ array_to_vectors,
61
61
  lerp,
62
62
  create_grid,
63
63
  create_circle,
64
+ _COMPILED as _VECTOR_COMPILED,
64
65
  )
65
66
 
66
67
 
67
68
  class DefEnv:
68
- ctx: moderngl.Context
69
+ ctx: ContextType
69
70
  def __init__(self) -> None: ...
70
71
 
71
72
  def draw(self) -> None: ...
@@ -75,13 +76,29 @@ class DefEnv:
75
76
  def on_resize(self, width: int, height: int) -> None: ...
76
77
 
77
78
  class RootEnv:
79
+ window_size: VectorType
80
+ target_fps: int
81
+ window: WindowType
82
+ ctx: ContextType
83
+ programs: dict[str, ProgramType]
84
+ compute_shaders: dict[str, ComputeShaderType]
85
+ buffers: dict[str, BufferType]
86
+ keyboard: Keyboard
87
+ mouse: Mouse
88
+ text_renderer: TextRenderer
89
+ shape_renderer: ShapeRenderer
90
+ delta: float
91
+ last_frame_time: float
92
+ start_time: float
93
+ env: DefEnv
94
+
78
95
  def __init__(
79
96
  self,
80
- window_size:tuple[int, int]=(1920, 1080),
81
- target_fps:int=60,
82
- vsync:bool=True,
83
- version:tuple[int, int]=(4,3),
84
- monitor:Optional[int]=None
97
+ window_size: tuple[int, int] = (1920, 1080),
98
+ target_fps: int = 60,
99
+ vsync: bool = True,
100
+ version: tuple[int, int] = (4, 3),
101
+ monitor: Optional[int] = None
85
102
  ) -> None:
86
103
 
87
104
  if not glfw.init():
@@ -96,7 +113,7 @@ class RootEnv:
96
113
  self.window_size = window_size
97
114
  self.target_fps = target_fps
98
115
 
99
- self.window : glfw._GLFWwindow = glfw.create_window(window_size[0], window_size[1], "e2D", monitor, None)
116
+ self.window = glfw.create_window(window_size[0], window_size[1], "e2D", monitor, None)
100
117
  if not self.window:
101
118
  glfw.terminate()
102
119
  raise RuntimeError("Failed to create GLFW window")
@@ -127,9 +144,9 @@ class RootEnv:
127
144
 
128
145
  glfw.set_window_size_callback(self.window, self._on_resize)
129
146
 
130
- self.programs :dict[str, moderngl.Program]= {}
131
- self.compute_shaders :dict[str, moderngl.ComputeShader]= {}
132
- self.buffers :dict[str, moderngl.Buffer]= {}
147
+ self.programs :dict[str, ProgramType]= {}
148
+ self.compute_shaders = {}
149
+ self.buffers :dict[str, BufferType]= {}
133
150
 
134
151
  self.keyboard = Keyboard()
135
152
  self.mouse = Mouse()
@@ -138,25 +155,53 @@ class RootEnv:
138
155
 
139
156
  # Delta time tracking
140
157
  self.delta = 0.0
141
- self.last_frame_time = time.time()
158
+ self.last_frame_time = time.perf_counter()
159
+
160
+ # Runtime tracking (elapsed time from initialization)
161
+ self.start_time = time.perf_counter()
142
162
 
143
163
  @property
144
164
  def window_size_f(self) -> tuple[float, float]:
145
165
  """Get window size as floats for shader uniforms."""
146
166
  return (float(self.window_size[0]), float(self.window_size[1]))
147
167
 
148
- def _on_resize(self, window, width, height) -> None:
168
+ @property
169
+ def runtime(self) -> float:
170
+ """Get total elapsed time since program initialization in seconds."""
171
+ return time.perf_counter() - self.start_time
172
+
173
+ def _on_resize(self, window: WindowType, width: int, height: int) -> None:
149
174
  self.window_size = (width, height)
150
175
  fb_size = glfw.get_framebuffer_size(window)
151
176
  self.ctx.viewport = (0, 0, fb_size[0], fb_size[1])
152
177
  if hasattr(self, 'env') and hasattr(self.env, 'on_resize'):
153
178
  self.env.on_resize(fb_size[0], fb_size[1])
154
179
 
155
- def init(self, env:DefEnv) -> "RootEnv":
180
+ def init(self, env: DefEnv) -> "RootEnv":
156
181
  self.env = env
157
182
  return self
158
183
 
159
- def load_shader_file(self, path:str) -> str:
184
+ def init_rec(self, fps: int = 30, draw_on_screen: bool = True, path: str = 'output.mp4') -> 'RootEnv':
185
+ """Initialize screen recording.
186
+
187
+ Args:
188
+ fps: Recording framerate (independent of application FPS)
189
+ draw_on_screen: Show recording stats overlay
190
+ path: Output video file path
191
+
192
+ Returns:
193
+ Self for method chaining
194
+
195
+ Controls:
196
+ F9: Pause/Resume recording
197
+ F10: Restart recording (clear buffer)
198
+ F12: Take screenshot
199
+ """
200
+ from .winrec import WinRec
201
+ self.__winrecorder__ = WinRec(self, fps=fps, draw_on_screen=draw_on_screen, path=path)
202
+ return self
203
+
204
+ def load_shader_file(self, path: str) -> str:
160
205
  """Load shader source code from a file.
161
206
 
162
207
  Args:
@@ -171,7 +216,7 @@ class RootEnv:
171
216
  with open(path, 'r', encoding='utf-8') as f:
172
217
  return f.read()
173
218
 
174
- def create_program(self, vertex_shader:str, fragment_shader:str, id:str) -> moderngl.Program:
219
+ def create_program(self, vertex_shader: str, fragment_shader: str, id: str) -> ProgramType:
175
220
  new_program = self.ctx.program(
176
221
  vertex_shader=vertex_shader,
177
222
  fragment_shader=fragment_shader
@@ -181,7 +226,7 @@ class RootEnv:
181
226
  self.programs[id] = new_program
182
227
  return new_program
183
228
 
184
- def create_program_from_files(self, vertex_path:str, fragment_path:str, id:str) -> moderngl.Program:
229
+ def create_program_from_files(self, vertex_path: str, fragment_path: str, id: str) -> ProgramType:
185
230
  """Create a shader program from shader files.
186
231
 
187
232
  Args:
@@ -196,14 +241,19 @@ class RootEnv:
196
241
  fragment_shader = self.load_shader_file(fragment_path)
197
242
  return self.create_program(vertex_shader, fragment_shader, id)
198
243
 
199
- def get_program(self, id:str) -> moderngl.Program | None:
244
+ def get_program(self, id:str) -> ProgramType | None:
200
245
  return self.programs.get(id, None)
201
246
 
202
247
  def __draw__(self) -> None:
203
248
  self.ctx.clear(0.0, 0.0, 0.0, 1.0)
204
249
  self.env.draw()
250
+
251
+ # Screen recording: capture frame before overlay, draw stats after
252
+ if hasattr(self, '__winrecorder__'):
253
+ self.__winrecorder__.update() # Captures clean frame
254
+ self.__winrecorder__.draw() # Draws stats overlay (not recorded)
205
255
 
206
- def create_compute_shader(self, compute_shader:str, id:str) -> moderngl.ComputeShader:
256
+ def create_compute_shader(self, compute_shader:str, id:str) -> ComputeShaderType:
207
257
  """Create and store a compute shader program."""
208
258
  new_compute = self.ctx.compute_shader(compute_shader)
209
259
  if id in self.compute_shaders:
@@ -211,7 +261,7 @@ class RootEnv:
211
261
  self.compute_shaders[id] = new_compute
212
262
  return new_compute
213
263
 
214
- def create_compute_shader_from_file(self, compute_path:str, id:str) -> moderngl.ComputeShader:
264
+ def create_compute_shader_from_file(self, compute_path:str, id:str) -> ComputeShaderType:
215
265
  """Create a compute shader from a file.
216
266
 
217
267
  Args:
@@ -224,11 +274,11 @@ class RootEnv:
224
274
  compute_shader = self.load_shader_file(compute_path)
225
275
  return self.create_compute_shader(compute_shader, id)
226
276
 
227
- def get_compute_shader(self, id:str) -> moderngl.ComputeShader | None:
277
+ def get_compute_shader(self, id:str) -> ComputeShaderType | None:
228
278
  """Retrieve a compute shader by id."""
229
279
  return self.compute_shaders.get(id, None)
230
280
 
231
- def create_buffer(self, data=None, reserve:int=0, id:Optional[str]=None, dynamic:bool=True) -> moderngl.Buffer:
281
+ def create_buffer(self, data=None, reserve:int=0, id:Optional[str]=None, dynamic:bool=True) -> BufferType:
232
282
  """Create and optionally store a buffer.
233
283
 
234
284
  Args:
@@ -252,11 +302,11 @@ class RootEnv:
252
302
 
253
303
  return buffer
254
304
 
255
- def get_buffer(self, id:str) -> moderngl.Buffer | None:
305
+ def get_buffer(self, id:str) -> BufferType | None:
256
306
  """Retrieve a buffer by id."""
257
307
  return self.buffers.get(id, None)
258
308
 
259
- def bind_buffer_to_storage(self, buffer:moderngl.Buffer|str, binding:int) -> None:
309
+ def bind_buffer_to_storage(self, buffer:BufferType|str, binding:int) -> None:
260
310
  """Bind a buffer to a shader storage binding point.
261
311
 
262
312
  Args:
@@ -272,9 +322,9 @@ class RootEnv:
272
322
  buffer.bind_to_storage_buffer(binding)
273
323
 
274
324
  def dispatch_compute(self,
275
- compute_id:str|moderngl.ComputeShader,
325
+ compute_id:str|ComputeShaderType,
276
326
  groups_x:int=1, groups_y:int=1, groups_z:int=1,
277
- buffers:Optional[dict[int, moderngl.Buffer|str]]=None,
327
+ buffers:Optional[dict[int, BufferType|str]]=None,
278
328
  wait:bool=True
279
329
  ) -> None:
280
330
  """Dispatch a compute shader with automatic buffer binding.
@@ -304,7 +354,7 @@ class RootEnv:
304
354
  if wait:
305
355
  self.ctx.memory_barrier()
306
356
 
307
- def read_buffer(self, buffer:moderngl.Buffer|str, dtype='f4') -> np.ndarray:
357
+ def read_buffer(self, buffer:BufferType|str, dtype='f4') -> np.ndarray:
308
358
  """Read data from a buffer into a numpy array.
309
359
 
310
360
  Args:
@@ -320,7 +370,7 @@ class RootEnv:
320
370
  data = np.frombuffer(buffer.read(), dtype=dtype)
321
371
  return data
322
372
 
323
- def write_buffer(self, buffer:moderngl.Buffer|str, data, offset:int=0) -> None:
373
+ def write_buffer(self, buffer:BufferType|str, data, offset:int=0) -> None:
324
374
  """Write data to a buffer.
325
375
 
326
376
  Args:
@@ -342,16 +392,16 @@ class RootEnv:
342
392
  def __update__(self) -> None:
343
393
  self.env.update()
344
394
 
345
- def get_pattr(self, prog_id:str|moderngl.Program, name:str) -> moderngl.Uniform | moderngl.UniformBlock | moderngl.Attribute | moderngl.Varying:
395
+ def get_pattr(self, prog_id:str|ProgramType, name:str) -> ProgramAttrType:
346
396
  return get_pattr(prog_id, name, programs=self.programs)
347
397
 
348
- def get_uniform(self, prog_id:str|moderngl.Program|moderngl.ComputeShader, name:str) -> moderngl.Uniform:
398
+ def get_uniform(self, prog_id:str|ProgramType|ComputeShaderType, name:str) -> UniformType:
349
399
  return get_uniform(prog_id, name, compute_shaders=self.compute_shaders, programs=self.programs)
350
400
 
351
- def get_pattr_value(self, prog_id:str|moderngl.Program, name:str) -> int|float|tuple|list:
401
+ def get_pattr_value(self, prog_id:str|ProgramType, name:str) -> Number|pArray:
352
402
  return get_pattr_value(prog_id, name, programs=self.programs)
353
403
 
354
- def set_pattr_value(self, prog_id:str|moderngl.Program, name: str, value, *, force_write: bool= False) -> None:
404
+ def set_pattr_value(self, prog_id:str|ProgramType, name: str, value, *, force_write: bool= False) -> None:
355
405
  return set_pattr_value(prog_id, name, value, force_write=force_write, programs=self.programs)
356
406
 
357
407
  def loop(self) -> None:
@@ -364,7 +414,7 @@ class RootEnv:
364
414
  target_frame_time = 1.0 / self.target_fps if (self.target_fps and self.target_fps > 0) else 0.0
365
415
 
366
416
  while not glfw.window_should_close(self.window):
367
- start_time = time.time()
417
+ start_time = time.perf_counter()
368
418
 
369
419
  # Calculate delta time
370
420
  self.delta = start_time - self.last_frame_time
@@ -389,20 +439,24 @@ class RootEnv:
389
439
 
390
440
  # FPS Limiting
391
441
  if target_frame_time > 0:
392
- elapsed = time.time() - start_time
442
+ elapsed = time.perf_counter() - start_time
393
443
  wait = target_frame_time - elapsed
394
444
  if wait > 0:
395
445
  time.sleep(wait)
396
446
 
447
+ # Cleanup recording before terminating
448
+ if hasattr(self, '__winrecorder__'):
449
+ self.__winrecorder__.quit()
450
+
397
451
  glfw.terminate()
398
452
 
399
453
  def print(
400
454
  self,
401
455
  text_or_label: str|TextLabel,
402
- position: tuple[float, float],
456
+ position: VectorType,
403
457
  scale: float = 1.0,
404
458
  style: TextStyle = DEFAULT_TEXT_STYLE,
405
- pivot: Pivots = Pivots.TOP_LEFT,
459
+ pivot: Pivots|int = Pivots.TOP_LEFT,
406
460
  save_cache: bool = False
407
461
  ) -> Optional[TextLabel]:
408
462
 
@@ -416,15 +470,15 @@ class RootEnv:
416
470
 
417
471
  # ========== Shape Drawing Methods ==========
418
472
 
419
- def draw_circle(self, center: tuple[float, float], radius: float, **kwargs) -> None:
473
+ def draw_circle(self, center: VectorType, radius: float, **kwargs) -> None:
420
474
  """Draw a circle. See ShapeRenderer.draw_circle for parameters."""
421
475
  self.shape_renderer.draw_circle(center, radius, **kwargs)
422
476
 
423
- def draw_rect(self, position: tuple[float, float], size: tuple[float, float], **kwargs) -> None:
477
+ def draw_rect(self, position: VectorType, size: VectorType, **kwargs) -> None:
424
478
  """Draw a rectangle. See ShapeRenderer.draw_rect for parameters."""
425
479
  self.shape_renderer.draw_rect(position, size, **kwargs)
426
480
 
427
- def draw_line(self, start: tuple[float, float], end: tuple[float, float], **kwargs) -> None:
481
+ def draw_line(self, start: VectorType, end: VectorType, **kwargs) -> None:
428
482
  """Draw a line. See ShapeRenderer.draw_line for parameters."""
429
483
  self.shape_renderer.draw_line(start, end, **kwargs)
430
484
 
@@ -432,15 +486,15 @@ class RootEnv:
432
486
  """Draw a polyline. See ShapeRenderer.draw_lines for parameters."""
433
487
  self.shape_renderer.draw_lines(points, **kwargs)
434
488
 
435
- def create_circle(self, center: tuple[float, float], radius: float, **kwargs) -> ShapeLabel:
489
+ def create_circle(self, center: VectorType, radius: float, **kwargs) -> ShapeLabel:
436
490
  """Create a cached circle. See ShapeRenderer.create_circle for parameters."""
437
491
  return self.shape_renderer.create_circle(center, radius, **kwargs)
438
492
 
439
- def create_rect(self, position: tuple[float, float], size: tuple[float, float], **kwargs) -> ShapeLabel:
493
+ def create_rect(self, position: VectorType, size: VectorType, **kwargs) -> ShapeLabel:
440
494
  """Create a cached rectangle. See ShapeRenderer.create_rect for parameters."""
441
495
  return self.shape_renderer.create_rect(position, size, **kwargs)
442
496
 
443
- def create_line(self, start: tuple[float, float], end: tuple[float, float], **kwargs) -> ShapeLabel:
497
+ def create_line(self, start: VectorType, end: VectorType, **kwargs) -> ShapeLabel:
444
498
  """Create a cached line. See ShapeRenderer.create_line for parameters."""
445
499
  return self.shape_renderer.create_line(start, end, **kwargs)
446
500
 
@@ -458,4 +512,77 @@ class RootEnv:
458
512
 
459
513
  def create_line_batch(self, max_shapes: int = 10000) -> InstancedShapeBatch:
460
514
  """Create a batch for drawing multiple lines using GPU instancing."""
461
- return self.shape_renderer.create_line_batch(max_shapes)
515
+ return self.shape_renderer.create_line_batch(max_shapes)
516
+
517
+
518
+ # Export all public symbols for easy access
519
+ __all__ = [
520
+ # Version info
521
+ '__version__',
522
+ '__author__',
523
+ '__email__',
524
+ # Core classes
525
+ 'RootEnv',
526
+ 'DefEnv',
527
+ # Vector classes and utilities
528
+ 'Vector2D',
529
+ 'V2',
530
+ 'CommonVectors',
531
+ 'batch_add_inplace',
532
+ 'batch_scale_inplace',
533
+ 'batch_normalize_inplace',
534
+ 'vectors_to_array',
535
+ 'array_to_vectors',
536
+ 'lerp',
537
+ 'create_grid',
538
+ 'create_circle',
539
+ # Type aliases
540
+ 'VectorType',
541
+ 'ColorType',
542
+ # Color utilities
543
+ 'Color',
544
+ 'normalize_color',
545
+ 'lerp_colors',
546
+ 'gradient',
547
+ 'batch_colors_to_array',
548
+ # Pre-defined colors
549
+ 'TRANSPARENT',
550
+ 'WHITE',
551
+ 'BLACK',
552
+ 'RED',
553
+ 'GREEN',
554
+ 'BLUE',
555
+ 'CYAN',
556
+ 'MAGENTA',
557
+ 'YELLOW',
558
+ 'ORANGE',
559
+ 'PURPLE',
560
+ 'PINK',
561
+ 'GRAY50',
562
+ 'COLOR_NAMES',
563
+ 'get_color',
564
+ 'has_color',
565
+ # Text rendering
566
+ 'TextRenderer',
567
+ 'TextLabel',
568
+ 'TextStyle',
569
+ 'Pivots',
570
+ 'DEFAULT_TEXT_STYLE',
571
+ # Shape rendering
572
+ 'ShapeRenderer',
573
+ 'ShapeLabel',
574
+ 'InstancedShapeBatch',
575
+ 'FillMode',
576
+ # Input devices
577
+ 'Keyboard',
578
+ 'Mouse',
579
+ 'KeyState',
580
+ # ModernGL utilities
581
+ 'get_pattr',
582
+ 'get_pattr_value',
583
+ 'set_pattr_value',
584
+ 'get_uniform',
585
+ # Compilation flags
586
+ '_VECTOR_COMPILED',
587
+ '_COLOR_COMPILED',
588
+ ]
e2D/__init__.pyi ADDED
@@ -0,0 +1,145 @@
1
+ """
2
+ Type stubs for e2D package
3
+ High-Performance 2D Graphics and Math Library
4
+ """
5
+
6
+ import numpy as np
7
+ from typing import Optional, Any
8
+ from .types import BufferType, ComputeShaderType, ContextType, Number, ProgramAttrType, ProgramType, ProgramType, UniformType, VectorType, pArray
9
+ from .text_renderer import DEFAULT_TEXT_STYLE, Pivots, TextRenderer, TextLabel, TextStyle
10
+ from .shapes import ShapeRenderer, ShapeLabel, InstancedShapeBatch
11
+ from .devices import Keyboard, Mouse
12
+
13
+ __version__: str
14
+ __author__: str
15
+ __email__: str
16
+
17
+ _VECTOR_COMPILED: bool
18
+
19
+ class DefEnv:
20
+ """Base environment class for user environments"""
21
+ ctx: ContextType
22
+
23
+ def __init__(self) -> None: ...
24
+ def draw(self) -> None: ...
25
+ def update(self) -> None: ...
26
+ def on_resize(self, width: int, height: int) -> None: ...
27
+
28
+ class RootEnv:
29
+ """Root environment managing the window, context, and main loop"""
30
+ window: Any # glfw._GLFWwindow
31
+ window_size: tuple[int, int]
32
+ target_fps: int
33
+ ctx: ContextType
34
+ programs: dict[str, ProgramType]
35
+ compute_shaders: dict[str, ComputeShaderType]
36
+ buffers: dict[str, BufferType]
37
+ keyboard: Keyboard
38
+ mouse: Mouse
39
+ text_renderer: TextRenderer
40
+ shape_renderer: ShapeRenderer
41
+ delta: float
42
+ last_frame_time: float
43
+ start_time: float
44
+ env: DefEnv
45
+
46
+ def __init__(
47
+ self,
48
+ window_size: tuple[int, int] = (1920, 1080),
49
+ target_fps: int = 60,
50
+ vsync: bool = True,
51
+ version: tuple[int, int] = (4, 3),
52
+ monitor: Optional[int] = None
53
+ ) -> None: ...
54
+
55
+ @property
56
+ def window_size_f(self) -> tuple[float, float]:
57
+ """Get window size as floats for shader uniforms."""
58
+ ...
59
+
60
+ @property
61
+ def runtime(self) -> float:
62
+ """Get total elapsed time since program initialization in seconds."""
63
+ ...
64
+
65
+ def init(self, env: DefEnv) -> "RootEnv": ...
66
+
67
+ def load_shader_file(self, path: str) -> str:
68
+ """Load shader source code from a file."""
69
+ ...
70
+
71
+ def create_program(self, vertex_shader: str, fragment_shader: str, id: str) -> ProgramType: ...
72
+ def create_program_from_files(self, vertex_path: str, fragment_path: str, id: str) -> ProgramType: ...
73
+ def get_program(self, id: str) -> Optional[ProgramType]: ...
74
+
75
+ def create_compute_shader(self, compute_shader: str, id: str) -> ComputeShaderType: ...
76
+ def create_compute_shader_from_file(self, compute_path: str, id: str) -> ComputeShaderType: ...
77
+ def get_compute_shader(self, id: str) -> Optional[ComputeShaderType]: ...
78
+
79
+ def create_buffer(
80
+ self,
81
+ data: Any = None,
82
+ reserve: int = 0,
83
+ id: Optional[str] = None,
84
+ dynamic: bool = True
85
+ ) -> BufferType: ...
86
+
87
+ def get_buffer(self, id: str) -> Optional[BufferType]: ...
88
+ def bind_buffer_to_storage(self, buffer: BufferType | str, binding: int) -> None: ...
89
+
90
+ def dispatch_compute(
91
+ self,
92
+ compute_id: str | ComputeShaderType,
93
+ groups_x: int = 1,
94
+ groups_y: int = 1,
95
+ groups_z: int = 1,
96
+ buffers: Optional[dict[int, BufferType | str]] = None,
97
+ wait: bool = True
98
+ ) -> None: ...
99
+
100
+ def read_buffer(self, buffer: BufferType | str, dtype: str = 'f4') -> np.ndarray: ...
101
+ def write_buffer(self, buffer: BufferType | str, data: Any, offset: int = 0) -> None: ...
102
+
103
+ def get_pattr(
104
+ self,
105
+ prog_id: str | ProgramType,
106
+ name: str
107
+ ) -> ProgramAttrType: ...
108
+
109
+ def get_uniform(
110
+ self,
111
+ prog_id: str | ProgramType | ComputeShaderType,
112
+ name: str
113
+ ) -> UniformType: ...
114
+
115
+ def get_pattr_value(self, prog_id: str | ProgramType, name: str) -> Number | pArray: ...
116
+ def set_pattr_value(self, prog_id: str | ProgramType, name: str, value: Any, *, force_write: bool = False) -> None: ...
117
+
118
+ def loop(self) -> None:
119
+ """Main rendering loop"""
120
+ ...
121
+
122
+ def print(
123
+ self,
124
+ text_or_label: str | TextLabel,
125
+ position: VectorType,
126
+ scale: float = 1.0,
127
+ style: TextStyle = DEFAULT_TEXT_STYLE,
128
+ pivot: Pivots|int = Pivots.TOP_LEFT,
129
+ save_cache: bool = False
130
+ ) -> Optional[TextLabel]: ...
131
+
132
+ # Shape drawing methods
133
+ def draw_circle(self, center: VectorType, radius: float, **kwargs: Any) -> None: ...
134
+ def draw_rect(self, position: VectorType, size: VectorType, **kwargs: Any) -> None: ...
135
+ def draw_line(self, start: VectorType, end: VectorType, **kwargs: Any) -> None: ...
136
+ def draw_lines(self, points: Any, **kwargs: Any) -> None: ...
137
+
138
+ def create_circle(self, center: VectorType, radius: float, **kwargs: Any) -> ShapeLabel: ...
139
+ def create_rect(self, position: VectorType, size: VectorType, **kwargs: Any) -> ShapeLabel: ...
140
+ def create_line(self, start: VectorType, end: VectorType, **kwargs: Any) -> ShapeLabel: ...
141
+ def create_lines(self, points: Any, **kwargs: Any) -> ShapeLabel: ...
142
+
143
+ def create_circle_batch(self, max_shapes: int = 10000) -> InstancedShapeBatch: ...
144
+ def create_rect_batch(self, max_shapes: int = 10000) -> InstancedShapeBatch: ...
145
+ def create_line_batch(self, max_shapes: int = 10000) -> InstancedShapeBatch: ...