Skip to content

Commit 2186b0b

Browse files
authored
Merge pull request #25 from CSSFrancis/release/v0.1.0
docs: add curated RELEASE_NOTES.md; use it for GitHub Release notes
2 parents 0c8d0a9 + 3ef70d4 commit 2186b0b

2 files changed

Lines changed: 108 additions & 8 deletions

File tree

‎.github/workflows/release.yml‎

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -82,18 +82,24 @@ jobs:
8282
name: dist
8383
path: dist/
8484

85-
- name: Extract release notes from CHANGELOG.rst
85+
- name: Prepare release notes
8686
env:
8787
TAG: ${{ github.ref_name }}
8888
shell: python
8989
run: |
9090
import re, pathlib, os
91-
tag = os.environ["TAG"]
92-
text = pathlib.Path("CHANGELOG.rst").read_text()
93-
parts = re.split(r"(?m)(?=^\S[^\n]*\n=+\n)", text)
94-
notes = next((p.strip() for p in parts if p.strip().startswith(tag)), None)
95-
fallback = "Release " + tag + "\n" + "=" * (len(tag) + 8) + "\n\nSee CHANGELOG.rst."
96-
pathlib.Path("release_notes.rst").write_text(notes if notes else fallback)
91+
tag = os.environ["TAG"]
92+
# Prefer the curated RELEASE_NOTES.md; fall back to the matching
93+
# CHANGELOG.rst section, then to a generic stub.
94+
curated = pathlib.Path("RELEASE_NOTES.md")
95+
if curated.exists():
96+
notes = curated.read_text()
97+
else:
98+
text = pathlib.Path("CHANGELOG.rst").read_text()
99+
parts = re.split(r"(?m)(?=^\S[^\n]*\n=+\n)", text)
100+
section = next((p.strip() for p in parts if p.strip().startswith(tag)), None)
101+
notes = section or ("Release " + tag + "\n\nSee CHANGELOG.rst.")
102+
pathlib.Path("release_notes.md").write_text(notes)
97103
98104
- name: Create GitHub Release
99105
env:
@@ -104,6 +110,6 @@ jobs:
104110
if [[ "$TAG" == *b* ]]; then PRERELEASE_FLAG="--prerelease"; fi
105111
gh release create "$TAG" \
106112
--title "$TAG" \
107-
--notes-file release_notes.rst \
113+
--notes-file release_notes.md \
108114
$PRERELEASE_FLAG \
109115
dist/*

‎RELEASE_NOTES.md‎

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# anyplotlib 0.1.0 — first public release
2+
3+
**anyplotlib** is a plotting library built on Python, JavaScript, and
4+
[anywidget](https://anywidget.dev/) for fast, interactive, in-browser plotting
5+
— in Jupyter, in the docs, and (new in this release) embedded in standalone apps.
6+
7+
This first release ships the full core: `Figure`, `Axes`, `GridSpec` /
8+
`subplots`, the `Plot1D` / `Plot2D` / `PlotMesh` / `Plot3D` / `PlotBar` /
9+
`PlotXY` plot types, a marker system, interactive overlay widgets, and a
10+
two-tier callback registry.
11+
12+
## Install
13+
14+
```bash
15+
pip install anyplotlib
16+
```
17+
18+
Requires Python ≥ 3.10. Optional extras: `anyplotlib[jupyter]`, `anyplotlib[docs]`.
19+
20+
## Highlights
21+
22+
### New plotting surfaces
23+
- **`PlotXY` / `Axes.axes2d`** — a blank data-coordinate 2-D axis (matplotlib
24+
`transData` + `PathCollection` model). Draw `scatter` / `plot` / `fill` /
25+
`text` in data coords with per-point face/edge colours and `aspect="equal"`
26+
— the surface for stereographic / IPF / pole-figure plots (e.g. an `orix`
27+
backend).
28+
- **Fast density heatmaps** — `PlotXY.pcolormesh` rasterizes regular meshes into
29+
a single blit whose cost is independent of cell count (a 256×256 heatmap draws
30+
as fast as 32×32). The underlying `add_raster` primitive draws an RGBA image
31+
between data-coord corners, with an optional `clip_path` polygon and a
32+
`smooth=True` bilinear option.
33+
- **`InsetAxes`** — floating overlay sub-plots above the main grid (`add_inset`),
34+
supporting every plot type plus interactive minimise / maximise / restore.
35+
- **True-colour `imshow`** — `(H, W, 3|4)` arrays now render as RGB(A) instead of
36+
silently dropping channels.
37+
38+
### 3-D and GPU
39+
- **`Axes.voxels()`** — volumes as shaded translucent cubes, with a draggable
40+
`PlaneWidget` slice selector that fires `pointer_move` / `pointer_up`
41+
callbacks; voxels on the slice plane glow.
42+
- **WebGPU acceleration** — `scatter3d` and `voxels` render on the GPU via WebGPU
43+
when available (`gpu="auto"`), with a transparent, identical-looking Canvas2D
44+
fallback when no GPU is present. Query the active path with `plot.gpu_active`.
45+
- **Richer 3-D scatter** — per-point `colors=`, a `bounds=` override, single-point
46+
`set_highlight()`, a shaded reference `set_sphere()`, and a proper turntable
47+
camera (matplotlib `azim` / `elev` semantics).
48+
49+
### Interactivity
50+
- **Touch support** — one-finger pan / orbit / drag, two-finger pinch zoom, and
51+
double-tap, on iPad / iPhone / trackpads, with no API change.
52+
- **Data-coordinate picking** — `double_click` on 1-D / `PlotXY` panels now
53+
reports `ydata` alongside `xdata`.
54+
- **Typography** — `fontsize` on labels/titles/colorbars, `set_tick_label_size()`,
55+
and a mini-TeX subset inside `$...$` (super/subscripts, Greek, `\times`, `\AA`,
56+
`\degree`) rendered natively on the canvas; text is no longer clipped.
57+
58+
### Performance
59+
- **Geometry sync channel** — large, slow-changing buffers are deduped by content
60+
hash, so view-only updates (highlight, view, zoom, widget drags) no longer
61+
re-transmit geometry.
62+
- **`Figure.batch()`** — coalesces panel pushes so linked-view handlers send one
63+
update per changed panel instead of one per mutation.
64+
- **Voxels ~2–3× faster** via sprite caching and cached projection/depth-sort.
65+
- **~50× faster Pyodide dispatch** — interactions call a pre-compiled dispatcher
66+
instead of recompiling Python source every frame, so the interactive docs keep
67+
up with gestures.
68+
69+
### Embedding outside Jupyter
70+
- `fig.save_html()` / `fig.to_html()` export a self-contained interactive page.
71+
- `figure_esm.js` exports a `mount(el, state, opts)` entry point for direct JS
72+
embedding (Electron, MDI windows, plain web pages) with `onEvent` callbacks,
73+
live `setPanelState`, `resize`, and `dispose`.
74+
- The new `anyplotlib.embed` module provides `figure_state()`, `esm_path()`, and a
75+
transport-agnostic `FigureBridge` for two-way Python ↔ JS sync over any pipe.
76+
77+
### Documentation tooling
78+
- The `anyplotlib.sphinx_anywidget` extension renders live, Pyodide-powered
79+
figures in the docs (`.. anywidget-figure::` directive, automatic wheel
80+
building, Sphinx Gallery integration).
81+
82+
## Bug fixes
83+
- 3-D plane-widget drags no longer snap back (view-only pushes stop clobbering an
84+
in-progress drag).
85+
- GPU 3-D panels self-heal when a WebGPU device is lost mid-draw (no more vanishing
86+
voxels/axes until a window resize) — seen on Safari's experimental WebGPU.
87+
- Large voxel volumes no longer render "empty" in WebGPU browsers (e.g. PyCharm's
88+
JCEF) — the plot canvas background is now transparent while the GPU path is active.
89+
- The 3-D voxel highlight no longer floats onto random voxels in large volumes.
90+
- Interactive (⚡) docs figures are much smoother under Pyodide (the ~50× dispatch
91+
speedup above).
92+
93+
## Full changelog
94+
See [`CHANGELOG.rst`](./CHANGELOG.rst) for the complete, detailed list.

0 commit comments

Comments
 (0)