Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 14 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,18 +82,24 @@ jobs:
name: dist
path: dist/

- name: Extract release notes from CHANGELOG.rst
- name: Prepare release notes
env:
TAG: ${{ github.ref_name }}
shell: python
run: |
import re, pathlib, os
tag = os.environ["TAG"]
text = pathlib.Path("CHANGELOG.rst").read_text()
parts = re.split(r"(?m)(?=^\S[^\n]*\n=+\n)", text)
notes = next((p.strip() for p in parts if p.strip().startswith(tag)), None)
fallback = "Release " + tag + "\n" + "=" * (len(tag) + 8) + "\n\nSee CHANGELOG.rst."
pathlib.Path("release_notes.rst").write_text(notes if notes else fallback)
tag = os.environ["TAG"]
# Prefer the curated RELEASE_NOTES.md; fall back to the matching
# CHANGELOG.rst section, then to a generic stub.
curated = pathlib.Path("RELEASE_NOTES.md")
if curated.exists():
notes = curated.read_text()
else:
text = pathlib.Path("CHANGELOG.rst").read_text()
parts = re.split(r"(?m)(?=^\S[^\n]*\n=+\n)", text)
section = next((p.strip() for p in parts if p.strip().startswith(tag)), None)
notes = section or ("Release " + tag + "\n\nSee CHANGELOG.rst.")
pathlib.Path("release_notes.md").write_text(notes)

- name: Create GitHub Release
env:
Expand All @@ -104,6 +110,6 @@ jobs:
if [[ "$TAG" == *b* ]]; then PRERELEASE_FLAG="--prerelease"; fi
gh release create "$TAG" \
--title "$TAG" \
--notes-file release_notes.rst \
--notes-file release_notes.md \
$PRERELEASE_FLAG \
dist/*
94 changes: 94 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# anyplotlib 0.1.0 — first public release

**anyplotlib** is a plotting library built on Python, JavaScript, and
[anywidget](https://anywidget.dev/) for fast, interactive, in-browser plotting
— in Jupyter, in the docs, and (new in this release) embedded in standalone apps.

This first release ships the full core: `Figure`, `Axes`, `GridSpec` /
`subplots`, the `Plot1D` / `Plot2D` / `PlotMesh` / `Plot3D` / `PlotBar` /
`PlotXY` plot types, a marker system, interactive overlay widgets, and a
two-tier callback registry.

## Install

```bash
pip install anyplotlib
```

Requires Python ≥ 3.10. Optional extras: `anyplotlib[jupyter]`, `anyplotlib[docs]`.

## Highlights

### New plotting surfaces
- **`PlotXY` / `Axes.axes2d`** — a blank data-coordinate 2-D axis (matplotlib
`transData` + `PathCollection` model). Draw `scatter` / `plot` / `fill` /
`text` in data coords with per-point face/edge colours and `aspect="equal"`
— the surface for stereographic / IPF / pole-figure plots (e.g. an `orix`
backend).
- **Fast density heatmaps** — `PlotXY.pcolormesh` rasterizes regular meshes into
a single blit whose cost is independent of cell count (a 256×256 heatmap draws
as fast as 32×32). The underlying `add_raster` primitive draws an RGBA image
between data-coord corners, with an optional `clip_path` polygon and a
`smooth=True` bilinear option.
- **`InsetAxes`** — floating overlay sub-plots above the main grid (`add_inset`),
supporting every plot type plus interactive minimise / maximise / restore.
- **True-colour `imshow`** — `(H, W, 3|4)` arrays now render as RGB(A) instead of
silently dropping channels.

### 3-D and GPU
- **`Axes.voxels()`** — volumes as shaded translucent cubes, with a draggable
`PlaneWidget` slice selector that fires `pointer_move` / `pointer_up`
callbacks; voxels on the slice plane glow.
- **WebGPU acceleration** — `scatter3d` and `voxels` render on the GPU via WebGPU
when available (`gpu="auto"`), with a transparent, identical-looking Canvas2D
fallback when no GPU is present. Query the active path with `plot.gpu_active`.
- **Richer 3-D scatter** — per-point `colors=`, a `bounds=` override, single-point
`set_highlight()`, a shaded reference `set_sphere()`, and a proper turntable
camera (matplotlib `azim` / `elev` semantics).

### Interactivity
- **Touch support** — one-finger pan / orbit / drag, two-finger pinch zoom, and
double-tap, on iPad / iPhone / trackpads, with no API change.
- **Data-coordinate picking** — `double_click` on 1-D / `PlotXY` panels now
reports `ydata` alongside `xdata`.
- **Typography** — `fontsize` on labels/titles/colorbars, `set_tick_label_size()`,
and a mini-TeX subset inside `$...$` (super/subscripts, Greek, `\times`, `\AA`,
`\degree`) rendered natively on the canvas; text is no longer clipped.

### Performance
- **Geometry sync channel** — large, slow-changing buffers are deduped by content
hash, so view-only updates (highlight, view, zoom, widget drags) no longer
re-transmit geometry.
- **`Figure.batch()`** — coalesces panel pushes so linked-view handlers send one
update per changed panel instead of one per mutation.
- **Voxels ~2–3× faster** via sprite caching and cached projection/depth-sort.
- **~50× faster Pyodide dispatch** — interactions call a pre-compiled dispatcher
instead of recompiling Python source every frame, so the interactive docs keep
up with gestures.

### Embedding outside Jupyter
- `fig.save_html()` / `fig.to_html()` export a self-contained interactive page.
- `figure_esm.js` exports a `mount(el, state, opts)` entry point for direct JS
embedding (Electron, MDI windows, plain web pages) with `onEvent` callbacks,
live `setPanelState`, `resize`, and `dispose`.
- The new `anyplotlib.embed` module provides `figure_state()`, `esm_path()`, and a
transport-agnostic `FigureBridge` for two-way Python ↔ JS sync over any pipe.

### Documentation tooling
- The `anyplotlib.sphinx_anywidget` extension renders live, Pyodide-powered
figures in the docs (`.. anywidget-figure::` directive, automatic wheel
building, Sphinx Gallery integration).

## Bug fixes
- 3-D plane-widget drags no longer snap back (view-only pushes stop clobbering an
in-progress drag).
- GPU 3-D panels self-heal when a WebGPU device is lost mid-draw (no more vanishing
voxels/axes until a window resize) — seen on Safari's experimental WebGPU.
- Large voxel volumes no longer render "empty" in WebGPU browsers (e.g. PyCharm's
JCEF) — the plot canvas background is now transparent while the GPU path is active.
- The 3-D voxel highlight no longer floats onto random voxels in large volumes.
- Interactive (⚡) docs figures are much smoother under Pyodide (the ~50× dispatch
speedup above).

## Full changelog
See [`CHANGELOG.rst`](./CHANGELOG.rst) for the complete, detailed list.
Loading