glplot 0.1.8__tar.gz → 0.1.9__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.8 → glplot-0.1.9}/CITATION.cff +2 -0
- {glplot-0.1.8 → glplot-0.1.9}/PKG-INFO +3 -1
- {glplot-0.1.8 → glplot-0.1.9}/glplot/core/panel.py +81 -1
- {glplot-0.1.8 → glplot-0.1.9}/glplot/engine.py +81 -24
- {glplot-0.1.8 → glplot-0.1.9}/glplot/pyplot.py +435 -50
- {glplot-0.1.8 → glplot-0.1.9}/glplot/renderers/axis.py +24 -10
- glplot-0.1.9/glplot/renderers/colorbar.py +415 -0
- glplot-0.1.9/glplot/renderers/contour_labels.py +274 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/renderers/scatter.py +27 -8
- {glplot-0.1.8 → glplot-0.1.9}/glplot/utils/preview.py +238 -13
- {glplot-0.1.8 → glplot-0.1.9}/glplot/utils/shaders.py +66 -1
- {glplot-0.1.8 → glplot-0.1.9}/pyproject.toml +18 -3
- glplot-0.1.9/tests/test_contour_labels.py +132 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gallery_integration.py +9 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_mpl_compat.py +276 -12
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_outline_2d.py +1 -0
- {glplot-0.1.8 → glplot-0.1.9}/.gitignore +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/CHANGELOG.md +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/LICENSE +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/README.md +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/__init__.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/__main__.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/anim/__init__.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/anim/applier.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/anim/primitives.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/anim/processes.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/animation.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/backend.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/controllers.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/core/__init__.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/core/camera3d.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/core/context.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/core/layers.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/core/layout.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/core/legacy.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/core/timeline.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/__init__.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/actions.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/app.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/clipboard.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/commands.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/dataio.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/datasets.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/dynamics.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/expressions.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/fuzzy.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/generators3d.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/history.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/icons.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/keys.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/layerops.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/layerops3d.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/mathops.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/panels/__init__.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/panels/annotate.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/panels/base.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/panels/data_editor.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/panels/dynamics.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/panels/functions.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/panels/help.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/panels/mathlab.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/panels/objects3d.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/panels/palette.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/panels/pipeline.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/panels/presentation.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/panels/scene.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/panels/selection.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/panels/style.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/panels/timeline.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/panels/view3d.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/pipeline.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/styles.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/theme.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/widgets.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/gui/workspace.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/managers/__init__.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/managers/axis.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/managers/effects.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/managers/hud.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/managers/hud_state.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/managers/picking.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/managers/renderer_manager.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/options.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/policy.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/renderers/__init__.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/renderers/axes3d.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/renderers/base.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/renderers/density.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/renderers/exact.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/renderers/fractal.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/renderers/geometry3d.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/renderers/interaction.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/renderers/legend.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/renderers/line_family.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/renderers/patch.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/renderers/polyline.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/renderers/text.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/utils/__init__.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/utils/anim_export.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/utils/blending.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/utils/export.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/utils/gl_utils.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/utils/mpl_bridge.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/utils/mpl_process.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/glplot/utils/scale.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/conftest.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_anim_applier.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_anim_export.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_anim_primitives.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_anim_processes.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_animation_api.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_animation_integration.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_api_consistency.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_axes.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_axes3d.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_axis_margins.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_blending_3d.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_camera3d.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_camera_anisotropy.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_camera_projections.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_compositing_3d.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_compositing_api.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_dataio.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_density_tint.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_edge_cases.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_effects_post.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_encodings.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_engine.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_export.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_fractal.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_function_layer.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_generators3d.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_generators3d_extra.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_geometry3d.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_3d_panels.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_actions.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_annotate.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_clipboard.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_commands.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_data_editor.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_datasets.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_dynamics.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_engine_integration.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_expressions.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_fields.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_fuzzy.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_history.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_import_safety.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_impostor_alignment.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_input_regressions.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_keyboard_capture.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_keys.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_layer_colormaps.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_layer_kinds.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_layer_params.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_legend.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_mathlab.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_mathops.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_pipeline.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_regression_r2.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_scene_delete.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_scene_order.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_style_layer_editor.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_styles.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_styles_extra.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_gui_timeline_panel.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_helpers.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_impostor_scatter_fidelity.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_layerops3d.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_layers.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_mpl_bridge.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_mpl_compat_3d.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_mpl_parity_surface.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_mpl_process.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_objects3d_preview.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_options.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_outline.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_panels.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_performance_benchmarks.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_plot_style_api.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_polyline_lod.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_pyplot.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_pyplot_3d.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_pyplot_integration.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_regression.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_robustness.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_scatter_renderer.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_selection_model.py +0 -0
- {glplot-0.1.8 → glplot-0.1.9}/tests/test_timeline.py +0 -0
|
@@ -12,8 +12,10 @@ authors:
|
|
|
12
12
|
orcid: "https://orcid.org/0009-0002-3961-1426"
|
|
13
13
|
- family-names: Riccius
|
|
14
14
|
given-names: Felix
|
|
15
|
+
orcid: "https://orcid.org/0000-0002-1331-4469"
|
|
15
16
|
- family-names: Holland
|
|
16
17
|
given-names: Julian
|
|
18
|
+
orcid: "https://orcid.org/0000-0003-0134-1790"
|
|
17
19
|
- family-names: Ducci
|
|
18
20
|
given-names: Gianmarco
|
|
19
21
|
identifiers:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: glplot
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.9
|
|
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
|
|
@@ -32,11 +32,13 @@ Provides-Extra: dev
|
|
|
32
32
|
Requires-Dist: build>=1.0; extra == 'dev'
|
|
33
33
|
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
34
34
|
Requires-Dist: pytest-mock>=3.10; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest-timeout>=2.1; extra == 'dev'
|
|
35
36
|
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
36
37
|
Requires-Dist: twine>=4.0; extra == 'dev'
|
|
37
38
|
Provides-Extra: test
|
|
38
39
|
Requires-Dist: pytest-cov>=4.0; extra == 'test'
|
|
39
40
|
Requires-Dist: pytest-mock>=3.10; extra == 'test'
|
|
41
|
+
Requires-Dist: pytest-timeout>=2.1; extra == 'test'
|
|
40
42
|
Requires-Dist: pytest>=7.0; extra == 'test'
|
|
41
43
|
Description-Content-Type: text/markdown
|
|
42
44
|
|
|
@@ -18,7 +18,8 @@ origin, which is what ``glViewport`` wants).
|
|
|
18
18
|
|
|
19
19
|
from __future__ import annotations
|
|
20
20
|
|
|
21
|
-
from
|
|
21
|
+
from dataclasses import dataclass
|
|
22
|
+
from typing import TYPE_CHECKING, Any, List, Optional, Tuple
|
|
22
23
|
|
|
23
24
|
from ..controllers import CameraController
|
|
24
25
|
from .camera3d import Axes3DOptions, Camera3D
|
|
@@ -29,6 +30,61 @@ if TYPE_CHECKING:
|
|
|
29
30
|
from ..options import EngineOptions
|
|
30
31
|
|
|
31
32
|
|
|
33
|
+
@dataclass
|
|
34
|
+
class ColorbarSpec:
|
|
35
|
+
"""One colorbar attached to a :class:`Panel`, drawn as a screen-space overlay.
|
|
36
|
+
|
|
37
|
+
Deliberately not a :class:`Panel` itself: any rect registered on ``fig.panels`` is
|
|
38
|
+
click-to-activate and pan/zoom-able (see ``GPULinePlot._panel_index_at``), so a
|
|
39
|
+
colorbar built that way would silently steal "current axes" the moment a script did
|
|
40
|
+
``im = plt.imshow(...); plt.colorbar(); plt.title(...)`` -- the title would land on
|
|
41
|
+
the colorbar. A ``ColorbarSpec`` instead just describes a strip of the figure and is
|
|
42
|
+
drawn by :func:`glplot.renderers.colorbar.draw_colorbars`, in the same whole-window,
|
|
43
|
+
camera-free pixel space :meth:`GPULinePlot._draw_panel_borders` already draws in.
|
|
44
|
+
|
|
45
|
+
``norm`` is always a concrete, bounded ``matplotlib.colors.Normalize`` instance (never
|
|
46
|
+
a bare scale name and never unbounded) -- the single source of truth the gradient fill,
|
|
47
|
+
the tick locator, and the headless ``savefig()`` reconstruction all read alike, so the
|
|
48
|
+
live bar and the exported PNG cannot independently drift from what was actually asked
|
|
49
|
+
for.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
#: (x0, y0, w, h) in figure fractions, bottom-left origin -- same convention as
|
|
53
|
+
#: :attr:`Panel.rect_frac`, and the strip vacated from the host panel's own rect.
|
|
54
|
+
rect_frac: Tuple[float, float, float, float]
|
|
55
|
+
#: "vertical" for a right/left bar, "horizontal" for a top/bottom one.
|
|
56
|
+
orientation: str
|
|
57
|
+
#: Which edge of the host panel this bar sits against: "right", "left", "top" or
|
|
58
|
+
#: "bottom". Distinct from ``orientation`` because a caller can ask for either
|
|
59
|
+
#: independently, as matplotlib's own ``colorbar()`` does.
|
|
60
|
+
location: str
|
|
61
|
+
#: Colormap name, resolved (never ``None``) at ``colorbar()`` call time.
|
|
62
|
+
cmap: str
|
|
63
|
+
#: A concrete ``matplotlib.colors.Normalize`` (or subclass) instance with real
|
|
64
|
+
#: ``vmin``/``vmax`` already resolved.
|
|
65
|
+
norm: Any
|
|
66
|
+
#: Explicit tick positions, or ``None`` to auto-locate from ``norm``'s type.
|
|
67
|
+
ticks: Optional[Any] = None
|
|
68
|
+
#: A tick label format (matplotlib ``Formatter``, format string, or ``None`` to
|
|
69
|
+
#: auto-format from the locator).
|
|
70
|
+
format: Optional[Any] = None
|
|
71
|
+
#: The colorbar's own axis label (``Colorbar.set_label``), or ``None``.
|
|
72
|
+
label: Optional[str] = None
|
|
73
|
+
#: Matplotlib-parity placement knobs, stored for the headless reconstruction (which
|
|
74
|
+
#: rebuilds a real matplotlib colorbar and can honour them precisely); the live
|
|
75
|
+
#: renderer's own bar geometry does not re-derive a precise aspect-ratio width from
|
|
76
|
+
#: these -- see ``colorbar()``'s docstring.
|
|
77
|
+
fraction: float = 0.15
|
|
78
|
+
pad: float = 0.05
|
|
79
|
+
shrink: float = 1.0
|
|
80
|
+
aspect: float = 20.0
|
|
81
|
+
#: When True, the bar sits *inside* the host panel's own rect (over the plotted
|
|
82
|
+
#: content, near ``location``'s edge) instead of shrinking the panel to make room
|
|
83
|
+
#: outside it. Ticks/label flip to point inward, toward the panel's centre, since
|
|
84
|
+
#: there is no room outside the panel for them to point into.
|
|
85
|
+
inset: bool = False
|
|
86
|
+
|
|
87
|
+
|
|
32
88
|
class Panel:
|
|
33
89
|
"""One plotting region: its scene, camera, interaction state, and window rectangle."""
|
|
34
90
|
|
|
@@ -64,6 +120,30 @@ class Panel:
|
|
|
64
120
|
#: panel that is never animated costs one object and no frame time.
|
|
65
121
|
self.timeline = Timeline()
|
|
66
122
|
|
|
123
|
+
#: Colorbars drawn against this panel's edges. Metadata only -- never a
|
|
124
|
+
#: :class:`Panel` of its own, never registered on ``fig.panels`` -- so a
|
|
125
|
+
#: colorbar can never become the active/pannable axes. See
|
|
126
|
+
#: :class:`ColorbarSpec` for why.
|
|
127
|
+
self.colorbars: List["ColorbarSpec"] = []
|
|
128
|
+
|
|
129
|
+
#: This panel's own axis names and axes title.
|
|
130
|
+
#:
|
|
131
|
+
#: Per panel for the same reason the cameras and scenes are: a split figure plots
|
|
132
|
+
#: four different quantities and has to be able to *name* them four different
|
|
133
|
+
#: ways. These were figure-global until they lived here, so a 2x2 grid could only
|
|
134
|
+
#: ever carry one x-name, one y-name and one title between all four panels --
|
|
135
|
+
#: whichever call ran last won, and the other three axes went unlabelled.
|
|
136
|
+
#:
|
|
137
|
+
#: The engine forwards ``xlabel``/``ylabel`` to the active panel's copy (the same
|
|
138
|
+
#: delegation ``scene``/``camera``/``cache`` use), so ``gplt.xlabel(...)`` keeps
|
|
139
|
+
#: writing "the current axes' x-name" and every existing single-panel caller is
|
|
140
|
+
#: unaffected. ``title`` is *not* forwarded that way -- ``engine.title`` is also
|
|
141
|
+
#: the GLFW window caption and is assigned in ``__init__`` before any panel
|
|
142
|
+
#: exists -- so ``set_title`` writes both, and readers prefer this one.
|
|
143
|
+
self.xlabel: str = ""
|
|
144
|
+
self.ylabel: str = ""
|
|
145
|
+
self.title: str = ""
|
|
146
|
+
|
|
67
147
|
#: Explicit dimensionality: 2, 3, or ``None`` for "infer from the layers".
|
|
68
148
|
#:
|
|
69
149
|
#: None is the default and reproduces GLPlot's historical behaviour exactly — a
|
|
@@ -55,6 +55,8 @@ from .renderers import axes3d
|
|
|
55
55
|
from .renderers.density import DensityRenderer
|
|
56
56
|
from .renderers.exact import ExactLineRenderer
|
|
57
57
|
from .renderers.interaction import InteractionRenderer
|
|
58
|
+
from .renderers.colorbar import draw_colorbars
|
|
59
|
+
from .renderers.contour_labels import draw_contour_labels
|
|
58
60
|
from .renderers.legend import draw_legend
|
|
59
61
|
from .utils.export import ExportManager
|
|
60
62
|
from .utils.scale import forward as _scale_forward
|
|
@@ -238,6 +240,34 @@ class GPULinePlot:
|
|
|
238
240
|
def cache(self, value: CacheState) -> None:
|
|
239
241
|
self.active_panel.cache = value
|
|
240
242
|
|
|
243
|
+
@property
|
|
244
|
+
def xlabel(self) -> str:
|
|
245
|
+
"""The active panel's x-axis name (``gplt.xlabel()``).
|
|
246
|
+
|
|
247
|
+
Forwarded to the panel for the same reason ``scene``/``camera`` are: a split
|
|
248
|
+
figure plots a different quantity per panel and has to be able to name each one.
|
|
249
|
+
Held as a plain attribute on the engine until this existed, which made it
|
|
250
|
+
figure-global -- a 2x2 grid got one x-name between all four panels.
|
|
251
|
+
|
|
252
|
+
Every reader keeps working unchanged: the live axis renderer reads
|
|
253
|
+
``plot.xlabel`` inside the per-panel overlay pass (``_draw_panels`` swaps
|
|
254
|
+
``active_panel_index`` around it), so it now resolves per panel for free.
|
|
255
|
+
"""
|
|
256
|
+
return self.active_panel.xlabel
|
|
257
|
+
|
|
258
|
+
@xlabel.setter
|
|
259
|
+
def xlabel(self, value: str) -> None:
|
|
260
|
+
self.active_panel.xlabel = "" if value is None else str(value)
|
|
261
|
+
|
|
262
|
+
@property
|
|
263
|
+
def ylabel(self) -> str:
|
|
264
|
+
"""The active panel's y-axis name (``gplt.ylabel()``). See :attr:`xlabel`."""
|
|
265
|
+
return self.active_panel.ylabel
|
|
266
|
+
|
|
267
|
+
@ylabel.setter
|
|
268
|
+
def ylabel(self, value: str) -> None:
|
|
269
|
+
self.active_panel.ylabel = "" if value is None else str(value)
|
|
270
|
+
|
|
241
271
|
@property
|
|
242
272
|
def camera3d(self) -> Camera3D:
|
|
243
273
|
"""The active panel's 3D point of view."""
|
|
@@ -324,6 +354,13 @@ class GPULinePlot:
|
|
|
324
354
|
"""
|
|
325
355
|
self.title = str(title)
|
|
326
356
|
self.options.title = str(title)
|
|
357
|
+
# Also the *active panel's* own axes title, so a split figure can title each
|
|
358
|
+
# panel separately. `self.title` stays the window caption (and the single-panel
|
|
359
|
+
# axes title) rather than becoming a forwarding property like `xlabel`/`ylabel`:
|
|
360
|
+
# it is assigned in `__init__` before any panel exists, so forwarding it would
|
|
361
|
+
# write to a panel that is not there yet.
|
|
362
|
+
if getattr(self, "panels", None):
|
|
363
|
+
self.active_panel.title = str(title)
|
|
327
364
|
self.options.axis_title_fontsize = fontsize
|
|
328
365
|
self.options.axis_title_color = color
|
|
329
366
|
self.frame.dirty_ui = True
|
|
@@ -2044,35 +2081,38 @@ class GPULinePlot:
|
|
|
2044
2081
|
# --------------------------------------------------------
|
|
2045
2082
|
|
|
2046
2083
|
def _draw_overlays(self) -> None:
|
|
2047
|
-
"""Draw the axis scale labels and the legend for the active panel.
|
|
2084
|
+
"""Draw the axis scale labels, contour labels and the legend for the active panel.
|
|
2048
2085
|
|
|
2049
2086
|
Screen-aligned overlays that must repaint every frame (so they stay live and survive
|
|
2050
2087
|
the cached impostor). Reads the active panel's camera and the engine's current pixel
|
|
2051
2088
|
dimensions, which ``_draw_panels`` sets to the panel's rect when several panels exist.
|
|
2052
2089
|
"""
|
|
2090
|
+
# Built once, outside the `axis_show_labels` gate below: the contour labels and the
|
|
2091
|
+
# legend are not axis annotations and must still draw when the axis numbers are off.
|
|
2092
|
+
window_world = self.camera_controller.world_window(self.width, self.height)
|
|
2093
|
+
mvp = self.camera_controller.mvp(self.width, self.height)
|
|
2094
|
+
ndc_scale, ndc_offset = self._get_ndc_transform(window_world)
|
|
2095
|
+
ctx_labels = RenderContext(
|
|
2096
|
+
mvp=mvp,
|
|
2097
|
+
window_world=window_world,
|
|
2098
|
+
ndc_scale=ndc_scale,
|
|
2099
|
+
ndc_offset=ndc_offset,
|
|
2100
|
+
width_px=self.width,
|
|
2101
|
+
height_px=self.height,
|
|
2102
|
+
fb_width=self.fb_width,
|
|
2103
|
+
fb_height=self.fb_height,
|
|
2104
|
+
dpr=self.fb_width / max(self.width, 1),
|
|
2105
|
+
mode=self.policy.runtime.current_mode,
|
|
2106
|
+
global_alpha=1.0,
|
|
2107
|
+
lod_keep_prob=1.0,
|
|
2108
|
+
is_density=self.display_density,
|
|
2109
|
+
time=time.perf_counter(),
|
|
2110
|
+
px_offset=self._panel_offset_px,
|
|
2111
|
+
)
|
|
2112
|
+
|
|
2053
2113
|
# Draw Axis Labels (Scale) on every frame so they update dynamically and persist when
|
|
2054
2114
|
# the scene is cached.
|
|
2055
2115
|
if self.options.axis_show_labels:
|
|
2056
|
-
window_world = self.camera_controller.world_window(self.width, self.height)
|
|
2057
|
-
mvp = self.camera_controller.mvp(self.width, self.height)
|
|
2058
|
-
ndc_scale, ndc_offset = self._get_ndc_transform(window_world)
|
|
2059
|
-
ctx_labels = RenderContext(
|
|
2060
|
-
mvp=mvp,
|
|
2061
|
-
window_world=window_world,
|
|
2062
|
-
ndc_scale=ndc_scale,
|
|
2063
|
-
ndc_offset=ndc_offset,
|
|
2064
|
-
width_px=self.width,
|
|
2065
|
-
height_px=self.height,
|
|
2066
|
-
fb_width=self.fb_width,
|
|
2067
|
-
fb_height=self.fb_height,
|
|
2068
|
-
dpr=self.fb_width / max(self.width, 1),
|
|
2069
|
-
mode=self.policy.runtime.current_mode,
|
|
2070
|
-
global_alpha=1.0,
|
|
2071
|
-
lod_keep_prob=1.0,
|
|
2072
|
-
is_density=self.display_density,
|
|
2073
|
-
time=time.perf_counter(),
|
|
2074
|
-
px_offset=self._panel_offset_px,
|
|
2075
|
-
)
|
|
2076
2116
|
if self._is_pure_3d_scene():
|
|
2077
2117
|
# A 3D scene's numbers live on the box's edges, not on two screen-aligned
|
|
2078
2118
|
# spines, so they come from the 3D anchors rather than the AxisManager.
|
|
@@ -2082,6 +2122,13 @@ class GPULinePlot:
|
|
|
2082
2122
|
self.axis_manager.update(ctx_labels)
|
|
2083
2123
|
self.renderer_manager.renderers["axis"]._draw_labels(self.axis_manager, ctx_labels)
|
|
2084
2124
|
|
|
2125
|
+
# Inline contour values, seated on their own level lines. Same draw list and the
|
|
2126
|
+
# same reasoning as the legend below: not an axis annotation, so outside that
|
|
2127
|
+
# gate, and 2D-only (a 3D contour is real `wireframe3d` geometry with no
|
|
2128
|
+
# screen-space line to seat a number on). Draws nothing unless `clabel()` asked.
|
|
2129
|
+
if not self._is_pure_3d_scene():
|
|
2130
|
+
draw_contour_labels(self, ctx_labels)
|
|
2131
|
+
|
|
2085
2132
|
# The legend, on the same background draw list as the axis labels and for the
|
|
2086
2133
|
# same reason: it must composite over the cached impostor, which is not
|
|
2087
2134
|
# redrawn during an interaction. Deliberately outside the `axis_show_labels`
|
|
@@ -2481,6 +2528,11 @@ class GPULinePlot:
|
|
|
2481
2528
|
# pixel size via _draw_panels, which swaps them for the panel's rect).
|
|
2482
2529
|
self._draw_panels(self._draw_overlays)
|
|
2483
2530
|
|
|
2531
|
+
# Colorbars, once per frame in whole-window pixel space (not per-panel, like
|
|
2532
|
+
# `_draw_panel_borders` one call earlier) -- see renderers/colorbar.py's module
|
|
2533
|
+
# docstring for why a colorbar is screen-space metadata, not a `Panel`.
|
|
2534
|
+
draw_colorbars(self)
|
|
2535
|
+
|
|
2484
2536
|
# HUD panels are only updated if HUD is enabled, but begin/end must wrap all
|
|
2485
2537
|
if self.policy.runtime.hud_enabled_this_frame:
|
|
2486
2538
|
self.hud.update()
|
|
@@ -3303,11 +3355,16 @@ class GPULinePlot:
|
|
|
3303
3355
|
from .utils.mpl_process import launch_snapshot_viewer
|
|
3304
3356
|
|
|
3305
3357
|
snap = self.capture_snapshot(scale=2.0)
|
|
3358
|
+
# `or None`, not a bare read: `xlabel`/`ylabel` are now always-present properties
|
|
3359
|
+
# forwarding to the active panel (they used to be attributes that simply did not
|
|
3360
|
+
# exist until `gplt.xlabel()` created them, which is what the `getattr` default
|
|
3361
|
+
# was catching). An unset label is the empty string now, and this viewer's
|
|
3362
|
+
# contract is that "no label" arrives as None.
|
|
3306
3363
|
launch_snapshot_viewer(
|
|
3307
3364
|
snap,
|
|
3308
|
-
xlabel=getattr(self, "xlabel", None),
|
|
3309
|
-
ylabel=getattr(self, "ylabel", None),
|
|
3310
|
-
title=getattr(self, "title", None),
|
|
3365
|
+
xlabel=getattr(self, "xlabel", None) or None,
|
|
3366
|
+
ylabel=getattr(self, "ylabel", None) or None,
|
|
3367
|
+
title=getattr(self, "title", None) or None,
|
|
3311
3368
|
)
|
|
3312
3369
|
|
|
3313
3370
|
def toggle_line_colormap(self) -> None:
|