4

I have a command \mrg takes two nodeparts as arguments and draw arrows between them, using east and west anchors:

\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}

enter image description here

I would like to change this command to reference north east and north west instead. I was hoping to write something like:

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

However, according to the PGF manual (v3.1.1, p.796), the only anchors defined for parts of a split vertical node are west and east. So, for instance, the.three north east is undefined. To use this coordinate, one has to reference the previous split instead: the.two split east. Doing so inside a command would be very inconvenient, since then I would also need to pass the previous split to it as a separate argument. Is there a better way to achieve this?

  • Welcome to TeX.SE! – Mensch Jun 12 at 12:12
3

Welcome to TeX.SE!

It is possible to use LaTeX3 for this task. One could of course use a simple list or mapping containing at most twenty elements, hardcoding the desired result for each possible input—since their number is quite limited here—but that would be ugly and much less interesting than the code below. :-)

\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}

Screenshot

2

with some effort to install path.ortho library designed by @Qrrbrbirlbel you can write your MWE as follows:

\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}

libraries can be found on tikzlibrarypaths.ortho.code.tex and tikzlibrarypaths.ortho.code.tex . for some example of their use see @Qrrbrbirlbel answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.