anyplotlib 0.4.2__tar.gz → 0.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (275) hide show
  1. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/.github/workflows/docs.yml +17 -3
  2. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/.gitignore +11 -0
  3. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/AGENTS.md +25 -2
  4. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/CHANGELOG.rst +123 -0
  5. anyplotlib-0.6.0/Examples/Interactive/plot_star_globe_explorer.py +341 -0
  6. anyplotlib-0.6.0/Examples/PlotTypes/plot_3d_texture.py +344 -0
  7. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/PKG-INFO +3 -2
  8. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/README.md +2 -1
  9. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/FIGURE_ESM.md +123 -39
  10. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/__init__.py +2 -1
  11. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/_base_plot.py +206 -0
  12. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/_repr_utils.py +11 -1
  13. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/_utils.py +97 -3
  14. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/axes/_axes.py +23 -3
  15. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/callbacks.py +6 -0
  16. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/figure/_figure.py +27 -1
  17. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/figure_esm.js +1248 -176
  18. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/markers.py +24 -0
  19. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/plot1d/_plot1d.py +30 -9
  20. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/plot2d/_plot2d.py +262 -14
  21. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/plot3d/_plot3d.py +199 -4
  22. anyplotlib-0.6.0/anyplotlib/tests/baselines/imshow_labels.png +0 -0
  23. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/conftest.py +64 -0
  24. anyplotlib-0.6.0/anyplotlib/tests/test_embed/test_export_widget_sync.py +92 -0
  25. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_examples/test_interactive_examples.py +1 -0
  26. anyplotlib-0.6.0/anyplotlib/tests/test_interactive/test_orbit_direction.py +219 -0
  27. anyplotlib-0.6.0/anyplotlib/tests/test_interactive/test_pointer_down_1d.py +144 -0
  28. anyplotlib-0.6.0/anyplotlib/tests/test_interactive/test_range_orientation_snap.py +286 -0
  29. anyplotlib-0.6.0/anyplotlib/tests/test_interactive/test_star_globe_linking.py +187 -0
  30. anyplotlib-0.6.0/anyplotlib/tests/test_interactive/test_widget_brush.py +777 -0
  31. anyplotlib-0.6.0/anyplotlib/tests/test_interactive/test_widget_set_notify.py +110 -0
  32. anyplotlib-0.6.0/anyplotlib/tests/test_interactive/test_widgets_line_2d.py +330 -0
  33. anyplotlib-0.6.0/anyplotlib/tests/test_labels/test_ylabel_clearance.py +129 -0
  34. anyplotlib-0.6.0/anyplotlib/tests/test_layouts/test_coord_conversion.py +181 -0
  35. anyplotlib-0.6.0/anyplotlib/tests/test_markers/test_per_marker_colors.py +188 -0
  36. anyplotlib-0.6.0/anyplotlib/tests/test_markers/test_size_units.py +110 -0
  37. anyplotlib-0.6.0/anyplotlib/tests/test_plot1d/test_line_none_rendering.py +118 -0
  38. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_plot1d/test_plot1d.py +21 -0
  39. anyplotlib-0.6.0/anyplotlib/tests/test_plot2d/test_colorbar_gap_scalebar.py +200 -0
  40. anyplotlib-0.6.0/anyplotlib/tests/test_plot3d/test_gpu_depth.py +139 -0
  41. anyplotlib-0.6.0/anyplotlib/tests/test_plot3d/test_texture.py +636 -0
  42. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/widgets/__init__.py +3 -1
  43. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/widgets/_base.py +44 -2
  44. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/widgets/_widgets1d.py +64 -7
  45. anyplotlib-0.6.0/anyplotlib/widgets/_widgets2d.py +511 -0
  46. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/docs/_root/index.html +3 -3
  47. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/docs/_root/switcher.json +10 -0
  48. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/docs/api/widgets.rst +7 -0
  49. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/docs/conf.py +1 -1
  50. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/pyproject.toml +8 -1
  51. anyplotlib-0.6.0/upcoming_changes/.gitkeep +3 -0
  52. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/upcoming_changes/README.rst +5 -0
  53. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/uv.lock +1 -1
  54. anyplotlib-0.4.2/anyplotlib/tests/baselines/imshow_labels.png +0 -0
  55. anyplotlib-0.4.2/anyplotlib/widgets/_widgets2d.py +0 -231
  56. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/.github/workflows/ci.yml +0 -0
  57. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/.github/workflows/prepare_release.yml +0 -0
  58. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/.github/workflows/release.yml +0 -0
  59. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/.github/workflows/tests.yml +0 -0
  60. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Benchmarks/README.rst +0 -0
  61. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Benchmarks/plot_benchmark_comparison.py +0 -0
  62. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Interactive/README.rst +0 -0
  63. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Interactive/plot_3d_spectral_viewer.py +0 -0
  64. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Interactive/plot_eels_explorer.py +0 -0
  65. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Interactive/plot_figure_annotations.py +0 -0
  66. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Interactive/plot_interactive_fft.py +0 -0
  67. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Interactive/plot_interactive_fitting.py +0 -0
  68. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Interactive/plot_ipf_density_map.py +0 -0
  69. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Interactive/plot_ipf_explorer.py +0 -0
  70. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Interactive/plot_key_bindings.py +0 -0
  71. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Interactive/plot_particle_picker.py +0 -0
  72. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Interactive/plot_point_widget.py +0 -0
  73. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Interactive/plot_segment_by_contrast.py +0 -0
  74. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Interactive/plot_segment_by_contrast_advanced.py +0 -0
  75. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Interactive/plot_spectra_roi_inspector.py +0 -0
  76. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Interactive/plot_threshold_explorer.py +0 -0
  77. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Interactive/plot_voxel_grain_explorer.py +0 -0
  78. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Markers/README.rst +0 -0
  79. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Markers/plot_arrows.py +0 -0
  80. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Markers/plot_circles.py +0 -0
  81. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Markers/plot_ellipses.py +0 -0
  82. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Markers/plot_horizontal_lines.py +0 -0
  83. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Markers/plot_line_segments.py +0 -0
  84. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Markers/plot_points.py +0 -0
  85. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Markers/plot_polygons.py +0 -0
  86. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Markers/plot_rectangles.py +0 -0
  87. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Markers/plot_squares.py +0 -0
  88. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Markers/plot_texts.py +0 -0
  89. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Markers/plot_transform_markers.py +0 -0
  90. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Markers/plot_vertical_lines.py +0 -0
  91. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/PlotTypes/README.rst +0 -0
  92. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/PlotTypes/plot_3d.py +0 -0
  93. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/PlotTypes/plot_bar.py +0 -0
  94. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/PlotTypes/plot_gpu_voxels.py +0 -0
  95. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/PlotTypes/plot_gridspec_custom.py +0 -0
  96. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/PlotTypes/plot_image2d.py +0 -0
  97. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/PlotTypes/plot_inset.py +0 -0
  98. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/PlotTypes/plot_label_formatting.py +0 -0
  99. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/PlotTypes/plot_line_styles.py +0 -0
  100. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/PlotTypes/plot_pcolormesh.py +0 -0
  101. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/PlotTypes/plot_spectra1d.py +0 -0
  102. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/PlotTypes/plot_step_and_log.py +0 -0
  103. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/PlotTypes/plot_tile_backend.py +0 -0
  104. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/PlotTypes/plot_twinx.py +0 -0
  105. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/README.rst +0 -0
  106. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Widgets/README.rst +0 -0
  107. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Widgets/plot_widget1d_hline.py +0 -0
  108. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Widgets/plot_widget1d_point.py +0 -0
  109. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Widgets/plot_widget1d_range.py +0 -0
  110. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Widgets/plot_widget1d_vline.py +0 -0
  111. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Widgets/plot_widget2d_annular.py +0 -0
  112. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Widgets/plot_widget2d_arrow.py +0 -0
  113. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Widgets/plot_widget2d_circle.py +0 -0
  114. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Widgets/plot_widget2d_crosshair.py +0 -0
  115. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Widgets/plot_widget2d_label.py +0 -0
  116. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Widgets/plot_widget2d_polygon.py +0 -0
  117. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Widgets/plot_widget2d_rectangle.py +0 -0
  118. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Examples/Widgets/plot_widget3d_plane.py +0 -0
  119. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/LICENSE +0 -0
  120. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/Makefile +0 -0
  121. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/_binary_frame.py +0 -0
  122. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/_electron.py +0 -0
  123. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/axes/__init__.py +0 -0
  124. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/axes/_inset_axes.py +0 -0
  125. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/conftest.py +0 -0
  126. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/embed.py +0 -0
  127. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/figure/__init__.py +0 -0
  128. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/figure/_gridspec.py +0 -0
  129. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/figure/_subplots.py +0 -0
  130. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/plot1d/__init__.py +0 -0
  131. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/plot1d/_plotbar.py +0 -0
  132. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/plot2d/__init__.py +0 -0
  133. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/plot2d/_layer.py +0 -0
  134. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/plot2d/_plotmesh.py +0 -0
  135. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/plot2d/_tile_backend.py +0 -0
  136. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/plot3d/__init__.py +0 -0
  137. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/plotxy/__init__.py +0 -0
  138. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/plotxy/_plotxy.py +0 -0
  139. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/sphinx_anywidget/__init__.py +0 -0
  140. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/sphinx_anywidget/_directive.py +0 -0
  141. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/sphinx_anywidget/_repr_utils.py +0 -0
  142. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/sphinx_anywidget/_scraper.py +0 -0
  143. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/sphinx_anywidget/_wheel_builder.py +0 -0
  144. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/sphinx_anywidget/static/anywidget_bridge.js +0 -0
  145. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/sphinx_anywidget/static/anywidget_overlay.css +0 -0
  146. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/sphinx_anywidget/tests/__init__.py +0 -0
  147. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/sphinx_anywidget/tests/conftest.py +0 -0
  148. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/sphinx_anywidget/tests/test_directive.py +0 -0
  149. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/sphinx_anywidget/tests/test_init.py +0 -0
  150. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/sphinx_anywidget/tests/test_repr_utils.py +0 -0
  151. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/sphinx_anywidget/tests/test_scraper.py +0 -0
  152. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/sphinx_anywidget/tests/test_wheel_builder.py +0 -0
  153. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/__init__.py +0 -0
  154. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/_gpu_clim_check.cjs +0 -0
  155. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/_png_utils.py +0 -0
  156. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/bar_basic.png +0 -0
  157. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/gridspec_3col_equal_spectra.png +0 -0
  158. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/gridspec_asymmetric_width_ratios.png +0 -0
  159. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/gridspec_height_ratio_image_histogram.png +0 -0
  160. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/gridspec_image_two_spectra.png +0 -0
  161. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/gridspec_side_by_side_1d.png +0 -0
  162. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/gridspec_spanning_top_two_bottom.png +0 -0
  163. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/imshow_axis_off.png +0 -0
  164. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/imshow_checkerboard.png +0 -0
  165. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/imshow_gradient.png +0 -0
  166. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/imshow_viridis.png +0 -0
  167. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/inset_1d.png +0 -0
  168. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/inset_maximized.png +0 -0
  169. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/inset_minimized.png +0 -0
  170. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/inset_normal_2d.png +0 -0
  171. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/inset_stacked.png +0 -0
  172. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/inset_stacked_one_minimized.png +0 -0
  173. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/pcolormesh_uniform.png +0 -0
  174. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/plot1d_all_linestyles.png +0 -0
  175. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/plot1d_alpha.png +0 -0
  176. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/plot1d_axis_off.png +0 -0
  177. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/plot1d_dashed.png +0 -0
  178. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/plot1d_marker_symbols.png +0 -0
  179. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/plot1d_markers.png +0 -0
  180. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/plot1d_multi.png +0 -0
  181. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/plot1d_sine.png +0 -0
  182. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/plot1d_title.png +0 -0
  183. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/plot1d_twinx.png +0 -0
  184. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/plot3d_surface.png +0 -0
  185. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/baselines/subplots_2x1.png +0 -0
  186. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_benchmarks/__init__.py +0 -0
  187. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_benchmarks/baselines.json +0 -0
  188. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_benchmarks/benchmarks/baselines.json +0 -0
  189. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_benchmarks/test_benchmarks.py +0 -0
  190. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_benchmarks/test_benchmarks_py.py +0 -0
  191. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_documentation/__init__.py +0 -0
  192. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_documentation/test_bridge.py +0 -0
  193. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_documentation/test_push_hook.py +0 -0
  194. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_documentation/test_scraper.py +0 -0
  195. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_electron/__init__.py +0 -0
  196. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_electron/test_binary_frame.py +0 -0
  197. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_embed/__init__.py +0 -0
  198. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_embed/test_embed_api.py +0 -0
  199. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_embed/test_embed_mount.py +0 -0
  200. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_embed/test_export_png.py +0 -0
  201. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_examples/__init__.py +0 -0
  202. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_interactive/__init__.py +0 -0
  203. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_interactive/_event_test_utils.py +0 -0
  204. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_interactive/test_blit_audit.py +0 -0
  205. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_interactive/test_callbacks_playwright.py +0 -0
  206. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_interactive/test_callbacks_unit.py +0 -0
  207. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_interactive/test_edit_chrome.py +0 -0
  208. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_interactive/test_edit_chrome_playwright.py +0 -0
  209. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_interactive/test_event_pause_hold.py +0 -0
  210. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_interactive/test_event_plots.py +0 -0
  211. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_interactive/test_event_settled.py +0 -0
  212. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_interactive/test_events_regression.py +0 -0
  213. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_interactive/test_tile_parity_playwright.py +0 -0
  214. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_interactive/test_title.py +0 -0
  215. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_interactive/test_touch.py +0 -0
  216. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_interactive/test_widget_max_extent.py +0 -0
  217. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_interactive/test_widgets.py +0 -0
  218. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_labels/__init__.py +0 -0
  219. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_labels/test_label_api.py +0 -0
  220. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_labels/test_label_rendering.py +0 -0
  221. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_labels/test_no_clipping.py +0 -0
  222. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_layouts/__init__.py +0 -0
  223. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_layouts/test_batch.py +0 -0
  224. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_layouts/test_geom_channel.py +0 -0
  225. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_layouts/test_gridspec.py +0 -0
  226. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_layouts/test_inset.py +0 -0
  227. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_layouts/test_inset_callout.py +0 -0
  228. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_layouts/test_interaction.py +0 -0
  229. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_layouts/test_visual.py +0 -0
  230. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_markers/__init__.py +0 -0
  231. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_markers/test_marker_transforms.py +0 -0
  232. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_markers/test_markers.py +0 -0
  233. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_plot1d/__init__.py +0 -0
  234. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_plot1d/test_plotbar.py +0 -0
  235. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_plot2d/__init__.py +0 -0
  236. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_plot2d/test_detail_tile.py +0 -0
  237. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_plot2d/test_first_paint_race.py +0 -0
  238. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_plot2d/test_gpu_image.py +0 -0
  239. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_plot2d/test_gpu_parity_playwright.py +0 -0
  240. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_plot2d/test_imshow.py +0 -0
  241. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_plot2d/test_imshow_rgb.py +0 -0
  242. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_plot2d/test_layers.py +0 -0
  243. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_plot2d/test_layers_playwright.py +0 -0
  244. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_plot2d/test_pcolormesh.py +0 -0
  245. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_plot2d/test_plot2d_api.py +0 -0
  246. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_plot2d/test_rapid_frame_update.py +0 -0
  247. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_plot2d/test_tile_backend.py +0 -0
  248. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_plot2d/test_tile_diagnostics.py +0 -0
  249. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_plot2d/test_tiled_imshow.py +0 -0
  250. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_plot2d/test_zoom_view_write.py +0 -0
  251. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_plot3d/__init__.py +0 -0
  252. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_plot3d/test_colors_highlight.py +0 -0
  253. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_plot3d/test_gpu_fallback.py +0 -0
  254. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_plot3d/test_gpu_hidden_reveal.py +0 -0
  255. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_plot3d/test_plot3d.py +0 -0
  256. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_plot3d/test_voxels_planes.py +0 -0
  257. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/tests/test_plotxy/test_plotxy.py +0 -0
  258. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/anyplotlib/widgets/_widgets3d.py +0 -0
  259. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/docs/_sg_html_scraper.py +0 -0
  260. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/docs/_static/anyplotlib.svg +0 -0
  261. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/docs/_static/custom.css +0 -0
  262. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/docs/_static/pyodide_bridge.js +0 -0
  263. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/docs/_templates/autosummary/class.rst +0 -0
  264. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/docs/api/callbacks.rst +0 -0
  265. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/docs/api/figure.rst +0 -0
  266. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/docs/api/figure_plots.rst +0 -0
  267. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/docs/api/index.rst +0 -0
  268. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/docs/api/markers.rst +0 -0
  269. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/docs/benchmarking.rst +0 -0
  270. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/docs/dev/index.rst +0 -0
  271. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/docs/embedding.rst +0 -0
  272. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/docs/events.rst +0 -0
  273. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/docs/getting_started.rst +0 -0
  274. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/docs/index.rst +0 -0
  275. {anyplotlib-0.4.2 → anyplotlib-0.6.0}/make.bat +0 -0
