e2D 2.0.3__tar.gz → 2.1.0__tar.gz

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.
Files changed (55) hide show
  1. {e2d-2.0.3 → e2d-2.1.0}/DEVELOPER_GUIDE.md +4 -33
  2. {e2d-2.0.3/e2D.egg-info → e2d-2.1.0}/PKG-INFO +22 -4
  3. {e2d-2.0.3 → e2d-2.1.0}/README.md +21 -3
  4. {e2d-2.0.3 → e2d-2.1.0}/e2D/__init__.py +180 -70
  5. {e2d-2.0.3 → e2d-2.1.0}/e2D/ccolors.c +152 -152
  6. {e2d-2.0.3 → e2d-2.1.0}/e2D/cvectors.c +1807 -872
  7. {e2d-2.0.3 → e2d-2.1.0}/e2D/cvectors.pyi +6 -1
  8. {e2d-2.0.3 → e2d-2.1.0}/e2D/cvectors.pyx +44 -0
  9. {e2d-2.0.3 → e2d-2.1.0}/e2D/devices.py +15 -14
  10. {e2d-2.0.3 → e2d-2.1.0}/e2D/plots.py +8 -7
  11. {e2d-2.0.3 → e2d-2.1.0}/e2D/shapes.py +20 -19
  12. {e2d-2.0.3 → e2d-2.1.0}/e2D/text_renderer.py +15 -7
  13. {e2d-2.0.3 → e2d-2.1.0}/e2D/types.py +1 -7
  14. {e2d-2.0.3 → e2d-2.1.0}/e2D/types.pyi +0 -5
  15. e2d-2.1.0/e2D/vectors.py +189 -0
  16. {e2d-2.0.3 → e2d-2.1.0/e2D.egg-info}/PKG-INFO +22 -4
  17. {e2d-2.0.3 → e2d-2.1.0}/e2D.egg-info/SOURCES.txt +0 -9
  18. {e2d-2.0.3 → e2d-2.1.0}/setup.cfg +1 -1
  19. e2d-2.0.3/e2D/__init__.pyi +0 -145
  20. e2d-2.0.3/e2D/colors.pyi +0 -104
  21. e2d-2.0.3/e2D/commons.pyi +0 -79
  22. e2d-2.0.3/e2D/devices.pyi +0 -65
  23. e2d-2.0.3/e2D/plots.pyi +0 -238
  24. e2d-2.0.3/e2D/shapes.pyi +0 -272
  25. e2d-2.0.3/e2D/text_renderer.pyi +0 -118
  26. e2d-2.0.3/e2D/vectors.py +0 -339
  27. e2d-2.0.3/e2D/vectors.pyi +0 -106
  28. e2d-2.0.3/e2D/winrec.pyi +0 -87
  29. {e2d-2.0.3 → e2d-2.1.0}/LICENSE +0 -0
  30. {e2d-2.0.3 → e2d-2.1.0}/MANIFEST.in +0 -0
  31. {e2d-2.0.3 → e2d-2.1.0}/docs/API_REFERENCE.md +0 -0
  32. {e2d-2.0.3 → e2d-2.1.0}/e2D/ccolors.pyi +0 -0
  33. {e2d-2.0.3 → e2d-2.1.0}/e2D/ccolors.pyx +0 -0
  34. {e2d-2.0.3 → e2d-2.1.0}/e2D/color_defs.py +0 -0
  35. {e2d-2.0.3 → e2d-2.1.0}/e2D/colors.py +0 -0
  36. {e2d-2.0.3 → e2d-2.1.0}/e2D/commons.py +0 -0
  37. {e2d-2.0.3 → e2d-2.1.0}/e2D/cvectors.pxd +0 -0
  38. {e2d-2.0.3 → e2d-2.1.0}/e2D/shaders/curve_fragment.glsl +0 -0
  39. {e2d-2.0.3 → e2d-2.1.0}/e2D/shaders/curve_vertex.glsl +0 -0
  40. {e2d-2.0.3 → e2d-2.1.0}/e2D/shaders/line_instanced_vertex.glsl +0 -0
  41. {e2d-2.0.3 → e2d-2.1.0}/e2D/shaders/plot_grid_fragment.glsl +0 -0
  42. {e2d-2.0.3 → e2d-2.1.0}/e2D/shaders/plot_grid_vertex.glsl +0 -0
  43. {e2d-2.0.3 → e2d-2.1.0}/e2D/shaders/segment_fragment.glsl +0 -0
  44. {e2d-2.0.3 → e2d-2.1.0}/e2D/shaders/segment_vertex.glsl +0 -0
  45. {e2d-2.0.3 → e2d-2.1.0}/e2D/shaders/stream_fragment.glsl +0 -0
  46. {e2d-2.0.3 → e2d-2.1.0}/e2D/shaders/stream_shift_compute.glsl +0 -0
  47. {e2d-2.0.3 → e2d-2.1.0}/e2D/shaders/stream_vertex.glsl +0 -0
  48. {e2d-2.0.3 → e2d-2.1.0}/e2D/test_colors.py +0 -0
  49. {e2d-2.0.3 → e2d-2.1.0}/e2D/winrec.py +0 -0
  50. {e2d-2.0.3 → e2d-2.1.0}/e2D.egg-info/dependency_links.txt +0 -0
  51. {e2d-2.0.3 → e2d-2.1.0}/e2D.egg-info/not-zip-safe +0 -0
  52. {e2d-2.0.3 → e2d-2.1.0}/e2D.egg-info/requires.txt +0 -0
  53. {e2d-2.0.3 → e2d-2.1.0}/e2D.egg-info/top_level.txt +0 -0
  54. {e2d-2.0.3 → e2d-2.1.0}/pyproject.toml +0 -0
  55. {e2d-2.0.3 → e2d-2.1.0}/setup.py +0 -0
