Make best legend placement content-aware
Merged
AL
2mo ago+4804+4804+0123456789012345678901234567890123456789-136-136-012345678901234567890123456789
Closes #485
What changed
The existing loc="best" pass only scored sampled point anchors once during payload construction. It could miss line crossings, area fills, marker extents, bar rectangles, and annotations, and the browser had no automatic-placement intent left to reconsider after layout or view changes.
This PR:
- measures the real legend footprint and scores the nine standard in-plot candidates against bounded emitted geometry for line, scatter, area, bar/column/histogram, density, and annotations;
- ships a concrete deterministic fallback plus
auto_loc="best", so older clients and static output remain safe while the live renderer can refine placement; - re-scores a fixed 96×72 rendered-pixel occupancy raster after responsive resize, settled pan/zoom, legend toggles, data updates, and LOD updates—never on every gesture frame and never by rescanning source rows;
- preserves explicit locations, anchors, and polar legend placement exactly;
- remeasures automatic legends against final SVG/raster export dimensions; and
- documents the placement, tie-break, wire, lifecycle, and bounded-work contracts.
Before / after
Same chart, data, 760×440 chart size, 800×500 Chrome viewport, and DPR 1. The baseline is commit 036271ac; both captures used their tree's own standalone client.
| Before — legend obscures the rising line | After — legend moves to the empty corner |
|---|---|
![]() | ![]() |
Capture provenance, DOM locations, dimensions, and SHA-256 hashes
Verification
uv run --with pre-commit pre-commit run --all-filesuv run ruff check .uv run ruff format --check ..venv/bin/ty checknpm run typechecknpm run build- full suite: 4,314 passed, 108 skipped
- focused real-Chromium adaptive-placement suite: 12 passed
Summary by CodeRabbit
-
New Features
- Added content-aware automatic placement for unanchored Cartesian legends using
loc="best". - Legends avoid chart marks and annotations based on rendered geometry.
- Placement updates after resizing, view changes, data updates, and responsive exports.
- Added stable placement behavior with fallback when measurements are unavailable.
- PNG and SVG exports recalculate placement when dimensions change.
- Explicit locations, anchors, and polar legends remain unchanged.
- Added content-aware automatic placement for unanchored Cartesian legends using
-
Documentation
- Added guidance and examples for automatic legend placement and its behavior.

