Skip to content

Add differential redraw for animated plots (new - prev)#5

Draft
matomatical wants to merge 1 commit into
mainfrom
differential-redraw
Draft

Add differential redraw for animated plots (new - prev)#5
matomatical wants to merge 1 commit into
mainfrom
differential-redraw

Conversation

@matomatical

Copy link
Copy Markdown
Owner

Animating a plot currently clears the whole region (-plot) and redraws every cell. This adds a differential path: new - prev (sugar for new.updatestr(prev), backed by CharArray.to_ansi_diff_str) emits a minimal ANSI sequence that repaints only the cells differing from the previous frame -- jumping the cursor with relative moves and carrying SGR colour state across the skipped gaps. Falls back to a full clear-and-redraw on a size change.

The cursor contract mirrors the existing idiom: assumes the cursor is just below prev (as after printing it) and leaves it just below new.

Correctness is verified against a small ANSI terminal emulator in the tests: applying a diff to a screen showing prev reproduces a full redraw of new, across 40 randomised partial-change cases, plus focused tests (no-change, single-cell, colour-only, size-mismatch fallback, final cursor position).

Byte savings are large for mostly-static animations (~13x on a dashboard with one moving bar) and modest for fully turbulent frames where most cells change.

Ticks the roadmap item; CHANGELOG updated.

What

Animating a plot currently clears the whole region (-plot) and repaints every
cell. This adds a differential path: new - prev (sugar for
new.updatestr(prev), backed by CharArray.to_ansi_diff_str) emits a minimal
ANSI sequence that repaints only the cells that changed since the previous
frame — stepping the cursor with relative moves and carrying SGR colour state
across the skipped gaps. Falls back to a full clear-and-redraw on a size change.

Implements the roadmap item "Faster animated plot redraws (differential
rendering with shortcut -)."

API

  prev = None
  for frame in frames:
      print(frame if prev is None else frame - prev, end="", flush=True)
      prev = frame

Cursor contract: assumes the cursor is just below prev (as after printing it)
and leaves it just below new; single-width glyphs at column 0.

Verified

  • New ANSI terminal-emulator tests: applying a diff to a screen showing prev
    reproduces a full redraw of new — 40 randomised partial-change cases, plus
    no-change / single-cell / colour-only / size-mismatch-fallback / cursor cases.
  • Full suite: 347 passing. mypy clean on the new code (the 2 histogram
    overload errors pre-exist on main).

Byte savings (encoded, vs full redraw)

scenario diff vs full factor
dashboard, one moving bar 7.8% 12.8×
reaction–diffusion, settling 81–88% 1.1–1.2×

Big win for mostly-static animations; modest for fully turbulent frames.

Notes

  • Version bump left for a release commit.
  • DOCS.md not regenerated (no generate_docs.py in repo); API docs come from
    pdoc reading the new docstrings.

🤖 Generated with Claude Code

Animating a plot currently clears the whole region (`-plot`) and redraws
every cell. This adds a differential path: `new - prev` (sugar for
`new.updatestr(prev)`, backed by `CharArray.to_ansi_diff_str`) emits a minimal
ANSI sequence that repaints only the cells differing from the previous frame --
jumping the cursor with relative moves and carrying SGR colour state across the
skipped gaps. Falls back to a full clear-and-redraw on a size change.

The cursor contract mirrors the existing idiom: assumes the cursor is just
below `prev` (as after printing it) and leaves it just below `new`.

Correctness is verified against a small ANSI terminal emulator in the tests:
applying a diff to a screen showing `prev` reproduces a full redraw of `new`,
across 40 randomised partial-change cases, plus focused tests (no-change,
single-cell, colour-only, size-mismatch fallback, final cursor position).

Byte savings are large for mostly-static animations (~13x on a dashboard with
one moving bar) and modest for fully turbulent frames where most cells change.

Ticks the roadmap item; CHANGELOG updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@matomatical matomatical self-assigned this Jun 23, 2026
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.

1 participant