@@ -62,12 +62,10 @@ e2D_2.0/
62
62
  ├── PUBLISHING.md # GitHub Actions publishing guide
63
63
  ├── LICENSE # MIT License
64
64
  ├── new_version.py # Version updater helper script
65
- ├── new_version.bat # Local version update script (optional)
66
- ├── build_dev.bat # Local development build script (optional)
67
- └── SET_TOKEN.bat # Local PyPI token setup (optional, ignored by git)
65
+ └── build_dev.bat # Local development build script (optional)
68
66
  ```
69
67
 
70
- **Note**: `.bat` files are optional local helpers. With GitHub Actions, you don't need them anymore!
68
+ **Note**: `.bat` files are optional local helpers.
71
69
 
72
70
  ## 🔧 Development Workflow
73
71
 
@@ -103,10 +101,6 @@ python setup.py build_ext --inplace
103
101
  # Test basic import
104
102
  python -c "import e2D; print(e2D.__version__)"
105
103
 
106
- # Run examples
107
- python examples/example_usage.py
108
- python examples/compare_performance.py
109
-
110
104
  # Test installation from scratch
111
105
  pip uninstall e2D
112
106
  pip install .
@@ -119,11 +113,6 @@ pip install .
119
113
  python new_version.py
120
114
  ```
121
115
 
122
- Or on Windows:
123
- ```cmd
124
- new_version.bat
125
- ```
126
-
127
116
  This updates:
128
117
  - `setup.cfg` → `version`
129
118
  - `e2D/__init__.py` → `__version__`
@@ -160,25 +149,8 @@ python -c "from e2D import Vector2D; v = Vector2D(1, 2); print(v)"
160
149
 
161
150
  ### 7. Publishing to PyPI
162
151
 
163
- #### Option A: Automated (Windows)
164
- ```cmd
165
- new_version.bat
166
- ```
167
-
168
- This script:
169
- 1. Updates version
170
- 2. Cleans build artifacts
171
- 3. Builds distributions
172
- 4. Uploads to PyPI
173
-
174
- #### Option B: Manual
175
- ```bash
176
- # Upload to PyPI
177
- python -m twine upload dist/*
178
-
179
- # Or upload to TestPyPI first
180
- python -m twine upload --repository testpypi dist/*
181
- ```
152
+ Just push everything in the main e2D folder to this git repo, create a new release and send request to me, it will automatically build it, debug back if errors are found, and if everything works fine it will create a Pypi deployment request to the repo authors (me).
153
+ No build necessary if not for local testing.
182
154
 
183
155
  ## 📋 Pre-Release Checklist
