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.
- {glplot-0.1.7 → glplot-0.1.8}/CITATION.cff +12 -7
- {glplot-0.1.7 → glplot-0.1.8}/PKG-INFO +39 -21
- {glplot-0.1.7 → glplot-0.1.8}/README.md +33 -15
- {glplot-0.1.7 → glplot-0.1.8}/glplot/__init__.py +12 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/anim/processes.py +5 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/engine.py +15 -6
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/actions.py +1 -1
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/icons.py +28 -29
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/keys.py +84 -17
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/mathops.py +6 -1
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/annotate.py +53 -57
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/data_editor.py +81 -86
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/dynamics.py +9 -7
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/functions.py +16 -15
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/help.py +21 -23
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/mathlab.py +19 -19
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/objects3d.py +4 -5
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/palette.py +37 -42
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/pipeline.py +17 -17
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/presentation.py +7 -7
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/scene.py +26 -37
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/selection.py +10 -13
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/style.py +70 -56
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/timeline.py +28 -23
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/view3d.py +1 -1
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/theme.py +60 -61
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/widgets.py +64 -60
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/workspace.py +36 -29
- {glplot-0.1.7 → glplot-0.1.8}/glplot/managers/hud.py +68 -56
- {glplot-0.1.7 → glplot-0.1.8}/glplot/policy.py +1 -1
- {glplot-0.1.7 → glplot-0.1.8}/glplot/pyplot.py +286 -15
- {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/axes3d.py +6 -6
- {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/axis.py +37 -58
- {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/legend.py +35 -38
- {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/text.py +5 -5
- {glplot-0.1.7 → glplot-0.1.8}/pyproject.toml +10 -8
- {glplot-0.1.7 → glplot-0.1.8}/tests/conftest.py +1 -1
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_anim_processes.py +9 -2
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_animation_api.py +1 -1
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_axes.py +12 -13
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_axes3d.py +7 -8
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_3d_panels.py +2 -3
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_actions.py +63 -5
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_annotate.py +6 -4
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_import_safety.py +31 -13
- glplot-0.1.8/tests/test_gui_input_regressions.py +183 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_keys.py +71 -14
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_legend.py +11 -15
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_mathlab.py +13 -12
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_pipeline.py +4 -6
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_scene_delete.py +2 -3
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_style_layer_editor.py +3 -4
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_timeline_panel.py +7 -5
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_mpl_parity_surface.py +7 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_objects3d_preview.py +9 -7
- {glplot-0.1.7 → glplot-0.1.8}/.gitignore +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/CHANGELOG.md +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/LICENSE +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/__main__.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/anim/__init__.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/anim/applier.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/anim/primitives.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/animation.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/backend.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/controllers.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/core/__init__.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/core/camera3d.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/core/context.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/core/layers.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/core/layout.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/core/legacy.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/core/panel.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/core/timeline.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/__init__.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/app.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/clipboard.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/commands.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/dataio.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/datasets.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/dynamics.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/expressions.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/fuzzy.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/generators3d.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/history.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/layerops.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/layerops3d.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/__init__.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/panels/base.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/pipeline.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/gui/styles.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/managers/__init__.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/managers/axis.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/managers/effects.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/managers/hud_state.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/managers/picking.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/managers/renderer_manager.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/options.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/__init__.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/base.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/density.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/exact.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/fractal.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/geometry3d.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/interaction.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/line_family.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/patch.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/polyline.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/renderers/scatter.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/utils/__init__.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/utils/anim_export.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/utils/blending.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/utils/export.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/utils/gl_utils.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/utils/mpl_bridge.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/utils/mpl_process.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/utils/preview.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/utils/scale.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/glplot/utils/shaders.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_anim_applier.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_anim_export.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_anim_primitives.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_animation_integration.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_api_consistency.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_axis_margins.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_blending_3d.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_camera3d.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_camera_anisotropy.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_camera_projections.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_compositing_3d.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_compositing_api.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_dataio.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_density_tint.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_edge_cases.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_effects_post.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_encodings.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_engine.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_export.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_fractal.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_function_layer.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gallery_integration.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_generators3d.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_generators3d_extra.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_geometry3d.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_clipboard.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_commands.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_data_editor.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_datasets.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_dynamics.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_engine_integration.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_expressions.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_fields.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_fuzzy.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_history.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_impostor_alignment.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_keyboard_capture.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_layer_colormaps.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_layer_kinds.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_layer_params.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_mathops.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_regression_r2.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_scene_order.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_styles.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_gui_styles_extra.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_helpers.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_impostor_scatter_fidelity.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_layerops3d.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_layers.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_mpl_bridge.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_mpl_compat.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_mpl_compat_3d.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_mpl_process.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_options.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_outline.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_outline_2d.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_panels.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_performance_benchmarks.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_plot_style_api.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_polyline_lod.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_pyplot.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_pyplot_3d.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_pyplot_integration.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_regression.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_robustness.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_scatter_renderer.py +0 -0
- {glplot-0.1.7 → glplot-0.1.8}/tests/test_selection_model.py +0 -0
- {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:
|
|
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/
|
|
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.
|
|
45
|
-
date-released: 2026-
|
|
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.
|
|
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.
|
|
24
|
+
Requires-Python: >=3.11
|
|
25
25
|
Requires-Dist: glfw>=2.5
|
|
26
|
-
Requires-Dist: imgui
|
|
27
|
-
Requires-Dist: matplotlib
|
|
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
|
[](https://github.com/AkarisDimitry/GLPlot/actions/workflows/lint.yml)
|
|
47
47
|
[](https://github.com/AkarisDimitry/GLPlot/actions/workflows/build.yml)
|
|
48
48
|
[](LICENSE)
|
|
49
|
-
[](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.
|
|
72
|
-
`imgui
|
|
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.
|
|
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
|
[](https://github.com/AkarisDimitry/GLPlot/actions/workflows/lint.yml)
|
|
5
5
|
[](https://github.com/AkarisDimitry/GLPlot/actions/workflows/build.yml)
|
|
6
6
|
[](LICENSE)
|
|
7
|
-
[](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.
|
|
30
|
-
`imgui
|
|
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.
|
|
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(
|
|
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(
|
|
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.
|
|
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(
|
|
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 +
|
|
91
|
-
cx +
|
|
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 +
|
|
104
|
-
cx +
|
|
105
|
-
cy +
|
|
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,
|
|
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.
|
|
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))
|