Files
dela db5598fbf7 feat: add superfig paper-figure toolkit
Standalone LaTeX/TikZ skill for non-tensor paper figures: node/edge
macros, lint-on-warning build, golden examples, and negative fixtures.
2026-08-17 09:40:12 +08:00

123 lines
4.6 KiB
Markdown

# superfig.sty API
```tex
\documentclass[border=10pt]{standalone}
\usepackage[cjk]{superfig} % cjk: ctex + fandol (XeLaTeX). en: English rail labels.
```
Build with `./scripts/build.sh fig.tex` — it puts `assets/` on `TEXINPUTS`.
The build first runs `scripts/lint.py`. It rejects ledger changes, more than four
active hues, raw `\draw`/`\fill`/`\path`, a formula placed before the last row, a
callout that is not an aside on a `\sfrow` band, a second callout, and a group
with fewer than two members. Intentional galleries may put
`% superfig-lint: allow-raw-tikz, allow-multiple-callouts` near the top; do not
add an exemption to a deliverable merely to make it pass.
Rules for *when* to use each primitive live in `grammar.md` and `layout.md`.
## Ledgers
```tex
\sfsetrole{input}{sfTeal} % role -> color. Macros take a ROLE, never a color.
\sfsetlabels{A}{O}{M} % override the three meaning-box rail labels
\sfsetrail{5.4em} % width of the bold label rail (`[en]` defaults wider)
```
Colors: `sfTeal sfOrange sfCoral sfViolet sfGray sfInk`. An unknown role falls
back to gray **and emits a package warning**, which `build.sh` turns into a
failed build. Redeclaring a role with a different color warns and keeps the
original mapping.
Lengths: `\sfgutter` (6 mm), `\sfrowgap` (3.5 mm), `\sfblockgap` (9 mm),
`\sflinklen` (10 mm).
## Flow layout
Empty `at` places the object at the cursor. Hand-written offsets are for
genuinely non-linear topology.
```tex
\sfstage{SA}{stage heading}
\sfrow{R1}{16mm}
\sfnode[role=input]{x}{输入 $x$}{16mm}{12mm}
\sfconn{e1}{预处理}
\sfnode[role=model]{f}{模型 $f_\theta$}{18mm}{12mm}
\sfrowend
\sflane{R1}
\sfcaption{x}{$x$}{原始输入}
```
| macro | does |
|---|---|
| `\sfstage{name}{text}` | stage heading on the left rail, below all ink so far |
| `\sfrow{name}{height}` | open a band; `height` is a length |
| `\sfrowend` | `fit` the band into `name` |
| `\sfnode[keys]{name}{label}{w}{h}` | one semantic object |
| `\sfop{name}{glyph}` | operator in the flow; reserves its own width |
| `\sfconn{name}{label}` | connector; empty label reserves `\sflinklen` of bare arrow |
| `\sfgap{4mm}` / `\sfvgap{4mm}` | extra space, horizontal / vertical |
| `\sfleftrail{x}` | move the left rail |
| `\sfbbox{all}` | everything drawn so far, as one node |
| `\sftopformula{F}{math}` | claim/formula, centered on what was actually drawn |
| `\sftrack{node}` | fold a hand-placed TikZ node into the bbox and vertical cursor |
| `\sflayoutreset` | start over |
`\sfnode` keys: `role=` (default `neutral`), `level=` (`1/2/3` → `!30/!55/!80`),
`gap=` (space *before* this object in a row), `bracket=` (matrix-style arms),
`at={}` (empty = cursor; a coordinate bypasses the row).
Call `\sftopformula` **after** the last `\sfrowend`. A formula placed first is
centered on a figure whose width is not yet known.
## Fixed topology
```tex
\sfnode[role=enc, at={(0,-22mm)}]{in}{输入}{16mm}{10mm}
\sfarrow{in.east}{attn.west}
\sfarrowlabel[bend left=18]{in.north}{add.north}{残差}
```
`\sfarrow` / `\sfarrowlabel` take the same `to[]` options as TikZ (`bend left`,
`out=south, in=north`). The label uses `auto` so a vertical edge does not sit
the text on the shaft. Route is on the background layer.
Keep `at=` on a coarse grid or derive it from existing anchors
(`at={($(te.east)!0.5!(ve.east)+(20mm,0)$)}`).
## Groups, captions, callouts
```tex
\sfgroup[role=attn]{block}{(attn)(ffn)}{} % empty overlay; caption the group
\sflane{R1}
\sfcaption{block}{主路模块}{注意力 + 前馈}
\sfcallout{N1}{38mm}{R1}{title}{body}
```
- `\sfgroup` needs at least two members in the fit list. A single-node outline
is a package warning. After a row, the group refits that row so `\sflane{R1}`
hangs captions below the outline.
- Prefer an empty overlay caption and `\sfcaption{group}{...}{...}`. An overlay
sits on the north-west corner and collides with skip edges.
- `\sfcallout{name}{width}{band}{title}{body}` hangs off a **finished** `\sfrow`
band, one per figure. Inside an open row it is a package error; a non-band
anchor or a second card is a warning.
- `\sfcaptiontop{name}{text}` is the occasional label above a node.
- `\sfnolane` turns the shared caption baseline off.
## Bottom
```tex
\sfbbox{all}
\sfmeaningbox{mb}{96mm}{all}{idea}{objects}{mechanism}
\sfsignature{subject}{mb} % optional; subject only
```
Arg 2 is the total box width. An empty `{}` row is dropped. `\usepackage[en]`
switches the rails to Concept / Objects / Mechanism.
## Lint exemptions
`% superfig-lint: allow-raw-tikz, allow-multiple-callouts, allow-single-group`
near the top of the source. Deliverables normally have none.