Add flow layout: cursor placement, left rail, declared band heights
Figures were positioned by hand-written offsets. Every gap was a magic number tuned against the content that happened to be there, so a label that grew two characters landed on the next tensor, and two stages started from two different x shared no rail. Both failures compile cleanly. Replace it with a cursor. Objects placed with an empty coordinate argument reserve their own width -- including a stack's offset sheets and a bracket's overhang -- and gaps are declared once (\stgutter, \strowgap, \stblockgap). The gap belongs to the object that follows it and the first object in a band gets none, so every band starts flush on a shared rail and gap=0pt states that two shards tile exactly. \stlink makes a connector's label a flow object, which is what removes the label-wider-than-its-arrow failure entirely. \stcol is a vertical sub-flow for a split along the contracted axis. \strow declares its height, so an object that does not fit -- or a column that does not add up to what it declared, and is therefore drawn off-center -- becomes a package warning, which build.sh fails on. Absolute placement is unchanged: passing a coordinate takes the original code path, and \sttrack folds a hand-placed node back into the cursor. All three golden examples and the new tests/flow.tex are converted and build clean.
This commit is contained in:
+35
-19
@@ -4,11 +4,29 @@ Everything on the canvas is a bounding box: tensors, full offset stacks, bracket
|
||||
operators, arrow labels, annotations, symbols, shape labels, stage headings, the meaning
|
||||
box, the signature. **Tangency counts as collision.**
|
||||
|
||||
## Place by cursor, not by coordinate
|
||||
|
||||
The invariants below are *statements about the finished figure*; the flow layout in
|
||||
`api.md` is how you get them without checking each one by hand. Use it by default:
|
||||
`\ststage` / `\strow` … `\strowend` / `\stcol` … `\stcolend`, with an empty coordinate
|
||||
argument on every face.
|
||||
|
||||
The failure it removes is specific. With hand-written offsets, each gap is a magic number
|
||||
tuned against the *current* content, so the day a label grows by two characters it silently
|
||||
lands on the next tensor — the figure still compiles and still looks clean. With the
|
||||
cursor, every object reserves its own width, so growing one object can only push the rest
|
||||
apart. And because a band declares its height, an object that does not fit becomes a
|
||||
`Package supertensor Warning`, which `build.sh` turns into a failed build.
|
||||
|
||||
Hand placement remains available for the cases the cursor cannot express. When you use it,
|
||||
wrap the node in `\sttrack` so the bounding box and the vertical cursor still see it.
|
||||
|
||||
## Gutters
|
||||
|
||||
Define one base gutter `g ≥ 1 em`. Unrelated boxes stay at least `g` apart; stage bands at
|
||||
least `1.5g`. In practice: `right=5mm–9mm` between an operator and its operands, `7mm–9mm`
|
||||
between the last caption of one row and the next stage heading.
|
||||
Define one base gutter `g ≥ 1 em`; that is what `\stgutter` (6 mm) is. Unrelated boxes stay
|
||||
at least `g` apart; stage bands at least `1.5g` (`\strowgap`, `\stblockgap`). Set them once
|
||||
at the top of the figure rather than per call — a per-call `gap=` is for stating a
|
||||
*relationship* (`gap=0pt` = these shards tile), not for nudging.
|
||||
|
||||
Overlap is allowed only inside one declared composite:
|
||||
|
||||
@@ -33,26 +51,22 @@ shapes <- \stcaption arg 3
|
||||
```
|
||||
|
||||
Faces of different heights would otherwise hang their captions at different depths. Fix it
|
||||
with a shared baseline:
|
||||
with a shared baseline — `\strowend` arms one automatically:
|
||||
|
||||
```tex
|
||||
\node[inner sep=0pt, fit=(Q)(KT)(S)] (rowA) {};
|
||||
\stlane{rowA}
|
||||
\strow{rowA}{T}
|
||||
...
|
||||
\strowend
|
||||
\stcaption{Q}{$\mathbf Q^{(i)}$}{$h\times T\times d_h$}
|
||||
...
|
||||
\stnolane
|
||||
```
|
||||
|
||||
Every `\stcaption` between `\stlane` and `\stnolane` hangs from the bottom of `rowA`, so
|
||||
symbols and shapes form two flat lanes.
|
||||
Under absolute placement, arm it yourself with `\stlane{rowA}` … `\stnolane`. Either way
|
||||
every `\stcaption` inside hangs from the bottom of `rowA`, so symbols and shapes form two
|
||||
flat lanes.
|
||||
|
||||
Stage headings share one left rail. Anchor each heading below the previous row but at the
|
||||
previous *heading's* x, not at the previous row's content:
|
||||
|
||||
```tex
|
||||
\coordinate (cy) at ($(I-shape.south)+(0,-9mm)$);
|
||||
\node[st stage, anchor=north west] (SC) at (SB.west |- cy) {...};
|
||||
```
|
||||
Stage headings share one left rail. `\ststage` puts them there: the rail is a single stored
|
||||
`x` (`\stleftrail` to move it), and the `y` is derived from the lowest ink drawn so far, so
|
||||
a heading can neither drift right nor collide with the row above it.
|
||||
|
||||
Explanatory prose belongs in the stage subtitle, the bottom box, or above its own
|
||||
connector. Never drop a floating commentary card between two operands unless it is a real
|
||||
@@ -67,8 +81,10 @@ never cover a face. Two consequences you still own:
|
||||
- A connector may not cross a box that is not one of its endpoints. Move the row, don't
|
||||
route over.
|
||||
- A label's white underlay may cover only its own connector — never a tensor, never
|
||||
another label. If the label is wider than the arrow, shorten the label or widen the gap.
|
||||
This is the single most common failure after a first draft.
|
||||
another label. This is the single most common failure after a first draft, and `\stlink`
|
||||
is the fix: it makes the label itself a flow object and draws the arrow to whatever
|
||||
lands on either side of it, so the label cannot be wider than its connector.
|
||||
`\starrowlabel` between two hand-placed nodes still has the old failure mode.
|
||||
|
||||
## Stacks
|
||||
|
||||
|
||||
Reference in New Issue
Block a user