Skip to content

Keep a decoded raster out of the state a zoom writes back - #76

Merged
CSSFrancis merged 3 commits into
mainfrom
fix/raster-cache-view-writeback
Sep 17, 2026
Merged

CSSFrancis merged 3 commits into
mainfrom
fix/raster-cache-view-writeback

Conversation

@CSSFrancis

Copy link
Copy Markdown
Owner

Zooming or panning a coordinate axis (PlotXY) that holds an add_raster image blanked the panel — the raster and every marker drawn after it — until the next update from Python.

Cause

The raster drawer cached its decoded image on the marker set itself (ms._rasterBmp / ms._rasterKey). The marker set is part of the panel state, and the wheel and pan handlers write that state back through the model, whose change listener redraws from the JSON round trip. A decoded OffscreenCanvas serialises to {}, so after the echo the panel held a truthy "cached image" under a matching key, skipped the decode, and called drawImage({}). That throws; the model swallows listener errors, so the draw stopped after the grid.

Fix

The decoded image lives in a per-panel map keyed by the marker set's id (_rasterBitmap), pruned to the sets still present. Nothing decoded is stored on serialised state any more.

Tests

  • New tests/test_plotxy/test_raster_zoom.py: a red raster on a coordinate axis, three wheel ticks, red pixels read back from the canvases. Fails on main (0 red pixels after the zoom), passes here.
  • tests/test_plotxy, tests/test_markers and tests/test_plot2d/test_zoom_view_write.py pass (196).

Found in SpyDE's orientation-mapping refine window (per-phase IPF heatmaps), where zooming a heatmap blanked it until the crosshair moved; with this file swapped in, the zoomed panel keeps its heatmap, outline, labels and marker.

A raster marker cached its decoded image on the marker set itself, and the
marker set is part of the panel state the wheel and pan handlers serialise back
through the model. A decoded canvas serialises to `{}`, so after the echo the
panel held a truthy "cached image" under a matching key, skipped the decode,
and drawImage threw. The model swallows listener errors, so the draw stopped
after the grid: the raster and every marker drawn after it vanished until the
next push from Python.

The decoded image now lives in a per-panel map keyed by the marker set's id,
pruned to the sets still present.

Found in SpyDE's orientation-mapping refine window, where zooming an IPF
heatmap blanked it until the crosshair moved.
@codecov-commenter

codecov-commenter commented Sep 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.20%. Comparing base (ccdf33e) to head (9658b5c).

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #76      +/-   ##
==========================================
+ Coverage   91.18%   91.20%   +0.02%     
==========================================
  Files          41       41              
  Lines        4880     4880              
==========================================
+ Hits         4450     4451       +1     
+ Misses        430      429       -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The cache key, regression coverage, and renderer documentation require updates.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Fixes PlotXY raster blanking after zoom/pan by moving decoded-image caching out of serialized marker state.

Changes:

  • Adds panel-local raster caching and pruning.
  • Adds a browser regression test.
  • Documents the bug fix.
File summaries
File Summary
upcoming_changes/76.bugfix.rst Documents the raster zoom fix.
anyplotlib/tests/test_plotxy/test_raster_zoom.py Tests raster visibility after zooming; should also verify markers drawn afterward.
anyplotlib/figure_esm.js Implements panel-local caching; cache keys should include dimensions, and the renderer map/note should be updated.
Review details

Suppressed comments (1)

anyplotlib/tests/test_plotxy/test_raster_zoom.py:40

  • This regression test only counts red raster pixels. The reported failure also prevents every marker after the raster from being drawn when drawImage throws, so a future regression could leave the raster visible while still breaking subsequent markers without failing this test. Add a differently colored marker after add_raster and assert its pixels after the wheel round-trip.
    coordinates.add_raster(red, extent=(0.0, 1.0, 0.0, 1.0))
  • Files reviewed: 3/3 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread anyplotlib/figure_esm.js
Comment on lines +7418 to +7433
function _rasterBitmap(p, id, b64, width, height){
if(!p._rasterCache) p._rasterCache=new Map();
const cached=p._rasterCache.get(id);
if(cached && cached.key===b64) return cached.bitmap;
let bitmap=null;
try{
const bin=atob(b64);
const bytes=new Uint8ClampedArray(bin.length);
for(let i=0;i<bin.length;i++) bytes[i]=bin.charCodeAt(i);
bitmap=new OffscreenCanvas(width,height);
bitmap.getContext('2d').putImageData(new ImageData(bytes, width, height),0,0);
}catch(_){ bitmap=null; }
p._rasterCache.set(id, {key:b64, bitmap});
return bitmap;
}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Right: image_b64 is only the bytes, so the same bytes at 1×4 and 2×2 collide. The cache now matches on the bytes and both dimensions (9658b5c).
  • New TestRasterRedecodes changes a raster's shape under the same bytes and checks the redraw. It fails on the previous commit, where the 4×1 draw came from the stale 2×2 decode.

Comment thread anyplotlib/figure_esm.js Outdated
Comment on lines +7415 to +7417
// The decoded image of raster marker set `id`, re-decoded only when its bytes
// change, or null if they cannot be decoded. Kept in a per-panel map rather
// than on the marker set itself, which is serialised state.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Fixed in 9658b5c. The _rasterBitmap insertion moved 62 anchors in FIGURE_ESM.md; I shifted them by the diff hunks and checked every function anchor against its declaration line (no mismatches).
  • _rasterBitmap is now in the 1-D row, and the raster note describes the per-panel cache instead of ms._rasterBmp.
  • AGENTS.md's "last verified" line count is now 12,425.

The per-panel raster cache matched on the bytes alone, but the same bytes are a
different image at a different shape (1x4 against 2x2), so a replaced raster of
the same bytes drew from the stale decode. The cache now matches on the bytes
and both dimensions.

The zoom test also draws a marker after the raster: a raster that throws takes
every later marker down with it, which is what made the original bug look like
a blank panel. A second test swaps a raster's shape under the same bytes and
checks the redraw.

FIGURE_ESM.md: the _rasterBitmap insertion moved 62 anchors; they are shifted
by the diff hunks and every function anchor checked against its declaration.
The raster note now describes the per-panel cache.
@CSSFrancis

Copy link
Copy Markdown
Owner Author

On Copilot's suppressed note about test_raster_zoom.py, also addressed in 9658b5c:

  • The zoom test now draws a green marker after the raster and checks it survives the wheel round-trip. A raster that throws takes every later marker with it, so this catches a regression that leaves the raster visible but drops what follows.
  • On main the test fails at the raster check (0 red pixels after the zoom); both tests pass here, along with test_plotxy, test_markers and test_zoom_view_write (197).

@CSSFrancis
CSSFrancis merged commit b38ca67 into main Sep 17, 2026
12 checks passed
@CSSFrancis CSSFrancis mentioned this pull request Sep 17, 2026
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants