anyplotlib 0.3.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/CHANGELOG.rst +60 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/PKG-INFO +1 -1
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/FIGURE_ESM.md +27 -11
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/_utils.py +38 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/axes/_inset_axes.py +163 -5
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/callbacks.py +26 -2
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/figure/_figure.py +29 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/figure_esm.js +477 -39
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/plot1d/_plot1d.py +46 -6
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/plot2d/_layer.py +18 -5
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/plot2d/_plot2d.py +48 -14
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_embed/test_export_png.py +131 -51
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_interactive/test_callbacks_playwright.py +100 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_interactive/test_callbacks_unit.py +72 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_interactive/test_widgets.py +196 -1
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_labels/test_label_api.py +46 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_layouts/test_inset.py +153 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_layouts/test_inset_callout.py +371 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_plot2d/test_layers.py +94 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_plot2d/test_layers_playwright.py +107 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_plot3d/test_gpu_fallback.py +1 -2
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/widgets/_widgets1d.py +21 -8
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/widgets/_widgets2d.py +23 -6
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/docs/_root/index.html +3 -3
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/docs/_root/switcher.json +5 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/docs/conf.py +1 -1
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/pyproject.toml +1 -1
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/uv.lock +1 -1
- anyplotlib-0.3.0/LARGE_IMAGE_WEBGPU_PLAN.md +0 -223
- anyplotlib-0.3.0/ORIX_BACKEND_PLAN.md +0 -133
- anyplotlib-0.3.0/RELEASE_NOTES.md +0 -94
- anyplotlib-0.3.0/RELEASE_PLAN.md +0 -104
- anyplotlib-0.3.0/WEBGPU_PLAN.md +0 -234
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/.github/workflows/ci.yml +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/.github/workflows/docs.yml +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/.github/workflows/prepare_release.yml +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/.github/workflows/release.yml +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/.github/workflows/tests.yml +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/.gitignore +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/AGENTS.md +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Benchmarks/README.rst +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Benchmarks/plot_benchmark_comparison.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Interactive/README.rst +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Interactive/plot_3d_spectral_viewer.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Interactive/plot_eels_explorer.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Interactive/plot_figure_annotations.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Interactive/plot_interactive_fft.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Interactive/plot_interactive_fitting.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Interactive/plot_ipf_density_map.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Interactive/plot_ipf_explorer.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Interactive/plot_key_bindings.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Interactive/plot_particle_picker.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Interactive/plot_point_widget.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Interactive/plot_segment_by_contrast.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Interactive/plot_segment_by_contrast_advanced.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Interactive/plot_spectra_roi_inspector.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Interactive/plot_threshold_explorer.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Interactive/plot_voxel_grain_explorer.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Markers/README.rst +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Markers/plot_arrows.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Markers/plot_circles.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Markers/plot_ellipses.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Markers/plot_horizontal_lines.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Markers/plot_line_segments.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Markers/plot_points.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Markers/plot_polygons.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Markers/plot_rectangles.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Markers/plot_squares.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Markers/plot_texts.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Markers/plot_transform_markers.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Markers/plot_vertical_lines.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/PlotTypes/README.rst +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/PlotTypes/plot_3d.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/PlotTypes/plot_bar.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/PlotTypes/plot_gpu_voxels.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/PlotTypes/plot_gridspec_custom.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/PlotTypes/plot_image2d.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/PlotTypes/plot_inset.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/PlotTypes/plot_label_formatting.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/PlotTypes/plot_line_styles.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/PlotTypes/plot_pcolormesh.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/PlotTypes/plot_spectra1d.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/PlotTypes/plot_step_and_log.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/PlotTypes/plot_tile_backend.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/PlotTypes/plot_twinx.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/README.rst +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Widgets/README.rst +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Widgets/plot_widget1d_hline.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Widgets/plot_widget1d_point.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Widgets/plot_widget1d_range.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Widgets/plot_widget1d_vline.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Widgets/plot_widget2d_annular.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Widgets/plot_widget2d_arrow.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Widgets/plot_widget2d_circle.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Widgets/plot_widget2d_crosshair.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Widgets/plot_widget2d_label.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Widgets/plot_widget2d_polygon.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Widgets/plot_widget2d_rectangle.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Examples/Widgets/plot_widget3d_plane.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/LICENSE +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/Makefile +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/README.md +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/__init__.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/_base_plot.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/_binary_frame.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/_electron.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/_repr_utils.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/axes/__init__.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/axes/_axes.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/conftest.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/embed.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/figure/__init__.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/figure/_gridspec.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/figure/_subplots.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/markers.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/plot1d/__init__.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/plot1d/_plotbar.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/plot2d/__init__.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/plot2d/_plotmesh.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/plot2d/_tile_backend.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/plot3d/__init__.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/plot3d/_plot3d.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/plotxy/__init__.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/plotxy/_plotxy.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/sphinx_anywidget/__init__.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/sphinx_anywidget/_directive.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/sphinx_anywidget/_repr_utils.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/sphinx_anywidget/_scraper.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/sphinx_anywidget/_wheel_builder.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/sphinx_anywidget/static/anywidget_bridge.js +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/sphinx_anywidget/static/anywidget_overlay.css +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/sphinx_anywidget/tests/__init__.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/sphinx_anywidget/tests/conftest.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/sphinx_anywidget/tests/test_directive.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/sphinx_anywidget/tests/test_init.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/sphinx_anywidget/tests/test_repr_utils.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/sphinx_anywidget/tests/test_scraper.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/sphinx_anywidget/tests/test_wheel_builder.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/__init__.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/_gpu_clim_check.cjs +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/_png_utils.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/bar_basic.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/gridspec_3col_equal_spectra.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/gridspec_asymmetric_width_ratios.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/gridspec_height_ratio_image_histogram.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/gridspec_image_two_spectra.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/gridspec_side_by_side_1d.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/gridspec_spanning_top_two_bottom.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/imshow_axis_off.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/imshow_checkerboard.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/imshow_gradient.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/imshow_labels.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/imshow_viridis.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/inset_1d.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/inset_maximized.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/inset_minimized.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/inset_normal_2d.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/inset_stacked.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/inset_stacked_one_minimized.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/pcolormesh_uniform.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/plot1d_all_linestyles.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/plot1d_alpha.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/plot1d_axis_off.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/plot1d_dashed.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/plot1d_marker_symbols.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/plot1d_markers.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/plot1d_multi.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/plot1d_sine.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/plot1d_title.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/plot1d_twinx.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/plot3d_surface.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/baselines/subplots_2x1.png +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/conftest.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_benchmarks/__init__.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_benchmarks/baselines.json +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_benchmarks/benchmarks/baselines.json +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_benchmarks/test_benchmarks.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_benchmarks/test_benchmarks_py.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_documentation/__init__.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_documentation/test_bridge.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_documentation/test_push_hook.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_documentation/test_scraper.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_electron/__init__.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_electron/test_binary_frame.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_embed/__init__.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_embed/test_embed_api.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_embed/test_embed_mount.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_examples/__init__.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_examples/test_interactive_examples.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_interactive/__init__.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_interactive/_event_test_utils.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_interactive/test_blit_audit.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_interactive/test_edit_chrome.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_interactive/test_edit_chrome_playwright.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_interactive/test_event_pause_hold.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_interactive/test_event_plots.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_interactive/test_event_settled.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_interactive/test_events_regression.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_interactive/test_tile_parity_playwright.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_interactive/test_title.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_interactive/test_touch.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_labels/__init__.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_labels/test_label_rendering.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_labels/test_no_clipping.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_layouts/__init__.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_layouts/test_batch.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_layouts/test_geom_channel.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_layouts/test_gridspec.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_layouts/test_interaction.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_layouts/test_visual.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_markers/__init__.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_markers/test_marker_transforms.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_markers/test_markers.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_plot1d/__init__.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_plot1d/test_plot1d.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_plot1d/test_plotbar.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_plot2d/__init__.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_plot2d/test_detail_tile.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_plot2d/test_first_paint_race.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_plot2d/test_gpu_image.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_plot2d/test_gpu_parity_playwright.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_plot2d/test_imshow.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_plot2d/test_imshow_rgb.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_plot2d/test_pcolormesh.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_plot2d/test_plot2d_api.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_plot2d/test_rapid_frame_update.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_plot2d/test_tile_backend.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_plot2d/test_tiled_imshow.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_plot2d/test_zoom_view_write.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_plot3d/__init__.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_plot3d/test_colors_highlight.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_plot3d/test_gpu_hidden_reveal.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_plot3d/test_plot3d.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_plot3d/test_voxels_planes.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/tests/test_plotxy/test_plotxy.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/widgets/__init__.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/widgets/_base.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/anyplotlib/widgets/_widgets3d.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/docs/_sg_html_scraper.py +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/docs/_static/anyplotlib.svg +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/docs/_static/custom.css +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/docs/_static/pyodide_bridge.js +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/docs/_templates/autosummary/class.rst +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/docs/api/callbacks.rst +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/docs/api/figure.rst +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/docs/api/figure_plots.rst +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/docs/api/index.rst +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/docs/api/markers.rst +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/docs/api/widgets.rst +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/docs/benchmarking.rst +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/docs/dev/index.rst +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/docs/embedding.rst +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/docs/events.rst +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/docs/getting_started.rst +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/docs/index.rst +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/make.bat +0 -0
- {anyplotlib-0.3.0 → anyplotlib-0.4.0}/upcoming_changes/README.rst +0 -0
|
@@ -10,6 +10,66 @@ Fragment files in ``upcoming_changes/`` are assembled into this file by
|
|
|
10
10
|
|
|
11
11
|
.. towncrier release notes start
|
|
12
12
|
|
|
13
|
+
0.4.0 (2026-07-18)
|
|
14
|
+
==================
|
|
15
|
+
|
|
16
|
+
New Features
|
|
17
|
+
------------
|
|
18
|
+
|
|
19
|
+
- Added :meth:`~anyplotlib.axes.InsetAxes.indicate_point` — the point sibling of
|
|
20
|
+
:meth:`~anyplotlib.axes.InsetAxes.indicate_region`: a circle-and-cross marker
|
|
21
|
+
at a data point of the parent plot plus a single leader line to the inset's
|
|
22
|
+
nearest corner, tracking zoom/pan and hiding the leader while minimized.
|
|
23
|
+
- Added :meth:`~anyplotlib.plot1d.Plot1D.set_legend_fontsize` to control the
|
|
24
|
+
legend text size on 1-D line plots.
|
|
25
|
+
- Added ``linewidth=`` to every overlay widget constructor and ``add_*_widget``
|
|
26
|
+
factory on :class:`~anyplotlib.plot2d.Plot2D` and
|
|
27
|
+
:class:`~anyplotlib.plot1d.Plot1D` (rectangle, circle, annular, crosshair,
|
|
28
|
+
polygon, vline, hline, range, point) — stroke width in px, default 2, stored
|
|
29
|
+
and round-tripped like ``color``.
|
|
30
|
+
- Added ``tint=`` to :meth:`~anyplotlib.plot2d.Plot2D.add_layer` and
|
|
31
|
+
:meth:`~anyplotlib.plot2d.Layer.set` — a ``#rgb``/``#rrggbb`` hex colour that
|
|
32
|
+
renders the layer as a clear→colour intensity ramp (transparent at low
|
|
33
|
+
intensity, opaque tint at high, via a 256×4 RGBA LUT) instead of a named
|
|
34
|
+
colormap; passing ``cmap=`` reverts a tinted layer to colormap display.
|
|
35
|
+
- An :meth:`~anyplotlib.figure.Figure.add_inset` with no title (the default,
|
|
36
|
+
``title=""``) now renders with NO title-bar strip at all — a clean bordered
|
|
37
|
+
plot box, content filling the whole area, instead of a useless empty header.
|
|
38
|
+
A titled inset is unchanged: its bar renders as before, with click-to-toggle
|
|
39
|
+
minimize. A title-less inset has no minimize affordance (there is no bar to
|
|
40
|
+
click), but drag-to-move / drag-to-resize in edit mode still work exactly as
|
|
41
|
+
before, since those gestures are wired on the inset body, not the bar.
|
|
42
|
+
- Double-clicking a plot's text chrome now reports which element was hit. The
|
|
43
|
+
``double_click`` :class:`~anyplotlib.callbacks.Event` gains a ``target`` field
|
|
44
|
+
naming the hit element — one of ``'title'``, ``'x_label'``, ``'x_ticks'``,
|
|
45
|
+
``'y_label'``, ``'y_ticks'``, ``'colorbar_label'`` or ``'legend'`` — so a host
|
|
46
|
+
can open the right edit affordance for the axis label vs the ticks vs the title
|
|
47
|
+
vs the colorbar label vs the legend. The axis gutters, colorbar strip and title
|
|
48
|
+
band each get their own hit-test (2-D panels emit from the separate axis/title
|
|
49
|
+
canvases; 1-D panels zone-split the single canvas around the plot rect and
|
|
50
|
+
legend box). A plain plot-area double-click is unchanged and carries no
|
|
51
|
+
``target`` (``event.target is None``), so existing handlers keep working.
|
|
52
|
+
- Insets can now be dragged and resized directly in the renderer's edit mode
|
|
53
|
+
(``edit_chrome``): drag the body to move an inset (a corner-stacked inset
|
|
54
|
+
converts to a free anchor and its siblings re-stack), or drag the bottom-right
|
|
55
|
+
grip to resize it (min 64 px per dimension). On release the renderer emits a
|
|
56
|
+
new figure-level ``inset_geometry_change`` event carrying the final
|
|
57
|
+
``anchor``/``w_frac``/``h_frac`` (figure fractions), which
|
|
58
|
+
:meth:`~anyplotlib.figure.Figure.add_event_handler` handlers can observe to
|
|
59
|
+
persist the layout. The same geometry is applied programmatically via the new
|
|
60
|
+
:meth:`~anyplotlib.axes.InsetAxes.set_geometry` (``anchor``, ``w_frac``,
|
|
61
|
+
``h_frac``). Off edit mode the affordances are hidden and the inset is inert.
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
Bug Fixes
|
|
65
|
+
---------
|
|
66
|
+
|
|
67
|
+
- Fixed ``exportPNG`` compositing WebGPU-rendered 3-D panels (``scatter3d`` /
|
|
68
|
+
``voxels``) as blank background rectangles — the 3-D render pass is now
|
|
69
|
+
re-rendered synchronously in-task before the canvas readback, exactly like
|
|
70
|
+
active-GPU 2-D image panels.
|
|
71
|
+
|
|
72
|
+
|
|
13
73
|
0.3.0b1 (2026-07-13)
|
|
14
74
|
====================
|
|
15
75
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: anyplotlib
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: A plotting library using python, javascript and anywidget for performant in browser plotting.
|
|
5
5
|
Project-URL: Homepage, https://cssfrancis.github.io/anyplotlib/
|
|
6
6
|
Project-URL: Repository, https://github.com/CSSFrancis/anyplotlib
|
|
@@ -148,18 +148,25 @@ inside the figure). Minimize / maximize / restore work for both — a maximized
|
|
|
148
148
|
inset floats centred at ~72 % (z 45); a minimized one collapses to its title
|
|
149
149
|
bar in place.
|
|
150
150
|
|
|
151
|
-
#### Region indications (callouts — `_drawCallouts`)
|
|
151
|
+
#### Region/point indications (callouts — `_drawCallouts`)
|
|
152
152
|
`layout.indications` is an array of mark_inset-style callouts, each
|
|
153
|
-
`{inset_id, parent_id, region:[x,y,w,h], color, linestyle, linewidth}
|
|
154
|
-
`
|
|
155
|
-
|
|
156
|
-
`
|
|
153
|
+
`{inset_id, parent_id, region:[x,y,w,h], color, linestyle, linewidth}` (from
|
|
154
|
+
`indicate_region`) or `{inset_id, parent_id, point:[x,y], color, linestyle,
|
|
155
|
+
linewidth, marker_size}` (from `indicate_point` — the `point` key selects the
|
|
156
|
+
branch). `_drawCallouts()` renders them onto a figure-level `calloutCanvas`
|
|
157
|
+
(z 30, above panels + insets, below maximized-inset float and the resize
|
|
158
|
+
handle, `pointer-events:none`):
|
|
157
159
|
- The **dashed source rect** maps `region` (parent DATA coords) through the
|
|
158
160
|
parent's `_imgToCanvas2d` every draw, so it tracks the parent's zoom/pan; it
|
|
159
161
|
is clipped to the parent's image area.
|
|
160
162
|
- Two **leader lines** connect the rect's corners facing the inset to the
|
|
161
163
|
inset's nearest corners (loc1/loc2-auto by comparing centres); they follow
|
|
162
164
|
the inset's live DOM rect and are **hidden while the inset is minimized**.
|
|
165
|
+
- A **point indication** draws a solid circle-and-cross marker (radius
|
|
166
|
+
`marker_size`, clipped to the parent image area like the rect) at the mapped
|
|
167
|
+
data point, plus ONE leader from the marker's rim to the inset's nearest
|
|
168
|
+
corner (same minimized-hide rule; the leader uses the indication's
|
|
169
|
+
linestyle, the marker itself is always solid).
|
|
163
170
|
|
|
164
171
|
`_drawCallouts()` is called at the end of `_redrawPanel` / `redrawAll` (tracks
|
|
165
172
|
zoom/pan), at the end of `_applyAllInsetStates` (inset moved), on `applyLayout`
|
|
@@ -345,9 +352,13 @@ draws each **visible** layer bottom-up on `plotCanvas`:
|
|
|
345
352
|
- `_layerBytes(st, layer)` prefers `layer_<id>_b64_bytes` (binary) over the entry
|
|
346
353
|
`image_b64` base64;
|
|
347
354
|
- `_layerBitmap(p, st, layer)` builds a LUT-colormapped RGBA `OffscreenCanvas`,
|
|
348
|
-
**cached per layer id** by `(pixel key, cmap, clim)` —
|
|
349
|
-
layer's data or appearance changes (a live scrub that
|
|
350
|
-
data rebuilds just that layer)
|
|
355
|
+
**cached per layer id** by `(pixel key, cmap, tint, has-alpha, clim)` —
|
|
356
|
+
rebuilt only when the layer's data or appearance changes (a live scrub that
|
|
357
|
+
only swaps one layer's data rebuilds just that layer). The LUT honours a 4th
|
|
358
|
+
(alpha) channel when present (`cmapData[i][3] ?? 255`) — a `tint=` layer
|
|
359
|
+
ships a 256×4 clear→colour ramp (`_build_tint_lut`), so per-texel alpha
|
|
360
|
+
composites through the unpremultiplied `ImageData` and multiplies naturally
|
|
361
|
+
with the per-layer `ctx.globalAlpha`;
|
|
351
362
|
- it blits with the SAME fit-rect + zoom/pan transform as the base blit
|
|
352
363
|
(`_imgFitRect` + the `zoom>=1` window math) at `ctx.globalAlpha = layer.alpha`,
|
|
353
364
|
so zoom/pan track the base exactly.
|
|
@@ -493,9 +504,14 @@ figure onto one offscreen canvas at `devicePixelRatio × scale`:
|
|
|
493
504
|
|
|
494
505
|
- **WebGPU hazard first**: a WebGPU canvas's drawing buffer is only valid right
|
|
495
506
|
after its render pass, so exportPNG force-calls `draw2d(p)` on every
|
|
496
|
-
active-GPU 2-D panel (`
|
|
497
|
-
|
|
498
|
-
|
|
507
|
+
active-GPU 2-D panel and `draw3d(p)` on every active-GPU 3-D panel
|
|
508
|
+
(`p._gpu==='active' && p.gpuCanvas` visible, `_gpuImg`/`_gpuObj` present) to
|
|
509
|
+
re-submit its pass, THEN composites in the SAME synchronous task — so
|
|
510
|
+
`drawImage(gpuCanvas,…)` reads live pixels, not a blank buffer. (draw3d's
|
|
511
|
+
active-GPU path uploads + submits in-task, no rAF, so the same-task re-render
|
|
512
|
+
suffices for 3-D too — without it a scatter3d/voxels panel exported as an
|
|
513
|
+
empty background rectangle; see `TestExportGpu3d` in
|
|
514
|
+
`tests/test_embed/test_export_png.py`.)
|
|
499
515
|
- **Extent**: `fig_width/height + 2×8 px` gridDiv padding (NOT the measured
|
|
500
516
|
`gridDiv` width — a bare `mount()` page has no `.apl-outer` inline-block CSS,
|
|
501
517
|
so the grid container can stretch to the viewport). **Origin**: `gridDiv`'s
|
|
@@ -192,6 +192,44 @@ def _build_colormap_lut(name: str) -> list:
|
|
|
192
192
|
return [[v, v, v] for v in range(256)]
|
|
193
193
|
|
|
194
194
|
|
|
195
|
+
def _parse_hex_color(color: str) -> tuple:
|
|
196
|
+
"""Parse a ``#rgb`` / ``#rrggbb`` hex string → ``(r, g, b)`` ints (0–255).
|
|
197
|
+
|
|
198
|
+
Raises ``ValueError`` for anything else — tint colours are user API input
|
|
199
|
+
and a silent fallback would just render the wrong colour.
|
|
200
|
+
"""
|
|
201
|
+
if not isinstance(color, str):
|
|
202
|
+
raise ValueError(f"expected a hex colour string, got {color!r}")
|
|
203
|
+
h = color.strip().lstrip("#")
|
|
204
|
+
try:
|
|
205
|
+
if len(h) == 3:
|
|
206
|
+
return tuple(int(c * 2, 16) for c in h)
|
|
207
|
+
if len(h) == 6:
|
|
208
|
+
return (int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16))
|
|
209
|
+
except ValueError:
|
|
210
|
+
pass
|
|
211
|
+
raise ValueError(
|
|
212
|
+
f"invalid hex colour {color!r} — expected '#rgb' or '#rrggbb'")
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
@functools.lru_cache(maxsize=64)
|
|
216
|
+
def _build_tint_lut(color: str) -> list:
|
|
217
|
+
"""Return a 256-entry ``[[r, g, b, a], ...]`` clear→colour TINT LUT.
|
|
218
|
+
|
|
219
|
+
RGB is the parsed *color* at every entry; alpha ramps linearly 0 → 255, so
|
|
220
|
+
a layer using this LUT is fully transparent at low intensity and the
|
|
221
|
+
opaque tint colour at high intensity (a solid-colour intensity ramp, the
|
|
222
|
+
overlay look, vs a named colormap's opaque colour gradient).
|
|
223
|
+
|
|
224
|
+
CACHED like :func:`_build_colormap_lut` and treated as read-only by
|
|
225
|
+
callers (they JSON-serialise it); do NOT mutate the returned list. The
|
|
226
|
+
4th (alpha) channel is honoured by the JS layer compositor
|
|
227
|
+
(``_layerBitmap`` reads ``cmapData[i][3] ?? 255``).
|
|
228
|
+
"""
|
|
229
|
+
r, g, b = _parse_hex_color(color)
|
|
230
|
+
return [[r, g, b, a] for a in range(256)]
|
|
231
|
+
|
|
232
|
+
|
|
195
233
|
def _resample_mesh(data: np.ndarray, x_edges, y_edges) -> np.ndarray:
|
|
196
234
|
"""Resample a mesh to a regular pixel grid via nearest-neighbour lookup.
|
|
197
235
|
|
|
@@ -96,9 +96,10 @@ class InsetAxes(Axes):
|
|
|
96
96
|
self.h_frac = h_frac
|
|
97
97
|
self.title = title
|
|
98
98
|
self._inset_state: str = "normal"
|
|
99
|
-
# Region indication (mark_inset-style callout) tied to this
|
|
100
|
-
# None. Set via :meth:`indicate_region
|
|
101
|
-
# :meth:`clear_indication`.
|
|
99
|
+
# Region/point indication (mark_inset-style callout) tied to this
|
|
100
|
+
# inset, or None. Set via :meth:`indicate_region` /
|
|
101
|
+
# :meth:`indicate_point`, cleared via :meth:`clear_indication`.
|
|
102
|
+
# Persisted in Figure.layout_json.
|
|
102
103
|
self._indication: dict | None = None
|
|
103
104
|
|
|
104
105
|
# ── state API ─────────────────────────────────────────────────────────
|
|
@@ -129,6 +130,76 @@ class InsetAxes(Axes):
|
|
|
129
130
|
self._inset_state = "normal"
|
|
130
131
|
self._fig._push_layout()
|
|
131
132
|
|
|
133
|
+
def set_geometry(self, *, anchor=None, w_frac: float | None = None,
|
|
134
|
+
h_frac: float | None = None) -> "InsetAxes":
|
|
135
|
+
"""Move and/or resize the inset in figure-fraction coordinates.
|
|
136
|
+
|
|
137
|
+
This is the Python-side counterpart to the interactive drag / resize
|
|
138
|
+
of an inset in the renderer's edit mode: the ``inset_geometry_change``
|
|
139
|
+
event dispatches here so the authoritative Python state converges with
|
|
140
|
+
what the user did on screen. It is also callable directly to place or
|
|
141
|
+
size an inset programmatically.
|
|
142
|
+
|
|
143
|
+
Passing *anchor* switches the inset to free (anchor) placement and
|
|
144
|
+
drops any corner snapping (``corner`` becomes ``None``), mirroring the
|
|
145
|
+
renderer's corner-to-anchor conversion on drag start.
|
|
146
|
+
|
|
147
|
+
Parameters
|
|
148
|
+
----------
|
|
149
|
+
anchor : (x_frac, y_frac), optional
|
|
150
|
+
New position of the inset's TOP-LEFT corner as fractions of the
|
|
151
|
+
figure size (0–1), measured from the figure's top-left. Each
|
|
152
|
+
component is clamped into ``[0, 1]``. When given, ``corner`` is
|
|
153
|
+
set to ``None`` (free placement). When omitted, the current
|
|
154
|
+
placement (corner or existing anchor) is left unchanged.
|
|
155
|
+
w_frac, h_frac : float, optional
|
|
156
|
+
New width / height as fractions of the figure size. Each is
|
|
157
|
+
clamped into ``(0, 1]`` (must be positive, at most the full
|
|
158
|
+
figure). When omitted, the corresponding dimension is unchanged.
|
|
159
|
+
|
|
160
|
+
Returns
|
|
161
|
+
-------
|
|
162
|
+
InsetAxes
|
|
163
|
+
``self``, for chaining.
|
|
164
|
+
|
|
165
|
+
Raises
|
|
166
|
+
------
|
|
167
|
+
ValueError
|
|
168
|
+
If *anchor* is not a pair of finite numbers, or ``w_frac`` /
|
|
169
|
+
``h_frac`` is not a finite number.
|
|
170
|
+
"""
|
|
171
|
+
if anchor is not None:
|
|
172
|
+
try:
|
|
173
|
+
ax_, ay_ = (float(v) for v in anchor)
|
|
174
|
+
except (TypeError, ValueError):
|
|
175
|
+
raise ValueError(
|
|
176
|
+
f"set_geometry: anchor must be 2 numbers (x_frac, y_frac), "
|
|
177
|
+
f"got {anchor!r}") from None
|
|
178
|
+
if not all(math.isfinite(v) for v in (ax_, ay_)):
|
|
179
|
+
raise ValueError(
|
|
180
|
+
f"set_geometry: anchor values must be finite, got "
|
|
181
|
+
f"({ax_}, {ay_})")
|
|
182
|
+
ax_ = min(1.0, max(0.0, ax_))
|
|
183
|
+
ay_ = min(1.0, max(0.0, ay_))
|
|
184
|
+
self.anchor = (ax_, ay_)
|
|
185
|
+
# Anchor placement supersedes corner (matches __init__ / the JS
|
|
186
|
+
# corner→anchor conversion on drag start).
|
|
187
|
+
self.corner = None
|
|
188
|
+
if w_frac is not None:
|
|
189
|
+
w = float(w_frac)
|
|
190
|
+
if not math.isfinite(w):
|
|
191
|
+
raise ValueError(
|
|
192
|
+
f"set_geometry: w_frac must be finite, got {w_frac!r}")
|
|
193
|
+
self.w_frac = min(1.0, max(1e-6, w))
|
|
194
|
+
if h_frac is not None:
|
|
195
|
+
h = float(h_frac)
|
|
196
|
+
if not math.isfinite(h):
|
|
197
|
+
raise ValueError(
|
|
198
|
+
f"set_geometry: h_frac must be finite, got {h_frac!r}")
|
|
199
|
+
self.h_frac = min(1.0, max(1e-6, h))
|
|
200
|
+
self._fig._push_layout()
|
|
201
|
+
return self
|
|
202
|
+
|
|
132
203
|
# ── region indication (mark_inset-style callout) ──────────────────────
|
|
133
204
|
|
|
134
205
|
def indicate_region(self, parent_plot, region, *,
|
|
@@ -218,8 +289,94 @@ class InsetAxes(Axes):
|
|
|
218
289
|
self._fig._push_layout()
|
|
219
290
|
return self
|
|
220
291
|
|
|
292
|
+
def indicate_point(self, parent_plot, point, *,
|
|
293
|
+
color: str = "#ff9800",
|
|
294
|
+
linestyle: str = "dashed",
|
|
295
|
+
linewidth: float = 1.5,
|
|
296
|
+
marker_size: float = 5.0) -> "InsetAxes":
|
|
297
|
+
"""Draw a callout tying this inset to a single POINT of *parent_plot*.
|
|
298
|
+
|
|
299
|
+
The point sibling of :meth:`indicate_region`: renders a small circular
|
|
300
|
+
marker (with a centre cross) at *point* — in the parent image's DATA
|
|
301
|
+
coordinates — plus ONE leader line joining the marker to the inset's
|
|
302
|
+
nearest corner. The marker tracks the parent's zoom / pan and the
|
|
303
|
+
leader follows the inset as it moves (the leader hides while the inset
|
|
304
|
+
is minimized), exactly like the region callout.
|
|
305
|
+
|
|
306
|
+
Calling ``indicate_point`` (or ``indicate_region``) again REPLACES any
|
|
307
|
+
previous indication for this inset — an inset carries at most one.
|
|
308
|
+
Remove it with :meth:`clear_indication`.
|
|
309
|
+
|
|
310
|
+
Parameters
|
|
311
|
+
----------
|
|
312
|
+
parent_plot : Plot2D
|
|
313
|
+
The parent image plot the point lives on. Must be a 2-D image
|
|
314
|
+
panel registered on the SAME figure as this inset.
|
|
315
|
+
point : tuple of float
|
|
316
|
+
The source point in the parent image's data coordinates, as
|
|
317
|
+
``(x, y)`` — same convention as :meth:`indicate_region`'s region
|
|
318
|
+
origin. Both values must be finite; a point outside the parent's
|
|
319
|
+
data bounds is allowed (it simply clips visually).
|
|
320
|
+
color : str, optional
|
|
321
|
+
Stroke colour of the marker and the leader line. Default warm
|
|
322
|
+
orange ``"#ff9800"``.
|
|
323
|
+
linestyle : str, optional
|
|
324
|
+
Leader style — ``"dashed"`` (default), ``"solid"``, or
|
|
325
|
+
``"dotted"``. The marker itself is always drawn solid.
|
|
326
|
+
linewidth : float, optional
|
|
327
|
+
Stroke width in CSS px. Default ``1.5``.
|
|
328
|
+
marker_size : float, optional
|
|
329
|
+
Marker circle radius in CSS px. Default ``5.0``. Must be > 0.
|
|
330
|
+
|
|
331
|
+
Returns
|
|
332
|
+
-------
|
|
333
|
+
InsetAxes
|
|
334
|
+
``self``, for chaining.
|
|
335
|
+
|
|
336
|
+
Raises
|
|
337
|
+
------
|
|
338
|
+
ValueError
|
|
339
|
+
If ``parent_plot`` has no panel id, is not registered on this
|
|
340
|
+
inset's Figure, ``point`` is not 2 finite numbers, or
|
|
341
|
+
``marker_size`` is not > 0.
|
|
342
|
+
"""
|
|
343
|
+
pid = getattr(parent_plot, "_id", None)
|
|
344
|
+
if pid is None:
|
|
345
|
+
raise ValueError("indicate_point: parent_plot has no panel id "
|
|
346
|
+
"(attach it to the figure first)")
|
|
347
|
+
if self._fig._plots_map.get(pid) is not parent_plot:
|
|
348
|
+
raise ValueError(
|
|
349
|
+
"indicate_point: parent_plot is not registered on this "
|
|
350
|
+
"inset's Figure — pass a plot created on the same figure "
|
|
351
|
+
"as this inset (fig.add_inset / fig.subplots)")
|
|
352
|
+
try:
|
|
353
|
+
x, y = (float(v) for v in point)
|
|
354
|
+
except (TypeError, ValueError):
|
|
355
|
+
raise ValueError(
|
|
356
|
+
f"indicate_point: point must be 2 numbers (x, y), "
|
|
357
|
+
f"got {point!r}") from None
|
|
358
|
+
if not all(math.isfinite(v) for v in (x, y)):
|
|
359
|
+
raise ValueError(
|
|
360
|
+
f"indicate_point: point values must be finite, got "
|
|
361
|
+
f"(x={x}, y={y})")
|
|
362
|
+
ms = float(marker_size)
|
|
363
|
+
if not (math.isfinite(ms) and ms > 0):
|
|
364
|
+
raise ValueError(
|
|
365
|
+
f"indicate_point: marker_size must be > 0, got {marker_size!r}")
|
|
366
|
+
self._indication = {
|
|
367
|
+
"parent_id": pid,
|
|
368
|
+
"point": [x, y],
|
|
369
|
+
"color": color,
|
|
370
|
+
"linestyle": linestyle,
|
|
371
|
+
"linewidth": float(linewidth),
|
|
372
|
+
"marker_size": ms,
|
|
373
|
+
}
|
|
374
|
+
self._fig._push_layout()
|
|
375
|
+
return self
|
|
376
|
+
|
|
221
377
|
def clear_indication(self) -> None:
|
|
222
|
-
"""Remove any region indication attached to this inset
|
|
378
|
+
"""Remove any region/point indication attached to this inset
|
|
379
|
+
(idempotent)."""
|
|
223
380
|
if self._indication is None:
|
|
224
381
|
return
|
|
225
382
|
self._indication = None
|
|
@@ -227,7 +384,8 @@ class InsetAxes(Axes):
|
|
|
227
384
|
|
|
228
385
|
@property
|
|
229
386
|
def indication(self) -> "dict | None":
|
|
230
|
-
"""The current
|
|
387
|
+
"""The current indication spec (``dict`` with either a ``region`` or a
|
|
388
|
+
``point`` key) or ``None``."""
|
|
231
389
|
return self._indication
|
|
232
390
|
|
|
233
391
|
# ── internal ──────────────────────────────────────────────────────────
|
|
@@ -24,7 +24,10 @@ from typing import Any, Callable
|
|
|
24
24
|
VALID_EVENT_TYPES = frozenset({
|
|
25
25
|
"pointer_down", "pointer_up", "pointer_move", "pointer_settled",
|
|
26
26
|
"pointer_enter", "pointer_leave", "double_click", "wheel",
|
|
27
|
-
"key_down", "key_up", "close", "view_changed",
|
|
27
|
+
"key_down", "key_up", "close", "view_changed",
|
|
28
|
+
# Figure-level inset drag / resize (JS → Python), so a host can persist the
|
|
29
|
+
# inset's new geometry. Fired through the figure's CallbackRegistry.
|
|
30
|
+
"inset_geometry_change", "*",
|
|
28
31
|
})
|
|
29
32
|
|
|
30
33
|
|
|
@@ -43,6 +46,7 @@ class Event:
|
|
|
43
46
|
ray — Plot3D only: {"origin": [...], "direction": [...]}
|
|
44
47
|
line_id — Plot1D only: set when pointer is over a line
|
|
45
48
|
dwell_ms — pointer_settled only: actual dwell time
|
|
49
|
+
target — double_click only: hit chrome element, or None for plot-area (title/x_label/x_ticks/y_label/y_ticks/colorbar_label/legend)
|
|
46
50
|
|
|
47
51
|
PlotBar extra fields (pointer_down only):
|
|
48
52
|
bar_index, value, x_label, group_index
|
|
@@ -54,6 +58,14 @@ class Event:
|
|
|
54
58
|
key — key name e.g. "q", "Enter", "ArrowLeft"
|
|
55
59
|
last_widget_id — id of the last widget the user clicked, or None
|
|
56
60
|
|
|
61
|
+
Panel-swap fields (figure-level panel_swap events):
|
|
62
|
+
source_panel_id, target_panel_id — the two panel ids dragged between
|
|
63
|
+
|
|
64
|
+
Inset drag/resize fields (figure-level inset_geometry_change events):
|
|
65
|
+
inset_id — the inset panel id
|
|
66
|
+
anchor — new top-left position [fx, fy] in figure fractions
|
|
67
|
+
w_frac, h_frac — new size in figure fractions
|
|
68
|
+
|
|
57
69
|
Propagation:
|
|
58
70
|
stop_propagation — set True inside a handler to halt remaining handlers
|
|
59
71
|
"""
|
|
@@ -94,6 +106,18 @@ class Event:
|
|
|
94
106
|
# ids the user dragged between. Set only on panel_swap; None otherwise.
|
|
95
107
|
source_panel_id: str | None = None
|
|
96
108
|
target_panel_id: str | None = None
|
|
109
|
+
# Inset drag / resize (figure-level inset_geometry_change events): the
|
|
110
|
+
# inset panel id plus its new geometry in figure fractions. Set only on
|
|
111
|
+
# inset_geometry_change; None otherwise.
|
|
112
|
+
inset_id: str | None = None
|
|
113
|
+
anchor: list | None = None
|
|
114
|
+
w_frac: float | None = None
|
|
115
|
+
h_frac: float | None = None
|
|
116
|
+
# Tagged double-click hit-target (double_click events): identifies WHICH
|
|
117
|
+
# text/chrome element was double-clicked — one of 'title', 'x_label',
|
|
118
|
+
# 'x_ticks', 'y_label', 'y_ticks', 'colorbar_label', 'legend'. None for a
|
|
119
|
+
# plain plot-area double_click (back-compatible) and every other event.
|
|
120
|
+
target: str | None = None
|
|
97
121
|
# Propagation (not repr'd)
|
|
98
122
|
stop_propagation: bool = field(default=False, repr=False)
|
|
99
123
|
|
|
@@ -101,7 +125,7 @@ class Event:
|
|
|
101
125
|
src = type(self.source).__name__ if self.source is not None else "None"
|
|
102
126
|
parts = [f"event_type={self.event_type!r}", f"source={src}"]
|
|
103
127
|
for fname in ("x", "y", "xdata", "ydata", "button", "key",
|
|
104
|
-
"line_id", "bar_index", "dwell_ms"):
|
|
128
|
+
"line_id", "bar_index", "dwell_ms", "target"):
|
|
105
129
|
v = getattr(self, fname)
|
|
106
130
|
if v is not None:
|
|
107
131
|
parts.append(f"{fname}={v!r}")
|
|
@@ -596,6 +596,23 @@ class Figure(anywidget.AnyWidget, _EventMixin):
|
|
|
596
596
|
self._push_layout()
|
|
597
597
|
return
|
|
598
598
|
|
|
599
|
+
# Inset drag / resize: the JS ships the inset's final geometry on
|
|
600
|
+
# pointer_up (anchor top-left fraction + size fractions). Apply it to
|
|
601
|
+
# the InsetAxes via set_geometry so the authoritative Python state
|
|
602
|
+
# converges (the event fires once, on release, so re-pushing layout is
|
|
603
|
+
# an idempotent echo), then fire the figure-level callbacks with the
|
|
604
|
+
# geometry so a host can persist it. Mirrors inset_state_change above.
|
|
605
|
+
if event_type == "inset_geometry_change":
|
|
606
|
+
inset_ax = self._insets_map.get(panel_id)
|
|
607
|
+
if inset_ax is not None:
|
|
608
|
+
anchor = msg.get("anchor")
|
|
609
|
+
w_frac = msg.get("w_frac")
|
|
610
|
+
h_frac = msg.get("h_frac")
|
|
611
|
+
inset_ax.set_geometry(anchor=anchor, w_frac=w_frac,
|
|
612
|
+
h_frac=h_frac)
|
|
613
|
+
self._fire_figure_event(event_type, msg)
|
|
614
|
+
return
|
|
615
|
+
|
|
599
616
|
plot = self._plots_map.get(panel_id)
|
|
600
617
|
if plot is None:
|
|
601
618
|
if event_type == "view_changed":
|
|
@@ -656,6 +673,7 @@ class Figure(anywidget.AnyWidget, _EventMixin):
|
|
|
656
673
|
display_height=msg.get("display_height"),
|
|
657
674
|
key=msg.get("key"),
|
|
658
675
|
last_widget_id=msg.get("last_widget_id"),
|
|
676
|
+
target=msg.get("target"),
|
|
659
677
|
)
|
|
660
678
|
plot.callbacks.fire(event)
|
|
661
679
|
|
|
@@ -683,6 +701,17 @@ class Figure(anywidget.AnyWidget, _EventMixin):
|
|
|
683
701
|
last_widget_id=msg.get("marker_id"),
|
|
684
702
|
source_panel_id=msg.get("source_panel_id"),
|
|
685
703
|
target_panel_id=msg.get("target_panel_id"),
|
|
704
|
+
# Inset drag / resize geometry — populated only for the
|
|
705
|
+
# inset_geometry_change event; None for every other figure-level
|
|
706
|
+
# event (figure_background click, figure-marker drag, panel_swap).
|
|
707
|
+
inset_id=(msg.get("panel_id")
|
|
708
|
+
if event_type == "inset_geometry_change" else None),
|
|
709
|
+
anchor=(msg.get("anchor")
|
|
710
|
+
if event_type == "inset_geometry_change" else None),
|
|
711
|
+
w_frac=(msg.get("w_frac")
|
|
712
|
+
if event_type == "inset_geometry_change" else None),
|
|
713
|
+
h_frac=(msg.get("h_frac")
|
|
714
|
+
if event_type == "inset_geometry_change" else None),
|
|
686
715
|
)
|
|
687
716
|
self.callbacks.fire(event)
|
|
688
717
|
|