184
156
 
@@ -255,7 +227,6 @@ xcode-select --install
255
227
  ```python
256
228
  import e2D
257
229
  print(f"Version: {e2D.__version__}")
258
- print(f"Compiled: {e2D._VECTORS_COMPILED}")
259
230
  ```
260
231
 
261
232
  ### Benchmarking
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: e2D
3
- Version: 2.0.3
3
+ Version: 2.1.0
4
4
  Summary: High-performance 2D graphics and math library with ultra-optimized vector operations
5
5
  Home-page: https://github.com/marick-py/e2D
6
6
  Author: Riccardo Mariani
@@ -126,6 +126,24 @@ pip install "e2D<2.0"
126
126
  - attrs (required - for data structures)
127
127
  - OpenCV-Python (optional, for recording - install with `[rec]` extra)
128
128
 
129
+ ### Linux-Specific Setup (Fedora/RHEL/CentOS)
130
+
131
+ If you encounter OpenGL library errors on Fedora-based systems:
132
+
133
+ ```bash
134
+ # Install Mesa OpenGL libraries
135
+ sudo dnf install mesa-libGL mesa-libEGL
136
+
137
+ # Create symlinks (ModernGL needs unversioned .so files)
138
+ sudo ln -s /usr/lib64/libGL.so.1 /usr/lib64/libGL.so
139
+ sudo ln -s /usr/lib64/libEGL.so.1 /usr/lib64/libEGL.so
140
+ ```
141
+
142
+ **Note:** On Debian/Ubuntu systems, these symlinks are usually created automatically. If you encounter similar issues:
143
+ ```bash
144
+ sudo apt-get install libgl1-mesa-glx libegl1-mesa
145
+ ```
146
+
129
147
  ## 🚀 Quick Start
130
148
 
131
149
  ### Optimized Vector Operations
@@ -160,6 +178,7 @@ pos_array = vectors_to_array(positions)
160
178
 
161
179
  ```python
162
180
  from e2D import RootEnv, DefEnv
181
+ from e2D.vectors import V2
163
182
 
164
183
  class MyApp(DefEnv):
165
184
  def __init__(self) -> None:
@@ -174,7 +193,7 @@ class MyApp(DefEnv):
174
193
  pass
175
194
 
176
195
  # Initialize and run
177
- rootEnv = RootEnv(window_size=(1920, 1080), target_fps=60)
196
+ rootEnv = RootEnv(window_size=V2(1920, 1080), target_fps=60)
178
197
  rootEnv.init(MyApp())
179
198
 
180
199
  # Optional: Enable screen recording
@@ -228,7 +247,6 @@ Perfect for:
228
247
 
229
248
  - **[API Reference](docs/API_REFERENCE.md)** - Complete API documentation
230
249
  - **[Developer Guide](DEVELOPER_GUIDE.md)** - Development workflow and contributing
231
- - **[Publishing Guide](PUBLISHING.md)** - GitHub Actions automated publishing
232
250
 
233
251
  ## 🎯 Use Cases
234
252
 
@@ -315,7 +333,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
315
333
  ## 📈 Version History
316
334
 
317
335
  ### Version 2.x (ModernGL-based - Current)
318
- - **2.0.3** (Current) - Bug fixes and documentation improvements
336
+ - **2.1.0** (Current) - Bug fixes and documentation improvements
319
337
  - **2.0.0** - Complete rewrite with ModernGL rendering, Cython-optimized vectors, modern color system, screen recording, removed pygame dependency
320
338
 
321
339
  ### Version 1.x (Pygame-based - Legacy)
@@ -76,6 +76,24 @@ pip install "e2D<2.0"
76
76
  - attrs (required - for data structures)
77
77
  - OpenCV-Python (optional, for recording - install with `[rec]` extra)
78
78
 
79
+ ### Linux-Specific Setup (Fedora/RHEL/CentOS)
80
+
81
+ If you encounter OpenGL library errors on Fedora-based systems:
82
+
83
+ ```bash
84
+ # Install Mesa OpenGL libraries
85
+ sudo dnf install mesa-libGL mesa-libEGL
86
+
87
+ # Create symlinks (ModernGL needs unversioned .so files)
88
+ sudo ln -s /usr/lib64/libGL.so.1 /usr/lib64/libGL.so
89
+ sudo ln -s /usr/lib64/libEGL.so.1 /usr/lib64/libEGL.so
90
+ ```
91
+
92
+ **Note:** On Debian/Ubuntu systems, these symlinks are usually created automatically. If you encounter similar issues:
93
+ ```bash
94
+ sudo apt-get install libgl1-mesa-glx libegl1-mesa
95
+ ```
96
+
79
97
  ## 🚀 Quick Start
80
98
 
81
99
  ### Optimized Vector Operations
@@ -110,6 +128,7 @@ pos_array = vectors_to_array(positions)
110
128
 
111
129
  ```python
