Skip to content

Draw on-gram labels on a white plate instead of a halo (#243) - #246

Merged
IanMayo merged 1 commit into
mainfrom
claude/gifted-turing-wlkave
Aug 21, 2026
Merged

Draw on-gram labels on a white plate instead of a halo (#243)#246
IanMayo merged 1 commit into
mainfrom
claude/gifted-turing-wlkave

Conversation

@IanMayo

@IanMayo IanMayo commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Closes #243.

Harmonic and sideband pin numbers, and analysis marker labels, were drawn as black glyphs inside a white halo — a stroke painted behind the letterforms. A halo only whitens the pixels immediately around each stroke, so a noisy gram still shows through the counters of the digits and the space between them. The legacy viewer plates its harmonic numbers instead: an opaque white rounded rectangle containing the characters, which clears one contiguous area so the contrast is the same everywhere in the label.

Every in-gram label now sits on such a plate — both the harmonic/sideband index labels and the cross-cursor text labels, as the issue asks.

What changed

  • New src/utils/labelPlate.js owns the plate. plateLabel() wraps a fully-attributed SVG text element in a group with the rectangle behind it, sized from the text measured at the font it is drawn in (canvas measurement, with a character-count fallback for the Node unit lane). The geometry is pure, so the placement rules can size their gaps from the same numbers the renderer draws with. It replaces src/utils/svg.js, whose only job was the halo.
  • Placement rules leave room for the plate. markerLabelPlacement and PinSetMode.labelStackPositions now measure their gaps to the plate's edge rather than to the text baseline, so the white rectangle clears the symbol, the crosshair's centre dot and the image's top edge by as much as the bare glyphs used to. A pin's grab region (labelStackBounds / labelStackHalfWidth) tracks the drawn plate too, and its width is now measured rather than estimated from a character count.
  • The text element is unchanged apart from its paint — same class, same attributes, same position — so selectors, CSS and existing tests still find the label where they did. The halo's stroke and paint-order attributes are gone; the glyphs are plain black.
  • Labels stay black-on-white whatever colour the feature is; identity is still carried by the pin's line and symbol colour.

The plate rises 0.95 em above the baseline — enough to cover the font's full ascent, so a tall or accented character can never clip the top edge. That makes it slightly taller than digits alone need; PLATE_ABOVE_RATIO in labelPlate.js is the one number to change if a tighter plate reads better on a real gram.

Testing

  • yarn test — 333 passed (includes the rewritten tests/harmonic-label-plate.spec.js, replacing harmonic-label-halo.spec.js, and three new marker-label tests: the plate is white and rounded, it covers the characters on every side, and it stays clear of the crosshair it annotates).
  • yarn test:unit — 149 passed (new tests/unit/label-plate.test.js covers the pure plate geometry: extents, anchoring, padding).
  • yarn typecheck, yarn lint, yarn hygiene — all clean.
  • Checked visually in the browser on debug.html: harmonic pins 1–5 and a labelled crosshair, over both dark and saturated areas of the gram.

Docs

CLAUDE.md's file list, docs/Gram-Modes.md, and the three docs pages that pointed at src/utils/svg.js (for helpers that no longer existed) now name the real modules.

Harmonic and sideband pin numbers, and analysis marker labels, were drawn
as black glyphs inside a white halo — a stroke painted behind the
letterforms. A halo only whitens the pixels immediately around each
stroke, so a noisy gram still shows through the counters of the digits
and the space between them. The legacy spectrogram viewer plates its
harmonic numbers instead: an opaque white rounded rectangle containing
the characters, which clears one contiguous area and reads the same
everywhere in the label.

Every in-gram label now sits on such a plate:

- New `src/utils/labelPlate.js` owns the plate. `plateLabel()` wraps a
  fully-attributed `<text>` in a group with the rectangle behind it, sized
  from the text measured at the font it is drawn in (canvas measurement,
  with a character-count fallback for the Node unit lane). The geometry
  is pure so the placement rules can size their gaps from it.
- The two placement rules — `markerLabelPlacement` and
  `PinSetMode.labelStackPositions` — now measure their gaps to the edge
  of the plate rather than to the baseline, so the white rectangle clears
  the symbol, the crosshair and the image's top edge by as much as the
  bare glyphs used to. A pin's grab region tracks the plate too.
- The text keeps its class and attributes, so selectors, CSS and tests
  still find the label where they did.

Labels stay black-on-white whatever colour the feature is; identity is
still carried by the pin's line and symbol.

Closes #243
@github-actions

Copy link
Copy Markdown
Contributor

PR Preview

Preview deployment is ready! View Preview

Last updated: 2026-08-21T13:39:36.783Z

@IanMayo
IanMayo merged commit d09c086 into main Aug 21, 2026
6 checks passed
@IanMayo
IanMayo deleted the claude/gifted-turing-wlkave branch August 21, 2026 13:42
github-actions Bot added a commit that referenced this pull request Aug 21, 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.

Make harmonic labels easier to read

2 participants