anyplotlib 0.3.0b1__tar.gz → 0.4.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.
Files changed (258) hide show
  1. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/.github/workflows/prepare_release.yml +42 -11
  2. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/CHANGELOG.rst +91 -0
  3. anyplotlib-0.4.1/Examples/Interactive/plot_figure_annotations.py +87 -0
  4. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Interactive/plot_voxel_grain_explorer.py +5 -2
  5. anyplotlib-0.4.1/Examples/Markers/plot_transform_markers.py +49 -0
  6. anyplotlib-0.4.1/Examples/PlotTypes/plot_gpu_voxels.py +82 -0
  7. anyplotlib-0.4.1/Examples/PlotTypes/plot_step_and_log.py +43 -0
  8. anyplotlib-0.4.1/Examples/PlotTypes/plot_tile_backend.py +184 -0
  9. anyplotlib-0.4.1/Examples/PlotTypes/plot_twinx.py +45 -0
  10. anyplotlib-0.4.1/Examples/Widgets/plot_widget1d_point.py +40 -0
  11. anyplotlib-0.4.1/Examples/Widgets/plot_widget2d_arrow.py +41 -0
  12. anyplotlib-0.4.1/Examples/Widgets/plot_widget3d_plane.py +47 -0
  13. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/PKG-INFO +1 -1
  14. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/FIGURE_ESM.md +27 -11
  15. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/__init__.py +4 -4
  16. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/_base_plot.py +69 -0
  17. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/_utils.py +38 -0
  18. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/axes/_axes.py +2 -2
  19. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/axes/_inset_axes.py +163 -5
  20. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/callbacks.py +26 -2
  21. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/figure/_figure.py +29 -0
  22. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/figure_esm.js +642 -57
  23. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/markers.py +3 -0
  24. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/plot1d/_plot1d.py +221 -18
  25. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/plot2d/_layer.py +18 -5
  26. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/plot2d/_plot2d.py +74 -18
  27. anyplotlib-0.4.1/anyplotlib/tests/baselines/plot1d_twinx.png +0 -0
  28. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_embed/test_export_png.py +131 -51
  29. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_interactive/test_callbacks_playwright.py +100 -0
  30. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_interactive/test_callbacks_unit.py +72 -0
  31. anyplotlib-0.4.1/anyplotlib/tests/test_interactive/test_widget_max_extent.py +371 -0
  32. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_interactive/test_widgets.py +196 -1
  33. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_labels/test_label_api.py +46 -0
  34. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_layouts/test_inset.py +153 -0
  35. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_layouts/test_inset_callout.py +371 -0
  36. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_layouts/test_visual.py +20 -0
  37. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_markers/test_markers.py +77 -0
  38. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_plot1d/test_plot1d.py +158 -0
  39. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_plot2d/test_layers.py +94 -0
  40. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_plot2d/test_layers_playwright.py +107 -0
  41. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_plot3d/test_gpu_fallback.py +1 -2
  42. anyplotlib-0.4.1/anyplotlib/tests/test_plot3d/test_gpu_hidden_reveal.py +330 -0
  43. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/widgets/_widgets1d.py +34 -8
  44. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/widgets/_widgets2d.py +40 -7
  45. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/docs/_root/index.html +3 -3
  46. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/docs/_root/switcher.json +15 -0
  47. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/docs/api/figure_plots.rst +7 -0
  48. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/docs/api/index.rst +3 -0
  49. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/docs/conf.py +10 -1
  50. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/pyproject.toml +1 -1
  51. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/uv.lock +1 -1
  52. anyplotlib-0.3.0b1/LARGE_IMAGE_WEBGPU_PLAN.md +0 -223
  53. anyplotlib-0.3.0b1/ORIX_BACKEND_PLAN.md +0 -133
  54. anyplotlib-0.3.0b1/RELEASE_NOTES.md +0 -94
  55. anyplotlib-0.3.0b1/RELEASE_PLAN.md +0 -104
  56. anyplotlib-0.3.0b1/WEBGPU_PLAN.md +0 -234
  57. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/.github/workflows/ci.yml +0 -0
  58. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/.github/workflows/docs.yml +0 -0
  59. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/.github/workflows/release.yml +0 -0
  60. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/.github/workflows/tests.yml +0 -0
  61. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/.gitignore +0 -0
  62. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/AGENTS.md +0 -0
  63. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Benchmarks/README.rst +0 -0
  64. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Benchmarks/plot_benchmark_comparison.py +0 -0
  65. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Interactive/README.rst +0 -0
  66. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Interactive/plot_3d_spectral_viewer.py +0 -0
  67. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Interactive/plot_eels_explorer.py +0 -0
  68. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Interactive/plot_interactive_fft.py +0 -0
  69. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Interactive/plot_interactive_fitting.py +0 -0
  70. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Interactive/plot_ipf_density_map.py +0 -0
  71. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Interactive/plot_ipf_explorer.py +0 -0
  72. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Interactive/plot_key_bindings.py +0 -0
  73. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Interactive/plot_particle_picker.py +0 -0
  74. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Interactive/plot_point_widget.py +0 -0
  75. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Interactive/plot_segment_by_contrast.py +0 -0
  76. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Interactive/plot_segment_by_contrast_advanced.py +0 -0
  77. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Interactive/plot_spectra_roi_inspector.py +0 -0
  78. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Interactive/plot_threshold_explorer.py +0 -0
  79. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Markers/README.rst +0 -0
  80. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Markers/plot_arrows.py +0 -0
  81. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Markers/plot_circles.py +0 -0
  82. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Markers/plot_ellipses.py +0 -0
  83. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Markers/plot_horizontal_lines.py +0 -0
  84. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Markers/plot_line_segments.py +0 -0
  85. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Markers/plot_points.py +0 -0
  86. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Markers/plot_polygons.py +0 -0
  87. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Markers/plot_rectangles.py +0 -0
  88. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Markers/plot_squares.py +0 -0
  89. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Markers/plot_texts.py +0 -0
  90. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Markers/plot_vertical_lines.py +0 -0
  91. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/PlotTypes/README.rst +0 -0
  92. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/PlotTypes/plot_3d.py +0 -0
  93. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/PlotTypes/plot_bar.py +0 -0
  94. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/PlotTypes/plot_gridspec_custom.py +0 -0
  95. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/PlotTypes/plot_image2d.py +0 -0
  96. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/PlotTypes/plot_inset.py +0 -0
  97. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/PlotTypes/plot_label_formatting.py +0 -0
  98. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/PlotTypes/plot_line_styles.py +0 -0
  99. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/PlotTypes/plot_pcolormesh.py +0 -0
  100. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/PlotTypes/plot_spectra1d.py +0 -0
  101. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/README.rst +0 -0
  102. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Widgets/README.rst +0 -0
  103. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Widgets/plot_widget1d_hline.py +0 -0
  104. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Widgets/plot_widget1d_range.py +0 -0
  105. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Widgets/plot_widget1d_vline.py +0 -0
  106. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Widgets/plot_widget2d_annular.py +0 -0
  107. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Widgets/plot_widget2d_circle.py +0 -0
  108. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Widgets/plot_widget2d_crosshair.py +0 -0
  109. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Widgets/plot_widget2d_label.py +0 -0
  110. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Widgets/plot_widget2d_polygon.py +0 -0
  111. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Examples/Widgets/plot_widget2d_rectangle.py +0 -0
  112. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/LICENSE +0 -0
  113. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/Makefile +0 -0
  114. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/README.md +0 -0
  115. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/_binary_frame.py +0 -0
  116. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/_electron.py +0 -0
  117. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/_repr_utils.py +0 -0
  118. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/axes/__init__.py +0 -0
  119. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/conftest.py +0 -0
  120. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/embed.py +0 -0
  121. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/figure/__init__.py +0 -0
  122. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/figure/_gridspec.py +0 -0
  123. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/figure/_subplots.py +0 -0
  124. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/plot1d/__init__.py +0 -0
  125. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/plot1d/_plotbar.py +0 -0
  126. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/plot2d/__init__.py +0 -0
  127. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/plot2d/_plotmesh.py +0 -0
  128. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/plot2d/_tile_backend.py +0 -0
  129. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/plot3d/__init__.py +0 -0
  130. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/plot3d/_plot3d.py +0 -0
  131. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/plotxy/__init__.py +0 -0
  132. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/plotxy/_plotxy.py +0 -0
  133. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/sphinx_anywidget/__init__.py +0 -0
  134. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/sphinx_anywidget/_directive.py +0 -0
  135. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/sphinx_anywidget/_repr_utils.py +0 -0
  136. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/sphinx_anywidget/_scraper.py +0 -0
  137. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/sphinx_anywidget/_wheel_builder.py +0 -0
  138. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/sphinx_anywidget/static/anywidget_bridge.js +0 -0
  139. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/sphinx_anywidget/static/anywidget_overlay.css +0 -0
  140. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/sphinx_anywidget/tests/__init__.py +0 -0
  141. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/sphinx_anywidget/tests/conftest.py +0 -0
  142. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/sphinx_anywidget/tests/test_directive.py +0 -0
  143. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/sphinx_anywidget/tests/test_init.py +0 -0
  144. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/sphinx_anywidget/tests/test_repr_utils.py +0 -0
  145. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/sphinx_anywidget/tests/test_scraper.py +0 -0
  146. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/sphinx_anywidget/tests/test_wheel_builder.py +0 -0
  147. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/__init__.py +0 -0
  148. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/_gpu_clim_check.cjs +0 -0
  149. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/_png_utils.py +0 -0
  150. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/bar_basic.png +0 -0
  151. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/gridspec_3col_equal_spectra.png +0 -0
  152. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/gridspec_asymmetric_width_ratios.png +0 -0
  153. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/gridspec_height_ratio_image_histogram.png +0 -0
  154. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/gridspec_image_two_spectra.png +0 -0
  155. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/gridspec_side_by_side_1d.png +0 -0
  156. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/gridspec_spanning_top_two_bottom.png +0 -0
  157. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/imshow_axis_off.png +0 -0
  158. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/imshow_checkerboard.png +0 -0
  159. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/imshow_gradient.png +0 -0
  160. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/imshow_labels.png +0 -0
  161. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/imshow_viridis.png +0 -0
  162. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/inset_1d.png +0 -0
  163. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/inset_maximized.png +0 -0
  164. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/inset_minimized.png +0 -0
  165. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/inset_normal_2d.png +0 -0
  166. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/inset_stacked.png +0 -0
  167. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/inset_stacked_one_minimized.png +0 -0
  168. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/pcolormesh_uniform.png +0 -0
  169. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/plot1d_all_linestyles.png +0 -0
  170. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/plot1d_alpha.png +0 -0
  171. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/plot1d_axis_off.png +0 -0
  172. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/plot1d_dashed.png +0 -0
  173. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/plot1d_marker_symbols.png +0 -0
  174. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/plot1d_markers.png +0 -0
  175. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/plot1d_multi.png +0 -0
  176. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/plot1d_sine.png +0 -0
  177. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/plot1d_title.png +0 -0
  178. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/plot3d_surface.png +0 -0
  179. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/baselines/subplots_2x1.png +0 -0
  180. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/conftest.py +0 -0
  181. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_benchmarks/__init__.py +0 -0
  182. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_benchmarks/baselines.json +0 -0
  183. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_benchmarks/benchmarks/baselines.json +0 -0
  184. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_benchmarks/test_benchmarks.py +0 -0
  185. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_benchmarks/test_benchmarks_py.py +0 -0
  186. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_documentation/__init__.py +0 -0
  187. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_documentation/test_bridge.py +0 -0
  188. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_documentation/test_push_hook.py +0 -0
  189. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_documentation/test_scraper.py +0 -0
  190. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_electron/__init__.py +0 -0
  191. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_electron/test_binary_frame.py +0 -0
  192. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_embed/__init__.py +0 -0
  193. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_embed/test_embed_api.py +0 -0
  194. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_embed/test_embed_mount.py +0 -0
  195. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_examples/__init__.py +0 -0
  196. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_examples/test_interactive_examples.py +0 -0
  197. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_interactive/__init__.py +0 -0
  198. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_interactive/_event_test_utils.py +0 -0
  199. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_interactive/test_blit_audit.py +0 -0
  200. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_interactive/test_edit_chrome.py +0 -0
  201. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_interactive/test_edit_chrome_playwright.py +0 -0
  202. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_interactive/test_event_pause_hold.py +0 -0
  203. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_interactive/test_event_plots.py +0 -0
  204. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_interactive/test_event_settled.py +0 -0
  205. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_interactive/test_events_regression.py +0 -0
  206. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_interactive/test_tile_parity_playwright.py +0 -0
  207. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_interactive/test_title.py +0 -0
  208. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_interactive/test_touch.py +0 -0
  209. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_labels/__init__.py +0 -0
  210. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_labels/test_label_rendering.py +0 -0
  211. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_labels/test_no_clipping.py +0 -0
  212. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_layouts/__init__.py +0 -0
  213. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_layouts/test_batch.py +0 -0
  214. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_layouts/test_geom_channel.py +0 -0
  215. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_layouts/test_gridspec.py +0 -0
  216. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_layouts/test_interaction.py +0 -0
  217. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_markers/__init__.py +0 -0
  218. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_markers/test_marker_transforms.py +0 -0
  219. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_plot1d/__init__.py +0 -0
  220. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_plot1d/test_plotbar.py +0 -0
  221. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_plot2d/__init__.py +0 -0
  222. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_plot2d/test_detail_tile.py +0 -0
  223. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_plot2d/test_first_paint_race.py +0 -0
  224. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_plot2d/test_gpu_image.py +0 -0
  225. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_plot2d/test_gpu_parity_playwright.py +0 -0
  226. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_plot2d/test_imshow.py +0 -0
  227. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_plot2d/test_imshow_rgb.py +0 -0
  228. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_plot2d/test_pcolormesh.py +0 -0
  229. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_plot2d/test_plot2d_api.py +0 -0
  230. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_plot2d/test_rapid_frame_update.py +0 -0
  231. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_plot2d/test_tile_backend.py +0 -0
  232. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_plot2d/test_tiled_imshow.py +0 -0
  233. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_plot2d/test_zoom_view_write.py +0 -0
  234. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_plot3d/__init__.py +0 -0
  235. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_plot3d/test_colors_highlight.py +0 -0
  236. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_plot3d/test_plot3d.py +0 -0
  237. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_plot3d/test_voxels_planes.py +0 -0
  238. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/tests/test_plotxy/test_plotxy.py +0 -0
  239. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/widgets/__init__.py +0 -0
  240. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/widgets/_base.py +0 -0
  241. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/anyplotlib/widgets/_widgets3d.py +0 -0
  242. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/docs/_sg_html_scraper.py +0 -0
  243. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/docs/_static/anyplotlib.svg +0 -0
  244. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/docs/_static/custom.css +0 -0
  245. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/docs/_static/pyodide_bridge.js +0 -0
  246. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/docs/_templates/autosummary/class.rst +0 -0
  247. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/docs/api/callbacks.rst +0 -0
  248. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/docs/api/figure.rst +0 -0
  249. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/docs/api/markers.rst +0 -0
  250. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/docs/api/widgets.rst +0 -0
  251. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/docs/benchmarking.rst +0 -0
  252. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/docs/dev/index.rst +0 -0
  253. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/docs/embedding.rst +0 -0
  254. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/docs/events.rst +0 -0
  255. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/docs/getting_started.rst +0 -0
  256. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/docs/index.rst +0 -0
  257. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/make.bat +0 -0
  258. {anyplotlib-0.3.0b1 → anyplotlib-0.4.1}/upcoming_changes/README.rst +0 -0
