anyplotlib 0.5.0__tar.gz → 0.7.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.
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/.github/workflows/docs.yml +17 -3
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/.gitignore +11 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/AGENTS.md +25 -2
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/CHANGELOG.rst +76 -0
- anyplotlib-0.7.0/Examples/Interactive/plot_star_globe_explorer.py +341 -0
- anyplotlib-0.7.0/Examples/PlotTypes/plot_3d_texture.py +344 -0
- anyplotlib-0.7.0/Examples/PlotTypes/plot_key_overlays.py +169 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/PKG-INFO +3 -2
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/README.md +2 -1
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/FIGURE_ESM.md +117 -33
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/__init__.py +4 -2
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/_base_plot.py +166 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/_utils.py +86 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/axes/_axes.py +20 -2
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/callbacks.py +6 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/figure/_figure.py +2 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/figure_esm.js +1171 -93
- anyplotlib-0.7.0/anyplotlib/keys.py +252 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/plot2d/_plot2d.py +75 -4
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/plot3d/_plot3d.py +199 -4
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/plotxy/_plotxy.py +1 -1
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/conftest.py +135 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_examples/test_interactive_examples.py +1 -0
- anyplotlib-0.7.0/anyplotlib/tests/test_interactive/test_orbit_direction.py +219 -0
- anyplotlib-0.7.0/anyplotlib/tests/test_interactive/test_star_globe_linking.py +187 -0
- anyplotlib-0.7.0/anyplotlib/tests/test_interactive/test_widget_brush.py +777 -0
- anyplotlib-0.7.0/anyplotlib/tests/test_keys/test_keys.py +413 -0
- anyplotlib-0.7.0/anyplotlib/tests/test_plot3d/test_gpu_depth.py +139 -0
- anyplotlib-0.7.0/anyplotlib/tests/test_plot3d/test_texture.py +636 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/widgets/__init__.py +2 -1
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/widgets/_widgets2d.py +240 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/docs/_root/index.html +3 -3
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/docs/_root/switcher.json +10 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/docs/api/index.rst +1 -0
- anyplotlib-0.7.0/docs/api/keys.rst +42 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/docs/api/widgets.rst +7 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/docs/conf.py +1 -1
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/pyproject.toml +8 -1
- anyplotlib-0.7.0/upcoming_changes/.gitkeep +3 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/upcoming_changes/README.rst +5 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/uv.lock +1 -1
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/.github/workflows/ci.yml +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/.github/workflows/prepare_release.yml +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/.github/workflows/release.yml +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/.github/workflows/tests.yml +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Benchmarks/README.rst +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Benchmarks/plot_benchmark_comparison.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Interactive/README.rst +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Interactive/plot_3d_spectral_viewer.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Interactive/plot_eels_explorer.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Interactive/plot_figure_annotations.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Interactive/plot_interactive_fft.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Interactive/plot_interactive_fitting.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Interactive/plot_ipf_density_map.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Interactive/plot_ipf_explorer.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Interactive/plot_key_bindings.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Interactive/plot_particle_picker.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Interactive/plot_point_widget.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Interactive/plot_segment_by_contrast.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Interactive/plot_segment_by_contrast_advanced.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Interactive/plot_spectra_roi_inspector.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Interactive/plot_threshold_explorer.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Interactive/plot_voxel_grain_explorer.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Markers/README.rst +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Markers/plot_arrows.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Markers/plot_circles.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Markers/plot_ellipses.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Markers/plot_horizontal_lines.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Markers/plot_line_segments.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Markers/plot_points.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Markers/plot_polygons.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Markers/plot_rectangles.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Markers/plot_squares.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Markers/plot_texts.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Markers/plot_transform_markers.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Markers/plot_vertical_lines.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/PlotTypes/README.rst +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/PlotTypes/plot_3d.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/PlotTypes/plot_bar.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/PlotTypes/plot_gpu_voxels.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/PlotTypes/plot_gridspec_custom.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/PlotTypes/plot_image2d.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/PlotTypes/plot_inset.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/PlotTypes/plot_label_formatting.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/PlotTypes/plot_line_styles.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/PlotTypes/plot_pcolormesh.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/PlotTypes/plot_spectra1d.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/PlotTypes/plot_step_and_log.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/PlotTypes/plot_tile_backend.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/PlotTypes/plot_twinx.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/README.rst +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Widgets/README.rst +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Widgets/plot_widget1d_hline.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Widgets/plot_widget1d_point.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Widgets/plot_widget1d_range.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Widgets/plot_widget1d_vline.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Widgets/plot_widget2d_annular.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Widgets/plot_widget2d_arrow.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Widgets/plot_widget2d_circle.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Widgets/plot_widget2d_crosshair.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Widgets/plot_widget2d_label.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Widgets/plot_widget2d_polygon.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Widgets/plot_widget2d_rectangle.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Examples/Widgets/plot_widget3d_plane.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/LICENSE +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/Makefile +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/_binary_frame.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/_electron.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/_repr_utils.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/axes/__init__.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/axes/_inset_axes.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/conftest.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/embed.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/figure/__init__.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/figure/_gridspec.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/figure/_subplots.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/markers.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/plot1d/__init__.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/plot1d/_plot1d.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/plot1d/_plotbar.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/plot2d/__init__.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/plot2d/_layer.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/plot2d/_plotmesh.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/plot2d/_tile_backend.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/plot3d/__init__.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/plotxy/__init__.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/sphinx_anywidget/__init__.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/sphinx_anywidget/_directive.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/sphinx_anywidget/_repr_utils.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/sphinx_anywidget/_scraper.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/sphinx_anywidget/_wheel_builder.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/sphinx_anywidget/static/anywidget_bridge.js +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/sphinx_anywidget/static/anywidget_overlay.css +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/sphinx_anywidget/tests/__init__.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/sphinx_anywidget/tests/conftest.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/sphinx_anywidget/tests/test_directive.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/sphinx_anywidget/tests/test_init.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/sphinx_anywidget/tests/test_repr_utils.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/sphinx_anywidget/tests/test_scraper.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/sphinx_anywidget/tests/test_wheel_builder.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/__init__.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/_gpu_clim_check.cjs +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/_png_utils.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/bar_basic.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/gridspec_3col_equal_spectra.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/gridspec_asymmetric_width_ratios.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/gridspec_height_ratio_image_histogram.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/gridspec_image_two_spectra.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/gridspec_side_by_side_1d.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/gridspec_spanning_top_two_bottom.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/imshow_axis_off.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/imshow_checkerboard.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/imshow_gradient.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/imshow_labels.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/imshow_viridis.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/inset_1d.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/inset_maximized.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/inset_minimized.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/inset_normal_2d.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/inset_stacked.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/inset_stacked_one_minimized.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/pcolormesh_uniform.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/plot1d_all_linestyles.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/plot1d_alpha.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/plot1d_axis_off.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/plot1d_dashed.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/plot1d_marker_symbols.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/plot1d_markers.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/plot1d_multi.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/plot1d_sine.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/plot1d_title.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/plot1d_twinx.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/plot3d_surface.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/baselines/subplots_2x1.png +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_benchmarks/__init__.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_benchmarks/baselines.json +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_benchmarks/benchmarks/baselines.json +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_benchmarks/test_benchmarks.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_benchmarks/test_benchmarks_py.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_documentation/__init__.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_documentation/test_bridge.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_documentation/test_push_hook.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_documentation/test_scraper.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_electron/__init__.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_electron/test_binary_frame.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_embed/__init__.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_embed/test_embed_api.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_embed/test_embed_mount.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_embed/test_export_png.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_embed/test_export_widget_sync.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_examples/__init__.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_interactive/__init__.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_interactive/_event_test_utils.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_interactive/test_blit_audit.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_interactive/test_callbacks_playwright.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_interactive/test_callbacks_unit.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_interactive/test_edit_chrome.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_interactive/test_edit_chrome_playwright.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_interactive/test_event_pause_hold.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_interactive/test_event_plots.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_interactive/test_event_settled.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_interactive/test_events_regression.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_interactive/test_pointer_down_1d.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_interactive/test_range_orientation_snap.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_interactive/test_tile_parity_playwright.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_interactive/test_title.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_interactive/test_touch.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_interactive/test_widget_max_extent.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_interactive/test_widget_set_notify.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_interactive/test_widgets.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_interactive/test_widgets_line_2d.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_labels/__init__.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_labels/test_label_api.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_labels/test_label_rendering.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_labels/test_no_clipping.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_labels/test_ylabel_clearance.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_layouts/__init__.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_layouts/test_batch.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_layouts/test_coord_conversion.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_layouts/test_geom_channel.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_layouts/test_gridspec.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_layouts/test_inset.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_layouts/test_inset_callout.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_layouts/test_interaction.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_layouts/test_visual.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_markers/__init__.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_markers/test_marker_transforms.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_markers/test_markers.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_markers/test_per_marker_colors.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_markers/test_size_units.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot1d/__init__.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot1d/test_line_none_rendering.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot1d/test_plot1d.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot1d/test_plotbar.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot2d/__init__.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot2d/test_colorbar_gap_scalebar.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot2d/test_detail_tile.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot2d/test_first_paint_race.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot2d/test_gpu_image.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot2d/test_gpu_parity_playwright.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot2d/test_imshow.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot2d/test_imshow_rgb.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot2d/test_layers.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot2d/test_layers_playwright.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot2d/test_pcolormesh.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot2d/test_plot2d_api.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot2d/test_rapid_frame_update.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot2d/test_tile_backend.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot2d/test_tile_diagnostics.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot2d/test_tiled_imshow.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot2d/test_zoom_view_write.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot3d/__init__.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot3d/test_colors_highlight.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot3d/test_gpu_fallback.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot3d/test_gpu_hidden_reveal.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot3d/test_plot3d.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plot3d/test_voxels_planes.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/tests/test_plotxy/test_plotxy.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/widgets/_base.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/widgets/_widgets1d.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/anyplotlib/widgets/_widgets3d.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/docs/_sg_html_scraper.py +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/docs/_static/anyplotlib.svg +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/docs/_static/custom.css +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/docs/_static/pyodide_bridge.js +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/docs/_templates/autosummary/class.rst +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/docs/api/callbacks.rst +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/docs/api/figure.rst +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/docs/api/figure_plots.rst +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/docs/api/markers.rst +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/docs/benchmarking.rst +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/docs/dev/index.rst +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/docs/embedding.rst +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/docs/events.rst +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/docs/getting_started.rst +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.0}/docs/index.rst +0 -0
- {anyplotlib-0.5.0 → anyplotlib-0.7.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
|
-
#
|
|
15
|
-
#
|
|
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 (~
|
|
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 (~
|
|
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,82 @@ Fragment files in ``upcoming_changes/`` are assembled into this file by
|
|
|
10
10
|
|
|
11
11
|
.. towncrier release notes start
|
|
12
12
|
|
|
13
|
+
0.7.0 (2026-07-31)
|
|
14
|
+
==================
|
|
15
|
+
|
|
16
|
+
New Features
|
|
17
|
+
------------
|
|
18
|
+
|
|
19
|
+
- Added :meth:`~anyplotlib.Plot2D.add_key` for pinning a floating image *key* over
|
|
20
|
+
a panel — an inverse pole figure triangle over an orientation map, a hue wheel
|
|
21
|
+
over a polarization field, a phase key over a segmentation. A key is the scale
|
|
22
|
+
bar's sibling: it floats in screen space and neither pans nor zooms with the
|
|
23
|
+
data, it takes an RGBA image so a triangle or a disc needs no rectangular card
|
|
24
|
+
around it, and ``labels=`` annotates the picture itself (an IPF triangle's
|
|
25
|
+
corner indices) in fractions of the key image. Optional ``bgcolor`` /
|
|
26
|
+
``border`` / ``alpha`` give it a card when the data underneath is busy, and
|
|
27
|
+
``hover_only=True`` reveals it only while the pointer is over the panel.
|
|
28
|
+
Available on every panel type, and included in PNG export.
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
0.6.0 (2026-07-31)
|
|
32
|
+
==================
|
|
33
|
+
|
|
34
|
+
API and Behaviour Changes
|
|
35
|
+
-------------------------
|
|
36
|
+
|
|
37
|
+
- **3-D orbit drags have reversed direction.** Both azimuth and elevation now
|
|
38
|
+
move the geometry *with* the cursor instead of away from it, matching
|
|
39
|
+
matplotlib's ``mplot3d`` and every other turntable control — dragging right
|
|
40
|
+
spins a globe right. Azimuth and elevation position the *camera*, so adding
|
|
41
|
+
the drag delta swept the surface the opposite way, as if you had grabbed its
|
|
42
|
+
far side. Any muscle memory (or scripted pointer drag) built against the old
|
|
43
|
+
direction is inverted; panels driven from Python with
|
|
44
|
+
:meth:`~anyplotlib.Plot3D.set_view` are unaffected.
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
New Features
|
|
48
|
+
------------
|
|
49
|
+
|
|
50
|
+
- :class:`~anyplotlib.Event` now carries ``azimuth`` and ``elevation`` for 3-D
|
|
51
|
+
orbit events. The renderer had always emitted them alongside ``zoom``, but they
|
|
52
|
+
were dropped on the way to Python — and since a JS-side drag does not sync back
|
|
53
|
+
into ``Plot3D._state``, a handler had no way to react to an orbit at all. See
|
|
54
|
+
the new ``Star Globe Explorer`` gallery example, which links a celestial sphere
|
|
55
|
+
to a sky map through them.
|
|
56
|
+
- Added :meth:`~anyplotlib.Plot2D.add_brush_widget` for painting freehand
|
|
57
|
+
multi-class label strokes on a 2-D image with Shift-drag, leaving a bare drag to
|
|
58
|
+
pan as before.
|
|
59
|
+
- Added :meth:`~anyplotlib.Plot3D.set_texture` for wrapping an image around a 3-D
|
|
60
|
+
surface — a globe, a planet, or a star chart on the celestial sphere — with
|
|
61
|
+
optional diffuse shading and backface culling. ``Axes.plot_surface`` gained
|
|
62
|
+
``texture=``, ``bounds=``, and ``gpu=`` to match. Textured surfaces render on
|
|
63
|
+
WebGPU when it is available (roughly 9k triangles at 54 ms/frame on Canvas2D
|
|
64
|
+
versus 0.4 ms on the GPU), falling back to Canvas2D silently otherwise.
|
|
65
|
+
``set_axis_off()`` now also hides a 3-D panel's axis lines, labels, and ticks.
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
Bug Fixes
|
|
69
|
+
---------
|
|
70
|
+
|
|
71
|
+
- Fixed WebGPU 3-D geometry being clipped at the corners of a cube-shaped
|
|
72
|
+
dataset. The clip-space depth scale let ``clip.z`` reach 1.09 for a point at
|
|
73
|
+
the far corner of the normalised bounds box, outside the ``[0, 1]`` range
|
|
74
|
+
WebGPU keeps, so the nearest corner of a dense :meth:`~anyplotlib.Axes.scatter3d`
|
|
75
|
+
cloud silently vanished at the default camera angles. Spherical geometry was
|
|
76
|
+
never affected.
|
|
77
|
+
- Fixed an inverted depth comparison in the WebGPU 3-D projection: a GPU-rendered
|
|
78
|
+
:meth:`~anyplotlib.Axes.scatter3d` cloud drew its far points on top of its near
|
|
79
|
+
ones wherever two points overlapped on screen. Voxel panels were unaffected
|
|
80
|
+
(they disable depth writes), and the Canvas2D path was always correct.
|
|
81
|
+
- Fixed the docs deployment racing itself on release. A push to ``main`` and its
|
|
82
|
+
release tag are different refs, so the ``docs-${{ github.ref }}`` concurrency
|
|
83
|
+
group put them in separate groups and both pushed to ``gh-pages`` at once; the
|
|
84
|
+
loser was rejected and its versioned directory never appeared, while
|
|
85
|
+
``switcher.json`` still advertised the version. The deploy job now uses a
|
|
86
|
+
ref-independent group so deployments queue instead.
|
|
87
|
+
|
|
88
|
+
|
|
13
89
|
0.5.0 (2026-07-26)
|
|
14
90
|
==================
|
|
15
91
|
|
|
@@ -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
|