@@ -11,10 +11,13 @@ on:
11
11
  # Allow manual re-builds from the Actions tab.
12
12
  workflow_dispatch:
13
13
 
14
- # Only one docs deployment should run at a time to avoid race conditions on
15
- # the gh-pages branch.
14
+ # Per-ref grouping so a new push supersedes an in-flight BUILD of the same
15
+ # ref (PR iterations, repeated pushes to main). This deliberately does NOT
16
+ # serialise deployments: a push to main and its release tag are different
17
+ # refs, so they land in different groups. The deploy job carries its own
18
+ # ref-independent group for that — see below.
16
19
  concurrency:
17
- group: docs-${{ github.ref }}
20
+ group: docs-build-${{ github.ref }}
18
21
  cancel-in-progress: true
19
22
 
20
23
  permissions:
@@ -91,6 +94,17 @@ jobs:
91
94
  # Skip deployment for pull requests.
92
95
  if: github.event_name != 'pull_request'
93
96
 
97
+ # Every deploy pushes to the SAME branch (gh-pages), so they must be
98
+ # serialised across refs, not per-ref. Releasing tags main and v* almost
99
+ # simultaneously, which previously raced: whichever run pushed second was
100
+ # rejected with "cannot lock ref 'refs/heads/gh-pages'" and its version
101
+ # directory never appeared, while switcher.json still advertised it.
102
+ # cancel-in-progress is false because these runs must QUEUE — cancelling
103
+ # one would drop a version directory just as surely as the race did.
104
+ concurrency:
105
+ group: docs-deploy
106
+ cancel-in-progress: false
107
+
94
108
  steps:
95
109
  - name: Checkout repository
96
110
  uses: actions/checkout@v4
@@ -51,3 +51,14 @@ docs/_static/anywidget_config.js
51
51
 
52
52
  # Generated by Sphinx-Gallery (anywidget iframe HTML) — never commit
53
53
  docs/_static/viewer_widgets/
54
+
55
+ # Scratch doc builds (`make html O=...` into a throwaway output dir)
56
+ docs/_dummy/
57
+
58
+ # Scratch working files that land in the repo root / Examples during
59
+ # development: saved diffs and throwaway notebooks. Real examples are
60
+ # `Examples/<Section>/plot_*.py` and are picked up by Sphinx-Gallery.
61
+ *.diff
62
+ *.patch
63
+ Examples/testing.ipynb
64
+ Examples/*.ipynb
@@ -7,7 +7,7 @@
7
7
  - **`Figure`** (`anyplotlib/figure/_figure.py`) — the only `anywidget.AnyWidget` subclass. Owns all traitlets and is the Python↔JS bridge.
8
8
  - **Plot objects** (`plot1d/`, `plot2d/`, `plot3d/`) — `Plot1D`, `PlotBar`, `Plot2D`, `PlotMesh`, `Plot3D` are **plain Python classes**, not widgets. They hold state in `_state` dicts and push to the Figure. Shared behaviour lives in `_base_plot.py` (`_BasePlot`, `_PanelMixin`, `_MarkerMixin`).
9
9
  - **`Axes`** (`axes/_axes.py`) — grid-cell container; factory methods (`imshow`, `plot`, `bar`, `pcolormesh`, `plot_surface`, …) create plot objects and attach them.
10
- - **`figure_esm.js`** — pure-JS canvas renderer (~4,400 lines); all rendering logic lives here. **Read `anyplotlib/FIGURE_ESM.md` first** — it is the section map.
10
+ - **`figure_esm.js`** — pure-JS canvas renderer (~9,300 lines); all rendering logic lives here. **Read `anyplotlib/FIGURE_ESM.md` first** — it is the section map.
11
11
  - **`markers.py`** — static visual overlays (circles, arrows, lines, etc.) with a two-level dict registry: `plot.markers[type][name]`.
12
12
  - **`widgets/`** — interactive draggable overlays (`RectangleWidget`, `CrosshairWidget`, etc.) that receive JS position updates.
13
13
  - **`callbacks.py`** — event system: `Event` dataclass, `CallbackRegistry` (priority ordering, wildcard, pause/hold), `_EventMixin` (`add_event_handler`).
@@ -95,8 +95,31 @@ make html
95
95
  make clean # wipe build artefacts
96
96
  ```
97
97
 
98
+ **After editing `figure_esm.js`, docs need `make clean` first.** Sphinx Gallery
99
+ re-executes an example only when its *source* MD5 changes, and each example's
100
+ interactive widget page (`build/html/_static/viewer_widgets/*.html`, ~1 MB) is
101
+ generated during that execution with the renderer **inlined**. A plain
102
+ `make html` therefore rebuilds the pages and the Pyodide wheel but silently
103
+ serves widgets built from an older `figure_esm.js` — the figures still work,
104
+ so nothing looks broken; they just behave like the old renderer. Check with
105
+ `grep -c '<some new symbol>' build/html/_static/viewer_widgets/<page>_001.html`
106
+ when a JS change doesn't seem to reach the docs.
107
+
108
+ **Any insertion into `figure_esm.js` shifts every `FIGURE_ESM.md` line anchor
109
+ below it.** The anchors are maintained by hand, so re-check them in the *same*
110
+ commit that moves them — a map that is 500 lines out is worse than no map. Dump
111
+ the real numbers with:
112
+
113
+ ```bash
114
+ grep -nE '^\s*(function|const|let) [A-Za-z_]' anyplotlib/figure_esm.js
115
+ ```
116
+
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 9,285 lines.
119
+
98
120
  Changelog entries: add a fragment file to `upcoming_changes/` (e.g.
99
121
  `123.new_feature.rst`) — towncrier assembles `CHANGELOG.rst` at release time.
122
+ Use `api_change` when existing behaviour changes, even if the change is a fix.
100
123
 
101
124
  ## Key Files
102
125
 
@@ -106,7 +129,7 @@ Changelog entries: add a fragment file to `upcoming_changes/` (e.g.
106
129
  | `anyplotlib/figure/_gridspec.py` | `GridSpec`, `SubplotSpec` |
107
130
  | `anyplotlib/figure/_subplots.py` | `subplots()` factory |
108
131
  | `anyplotlib/axes/_axes.py` | `Axes` — plot factory methods |
109
- | `anyplotlib/figure_esm.js` | All JS canvas rendering (~4,400 lines) |
132
+ | `anyplotlib/figure_esm.js` | All JS canvas rendering (~9,300 lines) |
110
133
  | `anyplotlib/FIGURE_ESM.md` | Section map for `figure_esm.js` — read this before editing the JS |
111
134
  | `anyplotlib/markers.py` | Static marker collections; `to_wire()` translation |
112
135
  | `anyplotlib/widgets/` | Interactive overlay widgets |
@@ -10,6 +10,129 @@ Fragment files in ``upcoming_changes/`` are assembled into this file by
10
10
 
11
11
  .. towncrier release notes start
12
12
 
13
+ 0.6.0 (2026-07-31)
14
+ ==================
15
+
16
+ API and Behaviour Changes
17
+ -------------------------
18
+
19
+ - **3-D orbit drags have reversed direction.** Both azimuth and elevation now
20
+ move the geometry *with* the cursor instead of away from it, matching
21
+ matplotlib's ``mplot3d`` and every other turntable control — dragging right
22
+ spins a globe right. Azimuth and elevation position the *camera*, so adding
23
+ the drag delta swept the surface the opposite way, as if you had grabbed its
24
+ far side. Any muscle memory (or scripted pointer drag) built against the old
25
+ direction is inverted; panels driven from Python with
26
+ :meth:`~anyplotlib.Plot3D.set_view` are unaffected.
27
+
28
+
29
+ New Features
30
+ ------------
31
+
32
+ - :class:`~anyplotlib.Event` now carries ``azimuth`` and ``elevation`` for 3-D
33
+ orbit events. The renderer had always emitted them alongside ``zoom``, but they
34
+ were dropped on the way to Python — and since a JS-side drag does not sync back
35
+ into ``Plot3D._state``, a handler had no way to react to an orbit at all. See
36
+ the new ``Star Globe Explorer`` gallery example, which links a celestial sphere
37
+ to a sky map through them.
38
+ - Added :meth:`~anyplotlib.Plot2D.add_brush_widget` for painting freehand
39
+ multi-class label strokes on a 2-D image with Shift-drag, leaving a bare drag to
40
+ pan as before.
41
+ - Added :meth:`~anyplotlib.Plot3D.set_texture` for wrapping an image around a 3-D
42
+ surface — a globe, a planet, or a star chart on the celestial sphere — with
43
+ optional diffuse shading and backface culling. ``Axes.plot_surface`` gained
44
+ ``texture=``, ``bounds=``, and ``gpu=`` to match. Textured surfaces render on
45
+ WebGPU when it is available (roughly 9k triangles at 54 ms/frame on Canvas2D
46
+ versus 0.4 ms on the GPU), falling back to Canvas2D silently otherwise.
47
+ ``set_axis_off()`` now also hides a 3-D panel's axis lines, labels, and ticks.
48
+
49
+
50
+ Bug Fixes
51
+ ---------
52
+
53
+ - Fixed WebGPU 3-D geometry being clipped at the corners of a cube-shaped
54
+ dataset. The clip-space depth scale let ``clip.z`` reach 1.09 for a point at
55
+ the far corner of the normalised bounds box, outside the ``[0, 1]`` range
56
+ WebGPU keeps, so the nearest corner of a dense :meth:`~anyplotlib.Axes.scatter3d`
57
+ cloud silently vanished at the default camera angles. Spherical geometry was
58
+ never affected.
59
+ - Fixed an inverted depth comparison in the WebGPU 3-D projection: a GPU-rendered
60
+ :meth:`~anyplotlib.Axes.scatter3d` cloud drew its far points on top of its near
61
+ ones wherever two points overlapped on screen. Voxel panels were unaffected
62
+ (they disable depth writes), and the Canvas2D path was always correct.
63
+ - Fixed the docs deployment racing itself on release. A push to ``main`` and its
64
+ release tag are different refs, so the ``docs-${{ github.ref }}`` concurrency
65
+ group put them in separate groups and both pushed to ``gh-pages`` at once; the
66
+ loser was rejected and its versioned directory never appeared, while
67
+ ``switcher.json`` still advertised the version. The deploy job now uses a
68
+ ref-independent group so deployments queue instead.
69
+
70
+
71
+ 0.5.0 (2026-07-26)
72
+ ==================
73
+
74
+ New Features
75
+ ------------
76
+
77
+ - :meth:`Widget.set` takes ``_notify=False`` to move a widget without firing
78
+ ``pointer_move`` callbacks, so a handler that writes back to its own widget no
79
+ longer feeds into itself. Widgets also gained a
80
+ :meth:`~anyplotlib.widgets.Widget.remove` method.
81
+ - :meth:`~anyplotlib.Plot1D.add_range_widget` takes ``orientation="vertical"``
82
+ for a band that selects a range of values, and ``snap_values`` to restrict a
83
+ drag to a set of allowed positions (matplotlib's
84
+ ``SpanSelector.snap_values``). ``snap_values`` is also available on the
85
+ vline, hline and point widgets.
86
+ - Added :meth:`~anyplotlib.Plot2D.set_scalebar_style` to recolour the automatic
87
+ scale bar, which was hardcoded white on a translucent dark pill and unreadable
88
+ over a light image. ``bgcolor="none"`` drops the pill entirely.
89
+ - Added ``linestyle="none"`` (also spelled ``"None"``) for a series drawn as
90
+ markers with no connecting line — matplotlib's scatter idiom,
91
+ ``ax.plot(y, linestyle="none", marker="o")``. An explicit ``linewidth=0``
92
+ now means the same thing; it previously fell back to the 1.5 default in the
93
+ renderer.
94
+ - Added three 2-D overlay widget kinds: ``line``
95
+ (:meth:`~anyplotlib.Plot2D.add_line_widget`), a bare two-endpoint segment for
96
+ line profiles and two-point measurements, and ``vline`` / ``hline``, full-height
97
+ and full-width rules grabbable anywhere along their length.
98
+ - Clicking a 1-D panel now emits a ``pointer_down`` event carrying the clicked
99
+ position as ``xdata``/``ydata``, matching 2-D panels; it previously fired only
100
+ when the click landed on a line. Clicks on a line still report ``line_id``,
101
+ so existing line-click handlers are unaffected.
102
+ - Panels expose their geometry through :meth:`~anyplotlib.Plot1D.plot_box`,
103
+ :meth:`~anyplotlib.Plot1D.data_to_display` and
104
+ :meth:`~anyplotlib.Plot1D.display_to_data`, so callers working in display space
105
+ no longer have to re-derive the renderer's layout constants and letterbox maths
106
+ themselves.
107
+ - Sized marker types take ``size_units="px"`` so their radii and widths stay
108
+ fixed in screen pixels through a zoom instead of scaling with the data — what
109
+ a marker standing in for a *point* wants, and what matplotlib does by sizing
110
+ scatter markers in display points.
111
+ - ``edgecolors`` and ``facecolors`` accept a sequence of colours parallel to the
112
+ markers — matplotlib's ``edgecolors=[...]`` / scatter ``c=[...]`` — for every
113
+ marker type on both 1-D and 2-D panels, where previously only ``points`` and
114
+ ``polygons`` on 1-D panels honoured it. A short sequence cycles.
115
+
116
+
117
+ Bug Fixes
118
+ ---------
119
+
120
+ - A ``crosshair`` widget can now be grabbed anywhere along either of its rules
121
+ rather than only at the one-pixel centre hotspot; grabbing a rule constrains
122
+ the drag to that rule's own axis.
123
+ - The 1-D y-axis label is no longer drawn through the tick numbers; its position
124
+ was a fixed fraction of the left gutter and is now measured against the widest
125
+ tick string.
126
+ - The colorbar strip is no longer drawn flush against the image: there is now a
127
+ 6 px gap, taken out of the image width so the strip cannot be pushed off the
128
+ panel, and settable with :meth:`~anyplotlib.Plot2D.set_colorbar_pad`. This
129
+ shifts every colorbar plot by 4 px.
130
+ - ``save_html`` / ``to_html`` / ``figure_state`` now capture overlay widgets at
131
+ their current positions; widget moves reach JS as targeted events that never
132
+ rewrite the panel traits, so a snapshot used to show every widget where it was
133
+ created.
134
+
135
+
13
136
  0.4.1 (2026-07-26)
14
137
  ==================
15
138
 
@@ -0,0 +1,341 @@
1
+ """
2
+ Star Globe Explorer — a linked celestial sphere and sky map
3
+ ===========================================================
4
+
5
+ A celestial globe wired to a flat star map, with a sidereal clock.
6
+
7
+ * **Left** — the celestial sphere. Whatever you turn toward the camera is
8
+ your **zenith**: the point directly overhead.
9
+ * **Right** — the same sky as an equirectangular map, with the zenith marked
10
+ and the **horizon** drawn as the great circle 90° away from it. Everything
11
+ inside the horizon is above your head right now; everything outside is
12
+ below the ground.
13
+ * **Bottom** — a sidereal clock and a latitude control.
14
+
15
+ The coupling runs both ways. Orbit the globe and the clock and latitude
16
+ follow, because a zenith *is* a time and a place: the right ascension
17
+ overhead equals the local sidereal time, and its declination equals your
18
+ latitude. Drag either control and the globe turns to match.
19
+
20
+ Star positions are real (approximate J2000, rounded to about an arcminute),
21
+ carried over from :doc:`../PlotTypes/plot_3d_texture`; the Milky Way band is
22
+ placed by true galactic latitude.
23
+
24
+ .. note::
25
+ The globe is the sky seen from *outside*, the way a physical celestial
26
+ globe is built — so the constellations read mirrored compared with lying
27
+ on your back and looking up. The flat map is the conventional
28
+ inside-out view.
29
+ """
30
+ import numpy as np
31
+
32
+ import anyplotlib as apl
33
+
34
+ # ── Star catalogue ───────────────────────────────────────────────────────────
35
+ # name: (right ascension in hours, declination in degrees, visual magnitude).
36
+ # Swapping in a full catalogue (e.g. HYG, ~9k stars to naked-eye magnitude)
37
+ # is a change to this table alone — everything below is generic.
38
+ STARS = {
39
+ "Sirius": (6.752, -16.72, -1.46), "Canopus": (6.399, -52.70, -0.74),
40
+ "Rigil Kent": (14.660, -60.83, -0.27), "Arcturus": (14.261, 19.18, -0.05),
41
+ "Vega": (18.615, 38.78, 0.03), "Capella": (5.278, 46.00, 0.08),
42
+ "Rigel": (5.242, -8.20, 0.13), "Procyon": (7.655, 5.22, 0.34),
43
+ "Achernar": (1.629, -57.24, 0.46), "Betelgeuse": (5.919, 7.41, 0.50),
44
+ "Hadar": (14.064, -60.37, 0.61), "Altair": (19.846, 8.87, 0.77),
45
+ "Acrux": (12.443, -63.10, 0.77), "Aldebaran": (4.599, 16.51, 0.85),
46
+ "Spica": (13.420, -11.16, 1.04), "Antares": (16.490, -26.43, 1.09),
47
+ "Pollux": (7.755, 28.03, 1.14), "Fomalhaut": (22.961, -29.62, 1.16),
48
+ "Deneb": (20.690, 45.28, 1.25), "Mimosa": (12.795, -59.69, 1.25),
49
+ "Regulus": (10.140, 11.97, 1.35), "Adhara": (6.977, -28.97, 1.50),
50
+ "Castor": (7.577, 31.89, 1.58), "Shaula": (17.560, -37.10, 1.62),
51
+ "Gacrux": (12.520, -57.11, 1.63), "Bellatrix": (5.418, 6.35, 1.64),
52
+ "Elnath": (5.438, 28.61, 1.65), "Miaplacidus": (9.220, -69.72, 1.67),
53
+ "Alnilam": (5.604, -1.20, 1.69), "Alnair": (22.137, -46.96, 1.74),
54
+ "Alioth": (12.900, 55.96, 1.76), "Alnitak": (5.679, -1.94, 1.77),
55
+ "Dubhe": (11.062, 61.75, 1.79), "Mirfak": (3.405, 49.86, 1.79),
56
+ "Wezen": (7.140, -26.39, 1.83), "Kaus Aust.": (18.403, -34.38, 1.85),
57
+ "Alkaid": (13.792, 49.31, 1.85), "Avior": (8.375, -59.51, 1.86),
58
+ "Sargas": (17.622, -43.00, 1.86), "Menkalinan": (5.992, 44.95, 1.90),
59
+ "Atria": (16.811, -69.03, 1.91), "Alhena": (6.628, 16.40, 1.93),
60
+ "Peacock": (20.427, -56.74, 1.94), "Polaris": (2.530, 89.26, 1.98),
61
+ "Mirzam": (6.378, -17.96, 1.98), "Alphard": (9.460, -8.66, 2.00),
62
+ "Hamal": (2.120, 23.46, 2.00), "Diphda": (0.727, -17.99, 2.04),
63
+ "Nunki": (18.921, -26.30, 2.05), "Menkent": (14.112, -36.37, 2.06),
64
+ "Mirach": (1.162, 35.62, 2.06), "Alpheratz": (0.140, 29.09, 2.06),
65
+ "Rasalhague": (17.582, 12.56, 2.08), "Kochab": (14.845, 74.16, 2.08),
66
+ "Algieba": (10.333, 19.84, 2.08), "Saiph": (5.796, -9.67, 2.09),
67
+ "Tiaki": (22.711, -46.88, 2.11), "Algol": (3.136, 40.96, 2.12),
68
+ "Denebola": (11.818, 14.57, 2.14), "Muhlifain": (12.692, -48.96, 2.20),
69
+ "Aspidiske": (9.285, -59.28, 2.21), "Alphecca": (15.578, 26.71, 2.22),
70
+ "Sadr": (20.370, 40.26, 2.23), "Mizar": (13.399, 54.93, 2.23),
71
+ "Eltanin": (17.943, 51.49, 2.23), "Suhail": (9.133, -43.43, 2.23),
72
+ "Schedar": (0.675, 56.54, 2.24), "Mintaka": (5.533, -0.30, 2.25),
73
+ "Caph": (0.153, 59.15, 2.28), "Gamma Cas": (0.945, 60.72, 2.47),
74
+ "Merak": (11.030, 56.38, 2.37), "Enif": (21.737, 9.88, 2.38),
75
+ "Phecda": (11.897, 53.69, 2.44), "Ruchbah": (1.430, 60.24, 2.68),
76
+ "Delta Cru": (12.252, -58.75, 2.79), "Alcyone": (3.792, 24.11, 2.87),
77
+ "Megrez": (12.257, 57.03, 3.31), "Segin": (1.907, 63.67, 3.38),
78
+ "Meissa": (5.585, 9.93, 3.39),
79
+ }
80
+
81
+ FIGURES = [
82
+ ["Betelgeuse", "Bellatrix", "Mintaka", "Rigel"],
83
+ ["Mintaka", "Alnilam", "Alnitak", "Saiph"],
84
+ ["Betelgeuse", "Alnitak"], ["Bellatrix", "Meissa", "Betelgeuse"],
85
+ ["Dubhe", "Merak", "Phecda", "Megrez", "Dubhe"],
86
+ ["Megrez", "Alioth", "Mizar", "Alkaid"],
87
+ ["Segin", "Ruchbah", "Gamma Cas", "Schedar", "Caph"],
88
+ ["Acrux", "Gacrux"], ["Mimosa", "Delta Cru"],
89
+ ["Castor", "Pollux"], ["Vega", "Deneb", "Altair", "Vega"],
90
+ ]
91
+
92
+ TEX_W, TEX_H = 1440, 720
93
+ NGP_RA, NGP_DEC = np.radians(192.85948), np.radians(27.12825)
94
+ GC_RA, GC_DEC = np.radians(266.41684), np.radians(-29.00781)
95
+
96
+
97
+ def sky_pixel(ra_h, dec_deg):
98
+ """Catalogue coordinates → (column, row) in the equirectangular map."""
99
+ return (ra_h / 24.0 * (TEX_W - 1), (90.0 - dec_deg) / 180.0 * (TEX_H - 1))
100
+
101
+
102
+ def angsep(ra, dec, ra0, dec0):
103
+ return np.arccos(np.clip(
104
+ np.sin(dec) * np.sin(dec0) + np.cos(dec) * np.cos(dec0) * np.cos(ra - ra0),
105
+ -1.0, 1.0))
106
+
107
+
108
+ def _splat(img, x, y, radius, rgb, amp):
109
+ r = int(np.ceil(3 * radius))
110
+ xs = np.arange(int(round(x)) - r, int(round(x)) + r + 1)
111
+ ys = np.arange(int(round(y)) - r, int(round(y)) + r + 1)
112
+ ys = ys[(ys >= 0) & (ys < img.shape[0])]
113
+ if len(ys) == 0:
114
+ return
115
+ g = np.exp(-((ys[:, None] - y) ** 2 + (xs[None, :] - x) ** 2)
116
+ / (2 * radius ** 2)) * amp
117
+ img[np.ix_(ys, xs % img.shape[1])] += g[..., None] * np.asarray(rgb)
118
+
119
+
120
+ def _stroke(img, p0, p1, rgb, amp):
121
+ (x0, y0), (x1, y1) = p0, p1
122
+ if abs(x1 - x0) > img.shape[1] / 2: # take the short way in RA
123
+ x1 += img.shape[1] if x0 > x1 else -img.shape[1]
124
+ n = int(max(abs(x1 - x0), abs(y1 - y0))) * 2 + 2
125
+ xs = np.linspace(x0, x1, n).astype(int) % img.shape[1]
126
+ ys = np.clip(np.linspace(y0, y1, n), 0, img.shape[0] - 1).astype(int)
127
+ img[ys, xs] = np.maximum(img[ys, xs], np.asarray(rgb) * amp)
128
+
129
+
130
+ # ── Build the sky image ──────────────────────────────────────────────────────
131
+ rng = np.random.default_rng(20260730)
132
+ ra_grid = np.linspace(0, 2 * np.pi, TEX_W)[None, :]
133
+ dec_grid = np.radians(np.linspace(90, -90, TEX_H))[:, None]
134
+
135
+ sky = np.zeros((TEX_H, TEX_W, 3), np.float32)
136
+ sky[:] = np.array([0.020, 0.028, 0.062], np.float32)
137
+ sky += (np.abs(np.sin(dec_grid)) ** 3 * 0.02)[..., None]
138
+
139
+ gal_lat = np.arcsin(np.sin(dec_grid) * np.sin(NGP_DEC)
140
+ + np.cos(dec_grid) * np.cos(NGP_DEC)
141
+ * np.cos(ra_grid - NGP_RA))
142
+ band = np.exp(-(gal_lat / np.radians(11.0)) ** 2)
143
+ band = band * (0.45 + 0.85 * np.exp(
144
+ -(angsep(ra_grid, dec_grid, GC_RA, GC_DEC) / np.radians(55.0)) ** 2))
145
+ dust, amp = np.zeros((TEX_H, TEX_W), np.float32), 1.0
146
+ for k in range(1, 6):
147
+ dust += amp * (np.sin(3 * k * ra_grid + rng.uniform(0, 2 * np.pi))
148
+ * np.sin(2.5 * k * (dec_grid + np.pi / 2)
149
+ + rng.uniform(0, 2 * np.pi)))
150
+ amp *= 0.55
151
+ band = band * np.clip(0.72 + 0.28 * dust, 0.25, 1.0)
152
+ sky += band[..., None] * np.array([0.46, 0.44, 0.55], np.float32)
153
+
154
+ n_faint = 24_000
155
+ f_dec = np.degrees(np.arcsin(rng.uniform(-1, 1, n_faint)))
156
+ fx = rng.uniform(0, TEX_W, n_faint).astype(int) % TEX_W
157
+ fy = ((90.0 - f_dec) / 180.0 * (TEX_H - 1)).astype(int)
158
+ keep = rng.random(n_faint) < (0.22 + 0.78 * band[fy, fx])
159
+ fx, fy = fx[keep], fy[keep]
160
+ sky[fy, fx] += rng.uniform(0.25, 0.95, (len(fx), 1)).astype(np.float32)
161
+
162
+ for chain in FIGURES:
163
+ for a, b in zip(chain, chain[1:]):
164
+ _stroke(sky, sky_pixel(*STARS[a][:2]), sky_pixel(*STARS[b][:2]),
165
+ (0.30, 0.58, 0.85), 1.0)
166
+
167
+ for name, (ra_h, dec_deg, mag) in STARS.items():
168
+ x, y = sky_pixel(ra_h, dec_deg)
169
+ flux = 10 ** (-0.4 * mag)
170
+ _splat(sky, x, y, 1.3 + 2.0 * flux ** 0.35, (1.0, 0.97, 0.90),
171
+ 0.85 + 2.2 * flux ** 0.30)
172
+
173
+ sky_u8 = (np.clip(sky, 0, 1) * 255).astype(np.uint8)
174
+
175
+ # ── Spherical geometry ───────────────────────────────────────────────────────
176
+ NDEC, NRA = 49, 97
177
+ _ra = np.linspace(0, 2 * np.pi, NRA)
178
+ _dec = np.linspace(np.pi / 2, -np.pi / 2, NDEC)
179
+ RA, DEC = np.meshgrid(_ra, _dec)
180
+ X, Y, Z = np.cos(DEC) * np.cos(RA), np.cos(DEC) * np.sin(RA), np.sin(DEC)
181
+
182
+
183
+ def camera_to_zenith(azimuth, elevation):
184
+ """Globe camera angles → the sky point facing you, as (RA hours, Dec°).
185
+
186
+ The renderer's turntable faces the unit vector with ``el = asin(vz)`` and
187
+ ``az = atan2(vx, -vy)``. Feeding that through this example's
188
+ ``x = cos δ cos α`` parameterisation collapses to ``α = azimuth − 90°``.
189
+ """
190
+ return ((azimuth - 90.0) % 360.0) / 15.0, elevation
191
+
192
+
193
+ def zenith_to_camera(lst_hours, latitude):
194
+ """The inverse: put (LST, latitude) overhead."""
195
+ return (lst_hours * 15.0 + 90.0) % 360.0, latitude
196
+
197
+
198
+ def horizon_segments(lst_hours, latitude, n=240):
199
+ """Map-space polyline for the horizon: the circle 90° from the zenith.
200
+
201
+ Built as an orthonormal frame about the zenith rather than by solving the
202
+ altitude equation, so it stays well conditioned at the poles. Returned as
203
+ ``(N, 2, 2)`` segments, split wherever the curve wraps in right ascension
204
+ so nothing is drawn straight across the map.
205
+ """
206
+ a0, d0 = np.radians(lst_hours * 15.0), np.radians(latitude)
207
+ n_hat = np.array([np.cos(d0) * np.cos(a0), np.cos(d0) * np.sin(a0), np.sin(d0)])
208
+ # Any vector not parallel to the zenith gives a usable first basis vector.
209
+ tmp = np.array([0.0, 0.0, 1.0]) if abs(n_hat[2]) < 0.9 else np.array([1.0, 0.0, 0.0])
210
+ e1 = np.cross(n_hat, tmp)
211
+ e1 /= np.linalg.norm(e1)
212
+ e2 = np.cross(n_hat, e1)
213
+
214
+ t = np.linspace(0, 2 * np.pi, n)
215
+ pts = np.cos(t)[:, None] * e1 + np.sin(t)[:, None] * e2
216
+ ra_h = (np.degrees(np.arctan2(pts[:, 1], pts[:, 0])) % 360.0) / 15.0
217
+ dec_d = np.degrees(np.arcsin(np.clip(pts[:, 2], -1, 1)))
218
+ px = np.column_stack(sky_pixel(ra_h, dec_d))
219
+
220
+ segs = np.stack([px[:-1], px[1:]], axis=1) # (N-1, 2, 2)
221
+ keep = np.abs(segs[:, 0, 0] - segs[:, 1, 0]) < TEX_W / 2 # drop wrap jumps
222
+ return segs[keep]
223
+
224
+
225
+ def visible_stars(lst_hours, latitude):
226
+ """Map pixels of the catalogue stars currently above the horizon."""
227
+ a0, d0 = np.radians(lst_hours * 15.0), np.radians(latitude)
228
+ out = []
229
+ for ra_h, dec_deg, _ in STARS.values():
230
+ alt = np.pi / 2 - angsep(np.radians(ra_h * 15.0), np.radians(dec_deg), a0, d0)
231
+ if alt > 0:
232
+ out.append(sky_pixel(ra_h, dec_deg))
233
+ return np.asarray(out) if out else np.zeros((0, 2))
234
+
235
+
236
+ # ── Figure ───────────────────────────────────────────────────────────────────
237
+ LST0, LAT0 = 5.5, 35.0 # Orion overhead, mid-northern latitude
238
+
239
+ fig = apl.Figure(figsize=(1080, 620))
240
+ gs = apl.GridSpec(3, 2, width_ratios=[1, 1.35], height_ratios=[6, 1, 1])
241
+
242
+ az0, el0 = zenith_to_camera(LST0, LAT0)
243
+ globe = fig.add_subplot(gs[0, 0]).plot_surface(
244
+ X, Y, Z, bounds=((-1, 1),) * 3, azimuth=az0, elevation=el0)
245
+ globe.set_axis_off()
246
+ globe.set_texture(sky_u8, cull_backfaces=True)
247
+ globe.set_title("celestial sphere — drag to move your zenith")
248
+
249
+ sky_map = fig.add_subplot(gs[0, 1]).imshow(sky_u8)
250
+ sky_map.set_title("sky map — inside the curve is above the horizon")
251
+
252
+ clock = fig.add_subplot(gs[1, :]).plot(
253
+ np.zeros(2), axes=[np.array([0.0, 24.0])], color="#37474f", linewidth=1)
254
+ clock.set_xlabel("local sidereal time (h)")
255
+
256
+ lat_ax = fig.add_subplot(gs[2, :]).plot(
257
+ np.zeros(2), axes=[np.array([-90.0, 90.0])], color="#37474f", linewidth=1)
258
+ lat_ax.set_xlabel("observer latitude ($\\degree$)")
259
+
260
+ # ── Overlays, redrawn whenever the zenith moves ──────────────────────────────
261
+ horizon = sky_map.add_lines(horizon_segments(LST0, LAT0), name="horizon",
262
+ edgecolors="#4dd0e1", linewidths=2.0)
263
+ # size_units="px" keeps the markers a fixed screen size as the map is zoomed.
264
+ zenith = sky_map.add_points(np.array([sky_pixel(LST0, LAT0)]), name="zenith",
265
+ sizes=9, color="#ffd54f", facecolors="#ffd54f",
266
+ alpha=1.0, size_units="px")
267
+ up_now = sky_map.add_points(visible_stars(LST0, LAT0), name="up",
268
+ sizes=4, color="#4dd0e1", facecolors="#4dd0e1",
269
+ alpha=0.55, size_units="px")
270
+
271
+ lst_line = clock.add_vline_widget(LST0, color="#ffd54f")
272
+ lat_line = lat_ax.add_vline_widget(LAT0, color="#ffd54f")
273
+
274
+ _syncing = False # guards the two-way coupling against feedback
275
+
276
+
277
+ def refresh(lst_hours, latitude, *, move_globe, move_sliders):
278
+ """Push a new zenith everywhere. One writer, three followers."""
279
+ global _syncing
280
+ if _syncing:
281
+ return
282
+ _syncing = True
283
+ try:
284
+ with fig.batch():
285
+ horizon.set(segments=horizon_segments(lst_hours, latitude))
286
+ zenith.set(offsets=np.array([sky_pixel(lst_hours, latitude)]))
287
+ up_now.set(offsets=visible_stars(lst_hours, latitude))
288
+ sky_map.set_title(
289
+ f"sky map — LST {lst_hours:04.1f} h, latitude {latitude:+.0f}°")
290
+ if move_globe:
291
+ az, el = zenith_to_camera(lst_hours, latitude)
292
+ globe.set_view(azimuth=az, elevation=el)
293
+ if move_sliders:
294
+ lst_line.set(x=lst_hours)
295
+ lat_line.set(x=latitude)
296
+ finally:
297
+ _syncing = False
298
+
299
+
300
+ @globe.add_event_handler("pointer_move")
301
+ def _on_orbit(event):
302
+ """Orbiting the globe IS choosing a time and a place."""
303
+ if event.azimuth is None:
304
+ return
305
+ lst_hours, latitude = camera_to_zenith(event.azimuth, event.elevation)
306
+ refresh(lst_hours, latitude, move_globe=False, move_sliders=True)
307
+
308
+
309
+ @lst_line.add_event_handler("pointer_move")
310
+ def _on_clock(event):
311
+ refresh(lst_line.x % 24.0, lat_line.x, move_globe=True, move_sliders=False)
312
+
313
+
314
+ @lat_line.add_event_handler("pointer_move")
315
+ def _on_latitude(event):
316
+ refresh(lst_line.x % 24.0, np.clip(lat_line.x, -89, 89),
317
+ move_globe=True, move_sliders=False)
318
+
319
+
320
+ refresh(LST0, LAT0, move_globe=False, move_sliders=False)
321
+
322
+ fig
323
+
324
+ # %%
325
+ # Reading it
326
+ # ----------
327
+ # The cyan circle is the horizon and the gold dot is the zenith. Stars inside
328
+ # the circle are above the ground and are marked in cyan; the rest have set.
329
+ #
330
+ # Two things fall out of the geometry that are worth watching for:
331
+ #
332
+ # * Push the latitude to :math:`+90°` and the horizon collapses onto the
333
+ # celestial equator — at the north pole the sky never rises or sets, it
334
+ # just wheels around Polaris.
335
+ # * Run the clock through 24 h at a mid latitude and the horizon sweeps the
336
+ # whole sky except two caps around the poles. Those caps are the
337
+ # circumpolar stars, the ones that never set from where you are standing.
338
+
339
+ refresh(2.0, 89.0, move_globe=True, move_sliders=True)
340
+
341
+ fig