Skip to content

feat(ascii): braille, half-block, edge & path-traced vector modes#13

Open
mixflavor wants to merge 2 commits into
mainfrom
feat/ascii-smooth-modes
Open

feat(ascii): braille, half-block, edge & path-traced vector modes#13
mixflavor wants to merge 2 commits into
mainfrom
feat/ascii-smooth-modes

Conversation

@mixflavor
Copy link
Copy Markdown
Contributor

@mixflavor mixflavor commented Jun 2, 2026

ASCII used to fill only with square density glyphs. This adds a mode option with smoother fills, all returning the same Cell[][] so the existing plain/HTML/ANSI serializers keep working unchanged — plus a standalone path-traced vector renderer that samples the SVG geometry directly.

Modes

mode resolution what it does
ramp (default) 1×1 original density-glyph renderer — byte-identical, untouched
braille ⣿ 2×4 dot matrix per cell, 8× resolution — curves read smooth, not stair-stepped
halfblock ▀ 1×2 mono ▀▄█; coloured carries top as foreground + bottom as background → two colours and double vertical detail
edge ╱ outline Sobel edge detect → stroke the contour with ─ │ ╱ ╲, leave flats blank
vector ╭╮ path-traced samples the real SVG geometry (getPointAtLength), draws the centreline as connected box-drawing — true corners (rounded ╭╮╯╰ / sharp ┌┐└┘), diagonals as smooth rounded staircases

Why vector is the crown

edge guesses contours by running Sobel on a rasterised mark. The studio owns the actual vector, so vector mode samples the path geometry itself and draws the centreline as a connected line: each cell's glyph is chosen by which neighbouring cells the line also passes through (box-drawing connectivity), so turns become real corners and there are no disjoint ╱╲ that never quite join.

  • Pure core (strokeToAscii in ascii.ts): grid-space polylines → connected box-drawing. Fully unit-tested.
  • Thin DOM membrane (ascii-stroke-dom.ts): parse → mount offscreen at viewBox size (so getCTM maps element space → grid cleanly) → walk each geometry element → break the polyline on sub-path hops so no stray line crosses a hole. Node-guarded; untested by design (CI build + click-test cover the live geometry).

Plumbing

  • Colour serializers gained background-colour support (HTML background:, ANSI 48;2); spaces and line-ends reset cleanly so half-block's two colours render right.
  • Studio's Charset picker is now a Style picker grouped Density ramp / Smooth / Vector. Vector adds a Rounded toggle. Seam-fill (.tight) applies to the solid-block tilers and the connected vector strokes; braille dots and sparse edge strokes stay crisp.

Tests

  • 34 unit tests pass (14 original ascii — default ramp path byte-identical — + 12 raster-mode + 7 vector-stroke + 1 DOM node-guard smoke).
  • Both Svelte components parse clean.

🤖 Generated with Claude Code

chodaict and others added 2 commits June 3, 2026 00:56
The renderer only did square density-glyph fills. Add a `mode` option with
three smoother methods, all returning the same Cell[][] so the existing
plain/HTML/ANSI serializers keep working:

- braille (⣿): 2×4 dot matrix per cell — 8× resolution, curves read smooth
- halfblock (▀): split each cell top/bottom; mono uses ▀▄█, coloured carries
  the top colour as foreground and the bottom as background (two colours +
  double vertical detail). Serializers gain background-colour support
  (HTML `background:`, ANSI `48;2`), with spaces/line-ends resetting cleanly.
- edge (╱): Sobel edge detect, then stroke the contour with direction glyphs
  (─ │ ╱ ╲) and leave flats blank — a hand-drawn outline. Crisp on vector
  input since the raster is clean, so gradient angles are accurate.

mode defaults to 'ramp': the original path is byte-identical (14 existing
tests unchanged). Studio's charset picker becomes a Style picker grouped
into Density ramp / Smooth; seam-fill applies only to the solid-block tilers.

12 new unit tests pin the sub-cell resolution, two-colour carriers, and edge
direction mapping. 204/204 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Where `edge` guesses contours with Sobel on a rasterised mark, this samples
the *real* SVG geometry — the studio owns the vector, so it can do better.

- ascii.ts: strokeToAscii() rasterises grid-space polylines into connected
  box-drawing art. Each cell's glyph is chosen by which neighbouring cells the
  line also passes through, so turns become real corners (rounded ╭╮╯╰ or
  sharp ┌┐└┘) and diagonals become smooth rounded staircases instead of a
  field of ╱╲ that never join. Pure + unit-tested (7 cases: horizontal,
  vertical, rounded vs sharp corner, 4-way ┼ crossing, colour carry, empty).
- ascii-stroke-dom.ts: the thin browser membrane. Parses the composed SVG,
  mounts it offscreen at viewBox size (so getCTM maps element space → grid
  cleanly), walks each geometry element with getPointAtLength, and breaks the
  polyline on sub-path hops so we never draw a stray line across a hole.
  Node-guarded (returns null without a DOM); a smoke test pins that.
- Studio: new "Vector → Stroke ╭╮ (path-traced)" style with a Rounded toggle;
  it bypasses the rasterise path entirely. Seam-fill (.tight) now also applies
  to vector so box-drawing connectors touch across the stretched preview.

34 unit tests pass; both Svelte components parse clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mixflavor mixflavor changed the title feat(ascii): braille, half-block & edge fill modes feat(ascii): braille, half-block, edge & path-traced vector modes Jun 2, 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.

2 participants