anyplotlib 0.9.0__tar.gz → 0.10.1__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.9.0 → anyplotlib-0.10.1}/AGENTS.md +1 -1
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/CHANGELOG.rst +32 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/PKG-INFO +1 -1
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/FIGURE_ESM.md +119 -84
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/_base_plot.py +99 -4
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/_repr_utils.py +18 -3
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/callbacks.py +1 -1
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/figure_esm.js +343 -116
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/markers.py +34 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/plot1d/_plot1d.py +16 -1
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/plot2d/_plot2d.py +91 -10
- anyplotlib-0.10.1/anyplotlib/tests/baselines/imshow_labels.png +0 -0
- anyplotlib-0.10.1/anyplotlib/tests/test_embed/test_host_saves_png.py +114 -0
- anyplotlib-0.10.1/anyplotlib/tests/test_markers/test_text_style.py +146 -0
- anyplotlib-0.10.1/anyplotlib/tests/test_plot2d/test_axis_ticks.py +205 -0
- anyplotlib-0.10.1/anyplotlib/tests/test_plot2d/test_colorbar_values.py +298 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot2d/test_imshow.py +13 -10
- anyplotlib-0.10.1/anyplotlib/tests/test_plot2d/test_pointer_pixel_centre.py +168 -0
- anyplotlib-0.10.1/anyplotlib/tests/test_plotxy/test_raster_zoom.py +109 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/docs/_root/index.html +3 -3
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/docs/_root/switcher.json +10 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/docs/conf.py +1 -1
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/docs/embedding.rst +3 -2
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/docs/events.rst +8 -4
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/docs/exporting.rst +20 -2
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/pyproject.toml +1 -1
- anyplotlib-0.9.0/anyplotlib/tests/baselines/imshow_labels.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/.github/workflows/ci.yml +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/.github/workflows/docs.yml +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/.github/workflows/prepare_release.yml +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/.github/workflows/release.yml +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/.github/workflows/tests.yml +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/.gitignore +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Benchmarks/README.rst +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Benchmarks/plot_benchmark_comparison.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Interactive/README.rst +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Interactive/plot_3d_spectral_viewer.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Interactive/plot_eels_explorer.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Interactive/plot_figure_annotations.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Interactive/plot_interactive_fft.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Interactive/plot_interactive_fitting.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Interactive/plot_ipf_density_map.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Interactive/plot_ipf_explorer.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Interactive/plot_key_bindings.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Interactive/plot_particle_picker.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Interactive/plot_point_widget.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Interactive/plot_segment_by_contrast.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Interactive/plot_segment_by_contrast_advanced.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Interactive/plot_spectra_roi_inspector.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Interactive/plot_star_globe_explorer.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Interactive/plot_threshold_explorer.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Interactive/plot_voxel_grain_explorer.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Markers/README.rst +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Markers/plot_arrows.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Markers/plot_circles.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Markers/plot_ellipses.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Markers/plot_horizontal_lines.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Markers/plot_line_segments.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Markers/plot_points.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Markers/plot_polygons.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Markers/plot_rectangles.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Markers/plot_squares.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Markers/plot_texts.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Markers/plot_transform_markers.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Markers/plot_vertical_lines.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/PlotTypes/README.rst +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/PlotTypes/plot_3d.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/PlotTypes/plot_3d_texture.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/PlotTypes/plot_aspect_ratio.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/PlotTypes/plot_bar.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/PlotTypes/plot_gpu_voxels.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/PlotTypes/plot_gridspec_custom.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/PlotTypes/plot_image2d.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/PlotTypes/plot_inset.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/PlotTypes/plot_key_overlays.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/PlotTypes/plot_label_formatting.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/PlotTypes/plot_line_styles.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/PlotTypes/plot_pcolormesh.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/PlotTypes/plot_spectra1d.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/PlotTypes/plot_step_and_log.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/PlotTypes/plot_subplots_adjust.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/PlotTypes/plot_tile_backend.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/PlotTypes/plot_twinx.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/README.rst +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Widgets/README.rst +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Widgets/plot_widget1d_hline.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Widgets/plot_widget1d_point.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Widgets/plot_widget1d_range.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Widgets/plot_widget1d_vline.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Widgets/plot_widget2d_annular.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Widgets/plot_widget2d_arrow.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Widgets/plot_widget2d_circle.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Widgets/plot_widget2d_crosshair.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Widgets/plot_widget2d_label.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Widgets/plot_widget2d_polygon.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Widgets/plot_widget2d_rectangle.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Examples/Widgets/plot_widget3d_plane.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/LICENSE +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/Makefile +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/README.md +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/__init__.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/_binary_frame.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/_electron.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/_export.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/_utils.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/axes/__init__.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/axes/_axes.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/axes/_inset_axes.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/conftest.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/embed.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/figure/__init__.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/figure/_figure.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/figure/_gridspec.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/figure/_subplots.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/keys.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/plot1d/__init__.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/plot1d/_plotbar.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/plot2d/__init__.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/plot2d/_layer.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/plot2d/_plotmesh.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/plot2d/_tile_backend.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/plot3d/__init__.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/plot3d/_plot3d.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/plotxy/__init__.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/plotxy/_plotxy.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/sphinx_anywidget/__init__.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/sphinx_anywidget/_directive.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/sphinx_anywidget/_repr_utils.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/sphinx_anywidget/_scraper.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/sphinx_anywidget/_wheel_builder.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/sphinx_anywidget/static/anywidget_bridge.js +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/sphinx_anywidget/static/anywidget_overlay.css +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/sphinx_anywidget/tests/__init__.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/sphinx_anywidget/tests/conftest.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/sphinx_anywidget/tests/test_directive.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/sphinx_anywidget/tests/test_init.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/sphinx_anywidget/tests/test_repr_utils.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/sphinx_anywidget/tests/test_scraper.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/sphinx_anywidget/tests/test_wheel_builder.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/__init__.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/_gpu_clim_check.cjs +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/_png_utils.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/bar_basic.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/gridspec_3col_equal_spectra.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/gridspec_asymmetric_width_ratios.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/gridspec_height_ratio_image_histogram.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/gridspec_image_two_spectra.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/gridspec_side_by_side_1d.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/gridspec_spanning_top_two_bottom.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/imshow_axis_off.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/imshow_checkerboard.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/imshow_gradient.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/imshow_viridis.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/inset_1d.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/inset_maximized.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/inset_minimized.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/inset_normal_2d.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/inset_stacked.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/inset_stacked_one_minimized.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/pcolormesh_uniform.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/plot1d_all_linestyles.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/plot1d_alpha.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/plot1d_axis_off.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/plot1d_dashed.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/plot1d_marker_symbols.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/plot1d_markers.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/plot1d_multi.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/plot1d_sine.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/plot1d_title.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/plot1d_twinx.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/plot3d_surface.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/baselines/subplots_2x1.png +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/conftest.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_benchmarks/__init__.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_benchmarks/baselines.json +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_benchmarks/benchmarks/baselines.json +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_benchmarks/test_benchmarks.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_benchmarks/test_benchmarks_py.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_documentation/__init__.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_documentation/test_bridge.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_documentation/test_push_hook.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_documentation/test_scraper.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_electron/__init__.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_electron/test_binary_frame.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_embed/__init__.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_embed/_export_utils.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_embed/conftest.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_embed/test_embed_3d.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_embed/test_embed_api.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_embed/test_embed_escaping.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_embed/test_embed_mount.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_embed/test_embed_navigated.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_embed/test_embed_set_image.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_embed/test_export_menu.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_embed/test_export_pixel_tokens.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_embed/test_export_png.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_embed/test_export_sources.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_embed/test_export_widget_sync.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_embed/test_savefig.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_examples/__init__.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_examples/test_interactive_examples.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_interactive/__init__.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_interactive/_event_test_utils.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_interactive/test_blit_audit.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_interactive/test_callbacks_playwright.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_interactive/test_callbacks_unit.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_interactive/test_circle_lock_center.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_interactive/test_edit_chrome.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_interactive/test_edit_chrome_playwright.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_interactive/test_event_pause_hold.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_interactive/test_event_plots.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_interactive/test_event_settled.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_interactive/test_events_regression.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_interactive/test_orbit_direction.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_interactive/test_pointer_down_1d.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_interactive/test_range_orientation_snap.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_interactive/test_star_globe_linking.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_interactive/test_tile_parity_playwright.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_interactive/test_title.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_interactive/test_touch.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_interactive/test_widget_brush.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_interactive/test_widget_max_extent.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_interactive/test_widget_set_notify.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_interactive/test_widgets.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_interactive/test_widgets_line_2d.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_keys/test_keys.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_labels/__init__.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_labels/test_label_api.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_labels/test_label_rendering.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_labels/test_no_clipping.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_labels/test_ylabel_clearance.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_layouts/__init__.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_layouts/test_batch.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_layouts/test_coord_conversion.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_layouts/test_geom_channel.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_layouts/test_gridspec.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_layouts/test_inset.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_layouts/test_inset_callout.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_layouts/test_interaction.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_layouts/test_visual.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_markers/__init__.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_markers/test_marker_transforms.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_markers/test_markers.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_markers/test_per_marker_colors.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_markers/test_size_units.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot1d/__init__.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot1d/test_line_none_rendering.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot1d/test_plot1d.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot1d/test_plotbar.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot2d/__init__.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot2d/test_colorbar_gap_scalebar.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot2d/test_detail_tile.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot2d/test_display_window.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot2d/test_first_paint_race.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot2d/test_gpu_image.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot2d/test_gpu_parity_playwright.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot2d/test_imshow_rgb.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot2d/test_layers.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot2d/test_layers_playwright.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot2d/test_pcolormesh.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot2d/test_plot2d_api.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot2d/test_rapid_frame_update.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot2d/test_status_pixel_value.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot2d/test_tile_backend.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot2d/test_tile_diagnostics.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot2d/test_tiled_imshow.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot2d/test_zoom_view_write.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot3d/__init__.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot3d/test_colors_highlight.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot3d/test_gpu_depth.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot3d/test_gpu_fallback.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot3d/test_gpu_hidden_reveal.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot3d/test_plot3d.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot3d/test_texture.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plot3d/test_voxels_planes.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/tests/test_plotxy/test_plotxy.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/widgets/__init__.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/widgets/_base.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/widgets/_widgets1d.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/widgets/_widgets2d.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/anyplotlib/widgets/_widgets3d.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/docs/_sg_html_scraper.py +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/docs/_static/anyplotlib.svg +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/docs/_static/custom.css +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/docs/_static/pyodide_bridge.js +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/docs/_templates/autosummary/class.rst +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/docs/api/callbacks.rst +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/docs/api/figure.rst +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/docs/api/figure_plots.rst +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/docs/api/index.rst +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/docs/api/keys.rst +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/docs/api/markers.rst +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/docs/api/widgets.rst +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/docs/benchmarking.rst +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/docs/dev/index.rst +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/docs/getting_started.rst +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/docs/index.rst +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/make.bat +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/upcoming_changes/.gitkeep +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/upcoming_changes/README.rst +0 -0
- {anyplotlib-0.9.0 → anyplotlib-0.10.1}/uv.lock +0 -0
|
@@ -115,7 +115,7 @@ grep -nE '^\s*(function|const|let) [A-Za-z_]' anyplotlib/figure_esm.js
|
|
|
115
115
|
```
|
|
116
116
|
|
|
117
117
|
and reconcile against the two numbered tables (the section map near the top and
|
|
118
|
-
the 2-D function table). Both were last verified at 12,
|
|
118
|
+
the 2-D function table). Both were last verified at 12,438 lines.
|
|
119
119
|
|
|
120
120
|
Changelog entries: add a fragment file to `upcoming_changes/` (e.g.
|
|
121
121
|
`123.new_feature.rst`) — towncrier assembles `CHANGELOG.rst` at release time.
|
|
@@ -10,6 +10,38 @@ Fragment files in ``upcoming_changes/`` are assembled into this file by
|
|
|
10
10
|
|
|
11
11
|
.. towncrier release notes start
|
|
12
12
|
|
|
13
|
+
0.10.1 (2026-09-17)
|
|
14
|
+
===================
|
|
15
|
+
|
|
16
|
+
New Features
|
|
17
|
+
------------
|
|
18
|
+
|
|
19
|
+
- A page embedding a figure in an iframe can post ``{type: 'anyplotlib_host', savesPng: true}`` to say it saves exported PNGs itself; the figure's "Save PNG…" then only sends the image to the host, without the in-figure "Save image as…" preview or the "Save as… (choose folder)" entry. (`#78 <https://github.com/CSSFrancis/anyplotlib/pull/78>`_)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
Bug Fixes
|
|
23
|
+
---------
|
|
24
|
+
|
|
25
|
+
- Zooming or panning a coordinate axis no longer blanks an :meth:`~anyplotlib.plotxy.PlotXY.add_raster` image, and every marker drawn after it, until the next update from Python. (`#76 <https://github.com/CSSFrancis/anyplotlib/pull/76>`_)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
0.10.0 (2026-09-14)
|
|
29
|
+
===================
|
|
30
|
+
|
|
31
|
+
API and Behaviour Changes
|
|
32
|
+
-------------------------
|
|
33
|
+
|
|
34
|
+
- 2-D pointer events (``pointer_down``, ``double_click``, ``pointer_settled``, ``key_down``) and the hover readout now report ``img_x``/``img_y`` in the pixel-centre convention that markers, widgets and :meth:`~anyplotlib.Plot2D.display_to_data` already used — they read half a pixel right and down before — so ``round(event.img_x)`` names the clicked pixel (``int()`` no longer does), brush strokes land under the cursor, and an ``imshow`` event's ``xdata``/``ydata`` is exactly the axis value of the pixel centre it hits (``pcolormesh`` values are unchanged). (`#73 <https://github.com/CSSFrancis/anyplotlib/pull/73>`_)
|
|
35
|
+
- 2-D tick labels now sit on the pixels they name: ``imshow`` axis values are placed at pixel centres (the image extends half a pixel past the first and last value, as matplotlib's ``imshow`` extent does) through the same transform markers and pointer events use, so ticks also follow a letterboxed image instead of spanning the whole gutter, and an ``origin='lower'`` image gets y ticks at all; :meth:`~anyplotlib.Plot2D.set_view` / ``set_xlim`` / ``set_ylim`` read the axis the same way — ``set_xlim(-0.5, n - 0.5)`` is the whole image, ``set_xlim(0, n - 1)`` now crops the outer half pixels — and accept a descending ``origin='lower'`` y axis. (`#74 <https://github.com/CSSFrancis/anyplotlib/pull/74>`_)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
New Features
|
|
39
|
+
------------
|
|
40
|
+
|
|
41
|
+
- The 2-D colorbar now writes its display minimum and maximum beside the strip — both ends in one format, and the strip itself coloured through the display window (saturated beyond it, the way the image is) and spanning the image rather than the whole panel — so a labelled scale says how much and not only which way; the image gives up a value gutter budgeted for the numbers (fixed for ordinary values so a contrast drag never moves the image, wider for the rare long ones, dropped in a cell too narrow to keep 40 px of image, and mirrored in :meth:`plot_box`) so they never clip. (`#71 <https://github.com/CSSFrancis/anyplotlib/pull/71>`_)
|
|
42
|
+
- Text markers (:meth:`~anyplotlib.Plot2D.add_texts`, :meth:`~anyplotlib.Plot1D.add_texts` and ``add_text``) gained ``fontweight`` for bold labels and ``outline_color`` / ``outline_width`` for a halo stroked under the text, which keeps a label legible over both light and dark parts of an image. (`#73 <https://github.com/CSSFrancis/anyplotlib/pull/73>`_)
|
|
43
|
+
|
|
44
|
+
|
|
13
45
|
0.9.0 (2026-09-11)
|
|
14
46
|
==================
|
|
15
47
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: anyplotlib
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.1
|
|
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
|
|
@@ -35,8 +35,8 @@ Rule 4 – Zoom is relative to the fit-rect.
|
|
|
35
35
|
zoom=Z → a 1/Z portion of the image fills the fit-rect.
|
|
36
36
|
|
|
37
37
|
Rule 5 – Text never clips. Optional gutters earn real layout space:
|
|
38
|
-
the colorbar (strip + label, _cbWidth) is
|
|
39
|
-
image width; the 2D title strip (_padT) grows for large or TeX
|
|
38
|
+
the colorbar (strip + value gutter + label, _cbWidth) is
|
|
39
|
+
subtracted from the image width; the 2D title strip (_padT) grows for large or TeX
|
|
40
40
|
titles; 1D/bar titles clamp their drawn size to the fixed strip
|
|
41
41
|
(_titlePx); edge tick labels are nudged inward.
|
|
42
42
|
```
|
|
@@ -50,55 +50,57 @@ Rule 5 – Text never clips. Optional gutters earn real layout space:
|
|
|
50
50
|
| Shared plot-area padding (`PAD_*`) | 9 |
|
|
51
51
|
| Theme (dark/light detection) | 26 |
|
|
52
52
|
| Shared math helpers | 64 |
|
|
53
|
-
| b64 array decode helpers |
|
|
54
|
-
| **Rich-text (mini-TeX) engine**: `_texRuns` / `_texLayout` / `_drawTex` |
|
|
55
|
-
| **2D gutter geometry**: `_cbWidth` / `_cbGap` / `_padT` / `_titlePx` |
|
|
56
|
-
| **Layout engine** `applyLayout` |
|
|
57
|
-
| `_buildCanvasStack` |
|
|
58
|
-
| `_createPanelDOM` |
|
|
59
|
-
| `_createInsetDOM` / `_applyAllInsetStates` |
|
|
60
|
-
| `_resizePanelDOM` |
|
|
61
|
-
| **2D drawing**: `_imgFitRect` |
|
|
62
|
-
| `draw2d` |
|
|
63
|
-
| `drawScaleBar2d` / `drawColorbar2d` |
|
|
64
|
-
| **Floating keys**: `_keyEnsure` / `_keyRect` / `drawKeys` |
|
|
65
|
-
| `_drawAxes2d` (ticks, labels, title) |
|
|
66
|
-
| `drawOverlay2d` / `drawMarkers2d` |
|
|
67
|
-
| **Image layers**: `_layerBytes` / `_layerBitmap` / `_drawLayers2d` |
|
|
68
|
-
| Binary-bytes splice: `_spliceBinaryBytes` / `_registerBinaryPixelListeners` |
|
|
69
|
-
| **Hover readout**: `_pixelValue2d` / `_readoutInfo2d` |
|
|
70
|
-
| `_notifyReadout` / `_updateStatus2d` / `_armValueProbe` |
|
|
71
|
-
| **3D drawing**: `draw3d` |
|
|
72
|
-
| Event emission `_emitEvent` |
|
|
73
|
-
| 3D event handlers `_attachEvents3d` |
|
|
74
|
-
| **1D drawing**: `draw1d` |
|
|
75
|
-
| `_drawLine` (1D series + markers) |
|
|
76
|
-
| `drawOverlay1d` / `drawMarkers1d` |
|
|
77
|
-
| Marker hit-test `_markerHitTest2d` |
|
|
53
|
+
| b64 array decode helpers | 141 |
|
|
54
|
+
| **Rich-text (mini-TeX) engine**: `_texRuns` / `_texLayout` / `_drawTex` | 193 / 260 / 282 |
|
|
55
|
+
| **2D gutter geometry**: `_cbLabelW` / `_cbValueChars` / `_cbTickW` / `_cbWidth` / `_cbGap` / `_padT` / `_titlePx` | 331 / 342 / 360 / 372 / 382 / 392 / 402 |
|
|
56
|
+
| **Layout engine** `applyLayout` | 893 |
|
|
57
|
+
| `_buildCanvasStack` | 976 |
|
|
58
|
+
| `_createPanelDOM` | 1118 |
|
|
59
|
+
| `_createInsetDOM` / `_applyAllInsetStates` | 1259 / 1653 |
|
|
60
|
+
| `_resizePanelDOM` | 2366 |
|
|
61
|
+
| **2D drawing**: `_imgFitRect` | 2537 |
|
|
62
|
+
| `draw2d` | 2881 |
|
|
63
|
+
| `drawScaleBar2d` / `drawColorbar2d` | 3076 / 3356 |
|
|
64
|
+
| **Floating keys**: `_keyEnsure` / `_keyRect` / `drawKeys` | 3175 / 3198 / 3211 |
|
|
65
|
+
| `_drawAxes2d` (ticks, labels, title) | 3468 |
|
|
66
|
+
| `drawOverlay2d` / `drawMarkers2d` | 3623 / 3787 |
|
|
67
|
+
| **Image layers**: `_layerBytes` / `_layerBitmap` / `_drawLayers2d` | 2701 / 2725 / 2786 |
|
|
68
|
+
| Binary-bytes splice: `_spliceBinaryBytes` / `_registerBinaryPixelListeners` | 849 / 880 |
|
|
69
|
+
| **Hover readout**: `_pixelValue2d` / `_readoutInfo2d` | 4631 / 4716 |
|
|
70
|
+
| `_notifyReadout` / `_updateStatus2d` / `_armValueProbe` | 4755 / 4770 / 4791 |
|
|
71
|
+
| **3D drawing**: `draw3d` | 5767 |
|
|
72
|
+
| Event emission `_emitEvent` | 6604 |
|
|
73
|
+
| 3D event handlers `_attachEvents3d` | 6661 |
|
|
74
|
+
| **1D drawing**: `draw1d` | 6885 |
|
|
75
|
+
| `_drawLine` (1D series + markers) | 7038 |
|
|
76
|
+
| `drawOverlay1d` / `_rasterBitmap` / `drawMarkers1d` | 7331 / 7419 / 7436 |
|
|
77
|
+
| Marker hit-test `_markerHitTest2d` | 7700 |
|
|
78
78
|
|
|
79
79
|
> **`raster` marker (1D/PlotXY)** — `drawMarkers1d` has a `type==='raster'`
|
|
80
80
|
> branch that blits a single RGBA image across data-coord `extent` (the fast
|
|
81
81
|
> path for dense `PlotXY.pcolormesh` heatmaps). The image bytes ride the geom
|
|
82
82
|
> channel as `st.raster_geom[id]` (Python `Plot1D._GEOM_KEYS`), so view-only
|
|
83
|
-
> redraws never re-transmit them
|
|
84
|
-
> the marker set
|
|
85
|
-
>
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
|
89
|
-
| 2D
|
|
90
|
-
|
|
|
91
|
-
|
|
|
92
|
-
|
|
|
93
|
-
|
|
|
94
|
-
|
|
|
95
|
-
|
|
|
96
|
-
| **
|
|
97
|
-
|
|
|
98
|
-
| **
|
|
99
|
-
|
|
|
100
|
-
| **
|
|
101
|
-
|
|
|
83
|
+
> redraws never re-transmit them. The decoded `OffscreenCanvas` is cached per
|
|
84
|
+
> panel by `_rasterBitmap` (keyed by the marker set's id, re-decoded when the
|
|
85
|
+
> bytes or the shape change) — never on the marker set, which is state the
|
|
86
|
+
> wheel and pan handlers serialise back. The shared `clip_path` block clips it
|
|
87
|
+
> to a curved sector.
|
|
88
|
+
| Panel event dispatch `_attachPanelEvents` | 7957 |
|
|
89
|
+
| 2D events `_attachEvents2d` | 8043 |
|
|
90
|
+
| 1D events `_attachEvents1d` | 8429 |
|
|
91
|
+
| 2D widget drag `_ovHitTest2d` / `_doDrag2d` | 8704 / 8983 |
|
|
92
|
+
| **Brush strokes**: `_brushLiveBegin` / `_brushCommit` / `_brushErase` / `_brushPaintAt` | 8896 / 8910 / 8939 / 8974 |
|
|
93
|
+
| 1D widget drag `_canvasXToFrac1d` … / snapping `_snapVal` | 9108 / 9181 |
|
|
94
|
+
| Shared-axis propagation `_getShareGroups` | 9252 |
|
|
95
|
+
| Figure resize `_applyFigResizeDOM` | 9316 |
|
|
96
|
+
| **Bar chart**: `_barGeom` / `drawBar` / `_attachEventsBar` | 9510 / 9573 / 9949 |
|
|
97
|
+
| Generic redraw `_redrawPanel` | 10139 |
|
|
98
|
+
| **PNG export**: `_compositeCanvas` / `exportCanvas` / `exportPNG` | 10302 / 10498 / 10557 |
|
|
99
|
+
| Native-resolution render `_withNativeSize` | 10278 |
|
|
100
|
+
| **Export UI**: `_toast` / `_downloadCanvas` / `_openMenu` | 10591 / 10713 / 10892 |
|
|
101
|
+
| Export registry `registerExportAction` | 10769 |
|
|
102
|
+
| **Embedding API**: `createLocalModel` / `mount` | 11283 / 11339 |
|
|
103
|
+
| **Navigated embed**: `decodeBlocks` / `mountNavigated` | 11594 / 11981 |
|
|
102
104
|
|
|
103
105
|
> **`brush` widget (2-D)** — the one widget whose drag is *modal*, and the one
|
|
104
106
|
> that must NOT write the model per tick. `_ovHitTest2d` takes an extra `mods`
|
|
@@ -148,7 +150,10 @@ with fallbacks to the historical defaults: `title_size||11`,
|
|
|
148
150
|
|
|
149
151
|
| Function | Purpose |
|
|
150
152
|
|----------|---------|
|
|
151
|
-
| `
|
|
153
|
+
| `_cbLabelW(st)` | The rotated-label gutter: `label_size + 8`, 0 without a label. |
|
|
154
|
+
| `_cbValueChars(st)` | Characters budgeted for the `display_min` / `display_max` values: 7 (all of `fmtRange`'s ordinary output, so a contrast drag never moves the image), more only for longer strings such as a three-digit exponent. `fmtRange` (next to `fmtVal`) formats BOTH ends alike — exponent notation when the larger magnitude is outside [1e-2, 1e4), else decimals from the span (three significant digits of it, so ±1.25 reads 1.25). Mirrored by Python `_colorbar_value_chars` / `colorbar_texts`. |
|
|
155
|
+
| `_cbTickW(st, availW)` | Width of the value gutter right of the strip: `round(chars * 0.6 * tick_size) + 3`. With `availW` (image + colorbar width after the axis gutters) it is 0 when the values would leave less than `CB_MIN_IMAGE_W` (40 px) of image — a narrow grid cell keeps the strip and label and drops the numbers. `_resizePanelDOM` stores the result on `p._cbTickW` for `drawColorbar2d`. Mirrored in Python's `plot_box`. |
|
|
156
|
+
| `_cbWidth(st, availW)` | Width reserved for the colorbar: 0 when hidden, else `16 + _cbTickW + _cbLabelW`. Subtracted from the image width in `_resizePanelDOM` / `_resizePanelCSS` so the strip, its values and its label always fit inside the panel. |
|
|
152
157
|
| `_padT(st)` | 2D title-strip height: `PAD_T` (12) for default-size plain titles (pixel-identical layouts); grows to `ceil(size*1.3)+2..4` for `title_size > 11` or TeX titles (superscript rise). Stored as `p._padT`. |
|
|
153
158
|
| `_titlePx(st)` | Drawn title size for fixed-strip panels (1D/bar): clamps to 11 (10 for TeX titles) so nothing clips. |
|
|
154
159
|
|
|
@@ -279,17 +284,36 @@ st.colorbar_label_size (label font sizes; optional)
|
|
|
279
284
|
|
|
280
285
|
| Function | Line | Purpose |
|
|
281
286
|
|----------|------|---------|
|
|
282
|
-
| **`_imgFitRect(iw,ih,cw,ch)`** | **
|
|
283
|
-
| `draw2d(p)` |
|
|
284
|
-
| `drawScaleBar2d(p)` |
|
|
285
|
-
| `_rawBand(st)` / `_buildLut32(st)` |
|
|
286
|
-
| `drawColorbar2d(p)` |
|
|
287
|
-
| `_drawAxes2d(p)` |
|
|
288
|
-
| `drawOverlay2d(p)` / `drawMarkers2d(p)` |
|
|
287
|
+
| **`_imgFitRect(iw,ih,cw,ch)`** / `_cbFitRect(st,imgW,imgH)` | **2537 / 2546** | Largest rect of aspect `iw:ih` centred in `cw×ch`; all 2-D coordinate functions derive from this. `_cbFitRect` is its vertical extent in whole px — the colorbar strip spans the letterboxed IMAGE, not the whole image area (`p._cbH`) |
|
|
288
|
+
| `draw2d(p)` | 2881 | Main render: `_resizePanelDOM` → decode → LUT → ImageBitmap → blit; then mask, axes, scale bar, colorbar, overlay, markers |
|
|
289
|
+
| `drawScaleBar2d(p)` | 3076 | Physical scale bar |
|
|
290
|
+
| `_rawBand(st)` / `_displayFrac(st, val)` / `_buildLut32(st)` | 2596 / 2606 / 2614 | The quantisation band the u8 bytes were encoded over, the ONE rule mapping a value through the display window and scale mode to a colormap fraction (shared by the LUT and the colorbar strip, so the strip's colours are the image's), then the 256-entry LUT built from both. `_rawBand` mirrors Python `_tile_quant_clim`: a DEGENERATE band (`raw_max <= raw_min`) is UNSET and falls back to `display_min/max`. Both render paths and the colorbar go through it — honouring a `(0, 0)` band paints solid black |
|
|
291
|
+
| `drawColorbar2d(p)` | 3356 | Gradient strip coloured through the display window (`_displayFrac` — saturated beyond it, like the image) + min/max marks (band-relative, via `_rawBand`) + the two values written beside them (`fmtRange`; kept inside the strip by the glyphs' measured extent, held apart on a tiny range, the maximum alone at its own place on a strip too short for both; omitted when the layout dropped the gutter, `p._cbTickW == 0`) + rotated label centred right after the values' measured width (never past the reserved gutter) |
|
|
292
|
+
| `_drawAxes2d(p)` | 3468 | Ticks placed through `_imgToCanvas2d` (pixel-centre axes, letterbox-aware; edge labels nudged inward both axes), axis labels + title via `_drawTex` |
|
|
293
|
+
| `drawOverlay2d(p)` / `drawMarkers2d(p)` | 3623 / 3787 | Widgets / marker groups |
|
|
289
294
|
|
|
290
295
|
Zoom model: at `zoom=1` the whole image fills the fit-rect; at `zoom=Z>1` a
|
|
291
296
|
`1/Z` region fills it. `_imgToCanvas2d` / `_canvasToImg2d` must stay exact
|
|
292
|
-
inverses of the blit geometry.
|
|
297
|
+
inverses of the blit geometry — and of each other. Both use the pixel-CENTRE
|
|
298
|
+
convention (integer *i* is the centre of pixel *i*, which spans
|
|
299
|
+
`[i − 0.5, i + 0.5)`), the same as marker offsets, widget positions and Python's
|
|
300
|
+
`data_to_display` / `display_to_data`; `_imgPix2d` / `_inImgAxis2d` name the
|
|
301
|
+
pixel under a coordinate and bound it. Pointer events and the readout map a
|
|
302
|
+
coordinate to `xdata`/`ydata` through `_imgToAxisVal2d` (imshow axes are
|
|
303
|
+
per-pixel centres, pcolormesh axes cell edges). Code that addresses texels or
|
|
304
|
+
the detail region by pixel EDGE (`_pixelValue2d`) adds the `0.5` back itself.
|
|
305
|
+
|
|
306
|
+
The tick gutters (`_drawAxes2d`) use the same chain in reverse: tick value →
|
|
307
|
+
`_axisValToImg2d` (imshow; pcolormesh goes through `_axisValToFrac` × n − ½) →
|
|
308
|
+
`_imgToCanvas2d`, so ticks honour the letterboxed fit rect, zoom < 1, pan
|
|
309
|
+
clamping and descending (`origin='lower'`) axes for free. The visible value
|
|
310
|
+
range is the gutter's two ends through `_canvasToImg2d`, clamped to the outer
|
|
311
|
+
pixel edges. Python's `Plot2D._axis_extent_frac` (behind `set_view` /
|
|
312
|
+
`set_xlim`) mirrors `_axisValToImg2d` — keep the two in step.
|
|
313
|
+
|
|
314
|
+
`texts` markers (1-D and 2-D) set their font and optional outline through
|
|
315
|
+
`_markerTextStyle(ctx, ms)` (`fontweight`, `outline_color`, `outline_width`)
|
|
316
|
+
and draw via `_drawMarkerText`, which strokes the halo under the fill.
|
|
293
317
|
|
|
294
318
|
### Hover readout
|
|
295
319
|
|
|
@@ -638,13 +662,13 @@ exportCanvas(same opts) → {canvas, width, height} // synchronous, throws
|
|
|
638
662
|
|
|
639
663
|
| Function | Line | Purpose |
|
|
640
664
|
|----------|------|---------|
|
|
641
|
-
| `_cssScale` |
|
|
642
|
-
| `_panelBox` |
|
|
643
|
-
| `_neutralizeView` / `_restoreView` |
|
|
644
|
-
| `_nativeGeom` / `_nativeGuard` |
|
|
645
|
-
| `_withNativeSize` |
|
|
646
|
-
| `_compositeCanvas` |
|
|
647
|
-
| `exportCanvas` / `exportPNG` |
|
|
665
|
+
| `_cssScale` | 10174 | inverse of `_applyScale`'s `transform:scale()` |
|
|
666
|
+
| `_panelBox` | 10185 | the element whose rect bounds one panel |
|
|
667
|
+
| `_neutralizeView` / `_restoreView` | 10194 / 10219 | transient whole-extent view |
|
|
668
|
+
| `_nativeGeom` / `_nativeGuard` | 10234 / 10253 | native size + why-not message |
|
|
669
|
+
| `_withNativeSize` | 10278 | resize → redraw → run → restore |
|
|
670
|
+
| `_compositeCanvas` | 10302 | the compositor (`_drawEl` / `_drawPanel` …) |
|
|
671
|
+
| `exportCanvas` / `exportPNG` | 10498 / 10557 | orchestrator / data-URL wrapper |
|
|
648
672
|
|
|
649
673
|
**The whole pipeline is ONE synchronous task** — theme swap, view reset, native
|
|
650
674
|
resize, composite, restore — so the browser never paints an intermediate state
|
|
@@ -740,13 +764,14 @@ leaders that cross into the panel included. Pinned by
|
|
|
740
764
|
|
|
741
765
|
| Function | Line | Purpose |
|
|
742
766
|
|----------|------|---------|
|
|
743
|
-
| `_toast` |
|
|
744
|
-
| `_copyCanvas` |
|
|
745
|
-
| `_showPngPreview` |
|
|
746
|
-
| `
|
|
747
|
-
| `
|
|
748
|
-
| `
|
|
749
|
-
| `
|
|
767
|
+
| `_toast` | 10591 | transient bottom-centre message |
|
|
768
|
+
| `_copyCanvas` | 10626 | clipboard write + feature detection |
|
|
769
|
+
| `_showPngPreview` | 10650 | framed-document download fallback |
|
|
770
|
+
| `_hostSavesPng` / `_canPickFile` | 10702 / 10708 | the parent saves PNGs itself / a system Save dialog exists |
|
|
771
|
+
| `_downloadCanvas` | 10713 | `<a download>`, the preview, or the host |
|
|
772
|
+
| `registerExportAction` | 10769 | downstream extension point |
|
|
773
|
+
| `_menuRows` / `_openMenu` | 10823 / 10892 | menu model / DOM |
|
|
774
|
+
| `_panelAtPoint` | 11004 | hit test (insets first — they sit on top) |
|
|
750
775
|
|
|
751
776
|
- **An `exportBtn` badge (⤓, beside the help badge) opens the same menu on an
|
|
752
777
|
ordinary left click.** It is a `role="button"` with `tabIndex=0` and
|
|
@@ -788,9 +813,15 @@ leaders that cross into the panel included. Pinned by
|
|
|
788
813
|
`save_html` page opened directly is top-level) from the unreliable one (VS
|
|
789
814
|
Code webviews, `_repr_html_` iframes, nbconvert output). When framed, the
|
|
790
815
|
result is posted to the parent under the existing
|
|
791
|
-
`anyplotlib_export_png_result` message
|
|
792
|
-
caption points at the browser's own
|
|
793
|
-
permission and is never blocked.
|
|
816
|
+
`anyplotlib_export_png_result` message (`requestId: null`, plus `filename`)
|
|
817
|
+
AND shown as an in-figure preview whose caption points at the browser's own
|
|
818
|
+
"Save image as…", which needs no permission and is never blocked.
|
|
819
|
+
- **Host saves** (`_hostSavesPng`): a parent that posts
|
|
820
|
+
`{type:'anyplotlib_host', savesPng:true}` sets `globalThis.__aplHostSavesPng`
|
|
821
|
+
(the page template's listener, parent frame only). Framed saves then post the
|
|
822
|
+
image WITHOUT the preview, and `_canPickFile()` is false, so the *Save as…*
|
|
823
|
+
row disappears — the host's own Save dialog chooses the folder, and an app
|
|
824
|
+
webview may have no "Save image as…" menu for the preview to point at.
|
|
794
825
|
- **Clipboard**: gated on `isSecureContext && navigator.clipboard &&
|
|
795
826
|
ClipboardItem && clipboard.write`. The Blob is built SYNCHRONOUSLY from the
|
|
796
827
|
data URL (not via the async `toBlob` callback) so the write stays inside the
|
|
@@ -807,6 +838,7 @@ Test hooks: `__apl_menuItems`, `__apl_toastText`, `__apl_menuTheme`,
|
|
|
807
838
|
Tests: `tests/test_embed/test_export_png.py` (the pre-existing contract),
|
|
808
839
|
`test_export_sources.py` (panelId / source / theme / CSS scale),
|
|
809
840
|
`test_export_menu.py` (menu, clipboard, download, registry),
|
|
841
|
+
`test_host_saves_png.py` (the host announcement and the framed save),
|
|
810
842
|
`test_savefig.py` (the Python entry point + view reconciliation).
|
|
811
843
|
|
|
812
844
|
The standalone HTML template (`_repr_utils.build_standalone_html`) captures
|
|
@@ -816,11 +848,14 @@ render()'s api into `_aplRenderApi`, **also assigns it to `window._aplRenderApi`
|
|
|
816
848
|
`{type:'anyplotlib_export_png', requestId, opts}` → `exportPNG(opts)` → replies
|
|
817
849
|
`{type:'anyplotlib_export_png_result', requestId, dataUrl, width, height}` (or
|
|
818
850
|
`{…, error}`) to `event.source` (targetOrigin `'*'`). `opts` is forwarded
|
|
819
|
-
verbatim, so the new fields work over that channel too.
|
|
851
|
+
verbatim, so the new fields work over that channel too. A second listener takes
|
|
852
|
+
`{type:'anyplotlib_host', savesPng}` from `window.parent` only and records it
|
|
853
|
+
in `globalThis.__aplHostSavesPng` (read by `_hostSavesPng`). Both live in
|
|
854
|
+
`PNG_HARVEST_LISTENER`, which the navigated embed installs too.
|
|
820
855
|
|
|
821
856
|
---
|
|
822
857
|
|
|
823
|
-
## Navigated-embed runtime (line
|
|
858
|
+
## Navigated-embed runtime (line 11346 to the end of the file)
|
|
824
859
|
|
|
825
860
|
Everything below `mount()` is module scope, outside `render()`'s closure: pure
|
|
826
861
|
functions over decoded data plus one entry point that wires them to a mounted
|
|
@@ -830,16 +865,16 @@ bindings, let it dispatch", rather than a hand-written program per result kind.
|
|
|
830
865
|
|
|
831
866
|
| Function | Line | Purpose |
|
|
832
867
|
|----------|------|---------|
|
|
833
|
-
| `decodeBlocks` |
|
|
834
|
-
| `dense` |
|
|
835
|
-
| `ragged` |
|
|
836
|
-
| `maskFromWidget` |
|
|
837
|
-
| `rasterDisks` |
|
|
838
|
-
| `robustLevels` / `toU8` |
|
|
839
|
-
| `panelAxis` |
|
|
840
|
-
| `installTouchShim` / `reportEmbedHeight` |
|
|
841
|
-
| `encodeBase64` / `typedArrayBytes` |
|
|
842
|
-
| `mountNavigated` |
|
|
868
|
+
| `decodeBlocks` | 11594 | one base64 `fetch` → one ArrayBuffer → a typed-array view per manifest entry |
|
|
869
|
+
| `dense` | 11620 | `at` / `gather` / `reduce` over a block whose leading axes are the nav axes |
|
|
870
|
+
| `ragged` | 11685 | the same three, over a row-pointer block (`offsets` + one array per column) |
|
|
871
|
+
| `maskFromWidget` | 11768 | rectangle / circle / annulus widget dict → `Uint8Array` (carries `width`/`height`) |
|
|
872
|
+
| `rasterDisks` | 11808 | splat `{x, y, intensity}` rows as filled disks — the base image of a vectors panel |
|
|
873
|
+
| `robustLevels` / `toU8` | 11837 / 11878 | the percentile window and the 8-bit code map, one implementation |
|
|
874
|
+
| `panelAxis` | 11956 | a 1-D panel's decoded x axis (`_1dXArr`, else `x_axis_b64`) |
|
|
875
|
+
| `installTouchShim` / `reportEmbedHeight` | 11893 / 11912 | page chrome: touch → mouse, `postMessage({aplEmbedHeight})` |
|
|
876
|
+
| `encodeBase64` / `typedArrayBytes` | 11932 / 11940 | a 3-D cloud's geometry channel is base64, not the binary side table |
|
|
877
|
+
| `mountNavigated` | 11981 | mount + bind + dispatch; resolves to the mount handle plus `dispatch`/`index`/`blocks` |
|
|
843
878
|
|
|
844
879
|
`mountNavigated(el, page, opts)` is **async** — the blob decode is a `fetch` of
|
|
845
880
|
a `data:` URL — so a host `await`s it. `page` is `{state, blocks, bindings,
|
|
@@ -6,11 +6,99 @@ Shared base classes and mixins for all plot panel types.
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
+
import decimal
|
|
10
|
+
import math
|
|
11
|
+
|
|
9
12
|
from contextlib import contextmanager
|
|
10
13
|
|
|
11
14
|
from anyplotlib.callbacks import _EventMixin
|
|
12
15
|
|
|
13
16
|
|
|
17
|
+
#: Below this many px of image the colorbar's value gutter is dropped
|
|
18
|
+
#: (mirrors ``CB_MIN_IMAGE_W`` in the JS).
|
|
19
|
+
COLORBAR_MIN_IMAGE_WIDTH = 40
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _js_round(x: float) -> int:
|
|
23
|
+
"""JavaScript's ``Math.round`` — half away from zero for positive values —
|
|
24
|
+
where Python's ``round`` would tie to even and put the two sides of the
|
|
25
|
+
layout mirror a pixel apart."""
|
|
26
|
+
return int(math.floor(float(x) + 0.5))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _js_exponential(value: float, digits: int) -> str:
|
|
30
|
+
"""JavaScript's ``Number.toExponential(digits)``: a signed mantissa with
|
|
31
|
+
*digits* decimals, rounded half away from zero on the exact binary value
|
|
32
|
+
(``12500`` → ``1.3e+4``, where ``f"{:e}"`` would tie to even), and an
|
|
33
|
+
exponent without leading zeros (``1.2e+4``, ``-1.5e-10``)."""
|
|
34
|
+
number = decimal.Decimal(value)
|
|
35
|
+
if number == 0:
|
|
36
|
+
return f"{0:.{digits}f}e+0"
|
|
37
|
+
sign = "-" if number < 0 else ""
|
|
38
|
+
number = abs(number)
|
|
39
|
+
exponent = number.adjusted()
|
|
40
|
+
quantum = decimal.Decimal(1).scaleb(-digits)
|
|
41
|
+
mantissa = number.scaleb(-exponent).quantize(quantum, rounding=decimal.ROUND_HALF_UP)
|
|
42
|
+
if mantissa >= 10:
|
|
43
|
+
mantissa = (mantissa / 10).quantize(quantum, rounding=decimal.ROUND_HALF_UP)
|
|
44
|
+
exponent += 1
|
|
45
|
+
return f"{sign}{mantissa}e{exponent:+d}"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _js_fixed(value: float, digits: int) -> str:
|
|
49
|
+
"""JavaScript's ``Number.toFixed(digits)``, which rounds half away from
|
|
50
|
+
zero on the exact binary value."""
|
|
51
|
+
quantum = decimal.Decimal(1).scaleb(-digits)
|
|
52
|
+
rounded = decimal.Decimal(value).quantize(quantum, rounding=decimal.ROUND_HALF_UP)
|
|
53
|
+
return f"{rounded:f}"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _strip_zeros(text: str) -> str:
|
|
57
|
+
return text.rstrip("0").rstrip(".") if "." in text else text
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def colorbar_texts(low: float, high: float) -> tuple[str, str]:
|
|
61
|
+
"""The two values the renderer writes beside the colorbar (``fmtRange``),
|
|
62
|
+
mirrored so Python can budget the same width.
|
|
63
|
+
|
|
64
|
+
One format for both ends, so ``0.02`` never sits beside ``-5.0e-3``:
|
|
65
|
+
exponent notation when the larger magnitude is outside ``[1e-2, 1e4)``,
|
|
66
|
+
else fixed with enough decimals to show the ends exactly — three
|
|
67
|
+
significant digits of the span, so ±1.25 reads ``1.25`` and not ``1.3``;
|
|
68
|
+
at most four decimals. Non-finite ends give ``""`` (the renderer draws
|
|
69
|
+
nothing)."""
|
|
70
|
+
low, high = float(low), float(high)
|
|
71
|
+
if not (math.isfinite(low) and math.isfinite(high)):
|
|
72
|
+
return "", ""
|
|
73
|
+
big = max(abs(low), abs(high))
|
|
74
|
+
if big == 0:
|
|
75
|
+
return "0", "0"
|
|
76
|
+
if big >= 1e4 or big < 1e-2:
|
|
77
|
+
return _js_exponential(low, 1), _js_exponential(high, 1)
|
|
78
|
+
span = abs(high - low) or big
|
|
79
|
+
decimals = min(4, max(0, math.ceil(-math.log10(span)) + 2))
|
|
80
|
+
return (_strip_zeros(_js_fixed(low, decimals)),
|
|
81
|
+
_strip_zeros(_js_fixed(high, decimals)))
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _colorbar_value_chars(state: dict) -> int:
|
|
85
|
+
"""Characters budgeted for the colorbar's min/max values: 7 in ordinary
|
|
86
|
+
use, so the layout is stable under a contrast drag, more only for the rare
|
|
87
|
+
longer strings. Mirrors ``_cbValueChars`` in the JS."""
|
|
88
|
+
chars = 7
|
|
89
|
+
lo, hi = state.get("display_min"), state.get("display_max")
|
|
90
|
+
if lo is not None and hi is not None:
|
|
91
|
+
chars = max(chars, *(len(t) for t in colorbar_texts(lo, hi)))
|
|
92
|
+
return chars
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _colorbar_value_width(state: dict) -> int:
|
|
96
|
+
"""The value gutter in px — mirrors ``_cbTickW`` in the JS before its
|
|
97
|
+
narrow-cell rule."""
|
|
98
|
+
tick = state.get("tick_size") or 10
|
|
99
|
+
return _js_round(_colorbar_value_chars(state) * 0.6 * tick) + 3
|
|
100
|
+
|
|
101
|
+
|
|
14
102
|
class _BasePlot(_EventMixin):
|
|
15
103
|
"""Universal base for Plot1D, Plot2D, PlotBar, and Plot3D.
|
|
16
104
|
|
|
@@ -376,14 +464,20 @@ class _PanelMixin:
|
|
|
376
464
|
width = max((pw - left - right) if has_axes else pw, 1.0)
|
|
377
465
|
height = max(ph - pad_t - (bottom if has_axes else 0.0), 1.0)
|
|
378
466
|
|
|
379
|
-
# The colorbar strip and its gap come out
|
|
467
|
+
# The colorbar strip, its value gutter, its label and its gap come out
|
|
468
|
+
# of the image width. Mirrors `_cbWidth` / `_cbTickW` in the JS,
|
|
469
|
+
# including the rule that drops the values in a cell too narrow for
|
|
470
|
+
# them (`width` here is the JS `availW`).
|
|
380
471
|
if self._state.get("show_colorbar") and not self._state.get("is_rgb"):
|
|
381
472
|
label = self._state.get("colorbar_label")
|
|
382
|
-
label_w =
|
|
473
|
+
label_w = _js_round((self._state.get("colorbar_label_size") or 10) + 8) \
|
|
383
474
|
if label else 0
|
|
475
|
+
tick_w = _colorbar_value_width(self._state)
|
|
384
476
|
pad = self._state.get("colorbar_pad")
|
|
385
477
|
gap = 6.0 if pad is None else max(0.0, float(pad))
|
|
386
|
-
width
|
|
478
|
+
if width - gap - 16 - label_w - tick_w < COLORBAR_MIN_IMAGE_WIDTH:
|
|
479
|
+
tick_w = 0
|
|
480
|
+
width = max(width - (16 + tick_w + label_w) - gap, 1.0)
|
|
387
481
|
|
|
388
482
|
if is_image:
|
|
389
483
|
# Images are drawn "contain" — see _imgFitRect.
|
|
@@ -625,7 +719,8 @@ class _MarkerMixin:
|
|
|
625
719
|
transform : str, optional
|
|
626
720
|
Coordinate system for ``(x, y)``. Default ``"data"``.
|
|
627
721
|
**kwargs : dict
|
|
628
|
-
Forwarded to :meth:`add_texts` (e.g. ``clip_display
|
|
722
|
+
Forwarded to :meth:`add_texts` (e.g. ``clip_display``,
|
|
723
|
+
``fontweight``, ``outline_color``).
|
|
629
724
|
|
|
630
725
|
Returns
|
|
631
726
|
-------
|
|
@@ -330,9 +330,10 @@ window.addEventListener('message', (e) => {{
|
|
|
330
330
|
|
|
331
331
|
|
|
332
332
|
# A host page (or the SpyDE report harvester) asks an embedded figure for a
|
|
333
|
-
# composite PNG over postMessage
|
|
334
|
-
#
|
|
335
|
-
#
|
|
333
|
+
# composite PNG over postMessage, and can announce that it saves exported PNGs
|
|
334
|
+
# itself. Both the standalone page and the navigated embed install these
|
|
335
|
+
# listeners; each assigns ``globalThis.__aplExportPNG`` once its figure is
|
|
336
|
+
# mounted, which is also what makes "not ready yet" answerable.
|
|
336
337
|
PNG_HARVEST_LISTENER = '''\
|
|
337
338
|
// ── PNG export protocol ──────────────────────────────────────────────────────
|
|
338
339
|
// A parent page (or the SpyDE report harvester) asks this frame over
|
|
@@ -370,6 +371,20 @@ window.addEventListener('message', (e) => {
|
|
|
370
371
|
reply({ error: String(err && err.message || err) });
|
|
371
372
|
}
|
|
372
373
|
});
|
|
374
|
+
|
|
375
|
+
// A host that saves exported PNGs itself (a desktop app with its own Save
|
|
376
|
+
// dialog) announces it once:
|
|
377
|
+
// → { type: 'anyplotlib_host', savesPng: true }
|
|
378
|
+
// after which the export menu's "Save PNG…" only posts the image to it
|
|
379
|
+
// ← { type: 'anyplotlib_export_png_result', requestId: null,
|
|
380
|
+
// dataUrl, width, height, filename }
|
|
381
|
+
// instead of also showing the in-figure "Save image as…" preview. Only the
|
|
382
|
+
// parent frame may say so.
|
|
383
|
+
window.addEventListener('message', (e) => {
|
|
384
|
+
if (!e.data || e.data.type !== 'anyplotlib_host') return;
|
|
385
|
+
if (e.source !== window.parent || window.parent === window) return;
|
|
386
|
+
globalThis.__aplHostSavesPng = e.data.savesPng === true;
|
|
387
|
+
});
|
|
373
388
|
'''
|
|
374
389
|
|
|
375
390
|
|
|
@@ -43,7 +43,7 @@ class Event:
|
|
|
43
43
|
button — 0=left 1=middle 2=right; None on move/enter/leave/settled
|
|
44
44
|
buttons — bitmask of currently held buttons
|
|
45
45
|
xdata, ydata — data-space coordinates (None for Plot3D)
|
|
46
|
-
img_x, img_y — Plot2D/PlotMesh: position in IMAGE PIXELS (column, row; row 0 at the top, origin already applied), so a handler can index the source array directly. None on other plot types.
|
|
46
|
+
img_x, img_y — Plot2D/PlotMesh: position in IMAGE PIXELS (column, row; row 0 at the top, origin already applied), so a handler can index the source array directly. Integer i is the CENTRE of pixel i (as for markers and widgets), so round() names the pixel. None on other plot types.
|
|
47
47
|
ray — Plot3D only: {"origin": [...], "direction": [...]}
|
|
48
48
|
line_id — Plot1D only: set when pointer is over a line
|
|
49
49
|
dwell_ms — pointer_settled only: actual dwell time
|