8

Motivated by this recent question, I tried to cook up a tikz based solution using the code provided in the answer to this question. More precisely, I tried to cook up equivalents of the pstricks \rnode and \ncline commands in tikz. This is what I got:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}
\newsavebox{\tempbox}

\newcommand{\tikznode}[2]{\relax\ifmmode\savebox{\tempbox}{\ensuremath{#2}}
\makebox[\wd\tempbox]{\tikz[overlay,remember picture,baseline=-0.8ex]%
 \node[minimum width=\wd\tempbox] (#1) {\ensuremath{#2}};}
\else\savebox{\tempbox}{#2}
\makebox[0.5\wd\tempbox]{\tikz[overlay,remember picture,baseline=-0.8ex,minimum width=\wd\tempbox]%
 \node[minimum width=\wd\tempbox] (#1) {#2};}\fi}

\tikzset{
    ncbar angle/.initial=-90,
    ncbar/.style={
        to path=(\tikztostart)
        -- ($(\tikztostart)!#1!\pgfkeysvalueof{/tikz/ncbar angle}:(\tikztotarget)$)
        -- ($(\tikztotarget)!($(\tikztostart)!#1!\pgfkeysvalueof{/tikz/ncbar angle}:(\tikztotarget)$)!\pgfkeysvalueof{/tikz/ncbar angle}:(\tikztostart)$)
        -- (\tikztotarget)
    },
    ncbar/.default=0.5cm
}

\begin{document}
blabla \tikznode{A}{A}\tikznode{B}{B}\tikznode{C}{C} blabla
\tikznode{X}{X}\tikznode{Y}{Y}\tikznode{Z}{Z} blabla
\tikz[overlay,remember picture]{\draw[->] (A) to [ncbar=0.4] (X);}%
\tikz[overlay,remember picture]{\draw[->] (B) to [ncbar=0.5] (Y);}%
\tikz[overlay,remember picture]{\draw[->] (C) to [ncbar=0.6] (Z);}%
\end{document}

It produces:

enter image description here

which looks not completely unreasonable. However, it seems unreasonably complicated and I assume that there is a much more elegant and stable solution. For instance, I need to shift the baseline by hand using baseline=-0.8ex. This doesn't seem right. I also need to compute the width of the text and multiply it by an arbitrary factor of 0.5, which again appears odd. Last but not least, I have to distinguish between math and text modes. (Yes, I know that in principle there is \mathchoice that may be used, but I feel that I've chosen an unnecessarily complicated approach, so I refrained from building this in.) My question is if someone knows how to make this more elegant and, in particular, stable.

2
  • Why do you have to distinguish between text and math mode? \tikz works in both, and the mode of the text in the node isn't affected by the mode in which the \tikz is placed. Nov 21, 2017 at 21:35
  • @TorbjørnT. because I'd also like to use it in math mode. More precisely, if I type $\tikz{\node(x){x};}$, the x is not in math mode, but with $\tikznode{x}{x}$ it is.
    – user121799
    Nov 21, 2017 at 21:42

2 Answers 2

7

I would probably do

\newcommand{\tikznode}[2]{%
\ifmmode%
\tikz[remember picture,baseline=(#1.base),inner sep=0pt] \node (#1) {$#2$};%
\else
\tikz[remember picture,baseline=(#1.base),inner sep=0pt] \node (#1) {#2};%
\fi}

By setting the baseline to the base anchor of the node, you don't have to use some specific value. With inner sep=0pt you wont get any extra space around the text in the node.

Note also that I removed the overlay option. That option means that you get a zero-size bounding box, which you don't want here.

output of code

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}

\newcommand{\tikznode}[2]{%
\ifmmode%
\tikz[remember picture,baseline=(#1.base),inner sep=0pt] \node (#1) {$#2$};%
\else
\tikz[remember picture,baseline=(#1.base),inner sep=0pt] \node (#1) {#2};%
\fi}

\tikzset{
    ncbar angle/.initial=-90,
    ncbar/.style={
        to path=(\tikztostart)
        -- ($(\tikztostart)!#1!\pgfkeysvalueof{/tikz/ncbar angle}:(\tikztotarget)$)
        -- ($(\tikztotarget)!($(\tikztostart)!#1!\pgfkeysvalueof{/tikz/ncbar angle}:(\tikztotarget)$)!\pgfkeysvalueof{/tikz/ncbar angle}:(\tikztostart)$)
        -- (\tikztotarget)
    },
    ncbar/.default=0.5cm
}

\begin{document}
blabla \tikznode{A}{A}$\tikznode{B}{B}$\tikznode{C}{C} blabla
\tikznode{X}{X}\tikznode{Y}{Y}\tikznode{Z}{Z} blabla
\begin{tikzpicture}[overlay,remember picture]
\draw[->] (A) to [ncbar=0.4] (X);
\draw[->] (B) to [ncbar=0.5] (Y);
\draw[->] (C) to [ncbar=0.6] (Z);
\end{tikzpicture}
\end{document}

If you want to implement \mathchoice in this, you get problems, because all the four different choices are typeset. Hence, the naive implementation of

\newcommand{\tikznode}[2]{%
\ifmmode%
\mathchoice
  {\tikz[remember picture,baseline=(#1.base),inner sep=0pt] \node (#1) {$\displaystyle #2$};}%
  {\tikz[remember picture,baseline=(#1.base),inner sep=0pt] \node (#1) {$\textstyle #2$};}%
  {\tikz[remember picture,baseline=(#1.base),inner sep=0pt] \node (#1) {$\scriptstyle #2$};}%
  {\tikz[remember picture,baseline=(#1.base),inner sep=0pt] \node (#1) {$\scriptscriptstyle #2$};}%
\else
\tikz[remember picture,baseline=(#1.base),inner sep=0pt] \node (#1) {#2};%
\fi}

doesnt't work, because the node name will always refer to the scriptscript-version, which isn't used at all in the three other styles. So for the example above, where the B is in textstyle, you get an arrow pointing out of the page.

Perhaps unsurprisingly though, someone has wondered about that problem before, and Heiko Oberdiek provided a solution to it in mathchoice and tikz's remember picture. The refmathstyle package he made has not yet been published on CTAN though, and the link to it in that answer is dead, so here is the non-package version:

output of below code

\documentclass{article}
\usepackage{tikz}
\usepackage{refcount}
\usetikzlibrary{calc}

% from https://tex.stackexchange.com/questions/122415/mathchoice-and-tikzs-remember-picture
\makeatletter
\newcounter{tikznode}
\renewcommand*{\thetikznode}{tikznode@\the\value{tikznode}}
\newcommand*{\tikznodestyle}{%
  \refused{\thetikznode}%
  \ifcase\getrefbykeydefault{\thetikznode}{}{0} %
    \displaystyle
  \or\textstyle
  \or\scriptstyle
  \or\scriptscriptstyle
  \fi
}
\newcommand{\tikznode}[2]{%
\ifmmode%
  \stepcounter{tikznode}%
  \mathchoice
  {\def\@currentlabel{0}\label{\thetikznode}}%
  {\def\@currentlabel{1}\label{\thetikznode}}%
  {\def\@currentlabel{2}\label{\thetikznode}}%
  {\def\@currentlabel{3}\label{\thetikznode}}%
  \tikz[remember picture,baseline=(#1.base),inner sep=0pt] \node (#1) {$\tikznodestyle #2$};
\else
  \tikz[remember picture,baseline=(#1.base),inner sep=0pt] \node (#1) {#2};%
\fi}
\makeatother

% from https://tex.stackexchange.com/questions/55068/is-there-a-tikz-equivalent-to-the-pstricks-ncbar-command
\tikzset{
    ncbar angle/.initial=-90,
    ncbar/.style={
        to path=(\tikztostart)
        -- ($(\tikztostart)!#1!\pgfkeysvalueof{/tikz/ncbar angle}:(\tikztotarget)$)
        -- ($(\tikztotarget)!($(\tikztostart)!#1!\pgfkeysvalueof{/tikz/ncbar angle}:(\tikztotarget)$)!\pgfkeysvalueof{/tikz/ncbar angle}:(\tikztostart)$)
        -- (\tikztotarget)
    },
    ncbar/.default=0.5cm
}

\begin{document}
blabla
$\displaystyle\tikznode{A}{A}\textstyle\tikznode{B}{B}_{\tikznode{C}{C}_{\tikznode{D}{D}}}$
blabla
\tikznode{X}{X}\tikznode{Y}{Y}\tikznode{Z}{Z}\tikznode{W}{W}
blabla
\begin{tikzpicture}[overlay,remember picture]
\draw[->] (A) to [ncbar=0.4] (X);
\draw[->] (B) to [ncbar=0.5] (Y);
\draw[->] (C) to [ncbar=0.6] (Z);
\draw[->] (D) to [ncbar=0.6] (W);
\end{tikzpicture}
\end{document}
19
  • I wouldn't be surprised if there are a number of caveats I haven't considered, so it this is useless let me know, I can always delete it. Nov 21, 2017 at 21:47
  • That's a great trick, so please don't delete it. Do you want to post it also as an answer to this question? (Since even you are a bit struggling with the math mode, perhaps it's really worthwhile to build in \mathchoice.)
    – user121799
    Nov 21, 2017 at 21:51
  • @marmot (I rarely/never dig in to lower-level stuff, so there is a lot I don't know.) I've never used \mathchoice, but you get some problems because all four options are typeset, meaning that the node names get overwritten. I can give an impractical workaround for that, I don't know if there are any good ones. Nov 21, 2017 at 22:07
  • 1
    @marmot I've added a version of \tikznode to the tikzmark library (and renamed it \tikzmarknode). Since remember picture already writes something to the aux file, I use that instead of needing an extra label. I haven't tried working with amsmath as yet. Code is on github. Sep 9, 2018 at 20:05
  • 1
    @DavidWright As for why, that's what ncbar does. The connecting line is parallel to the line between the two points. If the bar should always be horizontal, you could change the style to the much simpler ncbar/.style={to path={(\tikztostart) -- ++(0,-#1) -| (\tikztotarget)}}. Oct 1, 2019 at 15:36
2

Since it's buried in the comments on Torbjørn's answer, this is just to note that this is now a part of the tikzmark package, and correctly handles the different math modes. Here's a demo:

\documentclass{article}
%\url{https://tex.stackexchange.com/q/402462/86}
\usepackage{tikz}
\usetikzlibrary{tikzmark,calc}

\tikzset{
    ncbar angle/.initial=-90,
    ncbar/.style={
        to path=(\tikztostart)
        -- ($(\tikztostart)!#1!\pgfkeysvalueof{/tikz/ncbar angle}:(\tikztotarget)$)
        -- ($(\tikztotarget)!($(\tikztostart)!#1!\pgfkeysvalueof{/tikz/ncbar angle}:(\tikztotarget)$)!\pgfkeysvalueof{/tikz/ncbar angle}:(\tikztostart)$)
        -- (\tikztotarget)
    },
    ncbar/.default=0.5cm
}

\begin{document}
blabla \tikzmarknode{A}{A}\(\tikzmarknode{B}{B}^{\tikzmarknode{C}{C}}\) blabla
\tikzmarknode{X}{X}\tikzmarknode{Y}{Y}\tikzmarknode{Z}{Z} blabla
\begin{tikzpicture}[overlay,remember picture]
\draw[->] (A) to [ncbar=0.4] (X);
\draw[->] (B) to [ncbar=0.5] (Y);
\draw[->] (C) to [ncbar=0.6] (Z);
\end{tikzpicture}
\end{document}

Demonstration of tikzmarknode with different math modes

The lines from C to Z are angled because the bottoms of the nodes are at different levels. I don't know what the original PSTricks ncline does in that circumstance so I left that code as it is in Torbjørn's answer.

You must log in to answer this question.