Skip to content

Add a reproducible README asset pipeline (emitter + renderers + SVG drift-gate) - #43

Closed
gistrec wants to merge 2 commits into
readme/text-quick-winsfrom
readme/asset-pipeline
Closed

gistrec wants to merge 2 commits into
readme/text-quick-winsfrom
readme/asset-pipeline

Conversation

@gistrec

@gistrec gistrec commented Jul 7, 2026 •

Copy link
Copy Markdown
Owner

Adds the reproducible pipeline that generates the README imagery from the
library's own output — all the geo-math runs in C++ (tools/assets/emit.cpp,
reusing the examples/gps_track.cpp pipeline); the Python renderers only draw.

Stacked on #42 (readme/text-quick-wins). This PR's base is that branch,
not master, so its diff shows only the asset files. GitHub will retarget the
base to master automatically once #42 merges. No README.md change here —
wiring the generated images into the README is a deliberate follow-up (so the
gifs can be eyeballed on this PR first).

What's in it

  • tools/assets/emit.cpp — C++17 GeoJSON/JSONL emitter that reuses the exact
    decode → bounds → simplify → closest_point_on_path → point_at_distance → encode
    pipeline, so every picture is drawn from real library output (single source of
    truth for the geometry).
  • tools/render/{bench,hero,social,gallery}.py + tools/make-assets.sh.
  • Committed assets — docs/assets/benchmarks.svg, hero-pipeline.gif,
    social-preview.png, gallery/simplify.gif (the Douglas-Peucker collapse
    animated over real Tampa map tiles), and the emitter data under
    docs/assets/data/.

Reproducibility & CI

Renders are deterministic by construction: pinned matplotlib/pillow/numpy
(+ transitive deps that touch the bytes) in tools/render/requirements.txt, and
the nondeterminism knobs are disabled — fixed svg.hashsalt, svg.fonttype=path
(glyphs embedded as outlines, so output doesn't depend on installed fonts), no
Date metadata in the SVG, and a pinned Software tag in the PNG.

.github/workflows/assets.yml rebuilds the emitter (-Werror) and re-renders
every asset on macos-latest / Python 3.13 as a smoke test (they must build
and render without error), then byte-gates only benchmarks.svg — the one
genuinely platform-independent artifact (uncompressed text, glyph outlines via
pinned fonttools, no zlib/LZW amplification). The rasters and GIFs
(hero-pipeline.gif, gallery/simplify.gif, social-preview.png) are not
byte-diffed: byte-exact matplotlib Agg raster reproduction across independently
provisioned runners is unreliable, so — like the four gallery tile stills — they
are committed by hand. Regenerate everything locally with bash tools/make-assets.sh.

Deferred (not in this PR)

The four operations-gallery stills (great-circle / snap-to-route /
point-in-polygon / encode-decode .png, #5–#8) also fetch map tiles
(staticmap/cartopy + network) and are committed by hand; gallery.py
renders them (bash tools/make-assets.sh --all) and they land in a follow-up
commit. (The animated gallery/simplify.gif above is already rendered and
included.)

…ift-gate)

Build the visual assets from the library's own output, mirroring the
filecast ".tape next to the .gif" pattern: all geo-math stays in C++
(tools/assets/emit.cpp runs the real decode -> bounds -> simplify -> snap ->
point_at_distance -> encode pipeline over the same 95-point track as
examples/gps_track.cpp and emits GeoJSON/JSONL); the Python renderers only
draw the committed data.

Deterministic stages (byte-stable, CI drift-gated):
- tools/render/bench.py     -> docs/assets/benchmarks.svg
- tools/render/hero.py      -> docs/assets/hero-pipeline.gif + gallery/simplify.gif
- tools/render/social.py    -> docs/assets/social-preview.png
- tools/assets/emit.cpp     -> docs/assets/data/{track.geojson,dp.jsonl,eta.jsonl}

Manual stages (committed by hand, not gated):
- docs/assets/demo.tape     -> docs/assets/demo.gif   (VHS terminal capture)
- tools/render/gallery.py   -> docs/assets/gallery/*.png  (OSM/cartopy tiles)

Orchestration + gate:
- tools/make-assets.sh          one entry point (emitter + renders)
- tools/render/requirements.txt pinned deps (matplotlib bundles FreeType 2.6.1)
- .github/workflows/assets.yml  rebuilds the emitter and regenerates only the
                                deterministic assets, then git diff --exit-code

No library API changes; renderers are tooling only. README wiring is left to
the README briefs. gallery/*.png are deferred (need tile-server network +
staticmap/cartopy, unavailable in this sandbox).
@gistrec
gistrec marked this pull request as draft July 7, 2026 00:16
@gistrec gistrec closed this Jul 7, 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