pgfpositionnodelater 的多种用途

pgfpositionnodelater 的多种用途

我正在尝试在放置多个节点之前生成并测量它们。目前我正在使用以下宏:

% 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。

理想情况下,我能够将有关一组生成的节点的信息存储在expl3l3seq 中,因为我的包的用户可以定义任意数量的元素,并且所有元素的大小信息都会被使用。

最后,运行命令时,第一个宏中的两个\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

我修复了示例代码中的明显错误,现在运行时没有错误(但我不能说它完成了它应该做的事情)。

  1. 使用前应分配一个盒子
  2. 你有一个mts@前缀,应该是mtd@
  3. 您不能使用 \mtd@upper-width as a control sequence name; I remedied with\use:c{mtd@#1-width}`
  4. 在代码中,名称中带有空格的 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} 

相关内容