@@ -11,12 +11,13 @@ on:
11
11
  required: true
12
12
  type: choice
13
13
  options:
14
+ - finalize # drop the bN suffix: release the current beta's base as stable (0.3.0b2 -> 0.3.0)
14
15
  - minor
15
16
  - bugfix
16
17
  - major
17
18
  - pre-release # increments the bN counter on the current base version
18
19
  beta:
19
- description: "Mark as beta pre-release (adds bN suffix; always true for pre-release)"
20
+ description: "Mark as beta pre-release (adds bN suffix). Ignored for 'pre-release' (always beta) and 'finalize' (always stable)."
20
21
  required: false
21
22
  type: boolean
22
23
  default: false
@@ -67,17 +68,42 @@ jobs:
67
68
  minor = int(m.group(2))
68
69
  patch = int(m.group(3))
69
70
  beta_n = int(m.group(4)) if m.group(4) else None
70
-
71
- if bump == "major":
71
+ on_beta = beta_n is not None
72
+
73
+ if bump == "finalize":
74
+ # Release the current beta's base as stable: just drop the bN
75
+ # suffix, keep major.minor.patch. e.g. 0.3.0b2 -> 0.3.0.
76
+ if not on_beta:
77
+ raise SystemExit(
78
+ f"'finalize' requires a beta base version, but current "
79
+ f"version {current!r} has no bN suffix. Use minor / bugfix "
80
+ f"/ major to start a new release instead."
81
+ )
82
+ is_beta = False
83
+ elif bump == "pre-release":
84
+ # Keep the same base; just walk the beta counter forward.
85
+ is_beta = True
86
+ beta_n = (beta_n or 0) + 1
87
+ elif on_beta:
88
+ # We are on a beta of the NEXT release (e.g. 0.3.0b2). The base
89
+ # major.minor.patch is that upcoming version, so minor/bugfix/major
90
+ # must bump relative to the LAST STABLE (base - the in-progress
91
+ # component), not skip a whole version. The common intent from a
92
+ # beta is 'finalize', so steer the user there rather than guess.
93
+ raise SystemExit(
94
+ f"Current version {current!r} is a beta of the upcoming "
95
+ f"{major}.{minor}.{patch} release. To ship it, use bump="
96
+ f"'finalize' (-> {major}.{minor}.{patch}). A '{bump}' bump from "
97
+ f"a beta would skip {major}.{minor}.{patch} entirely "
98
+ f"(e.g. -> {'%d.%d.0' % (major, minor + 1) if bump == 'minor' else '...'}); "
99
+ f"that is almost never intended."
100
+ )
101
+ elif bump == "major":
72
102
  major, minor, patch = major + 1, 0, 0
