glplot 0.1.7__tar.gz → 0.1.8__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 (186) hide show
  1. {glplot-0.1.7 → glplot-0.1.8}/CITATION.cff +12 -7
  2. {glplot-0.1.7 → glplot-0.1.8}/PKG-INFO +39 -21
  3. {glplot-0.1.7 → glplot-0.1.8}/README.md +33 -15
  4. {glplot-0.1.7 → glplot-0.1.8}/glplot/__init__.py +12 -0
  5. {glplot-0.1.7 → glplot-0.1.8}/glplot/anim/processes.py +5 -0
  6. {glplot-0.1.7 → glplot-0.1.8}/glplot/engine.py +15 -6
  7. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/actions.py +1 -1
  8. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/icons.py +28 -29
  9. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/keys.py +84 -17
  10. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/mathops.py +6 -1
  11. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/annotate.py +53 -57
  12. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/data_editor.py +81 -86
  13. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/dynamics.py +9 -7
  14. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/functions.py +16 -15
  15. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/help.py +21 -23
  16. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/mathlab.py +19 -19
  17. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/objects3d.py +4 -5
  18. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/palette.py +37 -42
  19. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/pipeline.py +17 -17
  20. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/presentation.py +7 -7
  21. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/scene.py +26 -37
  22. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/selection.py +10 -13
  23. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/style.py +70 -56
  24. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/timeline.py +28 -23
  25. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/view3d.py +1 -1
  26. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/theme.py +60 -61
  27. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/widgets.py +64 -60
  28. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/workspace.py +36 -29
  29. {glplot-0.1.7 → glplot-0.1.8}/glplot/managers/hud.py +68 -56
  30. {glplot-0.1.7 → glplot-0.1.8}/glplot/policy.py +1 -1
  31. {glplot-0.1.7 → glplot-0.1.8}/glplot/pyplot.py +286 -15
  32. {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/axes3d.py +6 -6
  33. {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/axis.py +37 -58
  34. {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/legend.py +35 -38
  35. {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/text.py +5 -5
  36. {glplot-0.1.7 → glplot-0.1.8}/pyproject.toml +10 -8
  37. {glplot-0.1.7 → glplot-0.1.8}/tests/conftest.py +1 -1
  38. {glplot-0.1.7 → glplot-0.1.8}/tests/test_anim_processes.py +9 -2
  39. {glplot-0.1.7 → glplot-0.1.8}/tests/test_animation_api.py +1 -1
  40. {glplot-0.1.7 → glplot-0.1.8}/tests/test_axes.py +12 -13
  41. {glplot-0.1.7 → glplot-0.1.8}/tests/test_axes3d.py +7 -8
  42. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_3d_panels.py +2 -3
  43. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_actions.py +63 -5
  44. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_annotate.py +6 -4
  45. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_import_safety.py +31 -13
  46. glplot-0.1.8/tests/test_gui_input_regressions.py +183 -0
  47. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_keys.py +71 -14
  48. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_legend.py +11 -15
  49. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_mathlab.py +13 -12
  50. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_pipeline.py +4 -6
  51. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_scene_delete.py +2 -3
  52. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_style_layer_editor.py +3 -4
  53. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_timeline_panel.py +7 -5
  54. {glplot-0.1.7 → glplot-0.1.8}/tests/test_mpl_parity_surface.py +7 -0
  55. {glplot-0.1.7 → glplot-0.1.8}/tests/test_objects3d_preview.py +9 -7
  56. {glplot-0.1.7 → glplot-0.1.8}/.gitignore +0 -0
  57. {glplot-0.1.7 → glplot-0.1.8}/CHANGELOG.md +0 -0
  58. {glplot-0.1.7 → glplot-0.1.8}/LICENSE +0 -0
  59. {glplot-0.1.7 → glplot-0.1.8}/glplot/__main__.py +0 -0
  60. {glplot-0.1.7 → glplot-0.1.8}/glplot/anim/__init__.py +0 -0
  61. {glplot-0.1.7 → glplot-0.1.8}/glplot/anim/applier.py +0 -0
  62. {glplot-0.1.7 → glplot-0.1.8}/glplot/anim/primitives.py +0 -0
  63. {glplot-0.1.7 → glplot-0.1.8}/glplot/animation.py +0 -0
  64. {glplot-0.1.7 → glplot-0.1.8}/glplot/backend.py +0 -0
  65. {glplot-0.1.7 → glplot-0.1.8}/glplot/controllers.py +0 -0
  66. {glplot-0.1.7 → glplot-0.1.8}/glplot/core/__init__.py +0 -0
  67. {glplot-0.1.7 → glplot-0.1.8}/glplot/core/camera3d.py +0 -0
  68. {glplot-0.1.7 → glplot-0.1.8}/glplot/core/context.py +0 -0
  69. {glplot-0.1.7 → glplot-0.1.8}/glplot/core/layers.py +0 -0
  70. {glplot-0.1.7 → glplot-0.1.8}/glplot/core/layout.py +0 -0
  71. {glplot-0.1.7 → glplot-0.1.8}/glplot/core/legacy.py +0 -0
  72. {glplot-0.1.7 → glplot-0.1.8}/glplot/core/panel.py +0 -0
  73. {glplot-0.1.7 → glplot-0.1.8}/glplot/core/timeline.py +0 -0
  74. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/__init__.py +0 -0
  75. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/app.py +0 -0
  76. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/clipboard.py +0 -0
  77. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/commands.py +0 -0
  78. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/dataio.py +0 -0
  79. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/datasets.py +0 -0
  80. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/dynamics.py +0 -0
  81. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/expressions.py +0 -0
  82. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/fuzzy.py +0 -0
  83. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/generators3d.py +0 -0
  84. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/history.py +0 -0
  85. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/layerops.py +0 -0
  86. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/layerops3d.py +0 -0
  87. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/__init__.py +0 -0
  88. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/base.py +0 -0
  89. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/pipeline.py +0 -0
  90. {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/styles.py +0 -0
  91. {glplot-0.1.7 → glplot-0.1.8}/glplot/managers/__init__.py +0 -0
  92. {glplot-0.1.7 → glplot-0.1.8}/glplot/managers/axis.py +0 -0
  93. {glplot-0.1.7 → glplot-0.1.8}/glplot/managers/effects.py +0 -0
  94. {glplot-0.1.7 → glplot-0.1.8}/glplot/managers/hud_state.py +0 -0
  95. {glplot-0.1.7 → glplot-0.1.8}/glplot/managers/picking.py +0 -0
  96. {glplot-0.1.7 → glplot-0.1.8}/glplot/managers/renderer_manager.py +0 -0
  97. {glplot-0.1.7 → glplot-0.1.8}/glplot/options.py +0 -0
  98. {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/__init__.py +0 -0
  99. {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/base.py +0 -0
  100. {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/density.py +0 -0
  101. {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/exact.py +0 -0
  102. {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/fractal.py +0 -0
  103. {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/geometry3d.py +0 -0
  104. {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/interaction.py +0 -0
  105. {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/line_family.py +0 -0
  106. {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/patch.py +0 -0
  107. {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/polyline.py +0 -0
  108. {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/scatter.py +0 -0
  109. {glplot-0.1.7 → glplot-0.1.8}/glplot/utils/__init__.py +0 -0
  110. {glplot-0.1.7 → glplot-0.1.8}/glplot/utils/anim_export.py +0 -0
  111. {glplot-0.1.7 → glplot-0.1.8}/glplot/utils/blending.py +0 -0
  112. {glplot-0.1.7 → glplot-0.1.8}/glplot/utils/export.py +0 -0
  113. {glplot-0.1.7 → glplot-0.1.8}/glplot/utils/gl_utils.py +0 -0
  114. {glplot-0.1.7 → glplot-0.1.8}/glplot/utils/mpl_bridge.py +0 -0
  115. {glplot-0.1.7 → glplot-0.1.8}/glplot/utils/mpl_process.py +0 -0
  116. {glplot-0.1.7 → glplot-0.1.8}/glplot/utils/preview.py +0 -0
  117. {glplot-0.1.7 → glplot-0.1.8}/glplot/utils/scale.py +0 -0
  118. {glplot-0.1.7 → glplot-0.1.8}/glplot/utils/shaders.py +0 -0
  119. {glplot-0.1.7 → glplot-0.1.8}/tests/test_anim_applier.py +0 -0
  120. {glplot-0.1.7 → glplot-0.1.8}/tests/test_anim_export.py +0 -0
  121. {glplot-0.1.7 → glplot-0.1.8}/tests/test_anim_primitives.py +0 -0
  122. {glplot-0.1.7 → glplot-0.1.8}/tests/test_animation_integration.py +0 -0
  123. {glplot-0.1.7 → glplot-0.1.8}/tests/test_api_consistency.py +0 -0
  124. {glplot-0.1.7 → glplot-0.1.8}/tests/test_axis_margins.py +0 -0
  125. {glplot-0.1.7 → glplot-0.1.8}/tests/test_blending_3d.py +0 -0
  126. {glplot-0.1.7 → glplot-0.1.8}/tests/test_camera3d.py +0 -0
  127. {glplot-0.1.7 → glplot-0.1.8}/tests/test_camera_anisotropy.py +0 -0
  128. {glplot-0.1.7 → glplot-0.1.8}/tests/test_camera_projections.py +0 -0
  129. {glplot-0.1.7 → glplot-0.1.8}/tests/test_compositing_3d.py +0 -0
  130. {glplot-0.1.7 → glplot-0.1.8}/tests/test_compositing_api.py +0 -0
  131. {glplot-0.1.7 → glplot-0.1.8}/tests/test_dataio.py +0 -0
  132. {glplot-0.1.7 → glplot-0.1.8}/tests/test_density_tint.py +0 -0
  133. {glplot-0.1.7 → glplot-0.1.8}/tests/test_edge_cases.py +0 -0
  134. {glplot-0.1.7 → glplot-0.1.8}/tests/test_effects_post.py +0 -0
  135. {glplot-0.1.7 → glplot-0.1.8}/tests/test_encodings.py +0 -0
  136. {glplot-0.1.7 → glplot-0.1.8}/tests/test_engine.py +0 -0
  137. {glplot-0.1.7 → glplot-0.1.8}/tests/test_export.py +0 -0
  138. {glplot-0.1.7 → glplot-0.1.8}/tests/test_fractal.py +0 -0
  139. {glplot-0.1.7 → glplot-0.1.8}/tests/test_function_layer.py +0 -0
  140. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gallery_integration.py +0 -0
  141. {glplot-0.1.7 → glplot-0.1.8}/tests/test_generators3d.py +0 -0
  142. {glplot-0.1.7 → glplot-0.1.8}/tests/test_generators3d_extra.py +0 -0
  143. {glplot-0.1.7 → glplot-0.1.8}/tests/test_geometry3d.py +0 -0
  144. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_clipboard.py +0 -0
  145. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_commands.py +0 -0
  146. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_data_editor.py +0 -0
  147. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_datasets.py +0 -0
  148. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_dynamics.py +0 -0
  149. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_engine_integration.py +0 -0
  150. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_expressions.py +0 -0
  151. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_fields.py +0 -0
  152. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_fuzzy.py +0 -0
  153. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_history.py +0 -0
  154. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_impostor_alignment.py +0 -0
  155. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_keyboard_capture.py +0 -0
  156. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_layer_colormaps.py +0 -0
  157. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_layer_kinds.py +0 -0
  158. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_layer_params.py +0 -0
  159. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_mathops.py +0 -0
  160. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_regression_r2.py +0 -0
  161. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_scene_order.py +0 -0
  162. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_styles.py +0 -0
  163. {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_styles_extra.py +0 -0
  164. {glplot-0.1.7 → glplot-0.1.8}/tests/test_helpers.py +0 -0
  165. {glplot-0.1.7 → glplot-0.1.8}/tests/test_impostor_scatter_fidelity.py +0 -0
  166. {glplot-0.1.7 → glplot-0.1.8}/tests/test_layerops3d.py +0 -0
  167. {glplot-0.1.7 → glplot-0.1.8}/tests/test_layers.py +0 -0
  168. {glplot-0.1.7 → glplot-0.1.8}/tests/test_mpl_bridge.py +0 -0
  169. {glplot-0.1.7 → glplot-0.1.8}/tests/test_mpl_compat.py +0 -0
  170. {glplot-0.1.7 → glplot-0.1.8}/tests/test_mpl_compat_3d.py +0 -0
  171. {glplot-0.1.7 → glplot-0.1.8}/tests/test_mpl_process.py +0 -0
  172. {glplot-0.1.7 → glplot-0.1.8}/tests/test_options.py +0 -0
  173. {glplot-0.1.7 → glplot-0.1.8}/tests/test_outline.py +0 -0
  174. {glplot-0.1.7 → glplot-0.1.8}/tests/test_outline_2d.py +0 -0
  175. {glplot-0.1.7 → glplot-0.1.8}/tests/test_panels.py +0 -0
  176. {glplot-0.1.7 → glplot-0.1.8}/tests/test_performance_benchmarks.py +0 -0
  177. {glplot-0.1.7 → glplot-0.1.8}/tests/test_plot_style_api.py +0 -0
  178. {glplot-0.1.7 → glplot-0.1.8}/tests/test_polyline_lod.py +0 -0
  179. {glplot-0.1.7 → glplot-0.1.8}/tests/test_pyplot.py +0 -0
  180. {glplot-0.1.7 → glplot-0.1.8}/tests/test_pyplot_3d.py +0 -0
  181. {glplot-0.1.7 → glplot-0.1.8}/tests/test_pyplot_integration.py +0 -0
  182. {glplot-0.1.7 → glplot-0.1.8}/tests/test_regression.py +0 -0
  183. {glplot-0.1.7 → glplot-0.1.8}/tests/test_robustness.py +0 -0
  184. {glplot-0.1.7 → glplot-0.1.8}/tests/test_scatter_renderer.py +0 -0
  185. {glplot-0.1.7 → glplot-0.1.8}/tests/test_selection_model.py +0 -0
  186. {glplot-0.1.7 → glplot-0.1.8}/tests/test_timeline.py +0 -0
@@ -1,21 +1,26 @@
1
1
  cff-version: 1.2.0
2
2
  title: GLPlot - High-Performance GPU-Accelerated Plotting Library for Python
3
- message: "If you use GLPlot in your research, please cite it as below. Scientific paper with full benchmarks and technical details is in preparation."
3
+ message: >-
4
+ If you use GLPlot in your research, please cite it as below. Felix Riccius, Julian
5
+ Holland and Gianmarco Ducci contributed equally to this work.
4
6
  type: software
5
7
  authors:
6
8
  - family-names: Lombardi
7
9
  given-names: Juan Manuel
8
10
  email: lombardi@fhi-berlin.mpg.de
9
11
  affiliation: "Fritz Haber Institute of the Max Planck Society"
10
- orcid: "https://orcid.org/0000-0000-0000-0000"
12
+ orcid: "https://orcid.org/0009-0002-3961-1426"
13
+ - family-names: Riccius
14
+ given-names: Felix
15
+ - family-names: Holland
16
+ given-names: Julian
17
+ - family-names: Ducci
18
+ given-names: Gianmarco
11
19
  identifiers:
12
20
  - type: url
13
21
  value: "https://github.com/AkarisDimitry/GLPlot"
14
22
  - type: url
15
23
  value: "https://gitlab.mpcdf.mpg.de/jmlombardi/glplot"
16
- - type: doi
17
- description: "Zenodo deposit (pending - will be assigned upon publication)"
18
- value: "10.5281/zenodo.pending"
19
24
  repository-code: "https://github.com/AkarisDimitry/GLPlot"
20
25
  repository: "https://github.com/AkarisDimitry/GLPlot"
21
26
  url: "https://github.com/AkarisDimitry/GLPlot"
@@ -41,6 +46,6 @@ keywords:
41
46
  - data visualization
42
47
  license: MIT
43
48
  status: "active development"
44
- version: 0.1.3
45
- date-released: 2026-07-07
49
+ version: 0.1.7
50
+ date-released: 2026-08-18
46
51
  development-status: "Beta"
@@ -1,10 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: glplot
3
- Version: 0.1.7
3
+ Version: 0.1.8
4
4
  Summary: High-performance OpenGL plotting library for Python
5
5
  Project-URL: Homepage, https://github.com/AkarisDimitry/GLPlot
6
6
  Project-URL: Repository, https://github.com/AkarisDimitry/GLPlot
7
7
  Project-URL: Issues, https://github.com/AkarisDimitry/GLPlot/issues
8
+ Author: Felix Riccius, Julian Holland, Gianmarco Ducci
8
9
  Author-email: Juan Manuel Lombardi <lombardi@fhi-berlin.mpg.de>
9
10
  License: MIT
10
11
  License-File: LICENSE
@@ -15,16 +16,15 @@ Classifier: Intended Audience :: Science/Research
15
16
  Classifier: License :: OSI Approved :: MIT License
16
17
  Classifier: Operating System :: OS Independent
17
18
  Classifier: Programming Language :: Python :: 3
18
- Classifier: Programming Language :: Python :: 3.9
19
- Classifier: Programming Language :: Python :: 3.10
20
19
  Classifier: Programming Language :: Python :: 3.11
21
20
  Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
22
  Classifier: Topic :: Scientific/Engineering :: Visualization
23
23
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
- Requires-Python: >=3.9
24
+ Requires-Python: >=3.11
25
25
  Requires-Dist: glfw>=2.5
26
- Requires-Dist: imgui[glfw]>=2.0
27
- Requires-Dist: matplotlib>=3.6
26
+ Requires-Dist: imgui-bundle>=1.92
27
+ Requires-Dist: matplotlib<3.10,>=3.6
28
28
  Requires-Dist: numpy>=1.23
29
29
  Requires-Dist: pyopengl>=3.1.6
30
30
  Requires-Dist: scipy>=1.9
@@ -46,7 +46,7 @@ Description-Content-Type: text/markdown
46
46
  [![Lint](https://github.com/AkarisDimitry/GLPlot/workflows/Lint/badge.svg)](https://github.com/AkarisDimitry/GLPlot/actions/workflows/lint.yml)
47
47
  [![Build](https://github.com/AkarisDimitry/GLPlot/workflows/Build/badge.svg)](https://github.com/AkarisDimitry/GLPlot/actions/workflows/build.yml)
48
48
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
49
- [![Python Version](https://img.shields.io/badge/Python-3.9+-blue.svg)](pyproject.toml)
49
+ [![Python Version](https://img.shields.io/badge/Python-3.11+-blue.svg)](pyproject.toml)
50
50
 
51
51
  GLPlot is a Python plotting library with a Matplotlib-like API that renders on the GPU
52
52
  through OpenGL instead of the CPU. Plots that make Matplotlib chug — millions of points,
@@ -68,8 +68,8 @@ cd GLPlot
68
68
  pip install -e .
69
69
  ```
70
70
 
71
- Requires Python 3.9+. Core dependencies: numpy, scipy, matplotlib, glfw, PyOpenGL,
72
- `imgui[glfw]` (for the on-screen control panel).
71
+ Requires Python 3.11+. Core dependencies: numpy, scipy, matplotlib, glfw, PyOpenGL,
72
+ `imgui-bundle` (for the on-screen control panel).
73
73
 
74
74
  ## 30-second example
75
75
 
@@ -92,16 +92,18 @@ That's it — a real window opens, fully interactive (pan/zoom/rotate) from the
92
92
 
93
93
  ## Interactive Examples Gallery (Click any figure to view code)
94
94
 
95
- Explore 2D plots, 3D scenes, and real-time animations. **Click on any figure or title below to expand its full Python code.**
95
+ Explore 2D plots, 3D scenes, and real-time animations. **Click on any figure or title below to expand its full Python code, or click the file link to view the script.**
96
96
 
97
97
  ### 📊 2D Visualizations
98
98
 
99
99
  <details>
100
100
  <summary>
101
101
  <img src="examples/gallery/results/02_scatter_fill.png" alt="10M-point spiral scatter" width="650"/>
102
- <br><b>🔍 Click to view code: 10-Million Point Spiral Scatter (<code>02_scatter_fill.py</code>)</b>
102
+ <br><b>🔍 Click to view code: 10-Million Point Spiral Scatter (<a href="examples/gallery/02_scatter_fill.py"><code>02_scatter_fill.py</code></a>)</b>
103
103
  </summary>
104
104
 
105
+ 📄 **Full script:** [`examples/gallery/02_scatter_fill.py`](examples/gallery/02_scatter_fill.py)
106
+
105
107
  ```python
106
108
  import numpy as np
107
109
  import glplot.pyplot as plt
@@ -126,9 +128,11 @@ plt.show()
126
128
  <details>
127
129
  <summary>
128
130
  <img src="examples/gallery/results/10_massive_hist2d_density.png" alt="10M-sample 2D density histogram" width="650"/>
129
- <br><b>🔍 Click to view code: 10M-Sample 2D Density Histogram (<code>10_massive_hist2d_density.py</code>)</b>
131
+ <br><b>🔍 Click to view code: 10M-Sample 2D Density Histogram (<a href="examples/gallery/10_massive_hist2d_density.py"><code>10_massive_hist2d_density.py</code></a>)</b>
130
132
  </summary>
131
133
 
134
+ 📄 **Full script:** [`examples/gallery/10_massive_hist2d_density.py`](examples/gallery/10_massive_hist2d_density.py)
135
+
132
136
  ```python
133
137
  import numpy as np
134
138
  import glplot.pyplot as plt
@@ -162,9 +166,11 @@ plt.show()
162
166
  <details>
163
167
  <summary>
164
168
  <img src="examples/gallery/results/01_line_plot.png" alt="10,072-line oscilloscope ensemble" width="650"/>
165
- <br><b>🔍 Click to view code: 10,072-Line Oscilloscope Ensemble (<code>01_line_plot.py</code>)</b>
169
+ <br><b>🔍 Click to view code: 10,072-Line Oscilloscope Ensemble (<a href="examples/gallery/01_line_plot.py"><code>01_line_plot.py</code></a>)</b>
166
170
  </summary>
167
171
 
172
+ 📄 **Full script:** [`examples/gallery/01_line_plot.py`](examples/gallery/01_line_plot.py)
173
+
168
174
  ```python
169
175
  import colorsys
170
176
  import numpy as np
@@ -212,9 +218,11 @@ plt.show()
212
218
  <details>
213
219
  <summary>
214
220
  <img src="examples/gallery/results/07_projected_3d_cloud.png" alt="1M-point 3D Point Cloud" width="650"/>
215
- <br><b>🔍 Click to view code: 1M-Point Circumstellar Dust Halo (<code>07_projected_3d_cloud.py</code>)</b>
221
+ <br><b>🔍 Click to view code: 1M-Point Circumstellar Dust Halo (<a href="examples/gallery/07_projected_3d_cloud.py"><code>07_projected_3d_cloud.py</code></a>)</b>
216
222
  </summary>
217
223
 
224
+ 📄 **Full script:** [`examples/gallery/07_projected_3d_cloud.py`](examples/gallery/07_projected_3d_cloud.py)
225
+
218
226
  ```python
219
227
  import numpy as np
220
228
  import glplot.pyplot as plt
@@ -253,9 +261,11 @@ plt.show()
253
261
  <details>
254
262
  <summary>
255
263
  <img src="examples/gallery/results/13_volumetric_nebula.png" alt="1.75M-Point Volumetric Nebula" width="650"/>
256
- <br><b>🔍 Click to view code: 1.75M-Point Volumetric Nebula (<code>13_volumetric_nebula.py</code>)</b>
264
+ <br><b>🔍 Click to view code: 1.75M-Point Volumetric Nebula (<a href="examples/gallery/13_volumetric_nebula.py"><code>13_volumetric_nebula.py</code></a>)</b>
257
265
  </summary>
258
266
 
267
+ 📄 **Full script:** [`examples/gallery/13_volumetric_nebula.py`](examples/gallery/13_volumetric_nebula.py)
268
+
259
269
  ```python
260
270
  import numpy as np
261
271
  import glplot.pyplot as plt
@@ -299,9 +309,11 @@ plt.show()
299
309
  <details>
300
310
  <summary>
301
311
  <img src="examples/gallery/results/19_turbulent_vector_field_3d.png" alt="3D Turbulent Vector Field" width="650"/>
302
- <br><b>🔍 Click to view code: 3D Turbulent Vector Field (<code>19_turbulent_vector_field_3d.py</code>)</b>
312
+ <br><b>🔍 Click to view code: 3D Turbulent Vector Field (<a href="examples/gallery/19_turbulent_vector_field_3d.py"><code>19_turbulent_vector_field_3d.py</code></a>)</b>
303
313
  </summary>
304
314
 
315
+ 📄 **Full script:** [`examples/gallery/19_turbulent_vector_field_3d.py`](examples/gallery/19_turbulent_vector_field_3d.py)
316
+
305
317
  ```python
306
318
  import numpy as np
307
319
  import glplot.pyplot as plt
@@ -342,9 +354,11 @@ plt.show()
342
354
  <details>
343
355
  <summary>
344
356
  <img src="examples/gallery/results/28_chladni_wave_animation.gif" alt="Chladni wave animation" width="650"/>
345
- <br><b>🔍 Click to view code: Animated Standing-Wave Interference (<code>28_chladni_wave_animation.py</code>)</b>
357
+ <br><b>🔍 Click to view code: Animated Standing-Wave Interference (<a href="examples/gallery/28_chladni_wave_animation.py"><code>28_chladni_wave_animation.py</code></a>)</b>
346
358
  </summary>
347
359
 
360
+ 📄 **Full script:** [`examples/gallery/28_chladni_wave_animation.py`](examples/gallery/28_chladni_wave_animation.py)
361
+
348
362
  ```python
349
363
  import numpy as np
350
364
  import glplot.animation as animation
@@ -394,9 +408,11 @@ plt.show()
394
408
  <details>
395
409
  <summary>
396
410
  <img src="examples/gallery/animations/results/03_spectrum_analyzer_bars.gif" alt="Audio spectrum analyzer bars" width="650"/>
397
- <br><b>🔍 Click to view code: Real-Time Audio Spectrum Analyzer (<code>03_spectrum_analyzer_bars.py</code>)</b>
411
+ <br><b>🔍 Click to view code: Real-Time Audio Spectrum Analyzer (<a href="examples/gallery/animations/03_spectrum_analyzer_bars.py"><code>03_spectrum_analyzer_bars.py</code></a>)</b>
398
412
  </summary>
399
413
 
414
+ 📄 **Full script:** [`examples/gallery/animations/03_spectrum_analyzer_bars.py`](examples/gallery/animations/03_spectrum_analyzer_bars.py)
415
+
400
416
  ```python
401
417
  import numpy as np
402
418
  import glplot.animation as animation
@@ -428,9 +444,11 @@ plt.show()
428
444
  <details>
429
445
  <summary>
430
446
  <img src="examples/gallery/animations/results/01_orbiting_star_cluster.gif" alt="Orbiting star cluster" width="650"/>
431
- <br><b>🔍 Click to view code: Differential Keplerian Star Cluster (<code>01_orbiting_star_cluster.py</code>)</b>
447
+ <br><b>🔍 Click to view code: Differential Keplerian Star Cluster (<a href="examples/gallery/animations/01_orbiting_star_cluster.py"><code>01_orbiting_star_cluster.py</code></a>)</b>
432
448
  </summary>
433
449
 
450
+ 📄 **Full script:** [`examples/gallery/animations/01_orbiting_star_cluster.py`](examples/gallery/animations/01_orbiting_star_cluster.py)
451
+
434
452
  ```python
435
453
  import numpy as np
436
454
  import glplot.animation as animation
@@ -540,7 +558,7 @@ pytest tests/test_pyplot.py::test_plot_accepts_y_only_and_returns_artists # one
540
558
  ```
541
559
 
542
560
  6,800+ tests, run fully headless (no window is ever displayed) so they work in CI. The
543
- matrix covers Python 3.9–3.12 on Ubuntu, macOS, and Windows; `black`, `isort`, and `flake8`
561
+ matrix covers Python 3.11–3.13 on Ubuntu, macOS, and Windows; `black`, `isort`, and `flake8`
544
562
  are enforced on every push. See `examples/benchmark/` for reproducible performance
545
563
  comparisons against Matplotlib, VisPy, fastplotlib, Datashader, and hvPlot, and `tools/`
546
564
  for GPU/environment diagnostics.
@@ -557,7 +575,7 @@ for GPU/environment diagnostics.
557
575
  ```bibtex
558
576
  @software{lombardi2026glplot,
559
577
  title={GLPlot: High-Performance GPU-Accelerated Plotting Library for Python},
560
- author={Lombardi, Juan Manuel},
578
+ author={Lombardi, Juan Manuel and Riccius, Felix and Holland, Julian and Ducci, Gianmarco},
561
579
  year={2026},
562
580
  url={https://github.com/AkarisDimitry/GLPlot},
563
581
  doi={10.5281/zenodo.PLACEHOLDER}
@@ -4,7 +4,7 @@
4
4
  [![Lint](https://github.com/AkarisDimitry/GLPlot/workflows/Lint/badge.svg)](https://github.com/AkarisDimitry/GLPlot/actions/workflows/lint.yml)
5
5
  [![Build](https://github.com/AkarisDimitry/GLPlot/workflows/Build/badge.svg)](https://github.com/AkarisDimitry/GLPlot/actions/workflows/build.yml)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
- [![Python Version](https://img.shields.io/badge/Python-3.9+-blue.svg)](pyproject.toml)
7
+ [![Python Version](https://img.shields.io/badge/Python-3.11+-blue.svg)](pyproject.toml)
8
8
 
9
9
  GLPlot is a Python plotting library with a Matplotlib-like API that renders on the GPU
10
10
  through OpenGL instead of the CPU. Plots that make Matplotlib chug — millions of points,
@@ -26,8 +26,8 @@ cd GLPlot
26
26
  pip install -e .
27
27
  ```
28
28
 
29
- Requires Python 3.9+. Core dependencies: numpy, scipy, matplotlib, glfw, PyOpenGL,
30
- `imgui[glfw]` (for the on-screen control panel).
29
+ Requires Python 3.11+. Core dependencies: numpy, scipy, matplotlib, glfw, PyOpenGL,
30
+ `imgui-bundle` (for the on-screen control panel).
31
31
 
32
32
  ## 30-second example
33
33
 
@@ -50,16 +50,18 @@ That's it — a real window opens, fully interactive (pan/zoom/rotate) from the
50
50
 
51
51
  ## Interactive Examples Gallery (Click any figure to view code)
52
52
 
53
- Explore 2D plots, 3D scenes, and real-time animations. **Click on any figure or title below to expand its full Python code.**
53
+ Explore 2D plots, 3D scenes, and real-time animations. **Click on any figure or title below to expand its full Python code, or click the file link to view the script.**
54
54
 
55
55
  ### 📊 2D Visualizations
56
56
 
57
57
  <details>
58
58
  <summary>
59
59
  <img src="examples/gallery/results/02_scatter_fill.png" alt="10M-point spiral scatter" width="650"/>
60
- <br><b>🔍 Click to view code: 10-Million Point Spiral Scatter (<code>02_scatter_fill.py</code>)</b>
60
+ <br><b>🔍 Click to view code: 10-Million Point Spiral Scatter (<a href="examples/gallery/02_scatter_fill.py"><code>02_scatter_fill.py</code></a>)</b>
61
61
  </summary>
62
62
 
63
+ 📄 **Full script:** [`examples/gallery/02_scatter_fill.py`](examples/gallery/02_scatter_fill.py)
64
+
63
65
  ```python
64
66
  import numpy as np
65
67
  import glplot.pyplot as plt
@@ -84,9 +86,11 @@ plt.show()
84
86
  <details>
85
87
  <summary>
86
88
  <img src="examples/gallery/results/10_massive_hist2d_density.png" alt="10M-sample 2D density histogram" width="650"/>
87
- <br><b>🔍 Click to view code: 10M-Sample 2D Density Histogram (<code>10_massive_hist2d_density.py</code>)</b>
89
+ <br><b>🔍 Click to view code: 10M-Sample 2D Density Histogram (<a href="examples/gallery/10_massive_hist2d_density.py"><code>10_massive_hist2d_density.py</code></a>)</b>
88
90
  </summary>
89
91
 
92
+ 📄 **Full script:** [`examples/gallery/10_massive_hist2d_density.py`](examples/gallery/10_massive_hist2d_density.py)
93
+
90
94
  ```python
91
95
  import numpy as np
92
96
  import glplot.pyplot as plt
@@ -120,9 +124,11 @@ plt.show()
120
124
  <details>
121
125
  <summary>
122
126
  <img src="examples/gallery/results/01_line_plot.png" alt="10,072-line oscilloscope ensemble" width="650"/>
123
- <br><b>🔍 Click to view code: 10,072-Line Oscilloscope Ensemble (<code>01_line_plot.py</code>)</b>
127
+ <br><b>🔍 Click to view code: 10,072-Line Oscilloscope Ensemble (<a href="examples/gallery/01_line_plot.py"><code>01_line_plot.py</code></a>)</b>
124
128
  </summary>
125
129
 
130
+ 📄 **Full script:** [`examples/gallery/01_line_plot.py`](examples/gallery/01_line_plot.py)
131
+
126
132
  ```python
127
133
  import colorsys
128
134
  import numpy as np
@@ -170,9 +176,11 @@ plt.show()
170
176
  <details>
171
177
  <summary>
172
178
  <img src="examples/gallery/results/07_projected_3d_cloud.png" alt="1M-point 3D Point Cloud" width="650"/>
173
- <br><b>🔍 Click to view code: 1M-Point Circumstellar Dust Halo (<code>07_projected_3d_cloud.py</code>)</b>
179
+ <br><b>🔍 Click to view code: 1M-Point Circumstellar Dust Halo (<a href="examples/gallery/07_projected_3d_cloud.py"><code>07_projected_3d_cloud.py</code></a>)</b>
174
180
  </summary>
175
181
 
182
+ 📄 **Full script:** [`examples/gallery/07_projected_3d_cloud.py`](examples/gallery/07_projected_3d_cloud.py)
183
+
176
184
  ```python
177
185
  import numpy as np
178
186
  import glplot.pyplot as plt
@@ -211,9 +219,11 @@ plt.show()
211
219
  <details>
212
220
  <summary>
213
221
  <img src="examples/gallery/results/13_volumetric_nebula.png" alt="1.75M-Point Volumetric Nebula" width="650"/>
214
- <br><b>🔍 Click to view code: 1.75M-Point Volumetric Nebula (<code>13_volumetric_nebula.py</code>)</b>
222
+ <br><b>🔍 Click to view code: 1.75M-Point Volumetric Nebula (<a href="examples/gallery/13_volumetric_nebula.py"><code>13_volumetric_nebula.py</code></a>)</b>
215
223
  </summary>
216
224
 
225
+ 📄 **Full script:** [`examples/gallery/13_volumetric_nebula.py`](examples/gallery/13_volumetric_nebula.py)
226
+
217
227
  ```python
218
228
  import numpy as np
219
229
  import glplot.pyplot as plt
@@ -257,9 +267,11 @@ plt.show()
257
267
  <details>
258
268
  <summary>
259
269
  <img src="examples/gallery/results/19_turbulent_vector_field_3d.png" alt="3D Turbulent Vector Field" width="650"/>
260
- <br><b>🔍 Click to view code: 3D Turbulent Vector Field (<code>19_turbulent_vector_field_3d.py</code>)</b>
270
+ <br><b>🔍 Click to view code: 3D Turbulent Vector Field (<a href="examples/gallery/19_turbulent_vector_field_3d.py"><code>19_turbulent_vector_field_3d.py</code></a>)</b>
261
271
  </summary>
262
272
 
273
+ 📄 **Full script:** [`examples/gallery/19_turbulent_vector_field_3d.py`](examples/gallery/19_turbulent_vector_field_3d.py)
274
+
263
275
  ```python
264
276
  import numpy as np
265
277
  import glplot.pyplot as plt
@@ -300,9 +312,11 @@ plt.show()
300
312
  <details>
301
313
  <summary>
302
314
  <img src="examples/gallery/results/28_chladni_wave_animation.gif" alt="Chladni wave animation" width="650"/>
303
- <br><b>🔍 Click to view code: Animated Standing-Wave Interference (<code>28_chladni_wave_animation.py</code>)</b>
315
+ <br><b>🔍 Click to view code: Animated Standing-Wave Interference (<a href="examples/gallery/28_chladni_wave_animation.py"><code>28_chladni_wave_animation.py</code></a>)</b>
304
316
  </summary>
305
317
 
318
+ 📄 **Full script:** [`examples/gallery/28_chladni_wave_animation.py`](examples/gallery/28_chladni_wave_animation.py)
319
+
306
320
  ```python
307
321
  import numpy as np
308
322
  import glplot.animation as animation
@@ -352,9 +366,11 @@ plt.show()
352
366
  <details>
353
367
  <summary>
354
368
  <img src="examples/gallery/animations/results/03_spectrum_analyzer_bars.gif" alt="Audio spectrum analyzer bars" width="650"/>
355
- <br><b>🔍 Click to view code: Real-Time Audio Spectrum Analyzer (<code>03_spectrum_analyzer_bars.py</code>)</b>
369
+ <br><b>🔍 Click to view code: Real-Time Audio Spectrum Analyzer (<a href="examples/gallery/animations/03_spectrum_analyzer_bars.py"><code>03_spectrum_analyzer_bars.py</code></a>)</b>
356
370
  </summary>
357
371
 
372
+ 📄 **Full script:** [`examples/gallery/animations/03_spectrum_analyzer_bars.py`](examples/gallery/animations/03_spectrum_analyzer_bars.py)
373
+
358
374
  ```python
359
375
  import numpy as np
360
376
  import glplot.animation as animation
@@ -386,9 +402,11 @@ plt.show()
386
402
  <details>
387
403
  <summary>
388
404
  <img src="examples/gallery/animations/results/01_orbiting_star_cluster.gif" alt="Orbiting star cluster" width="650"/>
389
- <br><b>🔍 Click to view code: Differential Keplerian Star Cluster (<code>01_orbiting_star_cluster.py</code>)</b>
405
+ <br><b>🔍 Click to view code: Differential Keplerian Star Cluster (<a href="examples/gallery/animations/01_orbiting_star_cluster.py"><code>01_orbiting_star_cluster.py</code></a>)</b>
390
406
  </summary>
391
407
 
408
+ 📄 **Full script:** [`examples/gallery/animations/01_orbiting_star_cluster.py`](examples/gallery/animations/01_orbiting_star_cluster.py)
409
+
392
410
  ```python
393
411
  import numpy as np
394
412
  import glplot.animation as animation
@@ -498,7 +516,7 @@ pytest tests/test_pyplot.py::test_plot_accepts_y_only_and_returns_artists # one
498
516
  ```
499
517
 
500
518
  6,800+ tests, run fully headless (no window is ever displayed) so they work in CI. The
501
- matrix covers Python 3.9–3.12 on Ubuntu, macOS, and Windows; `black`, `isort`, and `flake8`
519
+ matrix covers Python 3.11–3.13 on Ubuntu, macOS, and Windows; `black`, `isort`, and `flake8`
502
520
  are enforced on every push. See `examples/benchmark/` for reproducible performance
503
521
  comparisons against Matplotlib, VisPy, fastplotlib, Datashader, and hvPlot, and `tools/`
504
522
  for GPU/environment diagnostics.
@@ -515,7 +533,7 @@ for GPU/environment diagnostics.
515
533
  ```bibtex
516
534
  @software{lombardi2026glplot,
517
535
  title={GLPlot: High-Performance GPU-Accelerated Plotting Library for Python},
518
- author={Lombardi, Juan Manuel},
536
+ author={Lombardi, Juan Manuel and Riccius, Felix and Holland, Julian and Ducci, Gianmarco},
519
537
  year={2026},
520
538
  url={https://github.com/AkarisDimitry/GLPlot},
521
539
  doi={10.5281/zenodo.PLACEHOLDER}
@@ -23,6 +23,18 @@ Author: Juan Manuel Lombardi
23
23
  Repository: https://github.com/AkarisDimitry/GLPlot
24
24
  """
25
25
 
26
+ try:
27
+ # Must run before anything imports the `glfw` package (`.engine` does, right below).
28
+ # imgui-bundle points pip's `glfw` at its own bundled libglfw via the PYGLFW_LIBRARY
29
+ # env var, but only if it gets a chance to set that *before* `glfw` loads its own
30
+ # copy -- otherwise both libglfw.3.dylib's end up loaded in the same process, and
31
+ # macOS logs "Class GLFWWindow is implemented in both ..." for every GLFW ObjC class
32
+ # (verified: importing imgui_bundle first eliminates the warning entirely; importing
33
+ # it after `glfw`, as a naive import order would, does not).
34
+ import imgui_bundle as _imgui_bundle # noqa: F401
35
+ except Exception: # pragma: no cover - imgui_bundle is optional at import time (GL-less use)
36
+ pass
37
+
26
38
  from .engine import GPULinePlot
27
39
  from .options import BlendMode, EngineOptions, RenderMode
28
40
 
@@ -111,6 +111,11 @@ per-quartile mean settles at -29.767, -29.772, -29.773, -29.773 and stays there.
111
111
  the property that matters, and it is the property forward Euler does not have -- an Euler
112
112
  two-body orbit gains energy monotonically and unwinds into a spiral within a few hundred
113
113
  steps, which is not a small quantitative error but a qualitatively wrong picture.
114
+
115
+ The NBODY row is measured on Accelerate/arm64; six mutually gravitating bodies is a
116
+ chaotic system, so a different libm/BLAS (OpenBLAS/x86_64, as in Windows and Linux CI)
117
+ lands on a different point of the same bounded orbit family and measures a band around
118
+ 5x larger. The other rows are far enough from chaos, or short enough, not to show this.
114
119
  """
115
120
 
116
121
  from __future__ import annotations
@@ -1830,6 +1830,10 @@ class GPULinePlot:
1830
1830
  glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3)
1831
1831
  glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3)
1832
1832
  glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
1833
+ # Required on macOS for any core-profile context >= 3.2 (NSGL rejects window
1834
+ # creation without it: "only supports forward-compatible core profile contexts");
1835
+ # a no-op on Windows/Linux, so it is set unconditionally rather than per-platform.
1836
+ glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, glfw.TRUE)
1833
1837
  glfw.window_hint(glfw.DOUBLEBUFFER, glfw.TRUE)
1834
1838
  if self.options.enable_multisample:
1835
1839
  glfw.window_hint(glfw.SAMPLES, 4)
@@ -2578,8 +2582,8 @@ class GPULinePlot:
2578
2582
  draw_list = self.hud.get_draw_list()
2579
2583
  if draw_list:
2580
2584
  color = 0x4C3366CC # Abgr: (0.3, 0.4, 0.8, 1.0) approx
2581
- draw_list.add_rect_filled(px, py, mx, my, color)
2582
- draw_list.add_rect(px, py, mx, my, 0xCC3366CC)
2585
+ draw_list.add_rect_filled((px, py), (mx, my), color)
2586
+ draw_list.add_rect((px, py), (mx, my), 0xCC3366CC)
2583
2587
 
2584
2588
  def _draw_panel_borders(self) -> None:
2585
2589
  """Outline each panel and highlight the active one, so the current axes is obvious.
@@ -2601,9 +2605,13 @@ class GPULinePlot:
2601
2605
  top = (1.0 - (y0f + hf)) * self.height # bottom-left origin -> top-left px
2602
2606
  bottom = (1.0 - y0f) * self.height
2603
2607
  if i == self.active_panel_index:
2604
- draw_list.add_rect(left, top, right, bottom, 0xFFDD9933, 0.0, 0, 2.0)
2608
+ draw_list.add_rect(
2609
+ (left, top), (right, bottom), 0xFFDD9933, rounding=0.0, thickness=2.0
2610
+ )
2605
2611
  else:
2606
- draw_list.add_rect(left, top, right, bottom, 0x55888888, 0.0, 0, 1.0)
2612
+ draw_list.add_rect(
2613
+ (left, top), (right, bottom), 0x55888888, rounding=0.0, thickness=1.0
2614
+ )
2607
2615
 
2608
2616
  def _draw_marquee_box(self) -> None:
2609
2617
  """Rubber band for the Shift+Drag marquee selection.
@@ -2619,8 +2627,8 @@ class GPULinePlot:
2619
2627
  if draw_list:
2620
2628
  # Packed ABGR literals, as in _draw_zoom_box. Cyan, to read as
2621
2629
  # distinct from the right-drag box-zoom rectangle.
2622
- draw_list.add_rect_filled(px, py, mx, my, 0x40CCAA33)
2623
- draw_list.add_rect(px, py, mx, my, 0xFFCCAA33)
2630
+ draw_list.add_rect_filled((px, py), (mx, my), 0x40CCAA33)
2631
+ draw_list.add_rect((px, py), (mx, my), 0xFFCCAA33)
2624
2632
 
2625
2633
  def _run_marquee_pick(self, mx: float, my: float) -> None:
2626
2634
  """Resolve a Shift+Drag marquee into the element selection.
@@ -2861,6 +2869,7 @@ class GPULinePlot:
2861
2869
  self.frame.dirty_scene = True
2862
2870
 
2863
2871
  def _on_cursor(self, window: Any, x: float, y: float) -> None:
2872
+ self.hud.on_cursor(window, x, y)
2864
2873
  if (x, y) == self.interaction.last_mouse:
2865
2874
  return
2866
2875
 
@@ -28,7 +28,7 @@ from .keys import parse as parse_chord
28
28
  from .keys import pressed as chord_pressed
29
29
 
30
30
  try:
31
- import imgui
31
+ from imgui_bundle import imgui
32
32
 
33
33
  IMGUI_AVAILABLE = True
34
34
  except (ImportError, Exception): # pragma: no cover - imgui is a hard dependency in CI
@@ -28,7 +28,7 @@ from typing import Callable, Dict, List, Optional, Sequence, Tuple
28
28
  from .theme import COLORS
29
29
 
30
30
  try:
31
- import imgui
31
+ from imgui_bundle import imgui
32
32
 
33
33
  IMGUI_AVAILABLE = True
34
34
  except (ImportError, Exception): # pragma: no cover - imgui is a hard dependency in CI
@@ -54,44 +54,47 @@ def _stroke(r: float) -> float:
54
54
 
55
55
  def _line(dl, cx: float, cy: float, r: float, col: int, t: float, x0, y0, x1, y1) -> None:
56
56
  """Line in normalised icon space."""
57
- dl.add_line(cx + x0 * r, cy + y0 * r, cx + x1 * r, cy + y1 * r, col, t)
57
+ dl.add_line((cx + x0 * r, cy + y0 * r), (cx + x1 * r, cy + y1 * r), col, t)
58
58
 
59
59
 
60
60
  def _poly(dl, cx, cy, r, col, t, pts: Sequence[Point], closed: bool = False) -> None:
61
61
  """Polyline in normalised icon space."""
62
- flags = imgui.DRAW_CLOSED if closed else 0
62
+ flags = imgui.ImDrawFlags_.closed if closed else 0
63
63
  dl.add_polyline([(cx + x * r, cy + y * r) for x, y in pts], col, flags=flags, thickness=t)
64
64
 
65
65
 
66
66
  def _rect(dl, cx, cy, r, col, t, x0, y0, x1, y1, rounding: float = 0.0) -> None:
67
67
  """Outlined rect in normalised icon space (``rounding`` is also normalised)."""
68
- dl.add_rect(cx + x0 * r, cy + y0 * r, cx + x1 * r, cy + y1 * r, col, rounding * r, 0, t)
68
+ dl.add_rect(
69
+ (cx + x0 * r, cy + y0 * r),
70
+ (cx + x1 * r, cy + y1 * r),
71
+ col,
72
+ rounding=rounding * r,
73
+ thickness=t,
74
+ )
69
75
 
70
76
 
71
77
  def _rect_filled(dl, cx, cy, r, col, x0, y0, x1, y1, rounding: float = 0.0) -> None:
72
78
  """Filled rect in normalised icon space."""
73
- dl.add_rect_filled(cx + x0 * r, cy + y0 * r, cx + x1 * r, cy + y1 * r, col, rounding * r, 0)
79
+ dl.add_rect_filled((cx + x0 * r, cy + y0 * r), (cx + x1 * r, cy + y1 * r), col, rounding * r, 0)
74
80
 
75
81
 
76
82
  def _circle(dl, cx, cy, r, col, t, x, y, rad, segments: int = 20) -> None:
77
83
  """Outlined circle in normalised icon space."""
78
- dl.add_circle(cx + x * r, cy + y * r, rad * r, col, segments, t)
84
+ dl.add_circle((cx + x * r, cy + y * r), rad * r, col, segments, t)
79
85
 
80
86
 
81
87
  def _dot(dl, cx, cy, r, col, x, y, rad) -> None:
82
88
  """Filled circle in normalised icon space."""
83
- dl.add_circle_filled(cx + x * r, cy + y * r, rad * r, col, 14)
89
+ dl.add_circle_filled((cx + x * r, cy + y * r), rad * r, col, 14)
84
90
 
85
91
 
86
92
  def _tri_filled(dl, cx, cy, r, col, p0: Point, p1: Point, p2: Point) -> None:
87
93
  """Filled triangle in normalised icon space."""
88
94
  dl.add_triangle_filled(
89
- cx + p0[0] * r,
90
- cy + p0[1] * r,
91
- cx + p1[0] * r,
92
- cy + p1[1] * r,
93
- cx + p2[0] * r,
94
- cy + p2[1] * r,
95
+ (cx + p0[0] * r, cy + p0[1] * r),
96
+ (cx + p1[0] * r, cy + p1[1] * r),
97
+ (cx + p2[0] * r, cy + p2[1] * r),
95
98
  col,
96
99
  )
97
100
 
@@ -99,14 +102,10 @@ def _tri_filled(dl, cx, cy, r, col, p0: Point, p1: Point, p2: Point) -> None:
99
102
  def _bezier(dl, cx, cy, r, col, t, p0: Point, p1: Point, p2: Point, p3: Point) -> None:
100
103
  """Cubic bezier in normalised icon space. ``thickness`` is required in pyimgui 2.0."""
101
104
  dl.add_bezier_cubic(
102
- cx + p0[0] * r,
103
- cy + p0[1] * r,
104
- cx + p1[0] * r,
105
- cy + p1[1] * r,
106
- cx + p2[0] * r,
107
- cy + p2[1] * r,
108
- cx + p3[0] * r,
109
- cy + p3[1] * r,
105
+ (cx + p0[0] * r, cy + p0[1] * r),
106
+ (cx + p1[0] * r, cy + p1[1] * r),
107
+ (cx + p2[0] * r, cy + p2[1] * r),
108
+ (cx + p3[0] * r, cy + p3[1] * r),
110
109
  col,
111
110
  t,
112
111
  )
@@ -114,8 +113,8 @@ def _bezier(dl, cx, cy, r, col, t, p0: Point, p1: Point, p2: Point, p3: Point) -
114
113
 
115
114
  def _arc(dl, cx, cy, r, col, t, x, y, rad, a_min, a_max, segments: int = 24) -> None:
116
115
  """Stroked arc in normalised icon space. Angles are radians, clockwise (y is down)."""
117
- dl.path_arc_to(cx + x * r, cy + y * r, rad * r, a_min, a_max, segments)
118
- dl.path_stroke(col, 0, t)
116
+ dl.path_arc_to((cx + x * r, cy + y * r), rad * r, a_min, a_max, segments)
117
+ dl.path_stroke(col, thickness=t)
119
118
 
120
119
 
121
120
  # --- shapes: edit ------------------------------------------------------------
@@ -345,8 +344,8 @@ def _filter(dl, cx, cy, r, col, t) -> None:
345
344
 
346
345
 
347
346
  def _eye(dl, cx, cy, r, col, t) -> None:
348
- dl.add_bezier_quadratic(cx - 0.95 * r, cy, cx, cy - 1.3 * r, cx + 0.95 * r, cy, col, t)
349
- dl.add_bezier_quadratic(cx - 0.95 * r, cy, cx, cy + 1.3 * r, cx + 0.95 * r, cy, col, t)
347
+ dl.add_bezier_quadratic((cx - 0.95 * r, cy), (cx, cy - 1.3 * r), (cx + 0.95 * r, cy), col, t)
348
+ dl.add_bezier_quadratic((cx - 0.95 * r, cy), (cx, cy + 1.3 * r), (cx + 0.95 * r, cy), col, t)
350
349
  _circle(dl, cx, cy, r, col, t, 0.0, 0.0, 0.32, 14)
351
350
  _dot(dl, cx, cy, r, col, 0.0, 0.0, 0.12)
352
351
 
@@ -979,7 +978,7 @@ def _icon_color(name: str, alpha: Optional[float] = None) -> int:
979
978
  """Pack a theme token for draw_list use, tolerating a theme that never got applied."""
980
979
  c = COLORS.get(name, (0.85, 0.86, 0.88, 1.0))
981
980
  a = c[3] if alpha is None else alpha
982
- return imgui.get_color_u32_rgba(c[0], c[1], c[2], a)
981
+ return imgui.get_color_u32((c[0], c[1], c[2], a))
983
982
 
984
983
 
985
984
  def icon_button(
@@ -1008,7 +1007,7 @@ def icon_button(
1008
1007
  # Read it before invisible_button advances it, or every icon lands one slot late.
1009
1008
  ox, oy = imgui.get_cursor_screen_pos()
1010
1009
 
1011
- clicked = imgui.invisible_button(id_str, size, size)
1010
+ clicked = imgui.invisible_button(id_str, (size, size))
1012
1011
  hovered = imgui.is_item_hovered()
1013
1012
  held = imgui.is_item_active()
1014
1013
 
@@ -1041,7 +1040,7 @@ def icon_button(
1041
1040
 
1042
1041
  def _rect_bg(dl, ox: float, oy: float, size: float, col: int, rounding: float) -> None:
1043
1042
  """Fill the button's hit rect. Kept separate so the state ladder above stays readable."""
1044
- dl.add_rect_filled(ox, oy, ox + size, oy + size, col, rounding, 0)
1043
+ dl.add_rect_filled((ox, oy), (ox + size, oy + size), col, rounding, 0)
1045
1044
 
1046
1045
 
1047
1046
  def icon_inline(shape: str, *, size: float = 16.0, color: Optional[str] = None) -> None:
@@ -1056,4 +1055,4 @@ def icon_inline(shape: str, *, size: float = 16.0, color: Optional[str] = None)
1056
1055
  ox, oy = imgui.get_cursor_screen_pos()
1057
1056
  col = _icon_color(color) if color else _icon_color("text", 0.82)
1058
1057
  draw_icon(dl, shape, ox + size * 0.5, oy + size * 0.5, size * 0.34, col)
1059
- imgui.dummy(size, size)
1058
+ imgui.dummy((size, size))