anyplotlib 0.7.2__tar.gz → 0.7.3__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 (279) hide show
  1. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/CHANGELOG.rst +14 -0
  2. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/PKG-INFO +2 -2
  3. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/figure_esm.js +10 -2
  4. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/plot2d/_plot2d.py +12 -3
  5. anyplotlib-0.7.3/anyplotlib/tests/test_interactive/test_circle_lock_center.py +160 -0
  6. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/widgets/_widgets2d.py +18 -2
  7. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/docs/_root/index.html +3 -3
  8. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/docs/_root/switcher.json +5 -0
  9. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/docs/conf.py +1 -1
  10. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/pyproject.toml +1 -1
  11. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/.github/workflows/ci.yml +0 -0
  12. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/.github/workflows/docs.yml +0 -0
  13. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/.github/workflows/prepare_release.yml +0 -0
  14. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/.github/workflows/release.yml +0 -0
  15. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/.github/workflows/tests.yml +0 -0
  16. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/.gitignore +0 -0
  17. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/AGENTS.md +0 -0
  18. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Benchmarks/README.rst +0 -0
  19. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Benchmarks/plot_benchmark_comparison.py +0 -0
  20. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Interactive/README.rst +0 -0
  21. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Interactive/plot_3d_spectral_viewer.py +0 -0
  22. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Interactive/plot_eels_explorer.py +0 -0
  23. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Interactive/plot_figure_annotations.py +0 -0
  24. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Interactive/plot_interactive_fft.py +0 -0
  25. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Interactive/plot_interactive_fitting.py +0 -0
  26. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Interactive/plot_ipf_density_map.py +0 -0
  27. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Interactive/plot_ipf_explorer.py +0 -0
  28. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Interactive/plot_key_bindings.py +0 -0
  29. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Interactive/plot_particle_picker.py +0 -0
  30. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Interactive/plot_point_widget.py +0 -0
  31. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Interactive/plot_segment_by_contrast.py +0 -0
  32. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Interactive/plot_segment_by_contrast_advanced.py +0 -0
  33. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Interactive/plot_spectra_roi_inspector.py +0 -0
  34. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Interactive/plot_star_globe_explorer.py +0 -0
  35. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Interactive/plot_threshold_explorer.py +0 -0
  36. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Interactive/plot_voxel_grain_explorer.py +0 -0
  37. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Markers/README.rst +0 -0
  38. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Markers/plot_arrows.py +0 -0
  39. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Markers/plot_circles.py +0 -0
  40. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Markers/plot_ellipses.py +0 -0
  41. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Markers/plot_horizontal_lines.py +0 -0
  42. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Markers/plot_line_segments.py +0 -0
  43. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Markers/plot_points.py +0 -0
  44. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Markers/plot_polygons.py +0 -0
  45. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Markers/plot_rectangles.py +0 -0
  46. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Markers/plot_squares.py +0 -0
  47. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Markers/plot_texts.py +0 -0
  48. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Markers/plot_transform_markers.py +0 -0
  49. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Markers/plot_vertical_lines.py +0 -0
  50. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/PlotTypes/README.rst +0 -0
  51. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/PlotTypes/plot_3d.py +0 -0
  52. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/PlotTypes/plot_3d_texture.py +0 -0
  53. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/PlotTypes/plot_bar.py +0 -0
  54. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/PlotTypes/plot_gpu_voxels.py +0 -0
  55. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/PlotTypes/plot_gridspec_custom.py +0 -0
  56. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/PlotTypes/plot_image2d.py +0 -0
  57. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/PlotTypes/plot_inset.py +0 -0
  58. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/PlotTypes/plot_key_overlays.py +0 -0
  59. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/PlotTypes/plot_label_formatting.py +0 -0
  60. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/PlotTypes/plot_line_styles.py +0 -0
  61. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/PlotTypes/plot_pcolormesh.py +0 -0
  62. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/PlotTypes/plot_spectra1d.py +0 -0
  63. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/PlotTypes/plot_step_and_log.py +0 -0
  64. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/PlotTypes/plot_tile_backend.py +0 -0
  65. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/PlotTypes/plot_twinx.py +0 -0
  66. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/README.rst +0 -0
  67. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Widgets/README.rst +0 -0
  68. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Widgets/plot_widget1d_hline.py +0 -0
  69. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Widgets/plot_widget1d_point.py +0 -0
  70. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Widgets/plot_widget1d_range.py +0 -0
  71. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Widgets/plot_widget1d_vline.py +0 -0
  72. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Widgets/plot_widget2d_annular.py +0 -0
  73. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Widgets/plot_widget2d_arrow.py +0 -0
  74. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Widgets/plot_widget2d_circle.py +0 -0
  75. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Widgets/plot_widget2d_crosshair.py +0 -0
  76. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Widgets/plot_widget2d_label.py +0 -0
  77. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Widgets/plot_widget2d_polygon.py +0 -0
  78. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Widgets/plot_widget2d_rectangle.py +0 -0
  79. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Examples/Widgets/plot_widget3d_plane.py +0 -0
  80. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/LICENSE +0 -0
  81. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/Makefile +0 -0
  82. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/README.md +0 -0
  83. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/FIGURE_ESM.md +0 -0
  84. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/__init__.py +0 -0
  85. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/_base_plot.py +0 -0
  86. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/_binary_frame.py +0 -0
  87. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/_electron.py +0 -0
  88. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/_repr_utils.py +0 -0
  89. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/_utils.py +0 -0
  90. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/axes/__init__.py +0 -0
  91. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/axes/_axes.py +0 -0
  92. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/axes/_inset_axes.py +0 -0
  93. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/callbacks.py +0 -0
  94. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/conftest.py +0 -0
  95. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/embed.py +0 -0
  96. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/figure/__init__.py +0 -0
  97. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/figure/_figure.py +0 -0
  98. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/figure/_gridspec.py +0 -0
  99. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/figure/_subplots.py +0 -0
  100. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/keys.py +0 -0
  101. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/markers.py +0 -0
  102. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/plot1d/__init__.py +0 -0
  103. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/plot1d/_plot1d.py +0 -0
  104. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/plot1d/_plotbar.py +0 -0
  105. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/plot2d/__init__.py +0 -0
  106. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/plot2d/_layer.py +0 -0
  107. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/plot2d/_plotmesh.py +0 -0
  108. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/plot2d/_tile_backend.py +0 -0
  109. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/plot3d/__init__.py +0 -0
  110. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/plot3d/_plot3d.py +0 -0
  111. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/plotxy/__init__.py +0 -0
  112. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/plotxy/_plotxy.py +0 -0
  113. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/sphinx_anywidget/__init__.py +0 -0
  114. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/sphinx_anywidget/_directive.py +0 -0
  115. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/sphinx_anywidget/_repr_utils.py +0 -0
  116. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/sphinx_anywidget/_scraper.py +0 -0
  117. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/sphinx_anywidget/_wheel_builder.py +0 -0
  118. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/sphinx_anywidget/static/anywidget_bridge.js +0 -0
  119. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/sphinx_anywidget/static/anywidget_overlay.css +0 -0
  120. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/sphinx_anywidget/tests/__init__.py +0 -0
  121. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/sphinx_anywidget/tests/conftest.py +0 -0
  122. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/sphinx_anywidget/tests/test_directive.py +0 -0
  123. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/sphinx_anywidget/tests/test_init.py +0 -0
  124. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/sphinx_anywidget/tests/test_repr_utils.py +0 -0
  125. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/sphinx_anywidget/tests/test_scraper.py +0 -0
  126. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/sphinx_anywidget/tests/test_wheel_builder.py +0 -0
  127. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/__init__.py +0 -0
  128. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/_gpu_clim_check.cjs +0 -0
  129. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/_png_utils.py +0 -0
  130. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/bar_basic.png +0 -0
  131. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/gridspec_3col_equal_spectra.png +0 -0
  132. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/gridspec_asymmetric_width_ratios.png +0 -0
  133. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/gridspec_height_ratio_image_histogram.png +0 -0
  134. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/gridspec_image_two_spectra.png +0 -0
  135. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/gridspec_side_by_side_1d.png +0 -0
  136. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/gridspec_spanning_top_two_bottom.png +0 -0
  137. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/imshow_axis_off.png +0 -0
  138. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/imshow_checkerboard.png +0 -0
  139. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/imshow_gradient.png +0 -0
  140. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/imshow_labels.png +0 -0
  141. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/imshow_viridis.png +0 -0
  142. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/inset_1d.png +0 -0
  143. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/inset_maximized.png +0 -0
  144. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/inset_minimized.png +0 -0
  145. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/inset_normal_2d.png +0 -0
  146. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/inset_stacked.png +0 -0
  147. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/inset_stacked_one_minimized.png +0 -0
  148. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/pcolormesh_uniform.png +0 -0
  149. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/plot1d_all_linestyles.png +0 -0
  150. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/plot1d_alpha.png +0 -0
  151. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/plot1d_axis_off.png +0 -0
  152. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/plot1d_dashed.png +0 -0
  153. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/plot1d_marker_symbols.png +0 -0
  154. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/plot1d_markers.png +0 -0
  155. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/plot1d_multi.png +0 -0
  156. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/plot1d_sine.png +0 -0
  157. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/plot1d_title.png +0 -0
  158. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/plot1d_twinx.png +0 -0
  159. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/plot3d_surface.png +0 -0
  160. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/baselines/subplots_2x1.png +0 -0
  161. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/conftest.py +0 -0
  162. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_benchmarks/__init__.py +0 -0
  163. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_benchmarks/baselines.json +0 -0
  164. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_benchmarks/benchmarks/baselines.json +0 -0
  165. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_benchmarks/test_benchmarks.py +0 -0
  166. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_benchmarks/test_benchmarks_py.py +0 -0
  167. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_documentation/__init__.py +0 -0
  168. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_documentation/test_bridge.py +0 -0
  169. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_documentation/test_push_hook.py +0 -0
  170. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_documentation/test_scraper.py +0 -0
  171. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_electron/__init__.py +0 -0
  172. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_electron/test_binary_frame.py +0 -0
  173. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_embed/__init__.py +0 -0
  174. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_embed/test_embed_api.py +0 -0
  175. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_embed/test_embed_mount.py +0 -0
  176. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_embed/test_export_pixel_tokens.py +0 -0
  177. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_embed/test_export_png.py +0 -0
  178. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_embed/test_export_widget_sync.py +0 -0
  179. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_examples/__init__.py +0 -0
  180. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_examples/test_interactive_examples.py +0 -0
  181. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_interactive/__init__.py +0 -0
  182. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_interactive/_event_test_utils.py +0 -0
  183. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_interactive/test_blit_audit.py +0 -0
  184. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_interactive/test_callbacks_playwright.py +0 -0
  185. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_interactive/test_callbacks_unit.py +0 -0
  186. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_interactive/test_edit_chrome.py +0 -0
  187. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_interactive/test_edit_chrome_playwright.py +0 -0
  188. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_interactive/test_event_pause_hold.py +0 -0
  189. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_interactive/test_event_plots.py +0 -0
  190. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_interactive/test_event_settled.py +0 -0
  191. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_interactive/test_events_regression.py +0 -0
  192. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_interactive/test_orbit_direction.py +0 -0
  193. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_interactive/test_pointer_down_1d.py +0 -0
  194. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_interactive/test_range_orientation_snap.py +0 -0
  195. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_interactive/test_star_globe_linking.py +0 -0
  196. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_interactive/test_tile_parity_playwright.py +0 -0
  197. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_interactive/test_title.py +0 -0
  198. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_interactive/test_touch.py +0 -0
  199. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_interactive/test_widget_brush.py +0 -0
  200. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_interactive/test_widget_max_extent.py +0 -0
  201. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_interactive/test_widget_set_notify.py +0 -0
  202. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_interactive/test_widgets.py +0 -0
  203. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_interactive/test_widgets_line_2d.py +0 -0
  204. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_keys/test_keys.py +0 -0
  205. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_labels/__init__.py +0 -0
  206. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_labels/test_label_api.py +0 -0
  207. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_labels/test_label_rendering.py +0 -0
  208. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_labels/test_no_clipping.py +0 -0
  209. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_labels/test_ylabel_clearance.py +0 -0
  210. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_layouts/__init__.py +0 -0
  211. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_layouts/test_batch.py +0 -0
  212. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_layouts/test_coord_conversion.py +0 -0
  213. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_layouts/test_geom_channel.py +0 -0
  214. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_layouts/test_gridspec.py +0 -0
  215. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_layouts/test_inset.py +0 -0
  216. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_layouts/test_inset_callout.py +0 -0
  217. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_layouts/test_interaction.py +0 -0
  218. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_layouts/test_visual.py +0 -0
  219. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_markers/__init__.py +0 -0
  220. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_markers/test_marker_transforms.py +0 -0
  221. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_markers/test_markers.py +0 -0
  222. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_markers/test_per_marker_colors.py +0 -0
  223. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_markers/test_size_units.py +0 -0
  224. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot1d/__init__.py +0 -0
  225. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot1d/test_line_none_rendering.py +0 -0
  226. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot1d/test_plot1d.py +0 -0
  227. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot1d/test_plotbar.py +0 -0
  228. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot2d/__init__.py +0 -0
  229. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot2d/test_colorbar_gap_scalebar.py +0 -0
  230. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot2d/test_detail_tile.py +0 -0
  231. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot2d/test_first_paint_race.py +0 -0
  232. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot2d/test_gpu_image.py +0 -0
  233. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot2d/test_gpu_parity_playwright.py +0 -0
  234. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot2d/test_imshow.py +0 -0
  235. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot2d/test_imshow_rgb.py +0 -0
  236. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot2d/test_layers.py +0 -0
  237. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot2d/test_layers_playwright.py +0 -0
  238. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot2d/test_pcolormesh.py +0 -0
  239. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot2d/test_plot2d_api.py +0 -0
  240. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot2d/test_rapid_frame_update.py +0 -0
  241. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot2d/test_tile_backend.py +0 -0
  242. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot2d/test_tile_diagnostics.py +0 -0
  243. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot2d/test_tiled_imshow.py +0 -0
  244. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot2d/test_zoom_view_write.py +0 -0
  245. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot3d/__init__.py +0 -0
  246. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot3d/test_colors_highlight.py +0 -0
  247. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot3d/test_gpu_depth.py +0 -0
  248. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot3d/test_gpu_fallback.py +0 -0
  249. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot3d/test_gpu_hidden_reveal.py +0 -0
  250. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot3d/test_plot3d.py +0 -0
  251. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot3d/test_texture.py +0 -0
  252. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plot3d/test_voxels_planes.py +0 -0
  253. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/tests/test_plotxy/test_plotxy.py +0 -0
  254. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/widgets/__init__.py +0 -0
  255. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/widgets/_base.py +0 -0
  256. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/widgets/_widgets1d.py +0 -0
  257. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/anyplotlib/widgets/_widgets3d.py +0 -0
  258. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/docs/_sg_html_scraper.py +0 -0
  259. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/docs/_static/anyplotlib.svg +0 -0
  260. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/docs/_static/custom.css +0 -0
  261. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/docs/_static/pyodide_bridge.js +0 -0
  262. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/docs/_templates/autosummary/class.rst +0 -0
  263. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/docs/api/callbacks.rst +0 -0
  264. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/docs/api/figure.rst +0 -0
  265. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/docs/api/figure_plots.rst +0 -0
  266. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/docs/api/index.rst +0 -0
  267. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/docs/api/keys.rst +0 -0
  268. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/docs/api/markers.rst +0 -0
  269. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/docs/api/widgets.rst +0 -0
  270. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/docs/benchmarking.rst +0 -0
  271. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/docs/dev/index.rst +0 -0
  272. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/docs/embedding.rst +0 -0
  273. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/docs/events.rst +0 -0
  274. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/docs/getting_started.rst +0 -0
  275. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/docs/index.rst +0 -0
  276. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/make.bat +0 -0
  277. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/upcoming_changes/.gitkeep +0 -0
  278. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/upcoming_changes/README.rst +0 -0
  279. {anyplotlib-0.7.2 → anyplotlib-0.7.3}/uv.lock +0 -0