112
130
  from e2D import RootEnv, DefEnv
131
+ from e2D.vectors import V2
113
132
 
114
133
  class MyApp(DefEnv):
115
134
  def __init__(self) -> None:
@@ -124,7 +143,7 @@ class MyApp(DefEnv):
124
143
  pass
125
144
 
126
145
  # Initialize and run
127
- rootEnv = RootEnv(window_size=(1920, 1080), target_fps=60)
146
+ rootEnv = RootEnv(window_size=V2(1920, 1080), target_fps=60)
128
147
  rootEnv.init(MyApp())
129
148
 
130
149
  # Optional: Enable screen recording
@@ -178,7 +197,6 @@ Perfect for:
178
197
 
179
198
  - **[API Reference](docs/API_REFERENCE.md)** - Complete API documentation
180
199
  - **[Developer Guide](DEVELOPER_GUIDE.md)** - Development workflow and contributing
181
- - **[Publishing Guide](PUBLISHING.md)** - GitHub Actions automated publishing
182
200
 
183
201
  ## 🎯 Use Cases
184
202
 
@@ -265,7 +283,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
265
283
  ## 📈 Version History
266
284
 
267
285
  ### Version 2.x (ModernGL-based - Current)
268
- - **2.0.3** (Current) - Bug fixes and documentation improvements
286
+ - **2.1.0** (Current) - Bug fixes and documentation improvements
269
287
  - **2.0.0** - Complete rewrite with ModernGL rendering, Cython-optimized vectors, modern color system, screen recording, removed pygame dependency
270
288
 
271
289
  ### Version 1.x (Pygame-based - Legacy)
@@ -6,7 +6,7 @@ Copyright (c) 2025 Riccardo Mariani
6
6
  MIT License
