render-pipeline: \frac is an Ord atom, not Inner (ps-calculus +1.82 bp quad/text gap) - #622
Conversation
…xt gap)
What changed:
- `N::Fraction` (`\frac`) converted through math-layout's `Atom::frac`,
which is the Inner atom of a bare `\over`. latex.ltx 15742 and
amsmath.sty 233 define `\frac` as `{\begingroup#1\endgroup\over#2}`, so
the braces make it Ord. As Inner it took TeX's Inner-Ord thin space
(3mu, 1.818 bp at 11 pt) before any following Ord: the +1.81 bp
`\frac{2}{5} \quad \text{as }` gap in ps-calculus p1. The pipeline now
sets the atom's class to Ord. `\dfrac` (`\genfrac`) was already Ord.
- tests/frac_ord_spacing.rs pins pdflatex's numerator-to-`as` distances
for `\quad`, `\qquad`, `\,`, nothing, inline `\frac`, and `\dfrac`.
What was run:
- cargo test --release --test frac_ord_spacing: `test result: ok. 1 passed`
(without the fix: 5 cases 1.818 bp too wide, e.g. `a -> as 19.627 bp,
pdflatex has 17.810`)
- amsmath oracle: `TOTAL 59/59 within 0.5 bp`
- amssymb oracle: `TOTAL 38/39 within 0.5 bp` (32-braces-narrow, same on main)
- display-placement oracle: `TOTAL 33/36 within 0.5 bp` (same 3 as main)
Next step: full render-pipeline suite, PR.
Implementation-Agent: claude-opus-5 subagent of daniel-parent
Commit-Executor: daniel-parent subagent
Claude-Session: https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz
|
daniel-parent independent review (subagent) Verdict: APPROVE Independently rebuilt and measured against pdflatex (TeX Live 2026) rather than just trusting the PR's own table; also diffed against a main-branch build. No blocking issues. Two non-blocking observations below (most notable first), then the checks that fully passed.
|
What
\fracis now converted to an Ord atom. It used to be Inner.In
crates/render-pipeline/src/typeset.rs,N::Fractionwent through math-layout'sAtom::frac, which gives the Inner atom of a bare\over. The pipeline now sets that atom's class toOrd. math-layout andvendor/are unchanged.Why
The regression bisect (#609 notes, "Corpus sweep 2 regressions") found a +1.81 bp excess in the
\frac{2}{5} \quad \text{as }gap onps-calculusp1. It is also on main.Root cause: fraction atom class, not the quad,
\text,\nulldelimiterspaceor italic correction.\fracas{\begingroup#1\endgroup\over#2}. The outer braces make it an Ord atom.\dfracgoes through\genfrac, which was already Ord, and was already exact. That made it the discriminating control.\overfills its whole group, so it has no neighbours to space against, and Ord is also right for it.Minimised against pdflatex (TeX Live, 11 pt article, T1, amsmath). Distances are from the numerator
a's origin toas's origin, in bp.\[ \frac{a}{b} \quad \text{as } x\to 0 \]\[ \frac{a}{b} \qquad \text{as } … \]\[ \frac{a}{b} \, \text{as } … \]\[ \frac{a}{b}\text{as } … \]Text $\frac{a}{b} \quad \text{as } x$ end.\[ \dfrac{a}{b} \quad \text{as } … \](control)The same +1.818 bp shows with no
\quadand no\textat all (\[ \frac{a}{b} x\to 0 \]).y \quad \text{as }was already exact on main.Real-world
ps-calculus, words aligned againstreference.pdfwithin 0.5 bp:On p1,
asis now at dx −0.807. The\frac{2}{5}→asgap is exact (0.000). The remaining offset is the display re-centring from main's 1.61 bp-short\longrightarrowglyph, which #536 fixes.Overlap check
\dotsas Inner. Neither touchesNucleus::FractionorAtom::frac.make_ordpairs only character nuclei and skips Inner neighbours. A fraction nucleus is not a character, so an Ord fraction gets no kern or ligature.gh pr list --search frac / nulldelimiter / quad: no open PR changes fraction class.coord.py claims: only this task (GH-DISPLAY-FRAC-QUAD-GAP, daniel-parent).Test results
cargo test --release --test frac_ord_spacing:test result: ok. 1 passed; 0 failed. Without the fix it fails, e.g.\[ \frac{a}{b} \quad \text{as } x\to 0 \]: a -> as 19.627 bp, pdflatex has 17.810.cargo test --release --no-fail-fast: every test binaryok, 376 passed, 0 failed.cargo test --release: 66 passed, 0 failed.TOTAL 59/59 within 0.5 bpTOTAL 38/39 within 0.5 bp(32-braces-narrow, same on main)TOTAL 33/36 within 0.5 bp(15/17/20, the same three as main)Not done
Nucleus::GenFractionstill says "unlike the plain\frac's innerNucleus::Fraction". It is incrates/compiler/vendor/compiler, so it is left for a compiler PR.Atom::fracstays Inner, which is correct for a primitive\over.flashtex-pdf-exactbuild in this lane).(daniel-parent subagent, GH-DISPLAY-FRAC-QUAD-GAP)
🤖 Generated with Claude Code
https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz