Harden \stgroup and tighten the callout budget (review follow-up)
- Bracket ink is part of the fit: \st@facebody drops -inkw/-inke extreme coordinates and \stface/\ststack register them with the enclosing group/col/row fit, so a group outline can no longer be crossed by a member's bracket arms - \stlink inside \stgroup or \stcol is now a package error: sub-flow members never terminate a pending connector, so the arrow was dropped silently while the label still rendered - \stgroup requires role= (explicit role=neutral for mixed groups) and must bind at least two members or one \stcol partition; a lone stack or face inside a group is a dirty-build warning - lint: default budget is one \stcallout per figure; the allow-multiple-callouts directive relaxes it to one per band - build.sh: clean-build hint no longer names hue budget (lint owns it) - tests/group-callout.tex reworked: multi-member group with a bracketed member as a regression probe, single callout; new negative fixtures group-link, group-norole, group-single, callout-budget - api.md, checklist.md, style.md, layout.md, SKILL.md updated to match
This commit is contained in:
+53
-2
@@ -146,6 +146,8 @@
|
||||
\newif\ifst@incol
|
||||
\newif\ifst@ingroup
|
||||
\newif\ifst@first
|
||||
\newcount\st@gcount % members registered by the open group
|
||||
\newif\ifst@ghascol % the open group contains a \stcol partition
|
||||
|
||||
\newcommand{\stlayoutreset}{%
|
||||
\global\st@railx=0pt \global\st@ycur=0pt
|
||||
@@ -176,11 +178,21 @@
|
||||
\xdef\st@collist{\st@collist(#1)}\st@regall{#1}%
|
||||
\else\ifst@ingroup
|
||||
\xdef\st@grouplist{\st@grouplist(#1)}\st@regall{#1}%
|
||||
\global\advance\st@gcount by 1
|
||||
\else
|
||||
\xdef\st@rowlist{\st@rowlist(#1)}\st@regall{#1}%
|
||||
\st@drawpendinglink{#1}%
|
||||
\gdef\st@lastnode{#1}%
|
||||
\fi\fi}
|
||||
% Bracket arms are drawn OUTSIDE the node box, so a fit over the node alone is
|
||||
% smaller than the ink. Register the bracket's extreme coordinates with
|
||||
% whatever fit will enclose the node, or a group outline (pad 1.6mm, overhang
|
||||
% 2.2mm) is crossed by the bracket it claims to contain.
|
||||
\newcommand{\st@regink}[1]{%
|
||||
\ifst@incol \xdef\st@collist{\st@collist#1}%
|
||||
\else\ifst@ingroup \xdef\st@grouplist{\st@grouplist#1}%
|
||||
\else \xdef\st@rowlist{\st@rowlist#1}%
|
||||
\fi\fi}
|
||||
|
||||
\newcommand{\st@needrow}[1]{%
|
||||
\ifst@inrow\else
|
||||
@@ -294,6 +306,7 @@
|
||||
Fix the height in \string\stcol}%
|
||||
\fi
|
||||
\st@checkh{\st@colname}{\the\dimen0}%
|
||||
\ifst@ingroup\global\st@ghascoltrue\fi
|
||||
\st@regrow{\st@colname}}}
|
||||
|
||||
% A node in the flow: an operator glyph, a collective, a note. It reserves its
|
||||
@@ -324,6 +337,19 @@
|
||||
% right-hand one exists, so a label can never be wider than its connector.
|
||||
\newcommand{\stlink}[2]{%
|
||||
\st@needrow{\stlink}%
|
||||
% Sub-flow members never terminate a pending connector (\st@regrow), so a
|
||||
% link opened inside one would build cleanly and draw no arrow at all.
|
||||
\ifst@ingroup
|
||||
\PackageError{supertensor}{\string\stlink\space inside \string\stgroup}%
|
||||
{A group member cannot terminate a connector, so the arrow would be
|
||||
dropped silently. Close the group first; the link then attaches to the
|
||||
outline itself.}%
|
||||
\fi
|
||||
\ifst@incol
|
||||
\PackageError{supertensor}{\string\stlink\space inside \string\stcol}%
|
||||
{A column item cannot terminate a connector. Close the column first;
|
||||
the link then attaches to the column as a whole.}%
|
||||
\fi
|
||||
\xdef\st@linkprev{\st@lastnode}%
|
||||
\ifblank{#2}%
|
||||
{\gdef\st@linklabel{}\stgap{\stlinklen}}%
|
||||
@@ -507,6 +533,7 @@
|
||||
\st@flowbegin{#2}{\st@tw}{\st@h}%
|
||||
\edef\st@do{\noexpand\st@facecore{#2}{\st@pos}}\st@do
|
||||
\st@regrow{#2}%
|
||||
\ifst@bracket\st@regink{(#2-inkw)(#2-inke)}\fi
|
||||
}{\st@facecore{#2}{#3}}%
|
||||
\endgroup}
|
||||
|
||||
@@ -587,6 +614,10 @@
|
||||
\draw[black!55, line width=0.5pt]
|
||||
($(#1.north east)+(1.1mm,0.6mm)$) -- ++(1.1mm,0)
|
||||
-- ($(#1.south east)+(2.2mm,-0.6mm)$) -- ++(-1.1mm,0);
|
||||
% The true ink extent, for \st@regink: fits over the bare node undershoot
|
||||
% the bracket by 2.2mm horizontally and 0.6mm vertically.
|
||||
\coordinate (#1-inkw) at ($(#1.south west)+(-2.2mm,-0.6mm)$);
|
||||
\coordinate (#1-inke) at ($(#1.north east)+(2.2mm,0.6mm)$);
|
||||
\fi}
|
||||
|
||||
% \st@tile{face}{row}{col}{level} -- one rounded tile with a white gutter.
|
||||
@@ -633,7 +664,8 @@
|
||||
\node[inner sep=0pt, outer sep=0pt,
|
||||
fit={(#2-front) ($(#2-front.north east)+(\st@back*\st@dx,\st@back*\st@dx)$)}]
|
||||
(#2) {};
|
||||
\ifblank{#3}{\st@regrow{#2}}{}%
|
||||
\ifblank{#3}{\st@regrow{#2}%
|
||||
\ifst@bracket\st@regink{(#2-front-inkw)(#2-front-inke)}\fi}{}%
|
||||
\endgroup}
|
||||
|
||||
% ------------------------------------------------------------- grouping ----
|
||||
@@ -657,7 +689,10 @@
|
||||
/st/group/.cd,
|
||||
role/.store in=\st@grole,
|
||||
pad/.store in=\st@gpad,
|
||||
role=neutral, pad=\stgrouppad,
|
||||
% No default role on purpose: a homogeneous group silently drawn in neutral
|
||||
% gray violates the "outline in the composite's own hue" rule without any
|
||||
% signal. A genuinely mixed group passes role=neutral explicitly.
|
||||
role={}, pad=\stgrouppad,
|
||||
}
|
||||
\newcommand{\stgroup}[2][]{%
|
||||
\st@needrow{\stgroup}%
|
||||
@@ -671,6 +706,11 @@
|
||||
the whole \string\stcol\space in the group instead.}%
|
||||
\fi
|
||||
\pgfkeys{/st/group/.cd,#1}%
|
||||
\ifdefempty{\st@grole}{%
|
||||
\PackageError{supertensor}{\string\stgroup\space `#2' has no role}%
|
||||
{The outline is drawn in the hue of the composite it names. Pass
|
||||
role=<declared role>, or role=neutral explicitly for a mixed group.}%
|
||||
\def\st@grole{neutral}}{}%
|
||||
\stcheckrole{\st@grole}%
|
||||
% Snapshot everything the closing macro needs: the keys are re-read by the
|
||||
% next face and the role ledger lookup must not be deferred.
|
||||
@@ -680,6 +720,7 @@
|
||||
\st@leadgap{\stgutter}%
|
||||
\global\advance\st@cx by \dimexpr\st@gpad\relax
|
||||
\gdef\st@grouplist{}%
|
||||
\global\st@gcount=0 \global\st@ghascolfalse
|
||||
\global\st@ingrouptrue\global\st@firsttrue}
|
||||
|
||||
\newcommand{\stgroupend}{%
|
||||
@@ -693,6 +734,16 @@
|
||||
fit={\st@grouplist}] (\st@gname) {};}\st@do
|
||||
\end{pgfonlayer}
|
||||
\global\st@firstfalse
|
||||
% A group must add information the members do not already carry: at least
|
||||
% two adjacent objects, or one \stcol partition. Around a single face or
|
||||
% stack the outline is decoration -- the stack already reads as one thing.
|
||||
\ifnum\st@gcount<2
|
||||
\ifst@ghascol\else
|
||||
\PackageWarning{supertensor}{Group `\st@gname' wraps a single object.
|
||||
Bind at least two members or one \string\stcol\space partition;
|
||||
a lone stack or face already reads as one composite}%
|
||||
\fi
|
||||
\fi
|
||||
% No \st@checkh: the overhang is 2*pad by construction, not driven by
|
||||
% content, and the members were already checked against the band. Warning
|
||||
% about it would only teach authors to inflate the declared band height.
|
||||
|
||||
Reference in New Issue
Block a user