7
7
  """
8
8
 
9
- __version__ = "2.0.3"
9
+ __version__ = "2.1.0"
10
10
  __author__ = "Riccardo Mariani"
11
11
  __email__ = "riccardo.mariani@emptyhead.dev"
12
12
 
@@ -18,12 +18,12 @@ import os
18
18
 
19
19
  # Import type definitions
20
20
  from .types import (
21
- ComputeShaderType, ProgramAttrType, UniformType, VectorType, ColorType, Number,
21
+ ComputeShaderType, ProgramAttrType, UniformType, ColorType, Number,
22
22
  ContextType, ProgramType, BufferType, WindowType, pArray
23
23
  )
24
24
 
25
25
  # Import original e2D modules
26
- from .text_renderer import DEFAULT_TEXT_STYLE, Pivots, TextRenderer, TextLabel, TextStyle
26
+ from .text_renderer import DEFAULT_16_TEXT_STYLE, MONO_16_TEXT_STYLE, Pivots, TextRenderer, TextLabel, TextStyle
27
27
  from .shapes import ShapeRenderer, ShapeLabel, InstancedShapeBatch, FillMode
28
28
  from .devices import Keyboard, Mouse, KeyState
29
29
  from .commons import get_pattr, get_pattr_value, set_pattr_value, get_uniform
@@ -43,7 +43,7 @@ from .color_defs import (
43
43
 
44
44
  # Try to import Cython-optimized color operations (optional batch utilities)
45
45
  try:
46
- from . import ccolors # type: ignore[import-not-found]
46
+ from . import ccolors
47
47
  _COLOR_COMPILED = True
48
48
  except ImportError:
49
49
  _COLOR_COMPILED = False
@@ -61,7 +61,6 @@ from .vectors import (
61
61
  lerp,
62
62
  create_grid,
63
63
  create_circle,
64
- _COMPILED as _VECTOR_COMPILED,
65
64
  )
66
65
 
67
66
 
@@ -76,8 +75,9 @@ class DefEnv:
76
75
  def on_resize(self, width: int, height: int) -> None: ...
77
76
 
78
77
  class RootEnv:
79
- window_size: VectorType
78
+ window_size: Vector2D
80
79
  target_fps: int
80
+ draw_fps: bool
81
81
  window: WindowType
82
82
  ctx: ContextType
83
83
  programs: dict[str, ProgramType]
@@ -94,11 +94,13 @@ class RootEnv:
94
94
 
95
95
  def __init__(
96
96
  self,
97
- window_size: tuple[int, int] = (1920, 1080),
97
+ window_size: Vector2D = V2(1920, 1080),
98
98
  target_fps: int = 60,
99
- vsync: bool = True,
99
+ vsync: bool = False,
100
+ resizable: bool = True,
100
101
  version: tuple[int, int] = (4, 3),
101
- monitor: Optional[int] = None
102
+ monitor: Optional[int] = None,
103
+ draw_fps: bool = False,
102
104
  ) -> None:
103
105
 
104
106
  if not glfw.init():
@@ -108,15 +110,25 @@ class RootEnv:
108
110
  glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, version[1])
109
111
  glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
110
112
  glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, True)
111
- glfw.window_hint(glfw.RESIZABLE, True)
113
+ glfw.window_hint(glfw.RESIZABLE, resizable)
112
114
 
113
115
  self.window_size = window_size
114
116
  self.target_fps = target_fps
117
+ self.draw_fps = draw_fps
118
+ self._resizable = resizable
119
+ self._vsync = vsync
115
120
 
116
- self.window = glfw.create_window(window_size[0], window_size[1], "e2D", monitor, None)
121
+ self.window = glfw.create_window(int(window_size[0]), int(window_size[1]), "e2D", monitor, None)
117
122
  if not self.window:
118
123
  glfw.terminate()
119
124
  raise RuntimeError("Failed to create GLFW window")
125
+
126
+ # Center window on screen by default
127
+ if monitor is None:
128
+ video_mode = glfw.get_video_mode(glfw.get_primary_monitor())
129
+ window_w, window_h = int(window_size[0]), int(window_size[1])
130
+ screen_w, screen_h = video_mode.size.width, video_mode.size.height
131
+ glfw.set_window_pos(self.window, (screen_w - window_w) // 2, (screen_h - window_h) // 2)
120
132
 
121
133
  glfw.make_context_current(self.window)
122
134
 
@@ -128,10 +140,7 @@ class RootEnv:
128
140
  raise
129
141
 
130
142
  # VSync control - must be set AFTER context creation
131
- if vsync:
132
- glfw.swap_interval(1)
133
- else:
134
- glfw.swap_interval(0)
143
+ glfw.swap_interval(1 if vsync else 0)
135
144
 
136
145
  print(f"OpenGL Context: {self.ctx.version_code} / {self.ctx.info['GL_RENDERER']}")
137
146
 
@@ -165,13 +174,88 @@ class RootEnv:
165
174
  """Get window size as floats for shader uniforms."""
166
175
  return (float(self.window_size[0]), float(self.window_size[1]))
167
176
 
177
+ @property
178
+ def window_position(self) -> Vector2D:
179
+ """Get current window position."""
180
+ pos = glfw.get_window_pos(self.window)
181
+ return V2(float(pos[0]), float(pos[1]))
182
+
183
+ @property
184
+ def resizable(self) -> bool:
185
+ """Get window resizable state."""
186
+ return self._resizable
187
+
188
+ @resizable.setter
189
+ def resizable(self, value: bool) -> None:
190
+ """Set window resizable state."""
191
+ self._resizable = value
192
+ glfw.set_window_attrib(self.window, glfw.RESIZABLE, glfw.TRUE if value else glfw.FALSE)
193
+
194
+ @property
195
+ def vsync(self) -> bool:
196
+ """Get VSync state."""
197
+ return self._vsync
198
+
199
+ @vsync.setter
200
+ def vsync(self, value: bool) -> None:
201
+ """Set VSync state. Changes take effect immediately."""
202
+ self._vsync = value
203
+ glfw.swap_interval(1 if value else 0)
204
+
168
205
  @property
169
206
  def runtime(self) -> float:
170
207
  """Get total elapsed time since program initialization in seconds."""
171
208
  return time.perf_counter() - self.start_time
172
209
 
210
+ def set_window_size(self, width: int | float, height: int | float) -> None:
211
+ """Set window size. Use this instead of modifying window_size directly.
212
+
213
+ Args:
214
+ width: New window width
215
+ height: New window height
216
+ """
217
+ width, height = int(width), int(height)
218
+ glfw.set_window_size(self.window, width, height)
219
+ self.window_size.set(width, height)
220
+
221
+ def set_window_position(self, x: int | float, y: int | float) -> None:
222
+ """Set window position on screen.
223
+
224
+ Args:
225
+ x: X position (left edge)
226
+ y: Y position (top edge)
227
+ """
228
+ glfw.set_window_pos(self.window, int(x), int(y))
229
+
230
+ def center_window(self) -> None:
231
+ """Center the window on the primary monitor."""
232
+ video_mode = glfw.get_video_mode(glfw.get_primary_monitor())
233
+ window_w, window_h = int(self.window_size[0]), int(self.window_size[1])
234
+ screen_w, screen_h = video_mode.size.width, video_mode.size.height
235
+ self.set_window_position((screen_w - window_w) // 2, (screen_h - window_h) // 2)
236
+
237
+ def set_window_title(self, title: str) -> None:
238
+ """Set window title.
239
+
240
+ Args:
241
+ title: New window title
242
+ """
243
+ glfw.set_window_title(self.window, title)
244
+
245
+ def maximize_window(self) -> None:
246
+ """Maximize the window."""
247
+ glfw.maximize_window(self.window)
248
+
249
+ def minimize_window(self) -> None:
250
+ """Minimize (iconify) the window."""
251
+ glfw.iconify_window(self.window)
252
+
253
+ def restore_window(self) -> None:
254
+ """Restore the window from maximized or minimized state."""
255
+ glfw.restore_window(self.window)
256
+
173
257
  def _on_resize(self, window: WindowType, width: int, height: int) -> None:
174
- self.window_size = (width, height)
258
+ self.window_size.set(width, height)
175
259
  fb_size = glfw.get_framebuffer_size(window)
176
260
  self.ctx.viewport = (0, 0, fb_size[0], fb_size[1])
177
261
  if hasattr(self, 'env') and hasattr(self.env, 'on_resize'):
@@ -181,35 +265,11 @@ class RootEnv:
181
265
  self.env = env
182
266
  return self
183
267
 
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
- """
268
+ def init_rec(self, fps: int = 30, draw_on_screen: bool = True, path: str = 'output.mp4') -> None:
200
269
  from .winrec import WinRec
201
270
  self.__winrecorder__ = WinRec(self, fps=fps, draw_on_screen=draw_on_screen, path=path)
202
- return self
203
271
 
204
272
  def load_shader_file(self, path: str) -> str:
205
- """Load shader source code from a file.
206
-
207
- Args:
208
- path: Path to shader file (relative to working directory or absolute)
209
-
210
- Returns:
211
- Shader source code as string
212
- """
213
273
  if not os.path.exists(path):
214
274
  raise FileNotFoundError(f"Shader file not found: {path}")
215
275
 
@@ -247,6 +307,10 @@ class RootEnv:
247
307
  def __draw__(self) -> None:
248
308
  self.ctx.clear(0.0, 0.0, 0.0, 1.0)
249
309
  self.env.draw()
310
+
311
+ if self.draw_fps:
312
+ fps = 1.0 / self.delta if self.delta > 0 else 0.0
313
+ self.print(f"FPS: {fps:.2f}", V2(10, 10), scale=1.0, style=MONO_16_TEXT_STYLE, pivot=Pivots.TOP_LEFT)
250
314
 
251
315
  # Screen recording: capture frame before overlay, draw stats after
252
316
  if hasattr(self, '__winrecorder__'):
@@ -453,9 +517,9 @@ class RootEnv:
453
517
  def print(
454
518
  self,
455
519
  text_or_label: str|TextLabel,
456
- position: VectorType,
520
+ position: Vector2D,
457
521
  scale: float = 1.0,
458
- style: TextStyle = DEFAULT_TEXT_STYLE,
522
+ style: TextStyle = MONO_16_TEXT_STYLE,
459
523
  pivot: Pivots|int = Pivots.TOP_LEFT,
460
524
  save_cache: bool = False
461
525
  ) -> Optional[TextLabel]:
