H3 and Fixing ASCII Boxes
Smaller updates today.
H3
H3 has been either mostly complete, closed, or abandoned for a while, but there’s still room for improvement.
I’m forking it here with my own refactors and improvements: https://github.com/mattsta/h3
Performance — the headline numbers
Single-cell hot path
| API | upstream → optimized | speedup |
|---|---|---|
cellToBoundary |
0.50 µs → 0.23 µs | ~2.13× |
cellToLatLng |
0.12 µs → 0.08 µs | ~1.55× |
cellToVertexes (full ring) |
19.5 µs → 15.1 µs | ~1.29× |
polygonToCellsExperimental (Full) |
varies | ~1.65–1.76× |
polygonToCellsExperimental (Overlapping) |
varies | ~1.32–1.49× |
polygonToCellsExperimental (Center) |
varies | ~1.39–1.46× |
polygonToCells (legacy polyfill) |
varies | ~1.13–1.18× |
gridDisk (k=10–40) |
varies | ~1.17–1.22× |
gridDisk pentagon paths (k=10–40) |
varies | ~1.51–1.84× |
Bulk cellToChildren (allCellsAtRes) |
varies | ~1.84–2.15× |
New
bulk APIs (cellsToLatLngs, on top of single-cell wins)
For applications converting millions of cells (visualization, map rendering, analytics pipelines), the new SIMD-batched bulk APIs deliver another ~2× throughput multiplier over a per-cell loop:
| batch size N | per-cell loop | batched | speedup |
|---|---|---|---|
| 16 | 1.48 µs | 0.73 µs | 2.03× |
| 256 | 23.86 µs | 11.45 µs | 2.08× |
| 1024 | 94.96 µs | 45.22 µs | 2.10× |
| 8192 | 767.52 µs | 388.09 µs | 1.98× |
Stacked end-to-end vs. upstream, the fastest paths reach
~2.18× cellToBoundary, ~1.80×
polygonToCellsExperimental, and ~1.98× bulk
cellToChildren on a single Apple Silicon core. Add
H3_ENABLE_OPENMP=ON and the bulk APIs scale further across
cores at batches above ~1024.
The improvements are not bit-identail to original H3 in every case, but the cost of the improvements is one bit difference for 2x faster speeds which amounts to 180 nm earth-surface difference in the encoding.
Fix ASCII Boxes
https://github.com/mattsta/fix-ascii-boxes
The hottest trend is computing is using agents to draw ascii art architecture diagrams then not auditing or fixing all the alignment problems in charts the agents draw with pipes and dashes and other fancy characters in potentially dozens or hundreds of files across a project.
so, i wrote/generated a thing to fix it: https://github.com/mattsta/fix-ascii-boxes
If you have markdown files with broken claude/codex style ascii art
box diagrams, the fix-ascii-art system aligns:
- outerborders
- inner borders if there are nested boxes
- can even re-flow or center nested ascii boxes and text inside other nested ascii boxes
# Install (uv / pip both work — only stdlib at runtime)
uv sync # builds + installs entry points
# Common operations
uv run fix-ascii-boxes docs/architecture.md # fix in place (align mode)
uv run fix-ascii-boxes --check docs/*.md # CI gate: exit 1 if changes needed
uv run fix-ascii-boxes --diff docs/architecture.md # preview the diff without writing
uv run fix-ascii-boxes --dry-run # show output, don't touch files
cat broken.md | uv run fix-ascii-boxes # stdin → stdoutFive commands ship as entry points:
| Command | What it does |
|---|---|
fix-ascii-boxes |
Repair / re-layout diagrams. Supports --mode,
--layout-strategy, --check,
--diff, --dry-run. |
fix-ascii-boxes-audit |
Read-only inspection of boxes, suspect rows, per-pass convergence. |
fix-ascii-boxes-validate |
Parse + round-trip every fenced block in a set of files via the AST pipeline. |
fix-ascii-boxes-bench |
Throughput benchmark on synthetic input. |
The two flags that change behavior the most:
--mode {align,reflow,distribute,layout} # which engine + how aggressive
--layout-strategy {natural,distributed,centered,flush_left} # for --mode layoutRun Modes
There are three basic run modes at various levels of complexity and features:
Each text-pipeline mode is a strict superset of the previous, so the
behavior monotonically increases. layout (the AST pipeline)
sits beside them and produces equivalent output on supported
diagrams.
| Mode | Safe in CI? | Re-flows widths? | Re-distributes gaps? | Round-trips? |
|---|---|---|---|---|
align (default) |
yes — idempotent, never truncates | no | no | only if source is already aligned |
reflow |
yes | yes — widens to fit overflow | no | no — overflow rows widen |
distribute |
yes | yes | yes | no — gaps re-balanced |
layout + --layout-strategy natural |
yes — pure parse + re-render | no | no | yes on supported shapes |
layout +
--layout-strategy distributed |
yes | as needed | yes — AST level | no — equivalent to distribute |
Layout strategies
Row.strategy controls how a band of siblings is
positioned. Set via apply_strategy(tree, name) or the
--layout-strategy CLI flag.
| Strategy | What it does | Use when |
|---|---|---|
natural |
Re-use source gaps exactly | You want round-trip — preserve everything |
distributed |
Re-allocate gaps evenly (lpad / between / rpad) | You want consistent visual spacing |
centered |
Single-pad on each side, MIN_GAP between |
You want the band centered in its parent |
flush_left |
No leading pad, MIN_GAP between |
You want to pack tightly to the left |
Example — same source band, three strategies:
source: │ ┌─────┐ ┌─────┐ ┌─────┐ │
natural: │ ┌─────┐ ┌─────┐ ┌─────┐ │
distributed: │ ┌─────┐ ┌─────┐ ┌─────┐ │
centered: │ ┌─────┐ ┌─────┐ ┌─────┐ │
flush_left: │┌─────┐ ┌─────┐ ┌─────┐ │
Examples
Example 1
— align mode repairs a misaligned right edge
Source:
┌──────────┐
│ Hello │
│ World │ ← right edge drifted past target
│ Foo │
└──────────┘
uv run fix-ascii-boxes --mode align re-snaps the right
edge:
┌──────────┐
│ Hello │
│ World │
│ Foo │
└──────────┘
If “World” had been wider than └──────────┘ allows, the
row would have been flagged as overflow instead of
misaligned and align mode would refuse to
touch it (reflow would widen the whole box).
Example 2 — overflow handling
Source has Coordinator (11 chars) crammed into a 10-wide
declared box:
┌──────────┐
│Coordinator│
│ Agent │
└──────────┘
--mode align refuses to touch it — the row is classified
as overflow, not misaligned, and align won’t
truncate. The diagram passes through unchanged with the misalignment
flagged in --check output.
--mode reflow widens the box’s declared width by enough
cols to fit the overflowing content (1 col here) and shifts every
non-flush row to match:
┌───────────┐
│Coordinator│
│ Agent │
└───────────┘
--mode layout produces the same output via the AST
pipeline: the parser’s _slice_interior scans ±3 cols for
the actual │, and Container.compute_size
widens to fit. Both modes are equivalent on this case.
Example 3
— distribute re-balances gaps across a band
Source has flush-left siblings:
┌─────────────────────────────────────────────────────────────────────┐
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Foo │ │ Bar │ │ Baz │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────────┘
uv run fix-ascii-boxes --mode distribute:
┌─────────────────────────────────────────────────────────────────────┐
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Foo │ │ Bar │ │ Baz │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────────┘
Siblings re-distribute across the parent’s interior; any
┬│▼ vertical pipelines are repositioned to track their
owning child.
Example 4
— layout mode with natural strategy
round-trips
Same input as Example 3, but:
uv run fix-ascii-boxes --mode layout --layout-strategy natural < band.mdReturns the source byte-identical. Useful for confirming the AST parser handles your diagram, or as a regression check before committing edits.
Example 5 — programmatic manipulation
import layout_engine as le
tree = le.parse(text)
# Find the Operation box and rename it
for row in tree.contents:
if isinstance(row, le.Row):
for child in row.children:
for content in child.contents:
if isinstance(content, le.TextLine) and "Operation" in content.text:
content.text = "Execute"
# Re-distribute the bands and render
le.apply_strategy(tree, "distributed")
print(le.render(tree))Validation & performance
uv run fix-ascii-boxes-validate FALSE_CASE.txt docs/*.md
uv run fix-ascii-boxes-bench --scaleSample throughput:
lines chars blocks seconds l/s c/s
----------------------------------------------------------------
1000 42192 72 0.012 74,997 3,377,026
10000 419576 716 0.117 79,616 3,588,477
50000 2093192 3572 0.576 80,643 3,635,090
conclusion
what else do you people want? you can’t complain about any of this because it’s all free.