Tikz 分割矩形中节点部分的锚点未定义

Tikz 分割矩形中节点部分的锚点未定义

我有一个命令,\mrg它以两个节点部分作为参数,并使用eastwest锚点在它们之间画箭头:

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{calc, positioning, arrows, shapes.multipart, intersections}

\newcommand{\mrg}[2]{\draw[->] let \p1=(#1 east), \p2=(#2 west) in (\p1) -| ({\x1+8pt}, \y1) |- (\p2);}

\begin{document}

\begin{tikzpicture}
\node[rectangle split, rectangle split parts=3, draw](the)
{
{the}
\nodepart{two}{\texttt{d}}
\nodepart{three}{\texttt={n}\rule{0pt}{2\baselineskip}}
};

\node[rectangle split, rectangle split parts=2, draw, right=of the](man)
{
{man}
\nodepart{two}{\texttt{n}}
};

\mrg{the.three}{man.two}
\end{tikzpicture}

\end{document}

在此处输入图片描述

我想将此命令改为引用north eastnorth west我希望写类似以下内容的内容:

\newcommand{\mrg}[2]{\draw[->] let \p1=(#1 north east), \p2=(#2 north west) in (\p1) -| ({\x1+8pt}, \y1) |- (\p2);}

但是,根据 PGF 手册 (v3.1.1,第 796 页),为分割垂直节点的部分定义的唯一锚点是westeast。因此,例如,the.three north east未定义。要使用此坐标,必须改为引用上一个分割:the.two split east。在命令中这样做会非常不方便,因为那时我还需要将上一个分割作为单独的参数传递给它。有没有更好的方法来实现这一点?

答案1

欢迎来到 TeX.SE!

可以使用 LaTeX3 来完成这项任务。当然,可以使用包含最多 20 个元素的简单列表或映射,对每个可能的输入硬编码所需的结果(因为这里的数量非常有限),但这会很丑陋,而且远不如下面的代码有趣。:-)

\documentclass{standalone}
\usepackage{tikz}
\usepackage{xparse}
\usetikzlibrary{calc, positioning, shapes.multipart}

\ExplSyntaxOn

\msg_new:nnn { mermolaeva } { invalid-name-for-node-split-part }
             { Invalid~name~for~node~split~part:~'\exp_not:n {#1}'. }

\seq_const_from_clist:Nn \c_mermolaeva_ordinals_seq
  {
    one, two, three, four, five, six, seven, eight, nine, ten, eleven, twelve,
    thirteen, fourteen, fifteen, sixteen, seventeen, eighteen, nineteen, twenty
  }

% Construct \c_mermolaeva_ordinals_seq_with_text_seq to be identical to
% \c_mermolaeva_ordinals_seq except for the first item, which is set to 'text'
% (without quotes). This is needed for proper naming of the first part of a
% rectangle split node in TikZ.
\seq_new:N \c_mermolaeva_ordinals_seq_with_text_seq
\seq_gset_eq:NN \c_mermolaeva_ordinals_seq_with_text_seq
                \c_mermolaeva_ordinals_seq
\seq_gpop_left:NN \c_mermolaeva_ordinals_seq_with_text_seq \l_tmpa_tl
\seq_gput_left:Nn \c_mermolaeva_ordinals_seq_with_text_seq { text }

\tl_new:N \g__mermolaeva_map_split_part_to_previous_tl

% Build a mapping suitable for the second argument of \str_case, that maps
% s[i] to s[i-1] for each i in [2, len(s)], where s is
% \c_mermolaeva_ordinals_seq_with_text_seq.
\int_step_inline:nn { \seq_count:N \c_mermolaeva_ordinals_seq_with_text_seq - 1 }
  {
    \tl_put_right:Nx \g__mermolaeva_map_split_part_to_previous_tl
      {
        { \seq_item:Nn \c_mermolaeva_ordinals_seq_with_text_seq {#1 + 1} }
        { \seq_item:Nn \c_mermolaeva_ordinals_seq_with_text_seq {#1} }
      }
  }

% Raise an error if #1 is neither 'text' nor an element of
% \c_mermolaeva_ordinals_seq
\cs_new_protected:Npn \mermolaeva_check_split_part_name:n #1
  {
    \seq_if_in:NnTF \c_mermolaeva_ordinals_seq {#1}
      { \bool_set_true:N \l_tmpa_bool }
      { \bool_set_false:N \l_tmpa_bool }

    \bool_if:nF { \bool_if_p:N \l_tmpa_bool || \str_if_eq_p:nn {#1} { text } }
      { \msg_error:nnn { mermolaeva } { invalid-name-for-node-split-part } {#1} }
  }

% text   -> north
% one    -> north
% two    -> text split
% three  -> two split
% four   -> three split
% ...
% twenty -> nineteen split
\cs_new:Npn \mermolaeva_previous_split_part:n #1
  {
    \str_case:nnF {#1}
      {
        { text } { north }
        { one }  { north }
      }
      {
        \str_case:nV {#1} \g__mermolaeva_map_split_part_to_previous_tl
        \c_space_tl % ~ would be ignored here (TeXbook pp. 46-47, state N)
        split
      }
  }

% Raise an error if the part name is invalid for a split part
\NewDocumentCommand \checksplitpartname { m }
  {
    \mermolaeva_check_split_part_name:n {#1}
  }

% The input part name must be valid (no error reporting in this function,
% because we want it to be expandable). See \mermolaeva_previous_split_part:n
% above to learn what the command expands to.
\NewExpandableDocumentCommand \prevsplitpart { m }
  {
    \mermolaeva_previous_split_part:n {#1}
  }

\NewDocumentCommand \mrg { O{} m m m m }
  {
    \mermolaeva_check_split_part_name:n {#3}
    \mermolaeva_check_split_part_name:n {#5}

    \draw[->, #1] let \p1 = (#2.\prevsplitpart{#3}~east),
                      \p2 = (#4.\prevsplitpart{#5}~west)
                  in (\p1) -| ({\x1+8pt}, \y1) |- (\p2);
  }

\ExplSyntaxOff

\begin{document}

\begin{tikzpicture}
  \node[rectangle split, rectangle split parts=3, draw] (the)
    {
      {the}
      \nodepart{two}{\texttt{d}}
      \nodepart{three}{\texttt={n}\rule{0pt}{2\baselineskip}}
    };

  \node[rectangle split, rectangle split parts=2, draw, right=of the] (man)
    {
      {man}
      \nodepart{two}{\texttt{n}}
    };

  \mrg[blue!40, dashed]{the}{one}{man}{one}
  \mrg{the}{two}{man}{two}
  \mrg[red!40, dotted]{the}{three}{man}{two}
\end{tikzpicture}

\end{document}

截屏

答案2

通过一些努力来安装path.ortho 由@Qrrbrbirlbel 设计的库,您可以按如下方式编写 MWE:

\documentclass[tikz, margin=3mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{arrows, calc, intersections, positioning, shapes.multipart}
\usetikzlibrary{paths.ortho}    % see @Qrrbrbirlbel answer on
                                % https://tex.stackexchange.com/questions/45347/
                                % vertical-and-horizontal-lines-in-pgf-tikz

\begin{document}
    \begin{tikzpicture}[
mpn/.style = {rectangle split,          % multi part node
              rectangle split parts=#1,
              draw}
                        ]
\node[mpn=3] (the)  {\nodepart{one}     the
                     \nodepart{two}     \texttt{d}
                     \nodepart{three}   \texttt={n}\rule{0pt}{2\baselineskip}
                    };
\node[mpn=2, right=of the]
             (man)  {\nodepart{one}     man
                     \nodepart{two}     \texttt{n}
                    };
\draw[->] (the.north east) -|- (man.north west);
\end{tikzpicture}
\end{document}

图书馆可以在tikzlibrarypaths.ortho.code.textikzlibrarypaths.ortho.code.tex有关其使用的一些示例,请参阅@Qrrbrbirlbel 回答

相关内容