我有一个命令,\mrg
它以两个节点部分作为参数,并使用east
和west
锚点在它们之间画箭头:
\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 east
。north 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 页),为分割垂直节点的部分定义的唯一锚点是west
和east
。因此,例如,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.tex和tikzlibrarypaths.ortho.code.tex有关其使用的一些示例,请参阅@Qrrbrbirlbel 回答。