Skip to content

Keep an enum's held codes and its changes visible at every zoom - #571

Draft
thebhef wants to merge 1 commit into
fix-ruff-default-rulesfrom
fix-enum-transitions-zoomed-out
Draft

thebhef wants to merge 1 commit into
fix-ruff-default-rulesfrom
fix-enum-transitions-zoomed-out

Conversation

@thebhef

@thebhef thebhef commented Oct 19, 2026

Copy link
Copy Markdown
Owner

The owner did not accept the residual held-code loss: "It's ok to lose
codes that are extremely narrow, but if there was a transition there we
should make sure it shows up when zoomed way out." The plot serve
dropped a held code 1.49 columns wide on a level-1 read, and on a
level-3 read a run 2.2 columns wide (one served column is one canvas
pixel). Two causes, both in the serve, neither in the pyramid fold:

  • decimate_min_max bucketed by point index. A pyramid level folds a
    flat bucket to one point and a busy one to two, so a bucket of points
    can be a fraction of a pixel in one place and several pixels in
    another, and a held run arriving as one point mid-bucket was neither
    first, last, min nor max. It now bins into max_points equal time
    columns over the window (clamped to the series' own span, so a
    fit-all serve over f64::MIN..MAX still works). It keeps each column's
    first, last, min and max, and the 4 * max_points bound is unchanged.
  • A level's in-window count only bounds its average spacing. Beside a
    burst of changes a coarse level's gaps run to several columns, and a
    run with no point of the slice inside it lies inside one gap.
    SignalCache::refine_gaps splices finer-level points into every gap
    wider than a column, one level at a time, and stops as soon as no gap
    is wider. Every pyramid point is a real sample, so a run held for a
    column can no longer hide.

The serve now guarantees, in served columns: a run held for at least
one column is served with its code at every alignment and at every
level read. A narrower run may lose its code but not its change: the
serve changes value in that column or the one beside it. The one column
of placement is inherent to reading the pyramid, since a kept extreme
carries its own timestamp from anywhere in its bucket. Placing it
exactly needs a raw read at every column edge, which at a wide zoom is
the whole capture. On the frontend, drawEnumTiles draws a served run
narrower than a device pixel as a one-CSS-pixel tick in its accent,
the full band tall, on the lanes axis and the single-enum ribbon,
because a sub-pixel fill antialiases to nothing.

Measured on the real SignalCache (100 Hz cycling 0..=5, 30 130
samples). The fold and the pyramid (10 034 points above level 0) are
unchanged:

largest held run lost control serve points read
before, 2248 pts (level 1) 1.49 col 5651 7536
after, 2248 pts 0.74 col 5986 7536
before, 280 pts (level 3) >= 2.20 col 492 492
after, 280 pts 0.98 col 840 1896

Rejected after measuring:

  • Reading a 4x finer level gets the change into exactly its own column
    in every sample tried, but costs +46 % serve and 4x the read.
  • A per-point transition count in the fold costs 16 B per pyramid
    point plus a persistence change, still cannot place a change more
    precisely than its bucket, and does nothing for the held code.

ADR 0026 records the guarantee and the measurements. Tests:

  • a_held_code_one_served_column_wide_survives_the_serve_at_every_alignment
    (two depths; red before);
  • every_raw_change_shows_as_a_served_change_within_a_column (random
    holds and blips; it already held, now pinned);
  • the sampler's column-edge sweep and a sparse-point case;
  • the sub-pixel tick on ribbon and lane at ratio 1 and 2.

The perf bench's naive-path call to decimate_min_max takes the new
whole-range arguments.

zoom_in_reads_a_finer_level_than_fit_data asserted 2 * max_points + 2.
That bound has been stale since first/last joined the reduction (it
passed by luck of its sawtooth), so it now asserts the documented
4 * max_points.

Co-Authored-By: Claude Fable 5.1 noreply@anthropic.com

This was referenced Oct 19, 2026
This was referenced Oct 19, 2026

thebhef commented Oct 19, 2026 •

Copy link
Copy Markdown
Owner Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

The owner did not accept the residual held-code loss: "It's ok to lose
codes that are extremely narrow, but if there was a transition there we
should make sure it shows up when zoomed way out." The plot serve
dropped a held code 1.49 columns wide on a level-1 read, and on a
level-3 read a run 2.2 columns wide (one served column is one canvas
pixel). Two causes, both in the serve, neither in the pyramid fold:

- decimate_min_max bucketed by point index. A pyramid level folds a
  flat bucket to one point and a busy one to two, so a bucket of points
  can be a fraction of a pixel in one place and several pixels in
  another, and a held run arriving as one point mid-bucket was neither
  first, last, min nor max. It now bins into max_points equal time
  columns over the window (clamped to the series' own span, so a
  fit-all serve over f64::MIN..MAX still works). It keeps each column's
  first, last, min and max, and the 4 * max_points bound is unchanged.
- A level's in-window count only bounds its average spacing. Beside a
  burst of changes a coarse level's gaps run to several columns, and a
  run with no point of the slice inside it lies inside one gap.
  SignalCache::refine_gaps splices finer-level points into every gap
  wider than a column, one level at a time, and stops as soon as no gap
  is wider. Every pyramid point is a real sample, so a run held for a
  column can no longer hide.

The serve now guarantees, in served columns: a run held for at least
one column is served with its code at every alignment and at every
level read. A narrower run may lose its code but not its change: the
serve changes value in that column or the one beside it. The one column
of placement is inherent to reading the pyramid, since a kept extreme
carries its own timestamp from anywhere in its bucket. Placing it
exactly needs a raw read at every column edge, which at a wide zoom is
the whole capture. On the frontend, drawEnumTiles draws a served run
narrower than a device pixel as a one-CSS-pixel tick in its accent,
the full band tall, on the lanes axis and the single-enum ribbon,
because a sub-pixel fill antialiases to nothing.

Measured on the real SignalCache (100 Hz cycling 0..=5, 30 130
samples). The fold and the pyramid (10 034 points above level 0) are
unchanged:

| | largest held run lost | control serve | points read |
|---|---|---|---|
| before, 2248 pts (level 1) | 1.49 col | 5651 | 7536 |
| after, 2248 pts | 0.74 col | 5986 | 7536 |
| before, 280 pts (level 3) | >= 2.20 col | 492 | 492 |
| after, 280 pts | 0.98 col | 840 | 1896 |

Rejected after measuring:
- Reading a 4x finer level gets the change into exactly its own column
  in every sample tried, but costs +46 % serve and 4x the read.
- A per-point transition count in the fold costs 16 B per pyramid
  point plus a persistence change, still cannot place a change more
  precisely than its bucket, and does nothing for the held code.

ADR 0026 records the guarantee and the measurements. Tests:
- a_held_code_one_served_column_wide_survives_the_serve_at_every_alignment
  (two depths; red before);
- every_raw_change_shows_as_a_served_change_within_a_column (random
  holds and blips; it already held, now pinned);
- the sampler's column-edge sweep and a sparse-point case;
- the sub-pixel tick on ribbon and lane at ratio 1 and 2.

The perf bench's naive-path call to decimate_min_max takes the new
whole-range arguments.

zoom_in_reads_a_finer_level_than_fit_data asserted 2 * max_points + 2.
That bound has been stale since first/last joined the reduction (it
passed by luck of its sawtooth), so it now asserts the documented
4 * max_points.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@thebhef
thebhef force-pushed the fix-enum-transitions-zoomed-out branch from 64af20f to eb5d8a4 Compare October 26, 2026 22:42
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