@@ -10,6 +10,20 @@ Fragment files in ``upcoming_changes/`` are assembled into this file by
10
10
 
11
11
  .. towncrier release notes start
12
12
 
13
+ 0.7.3 (2026-08-13)
14
+ ==================
15
+
16
+ New Features
17
+ ------------
18
+
19
+ - ``Plot2D.add_circle_widget`` gains ``lock_center``: the centre is pinned and
20
+ only the radius is draggable. A grab on the ring body is refused at hit-test
21
+ time and falls through to the plot's own pan, so the hover cursor never
22
+ promises a move and the centre cannot drift. Use it when the centre is fixed by
23
+ the data — a ring on a power spectrum is centred on the DC term, and one nudged
24
+ off-centre silently corrupts every radius measured from it.
25
+
26
+
13
27
  0.7.2 (2026-08-03)
14
28
  ==================
15
29
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: anyplotlib
3
- Version: 0.7.2
3
+ Version: 0.7.3
4
4
  Summary: A plotting library using python, javascript and anywidget for performant in browser plotting.
5
5
  Project-URL: Homepage, https://cssfrancis.github.io/anyplotlib/
6
6
  Project-URL: Repository, https://github.com/CSSFrancis/anyplotlib
@@ -8182,7 +8182,13 @@ fn fs(in : VsOut) -> @location(0) vec4<f32> {
8182
8182
  // outer radius handle
8183
8183
  if (Math.hypot(mx - (ccx + cr), my - ccy) <= HR)
8184
8184
  return { idx:i, mode:'resize_r', snapW:{...w}, startMX:mx, startMY:my };
8185
- // body (inside ring ± tolerance)
8185
+ // body (inside ring ± tolerance). `lock_center` refuses the grab
8186
+ // outright rather than letting the drag run and correcting afterwards:
8187
+ // a centre snapped back on release still tracks the cursor for the
8188
+ // whole drag, which reads as a broken lock. Refusing here also leaves
8189
+ // the hover cursor at 'default' and lets the drag fall through to the
8190
+ // plot's own pan, so the ring is simply not a handle any more.
8191
+ if (w.lock_center) continue;
8186
8192
  if (Math.abs(Math.hypot(mx-ccx, my-ccy) - cr) <= Math.max(HR, cr*0.18) ||
8187
8193
  Math.hypot(mx-ccx, my-ccy) <= HR)
8188
8194
  return { idx:i, mode:'move', snapW:{...w}, startMX:mx, startMY:my };
@@ -8441,7 +8447,9 @@ fn fs(in : VsOut) -> @location(0) vec4<f32> {
8441
8447
  }
8442
8448
 
8443
8449
  if (w.type === 'circle') {
8444
- if (d.mode === 'move') {
8450
+ // A drag already in flight when `lock_center` is turned on must not keep
8451
+ // translating the widget: the hit-test gate only runs at grab time.
8452
+ if (d.mode === 'move' && !w.lock_center) {
8445
8453
  w.cx = s.cx + dix; w.cy = s.cy + diy;
8446
8454
  } else if (d.mode === 'resize_r') {
8447
8455
  // distance from centre in image-px
@@ -1731,15 +1731,24 @@ class Plot2D(_BasePlot, _PanelMixin, _MarkerMixin):
1731
1731
  def add_circle_widget(self, cx: float | None = None, cy: float | None = None,
1732
1732
  r: float | None = None, color: str = "#00e5ff",
1733
1733
  linewidth: float = 2,
1734
- show_handles: bool = True) -> CircleWidget:
1735
- """Add a draggable circle overlay."""
1734
+ show_handles: bool = True,
1735
+ lock_center: bool = False) -> CircleWidget:
1736
+ """Add a draggable circle overlay.
1737
+
1738
+ ``lock_center`` pins the centre and leaves only the radius draggable —
1739
+ a grab on the ring body is refused and pans the plot instead. Use it
1740
+ when the centre is fixed by the data (a ring on a power spectrum is
1741
+ centred on the DC term), so it cannot be nudged off and silently
1742
+ corrupt every radius measured from it.
1743
+ """
1736
1744
  iw, ih = self._state["image_width"], self._state["image_height"]
1737
1745
  widget = CircleWidget(lambda: None,
1738
1746
  cx=float(cx) if cx is not None else iw / 2,
1739
1747
  cy=float(cy) if cy is not None else ih / 2,
1740
1748
  r=float(r) if r is not None else iw * 0.1,
1741
1749
  color=color, linewidth=linewidth,
1742
- show_handles=show_handles)
1750
+ show_handles=show_handles,
1751
+ lock_center=lock_center)
1743
1752
  widget._push_fn = self._make_widget_push_fn(widget)
1744
1753
  self._widgets[widget.id] = widget
1745
1754
  self._push()
@@ -0,0 +1,160 @@
1
+ """
2
+ tests/test_interactive/test_circle_lock_center.py
3
+ =================================================
4
+
5
+ ``lock_center`` — a circle whose centre is fixed by the data.
6
+
7
+ Why it exists: a ring measured on a power spectrum is centred on the DC term.
8
+ Its centre is not a free parameter, so a draggable one is a control that can
9
+ only ever be wrong — and a ring nudged off-centre silently corrupts every
10
+ radius measured from it.
11
+
12
+ Why it is enforced in the HIT-TEST and not afterwards. The obvious
13
+ implementation is a Python handler that watches the widget and snaps the centre
14
+ back when the drag settles. It cannot work: ``_doDrag2d`` recomputes the
15
+ position every frame from its own grab-time snapshot, so a pushed-back centre
16
+ is overwritten on the next mousemove. The ring tracks the cursor for the whole
17
+ drag and jumps back on release, which reads as a broken lock rather than a
18
+ locked ring. Refusing the grab is the only place the constraint holds.
19
+
20
+ Coordinate system mirrors figure_esm.js — see ``_img_to_page``.
21
+ """
22
+ from __future__ import annotations
23
+
24
+ import numpy as np
25
+ import pytest
26
+
27
+ import anyplotlib as apl
28
+ from anyplotlib.widgets import CircleWidget
29
+ from anyplotlib.tests.test_interactive._event_test_utils import (
30
+ _collect_events, _get_events,
31
+ )
32
+
33
+ FIG_W, FIG_H = 400, 300
34
+
35
+
36
+ # ═══════════════════════════════════════════════════════════════════════════
37
+ # 1. Python API — the flag has to reach the JS renderer to do anything
38
+ # ═══════════════════════════════════════════════════════════════════════════
39
+
40
+ class TestLockCenterAttribute:
41
+ def test_defaults_to_unlocked(self):
42
+ w = CircleWidget(lambda: None, cx=16, cy=16, r=6)
43
+ assert w.lock_center is False
44
+
45
+ def test_stores_the_flag(self):
46
+ w = CircleWidget(lambda: None, cx=16, cy=16, r=6, lock_center=True)
47
+ assert w.lock_center is True
48
+
49
+ def test_reaches_the_state_dict(self):
50
+ """JS reads the widget dict, so the flag has to survive to_dict()."""
51
+ w = CircleWidget(lambda: None, cx=16, cy=16, r=6, lock_center=True)
52
+ assert w.to_dict()["lock_center"] is True
53
+
54
+ def test_add_circle_widget_passes_it_through(self):
55
+ fig, ax = apl.subplots(1, 1, figsize=(FIG_W, FIG_H))
56
+ v = ax.imshow(np.zeros((32, 32), dtype=np.float32))
57
+ w = v.add_circle_widget(cx=16, cy=16, r=6, lock_center=True)
58
+ assert w.lock_center is True
59
+
60
+ def test_add_circle_widget_defaults_to_unlocked(self):
61
+ fig, ax = apl.subplots(1, 1, figsize=(FIG_W, FIG_H))
62
+ v = ax.imshow(np.zeros((32, 32), dtype=np.float32))
63
+ assert v.add_circle_widget(cx=16, cy=16, r=6).lock_center is False
64
+
65
+
66
+ # ═══════════════════════════════════════════════════════════════════════════
67
+ # 2. Real drags in a real browser — the only place the constraint is enforced
68
+ # ═══════════════════════════════════════════════════════════════════════════
69
+
70
+ _OVERLAY_RECT_JS = """() => {
71
+ for (const cv of document.querySelectorAll('canvas')) {
72
+ if (getComputedStyle(cv).pointerEvents === 'all') {
73
+ const r = cv.getBoundingClientRect();
74
+ return {left:r.left, top:r.top, w:r.width, h:r.height};
75
+ }
76
+ }
77
+ return null;
78
+ }"""
79
+
80
+
81
+ def _img_to_page(page, ix, iy, iw=32, ih=32):
82
+ """Image px → page coords, for an overlay laid out 'contain' with no zoom."""
83
+ r = page.evaluate(_OVERLAY_RECT_JS)
84
+ assert r is not None, "overlay canvas not found"
85
+ s = min(r["w"] / iw, r["h"] / ih)
86
+ ox = (r["w"] - iw * s) / 2.0
87
+ oy = (r["h"] - ih * s) / 2.0
88
+ return r["left"] + ox + ix * s, r["top"] + oy + iy * s
89
+
90
+
91
+ def _setup(interact_page, lock_center):
92
+ fig, ax = apl.subplots(1, 1, figsize=(FIG_W, FIG_H))
93
+ v = ax.imshow(np.zeros((32, 32), dtype=np.float32))
94
+ v.add_circle_widget(cx=16, cy=16, r=6, color="#ff0000",
95
+ lock_center=lock_center)
96
+ page = interact_page(fig)
97
+ _collect_events(page)
98
+ return page
99
+
100
+
101
+ def _drag(page, frm, to, steps=8):
102
+ page.mouse.move(*_img_to_page(page, *frm))
103
+ page.mouse.down()
104
+ page.mouse.move(*_img_to_page(page, *to), steps=steps)
105
+ page.mouse.up()
106
+ page.wait_for_timeout(80)
107
+
108
+
109
+ @pytest.mark.usefixtures("interact_page")
110
+ class TestLockedCentreDrag:
111
+ def test_body_drag_does_not_move_a_locked_circle(self, interact_page):
112
+ """THE regression. Grab the centre and pull: nothing about the widget
113
+ may change — not even transiently, which is why the assertion is on
114
+ every event and not only the last one."""
115
+ page = _setup(interact_page, lock_center=True)
116
+
117
+ _drag(page, (16, 16), (24, 24))
118
+
119
+ moved = [e for e in _get_events(page)
120
+ if e.get("cx") is not None
121
+ and (abs(e["cx"] - 16.0) > 0.5 or abs(e["cy"] - 16.0) > 0.5)]
122
+ assert not moved, (
123
+ f"a locked circle's centre moved during the drag: {moved[:3]}")
124
+
125
+ def test_an_unlocked_circle_still_moves(self, interact_page):
126
+ """The counterpart, so a lock that accidentally applied to every circle
127
+ would fail here rather than passing the whole file."""
128
+ page = _setup(interact_page, lock_center=False)
129
+
130
+ _drag(page, (16, 16), (20, 22))
131
+
132
+ last = _get_events(page, "pointer_up")[-1]
133
+ assert last["cx"] > 16.0 and last["cy"] > 16.0
134
+ assert last["r"] == pytest.approx(6.0, abs=0.5)
135
+
136
+ def test_the_radius_handle_still_drags_when_locked(self, interact_page):
137
+ """A locked centre must not cost the measurement. The handle sits at
138
+ the east point (cx+r, cy) = (22, 16); drag it out to (26, 16)."""
139
+ page = _setup(interact_page, lock_center=True)
140
+
141
+ _drag(page, (22, 16), (26, 16))
142
+
143
+ ups = _get_events(page, "pointer_up")
144
+ assert ups, "the radius drag should still emit a pointer_up"
145
+ last = ups[-1]
146
+ assert last["r"] > 6.0, "the radius handle stopped working under the lock"
147
+ assert last["cx"] == pytest.approx(16.0, abs=0.5)
148
+ assert last["cy"] == pytest.approx(16.0, abs=0.5)
149
+
150
+ def test_a_locked_ring_band_is_not_grabbable_either(self, interact_page):
151
+ """Not just the centre hotspot: the ring BAND is the circle's move
152
+ target, and it is the part a user actually grabs. (22, 16) is the
153
+ handle, so aim at the west point (10, 16) — same band, no handle."""
154
+ page = _setup(interact_page, lock_center=True)
155
+
156
+ _drag(page, (10, 16), (4, 16))
157
+
158
+ moved = [e for e in _get_events(page)
159
+ if e.get("cx") is not None and abs(e["cx"] - 16.0) > 0.5]
160
+ assert not moved, f"the ring band dragged the locked circle: {moved[:3]}"
@@ -74,13 +74,29 @@ class CircleWidget(Widget):
74
74
  Outline stroke width in px. Default 2.
75
75
  show_handles : bool, optional
76
76
  Draw the radius grab handle. Default ``True``.
77
+ lock_center : bool, optional
78
+ Pin ``cx, cy`` and let only the radius change. Default ``False``.
79
+
80
+ The centre is refused at HIT-TEST time, not corrected afterwards: a
81
+ grab on the ring body is simply not a widget grab, so it falls through
82
+ to the plot's own pan and the hover cursor never promises a move.
83
+ Enforcing it from Python instead — snapping the centre back when the
84
+ drag settles — cannot work, because the JS drag recomputes the position
85
+ from its own grab-time snapshot on every frame; the ring tracks the
86
+ cursor for the whole drag and only jumps back on release.
87
+
88
+ Use it when the centre is not a free parameter — a ring on a power
89
+ spectrum is centred on the DC term, so a draggable centre is a control
90
+ that can only ever be wrong, and one nudged off-centre silently
91
+ corrupts every radius measured from it.
77
92
  """
78
93
  def __init__(self, push_fn, *, cx, cy, r, color="#00e5ff",
79
- linewidth=2, show_handles=True):
94
+ linewidth=2, show_handles=True, lock_center=False):
80
95
  super().__init__("circle", push_fn,
81
96
  cx=float(cx), cy=float(cy), r=float(r), color=color,
82
97
  linewidth=float(linewidth),
83
- show_handles=bool(show_handles))
98
+ show_handles=bool(show_handles),
99
+ lock_center=bool(lock_center))
84
100
 
85
101
 
86
102
  class AnnularWidget(Widget):
@@ -4,12 +4,12 @@
4
4
  <meta charset="utf-8" />
5
5
  <title>anyplotlib – redirecting…</title>
6
6
  <!-- Redirect to the latest dev docs. -->
7
- <meta http-equiv="refresh" content="0; url=v0.7.2/" />
8
- <link rel="canonical" href="v0.7.2/" />
7
+ <meta http-equiv="refresh" content="0; url=v0.7.3/" />
8
+ <link rel="canonical" href="v0.7.3/" />
9
9
  </head>
10
10
  <body>
11
11
  <p>
12
- Redirecting to <a href="v0.7.2/">v0.7.2 documentation</a>…
12
+ Redirecting to <a href="v0.7.3/">v0.7.3 documentation</a>…
13
13
  </p>
14
14
  </body>
15
15
  </html>
@@ -4,6 +4,11 @@
4
4
  "version": "dev",
5
5
  "url": "https://cssfrancis.github.io/anyplotlib/dev/"
6
6
  },
7
+ {
8
+ "name": "v0.7.3 (stable)",
9
+ "version": "v0.7.3",
10
+ "url": "https://cssfrancis.github.io/anyplotlib/v0.7.3/"
11
+ },
7
12
  {
8
13
  "name": "v0.7.2 (stable)",
9
14
  "version": "v0.7.2",
@@ -17,7 +17,7 @@ from _sg_html_scraper import ViewerScraper # noqa: E402
17
17
  project = "anyplotlib"
18
18
  copyright = "2026, anyplotlib contributors"
19
19
  author = "anyplotlib contributors"
20
- release = "0.7.2"
20
+ release = "0.7.3"
21
21
 
22
22
  # When built in CI the workflow sets DOCS_VERSION to the tag name (e.g.
23
23
  # "v0.1.0") or "dev". Fall back to "dev" for local builds.
@@ -13,7 +13,7 @@ exclude = [
13
13
 
14
14
  [project]
15
15
  name = "anyplotlib"
16
- version = "0.7.2"
16
+ version = "0.7.3"
17
17
  description = "A plotting library using python, javascript and anywidget for performant in browser plotting."
18
18
  readme = "README.md"
19
19
  license = { text = "MIT" }
File without changes
File without changes
File without changes
File without changes
File without changes