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

4.6 KiB

superfig.sty API

\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

\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.

\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

\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

\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

\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.