我正在尝试在放置多个节点之前生成并测量它们。目前我正在使用以下宏:
% some helper / wrapper commands around \pgfplacenodelater
% #1 = identifier for nodes to be placed later
% #2 = elements to be placed
\DeclareDocumentCommand \@placenodelater {r() +m}{
\expandafter\DeclareDocumentCommand\csname mtd@#1-save\endcsname{}{
% Save Neccesary Registers
\global\expandafter\setbox\csname mtd@#1-box\endcsname=\box\pgfpositionnodelaterbox
\global\expandafter\let\csname mtd@#1-name\endcsname=\pgfpositionnodelatername
\global\expandafter\let\csname mtd@#1-minx\endcsname=\pgfpositionnodelaterminx
\global\expandafter\let\csname mtd@#1-miny\endcsname=\pgfpositionnodelaterminy
\global\expandafter\let\csname mtd@#1-maxx\endcsname=\pgfpositionnodelatermaxx
\global\expandafter\let\csname mtd@#1-maxy\endcsname=\pgfpositionnodelatermaxy
% Calculate Length and Width as well
\pgfmathsetmacro{\mtd@templen}
{\pgfpositionnodelatermaxx - \pgfpositionnodelaterminx}
\pgfmathsetmacro{\mtd@tempwid}
{\pgfpositionnodelatermaxy - \pgfpositionnodelaterminy}
\global\expandafter\let\csname mtd@#1-length\endcsname=\mtd@templen
\global\expandafter\let\csname mtd@#1-width\endcsname=\mtd@tempwid
% Print Some debug information.
\typeout{\pgfpositionnodelaterbox}
\typeout{\pgfpositionnodelatername}
}
{
\expandafter\pgfpositionnodelater\csname mtd@#1-save \endcsname
#2
}
}
% #1 = identifier for nodes already saved by \@placenodelater
% #2 = location for the node to be placed at
\DeclareDocumentCommand \@placenodenow {r() +m}{
\let\pgfpositionnodelatername\expandafter=\csname mtd@#1-name \endcsname
\let\pgfpositionnodelaterminx\expandafter=\csname mtd@#1-minx \endcsname
\let\pgfpositionnodelaterminy\expandafter=\csname mtd@#1-miny \endcsname
\let\pgfpositionnodelatermaxx\expandafter=\csname mtd@#1-maxx \endcsname
\let\pgfpositionnodelatermaxy\expandafter=\csname mtd@#1-maxy \endcsname
\setbox\pgfpositionnodelaterbox=\expandafter\box\csname mtd@#1-box \endcsname
\pgfpositionnodenow{#2}
}
我尝试使用的过程类似于以下内容:
- 生成节点 A 和 B 以供稍后放置。
- 使用有关 A 和 B 的大小的信息来生成并放置 C
- 相对于 C 的位置放置 A
- 将 B 放置在相对于 C 的位置的其他位置
当我尝试将 A 和 B 同时放置在同一个位置时,此操作往往会失败。我还收到一条错误消息,提示保存的框丢失并被视为 0。
理想情况下,我能够将有关一组生成的节点的信息存储在expl3
l3seq 中,因为我的包的用户可以定义任意数量的元素,并且所有元素的大小信息都会被使用。
最后,运行命令时,第一个宏中的两个\typeouts
似乎没有打印任何内容。
我有许多想要实现的节点放置特定算法,但最常见的情况如下:
- 有中心节点 C,每侧有 4 个方向节点(N、S、E、W)
- 定向节点必须直接位于 C 的一侧,并且 C 必须比 N 和 S 更宽,并且比 E 和 W 更高。
- 当定义宏时,每个节点的内容都是任意的和未知的。
- 在上述界限内,C 应该尽可能小。
\pgfpositionnodelater
我想这里的具体问题是:在放置任何节点之前,如何并行使用多次?
我还希望您能提供任何有关如何使上述命令能够处理递归的见解。例如,我应该能够在另一个调用的节点定义部分中调用\@placenodelater
并执行该操作。这并不是绝对必要的,但会使我的软件包的使用者更容易理解。\@placenodenow
\@placenodelater
谢谢。
编辑:这是一个更有用的最小(非)工作示例,使用了@egreg 提出的建议:
\documentclass[crop,tikz]{standalone}% 'crop' is the default for v1.0, before it was 'preview'
\RequirePackage{xparse}
\RequirePackage{expl3}
\usetikzlibrary{
graphs,
shapes,
calc,
intersections,
through,
fit,
backgrounds,
positioning,
arrows,%
shapes.misc,% wg. rounded rectangle
shapes.arrows,%
chains,%
matrix,%
scopes,%
decorations.pathmorphing,% /pgf/decoration/random steps | erste Graphik
shadows,%
fixedpointarithmetic
}
\makeatletter
\ExplSyntaxOn
% QUESTION: How can I convert these commands to treat the macro parameters like
% an expl3 typed parameter? can I do it while keeping the nice xparse
% syntax?
\DeclareDocumentCommand \@placenodelater {r() +m}{
\cs_new_protected:cpn {mtd@#1-save}
{
% Print Some debug information.
\typeout{\pgfpositionnodelatername}
\typeout{\pgfpositionnodelaterminx}
\typeout{\pgfpositionnodelaterminy}
\typeout{\pgfpositionnodelatermaxx}
\typeout{\pgfpositionnodelatermaxy}
% Save Necessary Registers
\cs_gset_eq:cN {mtd@#1-name} \pgfpositionnodelatername
\cs_gset_eq:cN {mtd@#1-minx} \pgfpositionnodelaterminx
\cs_gset_eq:cN {mtd@#1-miny} \pgfpositionnodelaterminy
\cs_gset_eq:cN {mtd@#1-maxx} \pgfpositionnodelatermaxx
\cs_gset_eq:cN {mtd@#1-maxy} \pgfpositionnodelatermaxy
\box_gset_eq:cN {mtd@#1-box} \pgfpositionnodelaterbox
% Calculate Length and Width as well
\pgfmathsetmacro{\mtd@templen}
{\pgfpositionnodelatermaxx - \pgfpositionnodelaterminx}
\pgfmathsetmacro{\mtd@tempwid}
{\pgfpositionnodelatermaxy - \pgfpositionnodelaterminy}
\cs_gset_eq:cN {mtd@#1-length} \mtd@templen
\cs_gset_eq:cN {mtd@#1-width} \mtd@tempwid
}
{
\exp_args:Nc \pgfpositionnodelater {mtd@#1-save} #2
}
}
% #1 = identifier for nodes already saved by \@placenodelater
% #2 = location for the node to be placed at
\DeclareDocumentCommand \@placenodenow {r() +m}{
\cs_set_eq:Nc \pgfpositionnodelatername {mtd@#1-name}
\cs_set_eq:Nc \pgfpositionnodelaterminx {mtd@#1-minx}
\cs_set_eq:Nc \pgfpositionnodelaterminy {mtd@#1-miny}
\cs_set_eq:Nc \pgfpositionnodelatermaxx {mtd@#1-maxx}
\cs_set_eq:Nc \pgfpositionnodelatermaxy {mtd@#1-maxy}
\box_set_eq:Nc \pgfpositionnodelaterbox {mtd@#1-box}
% Print Some debug information.
\typeout{\pgfpositionnodelatername}
\typeout{\pgfpositionnodelaterminx}
\typeout{\pgfpositionnodelaterminy}
\typeout{\pgfpositionnodelatermaxx}
\typeout{\pgfpositionnodelatermaxy}
\pgfpositionnodenow{#2}
}
% #1 = Contents of upper box
% #2 = contents of middle box
% #3 = contents of lower box
\DeclareDocumentCommand \placetriple {+m +m +m}{
\@placenodelater(upper){
\node[anchor = south](topnode){#1};
}
\@placenodelater(lower){
\node[anchor = north](bottomnode){#3};
}
\pgfmathsetmacro{\mts@wa}{\mts@upper-width + 1cm}
\pgfmathsetmacro{\mts@wb}{\mts@lower-width + 1cm}
\pgfmathsetmacro{\mts@wc}{max(\mts@wa,\mts@wb)}
\node[minimum width=\mts@wc](centernode){#2};
\@placenodenow(upper){\pgfpointanchor{centernode}{north}}
\@placenodenow(lower){\pgfpointanchor{centernode}{south}}
}
\ExplSyntaxOff
\makeatother
\begin{document}
\begin{tikzpicture}
\placetriple
{The center box should}
{always be wider}
{than the top and bottom, regardless of content.};
\end{tikzpicture}
\end{document}
我的实际目标是宏的更通用版本\placetriple
,但这会产生相同的错误。(即我的实际代码允许我将选项传递给我创建的各个节点,并使用参数生成名称,以便于重用)
编译时我得到的错误:
....
(/usr/share/texlive/texmf-dist/tex/generic/oberdiek/kvsetkeys.sty
(/usr/share/texlive/texmf-dist/tex/generic/oberdiek/etexcmds.sty)))
(/usr/share/texlive/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg))
not yet positioned@topnode
-52.26361pt
0.2pt
52.26361pt
13.81038pt
! Missing number, treated as zero.
<to be read again>
\mtd@upper-box
l.107 ...e top and bottom, regardless of content.}
;
我不确定这到底意味着什么,也不确定为什么经过一番摆弄之后我似乎无法复制它以进行编译,并在同一位置显示中心节点和下部节点。
编辑:这是该宏的一个经过一定程度清理的版本,供其他想要使用它的人使用。它似乎适用于基本用例,但是当它嵌套或稍后放置的任何节点包含\tikz[]{...}
环境时,它会崩溃。(后者似乎只在我更新了 texLive 安装后才会崩溃,不知道是什么原因造成的)
\makeatletter
\ExplSyntaxOn
% Define an identifier and a set of TIKZ commands so that you can get bounding
% box information before you place a node.
% #1 = identifier for the stuff you place
% #2 = tikz code you wish to place later
\DeclareDocumentCommand \placenodelater {r() +m}{
% Debug
\typeout{Starting_placenodelater_for_#1.}
% The macro the \pgfpositionnodelateruses as generated by the identifier
% given.
\cs_new_protected:cpn {pgf@#1-save-macro} {
% calculate width and height
\pgfmathsetmacro{\pgf@tempwid}{
\pgfpositionnodelatermaxx - \pgfpositionnodelaterminx
}
\pgfmathsetmacro{\pgf@temphei}{
\pgfpositionnodelatermaxy - \pgfpositionnodelaterminy
}
% Save all the location registers
\cs_gset_eq:cN {pgf@#1-name} \pgfpositionnodelatername
\cs_gset_eq:cN {pgf@#1-minx} \pgfpositionnodelaterminx
\cs_gset_eq:cN {pgf@#1-miny} \pgfpositionnodelaterminy
\cs_gset_eq:cN {pgf@#1-maxx} \pgfpositionnodelatermaxx
\cs_gset_eq:cN {pgf@#1-maxy} \pgfpositionnodelatermaxy
\cs_gset_eq:cN {pgf@#1-width} \pgf@tempwid
\cs_gset_eq:cN {pgf@#1-height} \pgf@temphei
\box_if_exist:cF {pgf@#1-box} {\box_new:c {pgf@#1-box}}
\box_gset_eq:cN {pgf@#1-box} \pgfpositionnodelaterbox
% Debug information
\typeout{__pgf@#1-save-macro_data_:}
\typeout{_____name___:_\use:c{pgf@#1-name}}
\typeout{_____min-x__:_\use:c{pgf@#1-minx}}
\typeout{_____max-x__:_\use:c{pgf@#1-maxx}}
\typeout{_____min-y__:_\use:c{pgf@#1-miny}}
\typeout{_____max-y__:_\use:c{pgf@#1-maxy}}
\typeout{_____height_:_\use:c{pgf@#1-height}}
\typeout{_____width__:_\use:c{pgf@#1-width}}
}
{
\exp_args:Nc \pgfpositionnodelater {pgf@#1-save-macro} #2
}
% Debug
\typeout{Ending_placenodelater_for_#1.}
}
% Place the node previously specified with the \placenodelater command
% #1 = identifier for nodes already saved by \placenodelater
% #2 = location for the node to be placed at
\DeclareDocumentCommand \placenodenow {r() +m}{
% Debug
\typeout{Starting_placenodenow_for_#1.}
% Move the various variables back
\cs_set_eq:Nc \pgfpositionnodelatername {pgf@#1-name}
\cs_set_eq:Nc \pgfpositionnodelaterminx {pgf@#1-minx}
\cs_set_eq:Nc \pgfpositionnodelaterminy {pgf@#1-miny}
\cs_set_eq:Nc \pgfpositionnodelatermaxx {pgf@#1-maxx}
\cs_set_eq:Nc \pgfpositionnodelatermaxy {pgf@#1-maxy}
\box_set_eq:Nc \pgfpositionnodelaterbox {pgf@#1-box}
% Call into PGF to place the node
\pgfpositionnodenow{#2}
% Debug information at time of placement
\typeout{__#1_data_:}
\typeout{_____name___:_\use:c{pgf@#1-name}}
\typeout{_____min-x__:_\use:c{pgf@#1-minx}}
\typeout{_____max-x__:_\use:c{pgf@#1-maxx}}
\typeout{_____min-y__:_\use:c{pgf@#1-miny}}
\typeout{_____max-y__:_\use:c{pgf@#1-maxy}}
\typeout{_____height_:_\use:c{pgf@#1-height}}
\typeout{_____width__:_\use:c{pgf@#1-width}}
% Debug
\typeout{Ending_placenodenow_for_#1.}
}
\ExplSyntaxOff
\makeatother
答案1
当您加载时xparse
,最好充分利用的功能expl3
以更简单的方式开展该业务。
\makeatletter
\ExplSyntaxOn
% some helper / wrapper commands around \pgfplacenodelater
% #1 = identifier for nodes to be placed later
% #2 = elements to be placed
\DeclareDocumentCommand \@placenodelater {r() +m}{
\cs_new_protected:cpn {mtd@#1-save}
{
% Save Necessary Registers
\box_gset_eq:cN {mtd@#1-box} \pgfpositionnodelaterbox
\cs_gset_eq:cN {mtd@#1-name} \pgfpositionnodelatername
\cs_gset_eq:cN {mtd@#1-minx} \pgfpositionnodelaterminx
\cs_gset_eq:cN {mtd@#1-miny} \pgfpositionnodelaterminy
\cs_gset_eq:cN {mtd@#1-maxx} \pgfpositionnodelatermaxx
\cs_gset_eq:cN {mtd@#1-maxy} \pgfpositionnodelatermaxy
% Calculate Length and Width as well
\pgfmathsetmacro{\mtd@templen}
{\pgfpositionnodelatermaxx - \pgfpositionnodelaterminx}
\pgfmathsetmacro{\mtd@tempwid}
{\pgfpositionnodelatermaxy - \pgfpositionnodelaterminy}
\cs_gset_eq:cN {mtd@#1-length} \mtd@templen
\cs_gset_eq:cN {mtd@#1-width} \mtd@tempwid
% Print Some debug information.
\typeout{\pgfpositionnodelaterbox}
\typeout{\pgfpositionnodelatername}
}
{
\exp_args:Nc \pgfpositionnodelater {mtd@#1-save} #2
}
}
% #1 = identifier for nodes already saved by \@placenodelater
% #2 = location for the node to be placed at
\DeclareDocumentCommand \@placenodenow {r() +m}{
\cs_set_eq:Nc \pgfpositionnodelatername {mtd@#1-name}
\cs_set_eq:Nc \pgfpositionnodelaterminx {mtd@#1-minx}
\cs_set_eq:Nc \pgfpositionnodelaterminy {mtd@#1-miny}
\cs_set_eq:Nc \pgfpositionnodelatermaxx {mtd@#1-maxx}
\cs_set_eq:Nc \pgfpositionnodelatermaxy {mtd@#1-maxy}
\box_set_eq:Nc \pgfpositionnodelaterbox {mtd@#1-box}
\pgfpositionnodenow{#2}
}
\ExplSyntaxOff
\makeatother
该构造\cs_gset_eq:cN {foo} \baz
相当于
\global\expandafter\let\csname foo\endcsname \baz
而\cs_set_eq:Nc \foo {baz}
相当于
\expandafter\let\expandafter\foo\csname baz\endcsname
gset
和的区别在于set
前者进行全局分配,而后者只进行局部分配。
请注意你的代码
\let\pgfpositionnodelatername\expandafter=\csname mtd@#1-name \endcsname
设置\pgfpositionnode
为等同于,\expandafter
并且以下标记将像在普通文本中一样进行处理。
\box_gset:cN
对于和 也类似\box_set:Nc
。
最后,笨拙的
\expandafter\pgfpositionnodelater\csname mtd@#1-save \endcsname
#2
可以变得更容易
\exp_args:Nc \pgfpositionnodelater {mtd@#1-save} #2
我修复了示例代码中的明显错误,现在运行时没有错误(但我不能说它完成了它应该做的事情)。
- 使用前应分配一个盒子
- 你有一个
mts@
前缀,应该是mtd@
- 您不能使用 \mtd@upper-width
as a control sequence name; I remedied with
\use:c{mtd@#1-width}` - 在代码中,名称中带有空格的 PGF 键应使用
~
(明确的空格)expl3
。
这是代码。
\documentclass[crop,tikz]{standalone}% 'crop' is the default for v1.0, before it was 'preview'
\usepackage{xparse}
%\usepackage{expl3} % redundant, already loaded by xparse
\usetikzlibrary{
graphs,
shapes,
calc,
intersections,
through,
fit,
backgrounds,
positioning,
arrows,%
shapes.misc,% wg. rounded rectangle
shapes.arrows,%
chains,%
matrix,%
scopes,%
decorations.pathmorphing,% /pgf/decoration/random steps | erste Graphik
shadows,%
fixedpointarithmetic
}
\makeatletter
\ExplSyntaxOn
% QUESTION: How can I convert these commands to treat the macro parameters like
% an expl3 typed parameter? can I do it while keeping the nice xparse
% syntax?
\DeclareDocumentCommand \@placenodelater {r() +m}{
\cs_new_protected:cpn {mtd@#1-save}
{
% Print Some debug information.
\typeout{\pgfpositionnodelatername}
\typeout{\pgfpositionnodelaterminx}
\typeout{\pgfpositionnodelaterminy}
\typeout{\pgfpositionnodelatermaxx}
\typeout{\pgfpositionnodelatermaxy}
% Save Necessary Registers
\cs_gset_eq:cN {mtd@#1-name} \pgfpositionnodelatername
\cs_gset_eq:cN {mtd@#1-minx} \pgfpositionnodelaterminx
\cs_gset_eq:cN {mtd@#1-miny} \pgfpositionnodelaterminy
\cs_gset_eq:cN {mtd@#1-maxx} \pgfpositionnodelatermaxx
\cs_gset_eq:cN {mtd@#1-maxy} \pgfpositionnodelatermaxy
\box_if_exist:cF{mtd@#1-box} {\box_new:c {mtd@#1-box}}
\box_gset_eq:cN {mtd@#1-box} \pgfpositionnodelaterbox
% Calculate Length and Width as well
\pgfmathsetmacro{\mtd@templen}
{\pgfpositionnodelatermaxx - \pgfpositionnodelaterminx}
\pgfmathsetmacro{\mtd@tempwid}
{\pgfpositionnodelatermaxy - \pgfpositionnodelaterminy}
\cs_gset_eq:cN {mtd@#1-length} \mtd@templen
\cs_gset_eq:cN {mtd@#1-width} \mtd@tempwid
}
{
\exp_args:Nc \pgfpositionnodelater {mtd@#1-save} #2
}
}
% #1 = identifier for nodes already saved by \@placenodelater
% #2 = location for the node to be placed at
\DeclareDocumentCommand \@placenodenow {r() +m}{
\cs_set_eq:Nc \pgfpositionnodelatername {mtd@#1-name}
\cs_set_eq:Nc \pgfpositionnodelaterminx {mtd@#1-minx}
\cs_set_eq:Nc \pgfpositionnodelaterminy {mtd@#1-miny}
\cs_set_eq:Nc \pgfpositionnodelatermaxx {mtd@#1-maxx}
\cs_set_eq:Nc \pgfpositionnodelatermaxy {mtd@#1-maxy}
\box_set_eq:Nc \pgfpositionnodelaterbox {mtd@#1-box}
% Print Some debug information.
\typeout{\pgfpositionnodelatername}
\typeout{\pgfpositionnodelaterminx}
\typeout{\pgfpositionnodelaterminy}
\typeout{\pgfpositionnodelatermaxx}
\typeout{\pgfpositionnodelatermaxy}
\pgfpositionnodenow{#2}
}
% #1 = Contents of upper box
% #2 = contents of middle box
% #3 = contents of lower box
\DeclareDocumentCommand \placetriple {+m +m +m}{
\@placenodelater(upper){
\node[anchor = south](topnode){#1};
}
\@placenodelater(lower){
\node[anchor = north](bottomnode){#3};
}
\pgfmathsetmacro{\mts@wa}{\use:c{mtd@upper-width} + 1cm}
\pgfmathsetmacro{\mts@wb}{\use:c{mtd@lower-width} + 1cm}
\pgfmathsetmacro{\mts@wc}{max(\mts@wa,\mts@wb)}
\node[minimum~width=\mts@wc](centernode){#2};
\@placenodenow(upper){\pgfpointanchor{centernode}{north}}
\@placenodenow(lower){\pgfpointanchor{centernode}{south}}
}
\ExplSyntaxOff
\makeatother
\begin{document}
\begin{tikzpicture}
\placetriple
{The center box should}
{always be wider}
{than the top and bottom, regardless of content.};
\end{tikzpicture}
\end{document}