anyplotlib 0.4.0__tar.gz → 0.4.2__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.4.0 → anyplotlib-0.4.2}/CHANGELOG.rst +31 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/PKG-INFO +1 -1
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/figure_esm.js +46 -11
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/plot1d/_plot1d.py +6 -1
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/plot2d/_plot2d.py +71 -26
- anyplotlib-0.4.2/anyplotlib/tests/test_interactive/test_widget_max_extent.py +371 -0
- anyplotlib-0.4.2/anyplotlib/tests/test_plot2d/test_tile_diagnostics.py +88 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/widgets/_widgets1d.py +15 -2
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/widgets/_widgets2d.py +18 -2
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/docs/_root/index.html +3 -3
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/docs/_root/switcher.json +10 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/docs/conf.py +1 -1
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/pyproject.toml +1 -1
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/.github/workflows/ci.yml +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/.github/workflows/docs.yml +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/.github/workflows/prepare_release.yml +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/.github/workflows/release.yml +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/.github/workflows/tests.yml +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/.gitignore +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/AGENTS.md +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Benchmarks/README.rst +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Benchmarks/plot_benchmark_comparison.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Interactive/README.rst +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Interactive/plot_3d_spectral_viewer.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Interactive/plot_eels_explorer.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Interactive/plot_figure_annotations.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Interactive/plot_interactive_fft.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Interactive/plot_interactive_fitting.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Interactive/plot_ipf_density_map.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Interactive/plot_ipf_explorer.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Interactive/plot_key_bindings.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Interactive/plot_particle_picker.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Interactive/plot_point_widget.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Interactive/plot_segment_by_contrast.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Interactive/plot_segment_by_contrast_advanced.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Interactive/plot_spectra_roi_inspector.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Interactive/plot_threshold_explorer.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Interactive/plot_voxel_grain_explorer.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Markers/README.rst +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Markers/plot_arrows.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Markers/plot_circles.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Markers/plot_ellipses.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Markers/plot_horizontal_lines.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Markers/plot_line_segments.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Markers/plot_points.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Markers/plot_polygons.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Markers/plot_rectangles.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Markers/plot_squares.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Markers/plot_texts.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Markers/plot_transform_markers.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Markers/plot_vertical_lines.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/PlotTypes/README.rst +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/PlotTypes/plot_3d.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/PlotTypes/plot_bar.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/PlotTypes/plot_gpu_voxels.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/PlotTypes/plot_gridspec_custom.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/PlotTypes/plot_image2d.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/PlotTypes/plot_inset.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/PlotTypes/plot_label_formatting.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/PlotTypes/plot_line_styles.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/PlotTypes/plot_pcolormesh.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/PlotTypes/plot_spectra1d.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/PlotTypes/plot_step_and_log.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/PlotTypes/plot_tile_backend.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/PlotTypes/plot_twinx.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/README.rst +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Widgets/README.rst +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Widgets/plot_widget1d_hline.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Widgets/plot_widget1d_point.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Widgets/plot_widget1d_range.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Widgets/plot_widget1d_vline.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Widgets/plot_widget2d_annular.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Widgets/plot_widget2d_arrow.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Widgets/plot_widget2d_circle.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Widgets/plot_widget2d_crosshair.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Widgets/plot_widget2d_label.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Widgets/plot_widget2d_polygon.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Widgets/plot_widget2d_rectangle.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Examples/Widgets/plot_widget3d_plane.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/LICENSE +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/Makefile +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/README.md +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/FIGURE_ESM.md +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/__init__.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/_base_plot.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/_binary_frame.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/_electron.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/_repr_utils.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/_utils.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/axes/__init__.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/axes/_axes.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/axes/_inset_axes.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/callbacks.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/conftest.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/embed.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/figure/__init__.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/figure/_figure.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/figure/_gridspec.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/figure/_subplots.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/markers.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/plot1d/__init__.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/plot1d/_plotbar.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/plot2d/__init__.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/plot2d/_layer.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/plot2d/_plotmesh.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/plot2d/_tile_backend.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/plot3d/__init__.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/plot3d/_plot3d.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/plotxy/__init__.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/plotxy/_plotxy.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/sphinx_anywidget/__init__.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/sphinx_anywidget/_directive.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/sphinx_anywidget/_repr_utils.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/sphinx_anywidget/_scraper.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/sphinx_anywidget/_wheel_builder.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/sphinx_anywidget/static/anywidget_bridge.js +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/sphinx_anywidget/static/anywidget_overlay.css +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/sphinx_anywidget/tests/__init__.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/sphinx_anywidget/tests/conftest.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/sphinx_anywidget/tests/test_directive.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/sphinx_anywidget/tests/test_init.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/sphinx_anywidget/tests/test_repr_utils.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/sphinx_anywidget/tests/test_scraper.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/sphinx_anywidget/tests/test_wheel_builder.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/__init__.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/_gpu_clim_check.cjs +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/_png_utils.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/bar_basic.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/gridspec_3col_equal_spectra.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/gridspec_asymmetric_width_ratios.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/gridspec_height_ratio_image_histogram.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/gridspec_image_two_spectra.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/gridspec_side_by_side_1d.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/gridspec_spanning_top_two_bottom.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/imshow_axis_off.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/imshow_checkerboard.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/imshow_gradient.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/imshow_labels.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/imshow_viridis.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/inset_1d.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/inset_maximized.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/inset_minimized.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/inset_normal_2d.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/inset_stacked.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/inset_stacked_one_minimized.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/pcolormesh_uniform.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/plot1d_all_linestyles.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/plot1d_alpha.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/plot1d_axis_off.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/plot1d_dashed.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/plot1d_marker_symbols.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/plot1d_markers.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/plot1d_multi.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/plot1d_sine.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/plot1d_title.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/plot1d_twinx.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/plot3d_surface.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/baselines/subplots_2x1.png +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/conftest.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_benchmarks/__init__.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_benchmarks/baselines.json +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_benchmarks/benchmarks/baselines.json +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_benchmarks/test_benchmarks.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_benchmarks/test_benchmarks_py.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_documentation/__init__.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_documentation/test_bridge.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_documentation/test_push_hook.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_documentation/test_scraper.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_electron/__init__.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_electron/test_binary_frame.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_embed/__init__.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_embed/test_embed_api.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_embed/test_embed_mount.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_embed/test_export_png.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_examples/__init__.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_examples/test_interactive_examples.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_interactive/__init__.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_interactive/_event_test_utils.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_interactive/test_blit_audit.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_interactive/test_callbacks_playwright.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_interactive/test_callbacks_unit.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_interactive/test_edit_chrome.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_interactive/test_edit_chrome_playwright.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_interactive/test_event_pause_hold.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_interactive/test_event_plots.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_interactive/test_event_settled.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_interactive/test_events_regression.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_interactive/test_tile_parity_playwright.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_interactive/test_title.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_interactive/test_touch.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_interactive/test_widgets.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_labels/__init__.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_labels/test_label_api.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_labels/test_label_rendering.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_labels/test_no_clipping.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_layouts/__init__.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_layouts/test_batch.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_layouts/test_geom_channel.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_layouts/test_gridspec.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_layouts/test_inset.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_layouts/test_inset_callout.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_layouts/test_interaction.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_layouts/test_visual.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_markers/__init__.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_markers/test_marker_transforms.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_markers/test_markers.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_plot1d/__init__.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_plot1d/test_plot1d.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_plot1d/test_plotbar.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_plot2d/__init__.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_plot2d/test_detail_tile.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_plot2d/test_first_paint_race.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_plot2d/test_gpu_image.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_plot2d/test_gpu_parity_playwright.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_plot2d/test_imshow.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_plot2d/test_imshow_rgb.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_plot2d/test_layers.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_plot2d/test_layers_playwright.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_plot2d/test_pcolormesh.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_plot2d/test_plot2d_api.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_plot2d/test_rapid_frame_update.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_plot2d/test_tile_backend.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_plot2d/test_tiled_imshow.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_plot2d/test_zoom_view_write.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_plot3d/__init__.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_plot3d/test_colors_highlight.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_plot3d/test_gpu_fallback.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_plot3d/test_gpu_hidden_reveal.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_plot3d/test_plot3d.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_plot3d/test_voxels_planes.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/tests/test_plotxy/test_plotxy.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/widgets/__init__.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/widgets/_base.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/anyplotlib/widgets/_widgets3d.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/docs/_sg_html_scraper.py +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/docs/_static/anyplotlib.svg +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/docs/_static/custom.css +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/docs/_static/pyodide_bridge.js +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/docs/_templates/autosummary/class.rst +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/docs/api/callbacks.rst +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/docs/api/figure.rst +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/docs/api/figure_plots.rst +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/docs/api/index.rst +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/docs/api/markers.rst +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/docs/api/widgets.rst +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/docs/benchmarking.rst +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/docs/dev/index.rst +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/docs/embedding.rst +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/docs/events.rst +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/docs/getting_started.rst +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/docs/index.rst +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/make.bat +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/upcoming_changes/README.rst +0 -0
- {anyplotlib-0.4.0 → anyplotlib-0.4.2}/uv.lock +0 -0
|
@@ -10,6 +10,37 @@ Fragment files in ``upcoming_changes/`` are assembled into this file by
|
|
|
10
10
|
|
|
11
11
|
.. towncrier release notes start
|
|
12
12
|
|
|
13
|
+
0.4.1 (2026-07-26)
|
|
14
|
+
==================
|
|
15
|
+
|
|
16
|
+
New Features
|
|
17
|
+
------------
|
|
18
|
+
|
|
19
|
+
- Added ``max_extent=`` to :class:`~anyplotlib.widgets.RangeWidget` and
|
|
20
|
+
:class:`~anyplotlib.widgets.RectangleWidget` (and the matching
|
|
21
|
+
``add_range_widget`` / ``add_rectangle_widget`` factories) — a size cap enforced
|
|
22
|
+
*while dragging*, so the widget physically stops growing instead of being
|
|
23
|
+
clamped after the fact. The dragged edge/corner pins and the opposite one stays
|
|
24
|
+
put, so the selection never jumps under the cursor. ``RangeWidget`` takes a span
|
|
25
|
+
width in data units; ``RectangleWidget`` takes a scalar (both axes) or a
|
|
26
|
+
``(max_w, max_h)`` pair. Default ``None`` leaves widgets unbounded.
|
|
27
|
+
|
|
28
|
+
Use it when a widget's size drives real downstream work — e.g. an integrating
|
|
29
|
+
selector whose span is a number of frames to read.
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
Bug Fixes
|
|
33
|
+
---------
|
|
34
|
+
|
|
35
|
+
- Fixed the band-style :class:`~anyplotlib.widgets.RangeWidget` being impossible
|
|
36
|
+
to drag by its body when narrow. Each edge claimed a fixed ±12 px grab zone, so
|
|
37
|
+
a band under ~24 px wide on screen (routine when zoomed out, or when its span is
|
|
38
|
+
capped) had no grabbable middle: aiming at the body to translate the band caught
|
|
39
|
+
an edge and resized it instead. Each edge now takes at most a third of the
|
|
40
|
+
band's width, leaving the middle third for the move handle. Wide bands are
|
|
41
|
+
unaffected.
|
|
42
|
+
|
|
43
|
+
|
|
13
44
|
0.4.0 (2026-07-18)
|
|
14
45
|
==================
|
|
15
46
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: anyplotlib
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
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
|
|
@@ -3185,6 +3185,15 @@ function render({ model, el, onResize }) {
|
|
|
3185
3185
|
const [rx,ry]=_imgToCanvas2d(w.x,w.y,st,imgW,imgH);
|
|
3186
3186
|
const rw=w.w*scale, rh=w.h*scale;
|
|
3187
3187
|
ovCtx.strokeRect(rx,ry,rw,rh);
|
|
3188
|
+
// Canvas-space geometry readback for Playwright (same role as
|
|
3189
|
+
// window._aplTiming). A test cannot derive a handle's page position from
|
|
3190
|
+
// figure padding: the image->canvas mapping depends on the zoom/extent
|
|
3191
|
+
// state, and the grab radius is only HR px — so a hard-coded corner
|
|
3192
|
+
// silently grabs the BODY instead, and a resize test then asserts
|
|
3193
|
+
// nothing. Publishing the drawn corners lets a test aim at a real handle.
|
|
3194
|
+
if(!window._aplWidgetGeom) window._aplWidgetGeom={};
|
|
3195
|
+
if(!window._aplWidgetGeom[p.id]) window._aplWidgetGeom[p.id]={};
|
|
3196
|
+
window._aplWidgetGeom[p.id][w.id]={type:'rectangle',rx,ry,rw,rh};
|
|
3188
3197
|
if(_handles){
|
|
3189
3198
|
_drawHandle2d(ovCtx,rx,ry,w.color);_drawHandle2d(ovCtx,rx+rw,ry,w.color);
|
|
3190
3199
|
_drawHandle2d(ovCtx,rx,ry+rh,w.color);_drawHandle2d(ovCtx,rx+rw,ry+rh,w.color);
|
|
@@ -7214,22 +7223,28 @@ fn fs(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
7214
7223
|
w.r_inner = newR;
|
|
7215
7224
|
}
|
|
7216
7225
|
} else if (w.type === 'rectangle') {
|
|
7226
|
+
// max_w / max_h cap the rectangle DURING the drag, so it physically stops
|
|
7227
|
+
// growing rather than being silently clamped afterwards. Each branch pins
|
|
7228
|
+
// the size and leaves the OPPOSITE corner where it is, so the anchor never
|
|
7229
|
+
// shifts and the rectangle cannot jump out from under the cursor.
|
|
7230
|
+
const capW = (v) => (w.max_w == null ? v : Math.min(v, w.max_w));
|
|
7231
|
+
const capH = (v) => (w.max_h == null ? v : Math.min(v, w.max_h));
|
|
7217
7232
|
if (d.mode === 'move') {
|
|
7218
7233
|
w.x = s.x + dix; w.y = s.y + diy;
|
|
7219
7234
|
} else if (d.mode === 'resize_br') {
|
|
7220
|
-
w.w = Math.max(1, s.w + dix); w.h = Math.max(1, s.h + diy);
|
|
7235
|
+
w.w = capW(Math.max(1, s.w + dix)); w.h = capH(Math.max(1, s.h + diy));
|
|
7221
7236
|
} else if (d.mode === 'resize_bl') {
|
|
7222
|
-
const newW = Math.max(1, s.w - dix);
|
|
7237
|
+
const newW = capW(Math.max(1, s.w - dix));
|
|
7223
7238
|
w.x = s.x + (s.w - newW); w.w = newW;
|
|
7224
|
-
w.h = Math.max(1, s.h + diy);
|
|
7239
|
+
w.h = capH(Math.max(1, s.h + diy));
|
|
7225
7240
|
} else if (d.mode === 'resize_tr') {
|
|
7226
|
-
w.w = Math.max(1, s.w + dix);
|
|
7227
|
-
const newH = Math.max(1, s.h - diy);
|
|
7241
|
+
w.w = capW(Math.max(1, s.w + dix));
|
|
7242
|
+
const newH = capH(Math.max(1, s.h - diy));
|
|
7228
7243
|
w.y = s.y + (s.h - newH); w.h = newH;
|
|
7229
7244
|
} else if (d.mode === 'resize_tl') {
|
|
7230
|
-
const newW = Math.max(1, s.w - dix);
|
|
7245
|
+
const newW = capW(Math.max(1, s.w - dix));
|
|
7231
7246
|
w.x = s.x + (s.w - newW); w.w = newW;
|
|
7232
|
-
const newH = Math.max(1, s.h - diy);
|
|
7247
|
+
const newH = capH(Math.max(1, s.h - diy));
|
|
7233
7248
|
w.y = s.y + (s.h - newH); w.h = newH;
|
|
7234
7249
|
}
|
|
7235
7250
|
} else if (w.type === 'crosshair') {
|
|
@@ -7307,9 +7322,16 @@ fn fs(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
7307
7322
|
if(Math.hypot(mx-px1b,my-pyHalf)<=HR+5)
|
|
7308
7323
|
return{idx:i,mode:'edge1',wtype:'range',startMX:mx,snapW:{...w}};
|
|
7309
7324
|
} else {
|
|
7310
|
-
|
|
7311
|
-
|
|
7325
|
+
// Band style. The edge grab zones are ±(HR+5)=12 px each, so on a
|
|
7326
|
+
// NARROW band (< ~24 px on screen — routine when the span is capped, or
|
|
7327
|
+
// just zoomed out) the two zones meet and swallow the body: the user
|
|
7328
|
+
// aims at the middle to translate the band and gets an edge resize
|
|
7329
|
+
// instead. Give each edge at most a THIRD of the band, so the middle
|
|
7330
|
+
// third always belongs to 'move' no matter how narrow the band gets.
|
|
7312
7331
|
const left=Math.min(px0,px1b),right=Math.max(px0,px1b);
|
|
7332
|
+
const grab=Math.min(HR+5,(right-left)/3);
|
|
7333
|
+
if(Math.abs(mx-px0)<=grab) return{idx:i,mode:'edge0',wtype:'range',startMX:mx,snapW:{...w}};
|
|
7334
|
+
if(Math.abs(mx-px1b)<=grab) return{idx:i,mode:'edge1',wtype:'range',startMX:mx,snapW:{...w}};
|
|
7313
7335
|
if(mx>=left&&mx<=right&&my>=r.y&&my<=r.y+r.h) return{idx:i,mode:'move',wtype:'range',startMX:mx,snapW:{...w}};
|
|
7314
7336
|
}
|
|
7315
7337
|
}
|
|
@@ -7329,8 +7351,21 @@ fn fs(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
7329
7351
|
if(w.type==='vline'){w.x=xUnit;}
|
|
7330
7352
|
else if(w.type==='hline'){w.y=st.data_max-((py-r.y)/(r.h||1))*(st.data_max-st.data_min);}
|
|
7331
7353
|
else if(w.type==='range'){
|
|
7332
|
-
|
|
7333
|
-
|
|
7354
|
+
// max_extent caps the span DURING the drag. The edge NOT being dragged is
|
|
7355
|
+
// the anchor and never moves, so the span stops growing at the cap without
|
|
7356
|
+
// the range jumping. (Clamping afterwards by rewriting whichever endpoint
|
|
7357
|
+
// happens to be numerically smaller moves the edge the user is not holding
|
|
7358
|
+
// — that reads as phantom movement.) Dragging the whole band is unaffected:
|
|
7359
|
+
// a translation can't change the width.
|
|
7360
|
+
const cap = (w.max_extent == null ? null : Math.abs(w.max_extent));
|
|
7361
|
+
if(d.mode==='edge0'){
|
|
7362
|
+
w.x0 = (cap!=null && Math.abs(w.x1-xUnit) > cap)
|
|
7363
|
+
? w.x1 + (xUnit < w.x1 ? -cap : cap) : xUnit;
|
|
7364
|
+
}
|
|
7365
|
+
else if(d.mode==='edge1'){
|
|
7366
|
+
w.x1 = (cap!=null && Math.abs(xUnit-w.x0) > cap)
|
|
7367
|
+
? w.x0 + (xUnit < w.x0 ? -cap : cap) : xUnit;
|
|
7368
|
+
}
|
|
7334
7369
|
else {
|
|
7335
7370
|
const snapPx=_fracToPx1d(xArr.length>=2?_axisValToFrac(xArr,s.x0):0,x0,x1,r);
|
|
7336
7371
|
const dxUnit=xArr.length>=2?_axisFracToVal(xArr,_canvasXToFrac1d(snapPx+(mx-d.startMX),x0,x1,r))-s.x0:(mx-d.startMX)/(r.w||1);
|
|
@@ -756,6 +756,7 @@ class Plot1D(_BasePlot, _PanelMixin, _MarkerMixin):
|
|
|
756
756
|
style: str = "band",
|
|
757
757
|
y: float = 0.0,
|
|
758
758
|
linewidth: float = 2,
|
|
759
|
+
max_extent: float | None = None,
|
|
759
760
|
_push: bool = True) -> _RangeWidget:
|
|
760
761
|
"""Add a draggable range overlay to this panel.
|
|
761
762
|
|
|
@@ -775,6 +776,10 @@ class Plot1D(_BasePlot, _PanelMixin, _MarkerMixin):
|
|
|
775
776
|
``style='fwhm'``. Ignored when ``style='band'``. Default 0.
|
|
776
777
|
linewidth : float, optional
|
|
777
778
|
Line stroke width in px. Default 2.
|
|
779
|
+
max_extent : float, optional
|
|
780
|
+
Maximum span width in data units. The span physically stops growing
|
|
781
|
+
at this width while dragging (the dragged edge pins, the opposite
|
|
782
|
+
edge stays put). ``None`` (default) leaves it unbounded.
|
|
778
783
|
_push : bool, optional
|
|
779
784
|
Push state to JS immediately. Set to ``False`` when adding
|
|
780
785
|
several widgets at once; call :meth:`_push` manually afterward.
|
|
@@ -787,7 +792,7 @@ class Plot1D(_BasePlot, _PanelMixin, _MarkerMixin):
|
|
|
787
792
|
"""
|
|
788
793
|
widget = _RangeWidget(lambda: None, x0=float(x0), x1=float(x1),
|
|
789
794
|
color=color, style=style, y=float(y),
|
|
790
|
-
linewidth=linewidth)
|
|
795
|
+
linewidth=linewidth, max_extent=max_extent)
|
|
791
796
|
widget._push_fn = self._make_widget_push_fn(widget)
|
|
792
797
|
self._widgets[widget.id] = widget
|
|
793
798
|
if _push:
|
|
@@ -568,13 +568,17 @@ class Plot2D(_BasePlot, _PanelMixin, _MarkerMixin):
|
|
|
568
568
|
self._state["base_width"] = int(ow)
|
|
569
569
|
self._state["base_height"] = int(oh)
|
|
570
570
|
self._overview_stale = False
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
571
|
+
# min/max are two more full passes over the overview — same guard rule
|
|
572
|
+
# as the FETCH diagnostic below (they are .debug() ARGUMENTS, so lazy
|
|
573
|
+
# formatting does not skip them).
|
|
574
|
+
if _TLOG.isEnabledFor(logging.DEBUG):
|
|
575
|
+
_TLOG.debug(
|
|
576
|
+
"[TILEDBG] overview refresh: overview=%s u8[min=%d max=%d] "
|
|
577
|
+
"display=(%s,%s) base_wh=(%d,%d) image_wh=(%s,%s)", (oh, ow),
|
|
578
|
+
int(img_u8.min()), int(img_u8.max()),
|
|
579
|
+
self._state.get("display_min"), self._state.get("display_max"),
|
|
580
|
+
ow, oh, self._state.get("image_width"),
|
|
581
|
+
self._state.get("image_height"))
|
|
578
582
|
except Exception as e:
|
|
579
583
|
_TLOG.warning("[TILEDBG] overview refresh FAILED: %s", e)
|
|
580
584
|
|
|
@@ -648,28 +652,47 @@ class Plot2D(_BasePlot, _PanelMixin, _MarkerMixin):
|
|
|
648
652
|
else:
|
|
649
653
|
out_h, out_w = dh, max(1, int(round(dh * aspect)))
|
|
650
654
|
b = self._tile_backend
|
|
651
|
-
# DIAGNOSTIC: the raw native region straight from the backend, BEFORE
|
|
652
|
-
# sampling — its shape + distinct-value count tells us whether the backend
|
|
653
|
-
# actually holds native pixels. If region is 82×82 but the raw crop has
|
|
654
|
-
# only ~100 distinct values, the backend source is a downsample (bug).
|
|
655
|
-
_raw = np.asarray(b._a[y0:y1, x0:x1]) if hasattr(b, "_a") else None
|
|
656
655
|
tile = self._tile_backend.sample(
|
|
657
656
|
x0, x1, y0, y1, out_w, out_h, self._integration_method)
|
|
658
657
|
if b.origin == "lower":
|
|
659
658
|
tile = np.flipud(tile)
|
|
660
659
|
self.set_detail(np.ascontiguousarray(tile), x0, x1, y0, y1)
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
660
|
+
# The FETCH diagnostic sorts the raw native crop and the tile
|
|
661
|
+
# (np.unique) — 259 ms on a 4096² frame at the zoom where VIEW_OVERFETCH
|
|
662
|
+
# makes the crop the whole image. Those were arguments to a .debug()
|
|
663
|
+
# call, so lazy %-formatting did NOT save them: every pan paid for them
|
|
664
|
+
# at any log level. Guard the whole block, never inline it again.
|
|
665
|
+
if _TLOG.isEnabledFor(logging.DEBUG):
|
|
666
|
+
self._log_fetch_diagnostic(
|
|
667
|
+
b, tile, zoom, x0, x1, y0, y1, rw, rh, out_w, out_h)
|
|
668
|
+
except Exception as e:
|
|
669
|
+
_TLOG.warning("[TILEDBG] view_changed tile update FAILED: %s", e)
|
|
670
|
+
|
|
671
|
+
def _log_fetch_diagnostic(self, b, tile, zoom, x0, x1, y0, y1, rw, rh,
|
|
672
|
+
out_w, out_h) -> None:
|
|
673
|
+
"""The expensive [TILEDBG] FETCH line — only ever call this behind an
|
|
674
|
+
``isEnabledFor(DEBUG)`` guard.
|
|
675
|
+
|
|
676
|
+
The distinct-value counts are the point of it: if the requested region is
|
|
677
|
+
82×82 but the backend's raw crop has only ~100 distinct values, the backend
|
|
678
|
+
source is a downsample rather than native pixels (a real bug this caught).
|
|
679
|
+
Both counts are full sorts of up to 16 M pixels, hence the guard."""
|
|
680
|
+
try:
|
|
681
|
+
raw = np.asarray(b._a[y0:y1, x0:x1]) if hasattr(b, "_a") else None
|
|
682
|
+
rawinfo = ("raw_crop=%s distinct=%d"
|
|
683
|
+
% (raw.shape, int(np.unique(raw).size))
|
|
684
|
+
) if raw is not None else "raw=?"
|
|
685
|
+
arr = np.asarray(tile)
|
|
665
686
|
_TLOG.debug(
|
|
666
687
|
"[TILEDBG] view_changed FETCH zoom=%.2f region=[x %d:%d y %d:%d] "
|
|
667
|
-
"(%dx%d logical) BACKEND_shape=%s %s → tile=%dx%d
|
|
668
|
-
"u8[min=%d max=%d]",
|
|
669
|
-
zoom, x0, x1, y0, y1, rw, rh,
|
|
670
|
-
|
|
688
|
+
"(%dx%d logical) BACKEND_shape=%s %s → tile=%dx%d "
|
|
689
|
+
"tile_distinct=%d u8[min=%d max=%d]",
|
|
690
|
+
zoom, x0, x1, y0, y1, rw, rh,
|
|
691
|
+
b.full_shape if hasattr(b, "full_shape") else "?", rawinfo,
|
|
692
|
+
out_w, out_h, int(np.unique(arr).size),
|
|
693
|
+
int(arr.min()), int(arr.max()))
|
|
671
694
|
except Exception as e:
|
|
672
|
-
_TLOG.
|
|
695
|
+
_TLOG.debug("[TILEDBG] FETCH diagnostic failed: %s", e)
|
|
673
696
|
|
|
674
697
|
@staticmethod
|
|
675
698
|
def _encode_bytes(arr: np.ndarray) -> str:
|
|
@@ -1610,16 +1633,38 @@ class Plot2D(_BasePlot, _PanelMixin, _MarkerMixin):
|
|
|
1610
1633
|
def add_rectangle_widget(self, x: float | None = None, y: float | None = None,
|
|
1611
1634
|
w: float | None = None, h: float | None = None,
|
|
1612
1635
|
color: str = "#00e5ff", linewidth: float = 2,
|
|
1613
|
-
show_handles: bool = True
|
|
1614
|
-
|
|
1636
|
+
show_handles: bool = True,
|
|
1637
|
+
max_extent=None) -> RectangleWidget:
|
|
1638
|
+
"""Add a draggable rectangle overlay.
|
|
1639
|
+
|
|
1640
|
+
``max_extent`` caps the rectangle's size in the widget's coordinates —
|
|
1641
|
+
a scalar caps both axes, a ``(max_w, max_h)`` pair caps them separately.
|
|
1642
|
+
The rectangle then physically stops growing at the cap while dragging
|
|
1643
|
+
(the dragged corner pins, the opposite corner stays put).
|
|
1644
|
+
"""
|
|
1615
1645
|
iw, ih = self._state["image_width"], self._state["image_height"]
|
|
1646
|
+
# The DEFAULT size honours max_extent. Without this a capped rectangle is
|
|
1647
|
+
# born at half the image and then snaps down to the cap on first use —
|
|
1648
|
+
# the ROI visibly appears huge and collapses, which looks like a bug.
|
|
1649
|
+
def _cap(axis_default, cap):
|
|
1650
|
+
return axis_default if cap is None else min(axis_default, float(cap))
|
|
1651
|
+
|
|
1652
|
+
if max_extent is None:
|
|
1653
|
+
cap_w = cap_h = None
|
|
1654
|
+
elif isinstance(max_extent, (tuple, list)):
|
|
1655
|
+
cap_w, cap_h = max_extent[0], max_extent[1]
|
|
1656
|
+
else:
|
|
1657
|
+
cap_w = cap_h = max_extent
|
|
1658
|
+
def_w = _cap(iw * 0.5, cap_w)
|
|
1659
|
+
def_h = _cap(ih * 0.5, cap_h)
|
|
1616
1660
|
widget = RectangleWidget(lambda: None,
|
|
1617
1661
|
x=float(x) if x is not None else iw * 0.25,
|
|
1618
1662
|
y=float(y) if y is not None else ih * 0.25,
|
|
1619
|
-
w=float(w) if w is not None else
|
|
1620
|
-
h=float(h) if h is not None else
|
|
1663
|
+
w=float(w) if w is not None else def_w,
|
|
1664
|
+
h=float(h) if h is not None else def_h,
|
|
1621
1665
|
color=color, linewidth=linewidth,
|
|
1622
|
-
show_handles=show_handles
|
|
1666
|
+
show_handles=show_handles,
|
|
1667
|
+
max_extent=max_extent)
|
|
1623
1668
|
widget._push_fn = self._make_widget_push_fn(widget)
|
|
1624
1669
|
self._widgets[widget.id] = widget
|
|
1625
1670
|
self._push()
|