docs: atlases are fixed-cell grids; state the constraint instead of discovering it

This commit is contained in:
Martino Ferrari committed 2026-09-28 10:10:41 +02:00
1 parent ffc060d6ca
commit e1f6b61b7d
2 files changed
+3 -2

No files matched your search

@@ -78,7 +78,7 @@ git add -A && git commit -m "refactor(render): TextBackend owns its atlas, and s
- [ ] **Step 1: Write the generator** - [ ] **Step 1: Write the generator**
`tools/fonts/gen_atlas.py` takes the source TTF path, an em size, and an output path; rasterises the coverage set with `fontTools` (`fontTools.pens`, grayscale supersampling with a simple coverage count — do not shell out to a rasteriser); packs glyphs into a single-row atlas in codepoint order; emits a C++ translation unit containing the pixel bytes, the per-glyph rect/bearing/advance table, and a header comment with provenance (source font file name, its version string, em size, supersample factor, generator invocation, coverage list, and the licence file path). `tools/fonts/gen_atlas.py` takes the source TTF path, an em size, and an output path; rasterises the coverage set with `fontTools` (`fontTools.pens`, grayscale supersampling with a simple coverage count — do not shell out to a rasteriser); packs glyphs into a single-row atlas in codepoint order using a **uniform cell** (cell width = the font's maximum advance, cell height = its ascent-to-descent extent; each glyph is drawn into its cell with its bearing preserved, never trimmed to ink, because the pipeline has one rect size per draw — see spec non-goals); emits a C++ translation unit containing the pixel bytes, the per-glyph rect/bearing/advance table, and a header comment with provenance (source font file name, its version string, em size, supersample factor, generator invocation, coverage list, and the licence file path).
Hard requirements: Hard requirements:
- Assert every requested codepoint produced non-empty ink, and exit non-zero if not. A silent blank glyph must be impossible. - Assert every requested codepoint produced non-empty ink, and exit non-zero if not. A silent blank glyph must be impossible.
@@ -310,7 +310,7 @@ Download `stb_truetype.h` (public domain) into `third_party/` with its version a
- [ ] **Step 2: The backend** - [ ] **Step 2: The backend**
Implement `TrueTypeFont` over `stb_truetype`: bake the coverage set at `px` into a packed single-row R8 atlas, build the glyph table (rect, bearing, advance) from `stbtt_GetCodepointBitmap` and `stbtt_GetCodepointHMetrics`, and implement the full `TextBackend`, including `revision()` (monotone; bumped by `set_coverage` and by re-baking). Reject a missing file, an unparsable font, a non-positive `px`, and a bake that produced no ink, filling `Status`. Implement `TrueTypeFont` over `stb_truetype`: bake the coverage set at `px` into a single-row R8 atlas of **uniform cells** (cell width `stbtt_ScaleForPixelHeight` applied to the font's maximum advance, cell height its ascent-to-descent extent; each codepoint's trimmed bitmap from `stbtt_GetCodepointBitmap` is blitted into its cell at the offset `stbtt_GetCodepointBitmapBox` reports, so the bearing is preserved and the atlas rect stays uniform), build the glyph table from `stbtt_GetCodepointHMetrics`, and implement the full `TextBackend`, including `revision()` (monotone; bumped by `set_coverage` and by re-baking). Declare the cell size the backend will report; do not let it vary per glyph. Reject a missing file, an unparsable font, a non-positive `px`, and a bake that produced no ink, filling `Status`.
**Important:** do **not** add this header to `include/plot/plot.hpp`. The umbrella must stay compilable with no third-party header on the include path, and the configure-time coverage check will reject a header it cannot compile standalone. Keep `plot/text/true_type_font.hpp` self-contained (it may include `plot/render/text.hpp` and the standard library, and must forward-declare nothing from stb_truetype), and document that it is built only by the optional target. **Important:** do **not** add this header to `include/plot/plot.hpp`. The umbrella must stay compilable with no third-party header on the include path, and the configure-time coverage check will reject a header it cannot compile standalone. Keep `plot/text/true_type_font.hpp` self-contained (it may include `plot/render/text.hpp` and the standard library, and must forward-declare nothing from stb_truetype), and document that it is built only by the optional target.
@@ -17,6 +17,7 @@ Five changes to the shipped library, driven by using it:
## Non-goals ## Non-goals
- Arbitrary font features: no kerning, no shaping, no hinting, no subpixel positioning. Explicitly deferred. - Arbitrary font features: no kerning, no shaping, no hinting, no subpixel positioning. Explicitly deferred.
- **Proportional (variable-width) text.** An atlas is a fixed-cell grid: every glyph occupies one cell of the same size. That is what keeps the staged instance at 6 floats and the vertex shader free of a per-glyph size, and it is why a proportional font must draw its glyphs into a uniform cell instead of trimming the atlas rect. The constraint is structural — declared by the backend, asserted in debug builds — not a note, because violating it silently draws every glyph at the first staged glyph's size.
- Text selection/editing, data export, annotations beyond what exists. - Text selection/editing, data export, annotations beyond what exists.
- A general constraint/layout engine — `Figure` is a fixed vertical stack, not a docking system. - A general constraint/layout engine — `Figure` is a fixed vertical stack, not a docking system.