@@ -464,43 +528,89 @@ class RootEnv:
464
528
  text_or_label.draw()
465
529
  else:
466
530
  if save_cache:
467
- return self.text_renderer.create_label(str(text_or_label), position[0], position[1], scale, style, pivot)
531
+ return self.text_renderer.create_label(str(text_or_label), position.x, position.y, scale, style, pivot)
468
532
  else:
469
- self.text_renderer.draw_text(str(text_or_label), position, scale, style, pivot)
533
+ self.text_renderer.draw_text(str(text_or_label), (position.x, position.y), scale, style, pivot)
470
534
 
471
535
  # ========== Shape Drawing Methods ==========
472
536
 
473
- def draw_circle(self, center: VectorType, radius: float, **kwargs) -> None:
537
+ def draw_circle(self, center: Vector2D, radius: float,
538
+ color: ColorType = (1.0, 1.0, 1.0, 1.0),
539
+ rotation: float = 0.0,
540
+ border_color: ColorType = (0.0, 0.0, 0.0, 0.0),
541
+ border_width: float = 0.0,
542
+ antialiasing: float = 1.0) -> None:
474
543
  """Draw a circle. See ShapeRenderer.draw_circle for parameters."""
475
- self.shape_renderer.draw_circle(center, radius, **kwargs)
476
-
477
- def draw_rect(self, position: VectorType, size: VectorType, **kwargs) -> None:
544
+ self.shape_renderer.draw_circle(center, radius, color=color, rotation=rotation,
545
+ border_color=border_color, border_width=border_width,
546
+ antialiasing=antialiasing)
547
+
548
+ def draw_rect(self, position: Vector2D, size: Vector2D,
549
+ color: ColorType = (1.0, 1.0, 1.0, 1.0),
550
+ rotation: float = 0.0,
551
+ corner_radius: float = 0.0,
552
+ border_color: ColorType = (0.0, 0.0, 0.0, 0.0),
553
+ border_width: float = 0.0,
554
+ antialiasing: float = 1.0) -> None:
478
555
  """Draw a rectangle. See ShapeRenderer.draw_rect for parameters."""
479
- self.shape_renderer.draw_rect(position, size, **kwargs)
480
-
481
- def draw_line(self, start: VectorType, end: VectorType, **kwargs) -> None:
556
+ self.shape_renderer.draw_rect(position, size, color=color, rotation=rotation,
557
+ corner_radius=corner_radius, border_color=border_color,
558
+ border_width=border_width, antialiasing=antialiasing)
559
+
560
+ def draw_line(self, start: Vector2D, end: Vector2D,
561
+ width: float = 1.0,
562
+ color: ColorType = (1.0, 1.0, 1.0, 1.0),
563
+ antialiasing: float = 1.0) -> None:
482
564
  """Draw a line. See ShapeRenderer.draw_line for parameters."""
483
- self.shape_renderer.draw_line(start, end, **kwargs)
484
-
485
- def draw_lines(self, points, **kwargs) -> None:
565
+ self.shape_renderer.draw_line((start.x, start.y), (end.x, end.y), width=width, color=color,
566
+ antialiasing=antialiasing)
567
+
568
+ def draw_lines(self, points,
569
+ width: float = 1.0,
570
+ color: ColorType = (1.0, 1.0, 1.0, 1.0),
571
+ antialiasing: float = 1.0,
572
+ closed: bool = False) -> None:
486
573
  """Draw a polyline. See ShapeRenderer.draw_lines for parameters."""