73
103
  elif bump == "minor":
74
104
  minor, patch = minor + 1, 0
75
105
  elif bump == "bugfix":
76
106
  patch += 1
77
- elif bump == "pre-release":
78
- # Keep the same base; just walk the beta counter forward.
79
- is_beta = True
80
- beta_n = (beta_n or 0) + 1
81
107
 
82
108
  if is_beta:
83
109
  if bump != "pre-release":
@@ -88,10 +114,15 @@ jobs:
88
114
  PYEOF
89
115
  )
90
116
 
117
+ # Derive is_beta from the COMPUTED version (ends in bN?), not the raw
118
+ # input — so 'finalize' is always treated as stable and a mismatched
119
+ # beta checkbox can't mislabel the switcher / skip the root redirect.
120
+ if [[ "$NEW_VERSION" =~ b[0-9]+$ ]]; then IS_BETA_OUT=true; else IS_BETA_OUT=false; fi
121
+
91
122
  echo "new_version=$NEW_VERSION" >> "$GITHUB_OUTPUT"
92
123
  echo "tag=v$NEW_VERSION" >> "$GITHUB_OUTPUT"
93
124
  echo "branch=release/v$NEW_VERSION" >> "$GITHUB_OUTPUT"
94
- echo "is_beta=${{ inputs.beta }}" >> "$GITHUB_OUTPUT"
125
+ echo "is_beta=$IS_BETA_OUT" >> "$GITHUB_OUTPUT"
95
126
  echo "Bumping (${{ inputs.bump }}): $CURRENT → $NEW_VERSION"