487
- self.shape_renderer.draw_lines(points, **kwargs)
488
-
489
- def create_circle(self, center: VectorType, radius: float, **kwargs) -> ShapeLabel:
574
+ self.shape_renderer.draw_lines(points, width=width, color=color,
575
+ antialiasing=antialiasing, closed=closed)
576
+
577
+ def create_circle(self, center: Vector2D, radius: float,
578
+ color: ColorType = (1.0, 1.0, 1.0, 1.0),
579
+ rotation: float = 0.0,
580
+ border_color: ColorType = (0.0, 0.0, 0.0, 0.0),
581
+ border_width: float = 0.0,
582
+ antialiasing: float = 1.0) -> ShapeLabel:
490
583
  """Create a cached circle. See ShapeRenderer.create_circle for parameters."""
491
- return self.shape_renderer.create_circle(center, radius, **kwargs)
492
-
493
- def create_rect(self, position: VectorType, size: VectorType, **kwargs) -> ShapeLabel:
584
+ return self.shape_renderer.create_circle(center, radius, color=color, rotation=rotation,
585
+ border_color=border_color, border_width=border_width,
586
+ antialiasing=antialiasing)
587
+
588
+ def create_rect(self, position: Vector2D, size: Vector2D,
589
+ color: ColorType = (1.0, 1.0, 1.0, 1.0),
590
+ rotation: float = 0.0,
591
+ corner_radius: float = 0.0,
592
+ border_color: ColorType = (0.0, 0.0, 0.0, 0.0),
593
+ border_width: float = 0.0,
594
+ antialiasing: float = 1.0) -> ShapeLabel:
494
595
  """Create a cached rectangle. See ShapeRenderer.create_rect for parameters."""
495
- return self.shape_renderer.create_rect(position, size, **kwargs)
596
+ return self.shape_renderer.create_rect(position, size, color=color, rotation=rotation, corner_radius=corner_radius, border_color=border_color, border_width=border_width, antialiasing=antialiasing)
496
597
 
497
- def create_line(self, start: VectorType, end: VectorType, **kwargs) -> ShapeLabel:
598
+ def create_line(self, start: Vector2D, end: Vector2D,
599
+ width: float = 1.0,
600
+ color: ColorType = (1.0, 1.0, 1.0, 1.0),
601
+ antialiasing: float = 1.0) -> ShapeLabel:
498
602
  """Create a cached line. See ShapeRenderer.create_line for parameters."""
499
- return self.shape_renderer.create_line(start, end, **kwargs)
500
-
501
- def create_lines(self, points, **kwargs) -> ShapeLabel:
603
+ return self.shape_renderer.create_line(start, end, width=width, color=color,
604
+ antialiasing=antialiasing)
605
+
606
+ def create_lines(self, points,
607
+ width: float = 1.0,
608
+ color: ColorType = (1.0, 1.0, 1.0, 1.0),
609
+ antialiasing: float = 1.0,
610
+ closed: bool = False) -> ShapeLabel:
502
611
  """Create a cached polyline. See ShapeRenderer.create_lines for parameters."""
503
- return self.shape_renderer.create_lines(points, **kwargs)
612
+ return self.shape_renderer.create_lines(points, width=width, color=color,
613
+ antialiasing=antialiasing, closed=closed)
504
614
 
505
615
  def create_circle_batch(self, max_shapes: int = 10000) -> InstancedShapeBatch:
506
616
  """Create a batch for drawing multiple circles using GPU instancing."""
@@ -537,7 +647,7 @@ __all__ = [
537
647
  'create_grid',
538
648
  'create_circle',
539
649
  # Type aliases
540
- 'VectorType',
650
+ 'Vector2D',
541
651
  'ColorType',
542
652
  # Color utilities
543
653
  'Color',
@@ -567,7 +677,8 @@ __all__ = [
567
677
  'TextLabel',
568
678
  'TextStyle',
569
679
  'Pivots',
570
- 'DEFAULT_TEXT_STYLE',
680
+ 'DEFAULT_16_TEXT_STYLE',
681
+ 'MONO_16_TEXT_STYLE',
571
682
  # Shape rendering
572
683
  'ShapeRenderer',
573
684
  'ShapeLabel',
@@ -583,6 +694,5 @@ __all__ = [
583
694
  'set_pattr_value',
584
695
  'get_uniform',
585
696
  # Compilation flags
586
- '_VECTOR_COMPILED',
587
697
  '_COLOR_COMPILED',
588
698
  ]