96
127
 
97
128
  # ── Bump version strings ─────────────────────────────────────────────
@@ -118,7 +149,7 @@ jobs:
118
149
  - name: Update docs/switcher.json
119
150
  env:
120
151
  VERSION_TAG: ${{ steps.version.outputs.tag }}
121
- IS_BETA: ${{ inputs.beta }}
152
+ IS_BETA: ${{ steps.version.outputs.is_beta }}
122
153
  shell: python
123
154
  run: |
124
155
  import json, re, pathlib, os
@@ -144,7 +175,7 @@ jobs:
144
175
 
145
176
  # ── Update root redirect for stable releases ─────────────────────────
146
177
  - name: Update root redirect (stable releases only)
147
- if: ${{ inputs.beta == false && inputs.bump != 'pre-release' }}
178
+ if: ${{ steps.version.outputs.is_beta == 'false' && inputs.bump != 'pre-release' }}
148
179
  env:
149
180
  VERSION_TAG: ${{ steps.version.outputs.tag }}
150
181
  shell: python
@@ -200,7 +231,7 @@ jobs:
200
231
  - Version bumped to \`${TAG}\` in \`pyproject.toml\` and \`docs/conf.py\`
201
232
  - \`CHANGELOG.rst\` updated from towncrier fragments
202
233
  - \`docs/_root/switcher.json\` updated with the new version entry
203
- $([ '${{ inputs.beta }}' = 'false' ] && echo '- Root redirect updated to point to this release' || echo '')
234
+ $([ '${{ steps.version.outputs.is_beta }}' = 'false' ] && echo '- Root redirect updated to point to this release' || echo '')
204
235
 
205
236
  ### Review checklist
206
237
  - [ ] \`CHANGELOG.rst\` reads well — edit the fragment text directly if needed
@@ -10,6 +10,97 @@ 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
+
44
+ 0.4.0 (2026-07-18)
45
+ ==================
46
+
47
+ New Features
48
+ ------------
49
+
50
+ - Added :meth:`~anyplotlib.axes.InsetAxes.indicate_point` — the point sibling of
51
+ :meth:`~anyplotlib.axes.InsetAxes.indicate_region`: a circle-and-cross marker
52
+ at a data point of the parent plot plus a single leader line to the inset's
53
+ nearest corner, tracking zoom/pan and hiding the leader while minimized.
54
+ - Added :meth:`~anyplotlib.plot1d.Plot1D.set_legend_fontsize` to control the
55
+ legend text size on 1-D line plots.
56
+ - Added ``linewidth=`` to every overlay widget constructor and ``add_*_widget``
57
+ factory on :class:`~anyplotlib.plot2d.Plot2D` and
58
+ :class:`~anyplotlib.plot1d.Plot1D` (rectangle, circle, annular, crosshair,
59
+ polygon, vline, hline, range, point) — stroke width in px, default 2, stored
60
+ and round-tripped like ``color``.
61
+ - Added ``tint=`` to :meth:`~anyplotlib.plot2d.Plot2D.add_layer` and
62
+ :meth:`~anyplotlib.plot2d.Layer.set` — a ``#rgb``/``#rrggbb`` hex colour that
63
+ renders the layer as a clear→colour intensity ramp (transparent at low
64
+ intensity, opaque tint at high, via a 256×4 RGBA LUT) instead of a named
65
+ colormap; passing ``cmap=`` reverts a tinted layer to colormap display.
66
+ - An :meth:`~anyplotlib.figure.Figure.add_inset` with no title (the default,
67
+ ``title=""``) now renders with NO title-bar strip at all — a clean bordered
68
+ plot box, content filling the whole area, instead of a useless empty header.
69
+ A titled inset is unchanged: its bar renders as before, with click-to-toggle
70
+ minimize. A title-less inset has no minimize affordance (there is no bar to
71
+ click), but drag-to-move / drag-to-resize in edit mode still work exactly as
72
+ before, since those gestures are wired on the inset body, not the bar.
73
+ - Double-clicking a plot's text chrome now reports which element was hit. The
74
+ ``double_click`` :class:`~anyplotlib.callbacks.Event` gains a ``target`` field
75
+ naming the hit element — one of ``'title'``, ``'x_label'``, ``'x_ticks'``,
76
+ ``'y_label'``, ``'y_ticks'``, ``'colorbar_label'`` or ``'legend'`` — so a host
77
+ can open the right edit affordance for the axis label vs the ticks vs the title
78
+ vs the colorbar label vs the legend. The axis gutters, colorbar strip and title
79
+ band each get their own hit-test (2-D panels emit from the separate axis/title
80
+ canvases; 1-D panels zone-split the single canvas around the plot rect and
81
+ legend box). A plain plot-area double-click is unchanged and carries no
82
+ ``target`` (``event.target is None``), so existing handlers keep working.
83
+ - Insets can now be dragged and resized directly in the renderer's edit mode
84
+ (``edit_chrome``): drag the body to move an inset (a corner-stacked inset
85
+ converts to a free anchor and its siblings re-stack), or drag the bottom-right
86
+ grip to resize it (min 64 px per dimension). On release the renderer emits a
87
+ new figure-level ``inset_geometry_change`` event carrying the final
88
+ ``anchor``/``w_frac``/``h_frac`` (figure fractions), which
89
+ :meth:`~anyplotlib.figure.Figure.add_event_handler` handlers can observe to
90
+ persist the layout. The same geometry is applied programmatically via the new
91
+ :meth:`~anyplotlib.axes.InsetAxes.set_geometry` (``anchor``, ``w_frac``,
92
+ ``h_frac``). Off edit mode the affordances are hidden and the inset is inert.
93
+
94
+
95
+ Bug Fixes
96
+ ---------
97
+
98
+ - Fixed ``exportPNG`` compositing WebGPU-rendered 3-D panels (``scatter3d`` /
99
+ ``voxels``) as blank background rectangles — the 3-D render pass is now
100
+ re-rendered synchronously in-task before the canvas readback, exactly like
101
+ active-GPU 2-D image panels.
102
+
103
+
13
104
  0.3.0b1 (2026-07-13)
14
105
  ====================
15
106
 
@@ -0,0 +1,87 @@
1
+ """
2
+ Interactive figure annotations — double-click to add, drag to place
3
+ ===================================================================
4
+
5
+ anyplotlib has a *figure-level* annotation layer that floats above the panels,
6
+ positioned in **figure fractions** (0…1, origin top-left) rather than any
7
+ panel's data coordinates. Set it with
8
+ :meth:`~anyplotlib.Figure.set_figure_markers`; the supported kinds are
9
+ ``"text"``, ``"circle"``, ``"rect"`` and ``"arrow"``.
10
+
11
+ Turn on ``fig.edit_chrome`` and every annotation becomes draggable in the
12
+ browser. Combine that with a ``double_click`` handler and you get a simple
13
+ annotate-by-clicking workflow: double-click a feature to drop a labelled
14
+ arrow, then drag it to line it up. Positions round-trip back to Python via
15
+ :attr:`~anyplotlib.Figure.figure_markers`.
16
+ """
17
+ import numpy as np
18
+ import anyplotlib as apl
19
+
20
+ rng = np.random.default_rng(7)
21
+ data = rng.standard_normal((160, 160)).cumsum(0).cumsum(1)
22
+ data = (data - data.min()) / (data.max() - data.min())
23
+
24
+ fig, ax = apl.subplots(1, 1, figsize=(520, 520))
25
+ v = ax.imshow(data, cmap="magma", units="px")
26
+
27
+ # Enable the editable-annotation ("report builder") mode so figure markers are
28
+ # hit-testable and draggable, and the figure emits background/marker events.
29
+ fig.edit_chrome = True
30
+
31
+ # %%
32
+ # Seed a couple of annotations
33
+ # ----------------------------
34
+ # Each marker is a dict with a ``kind`` and fraction-space geometry. A text
35
+ # label and an arrow pointing into the image to start with.
36
+
37
+ fig.set_figure_markers([
38
+ {"kind": "text", "x": 0.5, "y": 0.06,
39
+ "text": "Double-click a feature to annotate it",
40
+ "color": "#ffffff", "fontsize": 14},
41
+ {"kind": "arrow", "x": 0.20, "y": 0.30, "u": 0.12, "v": 0.12,
42
+ "color": "#ffd54f", "linewidth": 2},
43
+ ])
44
+
45
+ fig
46
+
47
+ # %%
48
+ # Double-click to drop a new annotation
49
+ # -------------------------------------
50
+ # On a single-panel figure the panel nearly fills the canvas, so we turn the
51
+ # click's device pixels into a figure fraction with the panel's
52
+ # ``display_width`` / ``display_height`` and append an arrow + label there.
53
+ # Because ``edit_chrome`` is on, the new marker is immediately draggable.
54
+
55
+
56
+ def _on_double_click(event):
57
+ if event.x is None or event.display_width is None:
58
+ return
59
+ fx = float(np.clip(event.x / event.display_width, 0.02, 0.98))
60
+ fy = float(np.clip(event.y / event.display_height, 0.02, 0.98))
61
+ markers = fig.figure_markers # current list (a copy)
62
+ n = sum(1 for m in markers if m["kind"] == "text")
63
+ markers.append({"kind": "arrow", "x": fx, "y": fy,
64
+ "u": 0.08, "v": -0.08, "color": "#40c4ff", "linewidth": 2})
65
+ markers.append({"kind": "text", "x": fx + 0.08, "y": fy - 0.10,
66
+ "text": f"mark {n}", "color": "#40c4ff", "fontsize": 13})
67
+ fig.set_figure_markers(markers)
68
+
69
+
70
+ v.add_event_handler(_on_double_click, "double_click")
71
+
72
+ fig.set_help(
73
+ "Double-click on the image to drop a labelled arrow.\n"
74
+ "Drag any annotation to reposition it (edit mode is on)."
75
+ )
76
+
77
+ fig # Interactive
78
+
79
+ # %%
80
+ # Read the placements back
81
+ # ------------------------
82
+ # After the user drags things around, ``fig.figure_markers`` reflects the
83
+ # current fraction positions — persist them, export them, or feed them into a
84
+ # report.
85
+
86
+ for m in fig.figure_markers:
87
+ print(m["kind"], round(m["x"], 3), round(m["y"], 3))
@@ -29,7 +29,7 @@ import anyplotlib as apl
29
29
  rng = np.random.default_rng(11)
30
30
 
31
31
  # ── 1. Synthetic 3-D polycrystal: nearest-seed voxel grain map ──────────────
32
- N = 48 # volume is N³ voxels, indexed V[z, y, x]
32
+ N = 24 # volume is N³ voxels, indexed V[z, y, x]
33
33
  N_GRAINS = 40
34
34
 
35
35
  seeds = rng.uniform(0, N, size=(N_GRAINS, 3)) # (z, y, x)
@@ -76,7 +76,8 @@ grain_rgb_u8 = (rgb * 255).astype(np.uint8) # (N_GRAINS, 3)
76
76
  # planes. This anchors the highlight exactly where the slices intersect,
77
77
  # shows real slice contents in 3-D, and scales: the on-plane count is
78
78
  # ~3·(N/step)² regardless of N, so it stays fast even for a 256³ volume.
79
- VSTEP = max(1, N // 48) # in-plane downsample ~48² cubes per plane
79
+ VSTEP = max(1, N // 48) # in-plane downsample, capping at ~48² cubes/plane
80
+ # for large N (no downsampling at this N=24)
80
81
 
81
82
  # Voxel cube size in data units. A touch larger than VSTEP so the three
82
83
  # slabs read as solid sheets rather than a dotted grid.
@@ -140,6 +141,8 @@ v_vol = ax_vol.voxels(
140
141
  size=VOXSIZE, alpha=0.55,
141
142
  x_label="x", y_label="y", z_label="z",
142
143
  bounds=((0, N - 1),) * 3, zoom=1.1,
144
+ # gpu="auto" (default): ~1.7k cubes is over the ~1k voxel threshold, so the
145
+ # WebGPU instanced path handles each drag re-slice when WebGPU is present.
143
146
  )
144
147
  v_vol.set_title("Grain volume — drag a plane to re-slice")
145
148
 
@@ -0,0 +1,49 @@
1
+ """
2
+ Coordinate transforms — pin markers to the axes or the screen
3
+ =============================================================
4
+
5
+ Every ``add_*`` marker method takes a ``transform`` that decides which
6
+ coordinate system its positions live in:
7
+
8
+ - ``"data"`` (default) — data coordinates; the marker moves and scales with
9
+ zoom / pan, staying glued to the underlying data.
10
+ - ``"axes"`` — axes-normalised ``(0, 0)`` bottom-left … ``(1, 1)`` top-right;
11
+ the marker stays in the same corner of the panel no matter how you zoom.
12
+ - ``"display"`` — raw CSS pixels within the panel; a fixed-size decoration.
13
+
14
+ The ``"axes"`` and ``"display"`` transforms are how you build overlays that
15
+ should *not* track the data — a navigation index in the corner, a scale bar, a
16
+ persistent legend chip.
17
+ """
18
+ import numpy as np
19
+ import anyplotlib as apl
20
+
21
+ rng = np.random.default_rng(1)
22
+ data = rng.standard_normal((128, 128)).cumsum(0).cumsum(1)
23
+ data = (data - data.min()) / (data.max() - data.min())
24
+ xy = np.linspace(0, 10, 128)
25
+
26
+ fig, ax = apl.subplots(1, 1, figsize=(480, 480))
27
+ v = ax.imshow(data, axes=[xy, xy], units="nm")
28
+
29
+ # Data-anchored label — sits at (5, 5) in nm and rides along on zoom/pan.
30
+ v.add_texts(offsets=[(5, 5)], texts=["feature @ (5, 5)"],
31
+ color="#ffffff", fontsize=12, name="data_label")
32
+
33
+ # Axes-anchored index — stays pinned to the top-left corner of the panel
34
+ # regardless of zoom (0, 1 = top-left in axes fractions).
35
+ v.add_texts(offsets=[(0.04, 0.96)], texts=["frame 3 / 20"],
36
+ transform="axes", color="#ffd54f", fontsize=13, name="nav_index")
37
+
38
+ fig
39
+
40
+ # %%
41
+ # Try it
42
+ # ------
43
+ # Zoom into the image: the white ``feature`` label moves with the data, while
44
+ # the yellow ``frame 3 / 20`` index stays locked to the corner — because it is
45
+ # positioned in ``"axes"`` coordinates.
46
+
47
+ fig.set_help("Zoom in: the corner index stays put; the data label moves.")
48
+
49
+ fig # Interactive
@@ -0,0 +1,82 @@
1
+ """
2
+ GPU-accelerated voxels
3
+ ======================
4
+
5
+ :meth:`~anyplotlib.Axes.voxels` renders shaded translucent cubes for a
6
+ volumetric field. With ``gpu="auto"`` (the default) anyplotlib uses WebGPU
7
+ instancing when a GPU is available and the cube count is large, so hundreds of
8
+ thousands of voxels stay interactive; otherwise it falls back to the Canvas2D
9
+ path. Read :attr:`~anyplotlib.Plot3D.gpu_active` after the first frame to see
10
+ which path was chosen.
11
+
12
+ Here we build a dense spherical shell — enough cubes that the WebGPU path
13
+ kicks in — and drop a draggable :class:`~anyplotlib.PlaneWidget` through it as
14
+ a slice selector.
15
+ """
16
+ import numpy as np
17
+ import anyplotlib as apl
18
+
19
+ # %%
20
+ # Build a volumetric field
21
+ # ------------------------
22
+ # Voxel *centres* are passed as three flat coordinate arrays (not a dense 3-D
23
+ # grid), so you only send the cubes you actually want drawn. We keep the
24
+ # voxels inside a spherical shell and colour them by radius.
25
+
26
+ N = 64
27
+ g = np.arange(N)
28
+ Z, Y, X = np.meshgrid(g, g, g, indexing="ij")
29
+ r = np.sqrt((X - N / 2) ** 2 + (Y - N / 2) ** 2 + (Z - N / 2) ** 2)
30
+ shell = (r > N * 0.30) & (r < N * 0.42) # a hollow sphere
31
+
32
+ xs, ys, zs = X[shell], Y[shell], Z[shell]
33
+ print(f"{xs.size:,} voxels") # tens of thousands → GPU path under gpu='auto'
34
+
35
+ # Colour by radius with the viridis-ish default cycle mapped through intensity.
36
+ t = (r[shell] - r[shell].min()) / (np.ptp(r[shell]) + 1e-9)
37
+ colors = np.stack([0.2 + 0.8 * t, 0.4 * np.ones_like(t), 1.0 - 0.8 * t], axis=1)
38
+
39
+ fig, ax = apl.subplots(1, 1, figsize=(560, 520))
40
+ vol = ax.voxels(
41
+ xs, ys, zs, colors=colors,
42
+ size=1.0, alpha=0.35,
43
+ bounds=((0, N - 1),) * 3,
44
+ azimuth=-55, elevation=28, zoom=1.1,
45
+ gpu="auto", # WebGPU when available, Canvas2D otherwise
46
+ )
47
+ vol.set_title("Spherical shell — drag to rotate, scroll to zoom")
48
+
49
+ # %%
50
+ # Add a slice-selector plane
51
+ # --------------------------
52
+ # A :class:`~anyplotlib.PlaneWidget` is a draggable axis-aligned plane. Voxels
53
+ # lying on it render more opaque, so the current slice pops out of the
54
+ # translucent volume. Drag it along z in the browser, or move it from Python.
55
+
56
+ plane = vol.add_widget("plane", axis="z", position=N // 2,
57
+ color="#40c4ff", alpha=0.18)
58
+
59
+
60
+ @plane.add_event_handler("pointer_move")
61
+ def _on_slice(event):
62
+ # Fires while the plane is dragged; pw.position holds the live slice index.
63
+ print("slice at z =", round(plane.position, 1))
64
+
65
+
66
+ fig.set_help(
67
+ "Drag: rotate · Scroll: zoom · R: reset view\n"
68
+ "Drag the blue plane to slide the z-slice through the shell."
69
+ )
70
+
71
+ fig # Interactive
72
+
73
+ # %%
74
+ # Which render path ran?
75
+ # ----------------------
76
+ # ``gpu_active`` is populated once the browser reports back after the first
77
+ # frame. It is ``True`` when the WebGPU instanced path is live, ``False`` on
78
+ # the Canvas2D fallback, and ``None`` before the first frame (as when this
79
+ # gallery page is built headlessly). Force a path with ``gpu=True`` /
80
+ # ``gpu=False`` if you need determinism.
81
+
82
+ print("gpu_active:", vol.gpu_active)
@@ -0,0 +1,43 @@
1
+ """
2
+ Step lines and log-scale spectra
3
+ ================================
4
+
5
+ Two 1-D options that suit spectral data: a **mid-riser step** line (constant
6
+ within each bin, jumping at bin midpoints) via ``linestyle="step-mid"``, and a
7
+ **logarithmic y-axis** via :meth:`~anyplotlib.Axes.semilogy` (or
8
+ ``ax.plot(..., yscale="log")``).
9
+ """
10
+ import numpy as np
11
+ import anyplotlib as apl
12
+
13
+ # A noisy binned spectrum.
14
+ rng = np.random.default_rng(0)
15
+ energy = np.linspace(0, 20, 60)
16
+ counts = (np.exp(-(energy - 6) ** 2 / 4) * 1000
17
+ + np.exp(-(energy - 13) ** 2 / 8) * 400
18
+ + rng.uniform(0, 20, energy.size))
19
+
20
+ # %%
21
+ # Step line
22
+ # ---------
23
+ # ``linestyle="step-mid"`` draws a horizontal segment centred on each x value
24
+ # with vertical risers between them — the standard way to show histogram-like
25
+ # spectra without implying interpolation between channels.
26
+
27
+ fig, ax = apl.subplots(1, 1, figsize=(560, 340))
28
+ ax.plot(counts, axes=[energy], color="#4fc3f7",
29
+ linestyle="step-mid", label="counts")
30
+
31
+ fig
32
+
33
+ # %%
34
+ # Log y-axis
35
+ # ----------
36
+ # ``semilogy`` is shorthand for a log y-scale, which brings out the small
37
+ # secondary peak that the linear plot flattens. Combine it with the step line
38
+ # for a classic spectroscopy view.
39
+
40
+ fig2, ax2 = apl.subplots(1, 1, figsize=(560, 340))
41
+ ax2.semilogy(counts, axes=[energy], color="#ff7043", linestyle="step-mid")
42
+
43
+ fig2