12

Suppose that an arrow can be

  1. With one head (o), two heads (t), or "squiggly" (g),
  2. Single-lined (s) or double-lined (d),
  3. With (w) or without (o) a label (i.e., text above)

Some of those arrows are defined by "standard" packages, like \xrightarrow{abc}, which corresponds to osw, some of them needs more "exotic" packages, like \xLongrightarrow (tdw), defined by extarrows.

Some needs to be drawn with Tikz, like \xtwoheadrightarrow (tsw), cf. https://tex.stackexchange.com/a/60219/34551

I tried to have a macro allowing me to define all those combinations at once, and to display them with a uniform appearance. I tried to use xparse and l3regex, which seemed to be a good combination to handle it (shout-out to https://tex.stackexchange.com/a/188593/34551).

I think I managed to define a macro that define them all at once, but they look terrible, i.e., really not uniform, and the hack I used for tdw is simply awful.

Should I give up my idea of taking an arrow there and another there, and just draw them all in tiKz? How to make sure, in this case, that they do behave like mathematical operators?

Here's what I managed to get:

enter image description here

Using the following code:

\documentclass{article}
\usepackage{xparse,l3regex}
\usepackage{amssymb}
\usepackage{amsmath}
\usepackage{extarrows} % For \xLongrightarrow
\usepackage{mathtools}
\usepackage{tikz}
\usetikzlibrary{calc,decorations.pathmorphing,shapes,arrows}

\newcommand\xRightsquigarrow[1]{% https://tex.stackexchange.com/a/83086
  \mathrel{%
    \begin{tikzpicture}[baseline= {( $ (current bounding box.south) + (0,-0.5ex) $ )}]
      \node[inner sep=.5ex] (a) {$\scriptstyle #1$};
      \path[draw,implies-,double distance between line centers=1.5pt,decorate,
      decoration={zigzag,amplitude=0.7pt,segment length=1.2mm,pre=lineto,
    pre   length=4pt}] 
      (a.south east) -- (a.south west);
    \end{tikzpicture}}%
}
\newcommand\Rightsquigarrow{%
  \mathrel{%
    \begin{tikzpicture}[baseline= {( $ (current bounding box.south) + (0,-0.5ex) $ )}]
      \node[inner sep=.5ex] (a) {\hspace*{1em}};
      \path[draw,implies- ,double distance between line centers=1.5pt,decorate,
      decoration={zigzag,amplitude=0.7pt,segment length=1.2mm,pre=lineto,
    pre   length=4pt}] 
      (a.south east) -- (a.south west);
    \end{tikzpicture}}%
}

\makeatletter % https://tex.stackexchange.com/a/114242/34551
\providecommand*{\twoheadrightarrowfill@}{%
  \arrowfill@\relbar\relbar\twoheadrightarrow
}
\providecommand*{\xtwoheadrightarrow}[2][]{%
  \ext@arrow 0579\twoheadrightarrowfill@{#1}{#2}%
}
\makeatother

\ExplSyntaxOn
\NewDocumentCommand{\ar}{ m o }
{
  \IfNoValueTF{#2}
  {% there is no trailing optional argument, set the boolean to false
    \bool_set_false:N \l_label_bool
  }
  {% there is a trailing optional argument, set the boolean to true
    \bool_set_true:N \l_label_bool
    % and store the argument for later usage
    \tl_set:Nn \l_label_tl { #2 }
  }
  \ar_short:n { #1 }
}


% First byte: o, t, g     (one head, two heads, squiggly)
% Second byte: s, d       (single or double)
% Third byte:             (label to write on it)

\bool_new:N \l_label_bool
\tl_new:N \l_label_tl

\cs_new_protected:Npn \ar_short:n #1
{% separate the two characters
  \ar_short_aux:NN #1
}

\cs_new_protected:Npn \ar_short_aux:NN #1 #2{
  \str_case:nn { #1 }
  {
    {o}% One head
    {
      \str_case:nn { #2 }
      {{s}{% Single
      \xtwoheadrightarrow[]{  \bool_if:NT \l_label_bool { \l_label_tl }}
    }{d}{% Double
      \xRightarrow{  \bool_if:NT \l_label_bool { \l_label_tl }}\!\!\!\!\Rightarrow}
      }
    }
    {t}% Two heads
    {\str_case:nn { #2 }
      {{s}{% Single
      \xrightarrow{  \bool_if:NT \l_label_bool { \l_label_tl }}
    }{d}{% Double
      \xLongrightarrow{  \bool_if:NT \l_label_bool { \l_label_tl }}
    }
      }
    }
    {g}% Squiggly
    {\str_case:nn { #2 }
      {{s}{% Single
      \bool_if:NTF \l_label_bool {\overset{\l_label_tl}{\rightsquigarrow}}{\rightsquigarrow}
    }
    {d}{% Double
      \bool_if:NTF \l_label_bool {\xRightsquigarrow{\l_label_tl}}{\Rightsquigarrow}}
      }
    }
  }
}



\ExplSyntaxOff

\begin{document}

  $a \ar{os} b$

  $a \ar{od} b$

  $a \ar{ts} b$

  $a \ar{td} b$

  $a \ar{gs} b$

  $a \ar{gd} b$

  $a \ar{os}[i_2 : \alpha, 34] b$

  $a \ar{od}[i_2 : \alpha, 34] b$

  $a \ar{ts}[i_2 : \alpha, 34] b$

  $a \ar{td}[i_2 : \alpha, 34] b$

  $a \ar{gs}[i_2 : \alpha, 34] b$

  $a \ar{gd}[i_2 : \alpha, 34] b$

\end{document}

The difference of spacing between them, and the fact that one of them (gsw) is not extensible, are what bug me the most.

7
  • Should you allow subscripts too? :)
    – user30471
    Commented Aug 12, 2016 at 6:43
  • Well, if you want to make it harder, sure ;-). I could also asked for "starred" version, i.e., with a star as an exponent, to denote transitivity, but I think that, generally, a ^* is enough for that.
    – Clément
    Commented Aug 12, 2016 at 6:48
  • If you really want to make it harder, I guess an arrow can be barred, too, to denote that b is not accessible from a using that kind of transition. \centernot is not always good-looking…
    – Clément
    Commented Aug 12, 2016 at 7:02
  • Isn't it rather confusing to use o for two different things?
    – cfr
    Commented Aug 24, 2016 at 23:52
  • 1
    xypic has a powerful engine generating arrows. You might want to study its logic.
    – Symbol 1
    Commented Aug 30, 2016 at 14:56

2 Answers 2

13
+150

Substantially REVISED to achieve what was missing on the earlier post. Namely, all desired arrow types are supported (albeit with wavy, rather than jagged, squiggle), uniform arrow lengths are achieved for a given overset, regardless of arrow type, all math styles are supported.

Some tweaks would be required for a font-size change (currently 10pt) in order to achieve uniformly sized building blocks and arrowheads (see bottom of answer for details).

I have not wrapped it in an overarching macro, but the individual arrow types are called \zigzagarrow (single squiggly line, single arrowhead), \ZigZagarrow (double squiggly line, single arrowhead), \linearrow (single line, single arrowhead), \Linearrow (double line, single arrowhead), \zzigzagarrow (single squiggly line, double arrowhead), \ZZigZagarrow (double squiggly line, double arrowhead), \llinearrow (single line, double arrowhead), and \LLinearrow (double line, double arrowhead),

\documentclass[10pt]{article}
\usepackage{stackengine,scalerel,graphicx,trimclip,amsmath,wasysym}
% UNIFORMLY SIZED BUILDING BLOCKS
\savestack\zigzagtextstyle{\vphantom{()}\scalebox{.86666}[1]{\kern-.5pt\AC\kern-.5pt}}
\savestack\onelinetextstyle{\vphantom{()}\scalebox{1}[1]{\kern-1pt{$-$}\kern-1pt}}
\savestack\twolinetextstyle{\vphantom{()}\scalebox{1}[1]{\kern-1pt{$=$}\kern-1pt}}
\savestack\ZigZagtextstyle{% DERIVATIVE OF \zigzagtextstyle
  $\vphantom{()}\smash{\raisebox{-.002em}{$\vcenter{\hbox{%
   \stackengine{-.805em}{\zigzagtextstyle}{\zigzagtextstyle}{O}{c}{F}{F}{S}}}$}}$}
\newlength\repwidth
\repwidth=\wd\zigzagtextstylecontent\relax
% UNIFORMLY SIZED ARROWHEADS
\newcommand\rightarrowhead{\clipbox{4pt -2pt 0pt -2pt}{$\rightarrow$}}
\newcommand\Rightarrowhead{\clipbox{4pt -2pt 0pt -2pt}{$\Rightarrow$}}
\newcommand\rrightarrowhead{\clipbox{5.5pt -2pt 0pt -2pt}{$\rightarrow\kern-8.5pt\rightarrow$}}
\newcommand\RRightarrowhead{\clipbox{5.5pt -2pt 0pt -2pt}{$\Rightarrow\kern-8.5pt\Rightarrow$}}
% HALF-CYCLE BLOCKS PLUS ARROWHEAD
\newcommand\fXarrowtextstyle[2]{$\vphantom{()}\smash{\vcenter{\hbox{\kern-.03\repwidth%
   \clipbox{-.03\repwidth{} 0pt .527\repwidth{} 0pt}{#1}}}}#2$}
%
\savestack\fzigarrowtextstyle{\fXarrowtextstyle{\zigzagtextstyle}{\rightarrowhead}}
\savestack\fZigarrowtextstyle{\fXarrowtextstyle{\ZigZagtextstyle}{\Rightarrowhead}}
\savestack\flinearrowtextstyle{\fXarrowtextstyle{\onelinetextstyle}{\rightarrowhead}}
\savestack\fLinearrowtextstyle{\fXarrowtextstyle{\twolinetextstyle}{\Rightarrowhead}}
\savestack\fzzigarrowtextstyle{\fXarrowtextstyle{\zigzagtextstyle}{\rrightarrowhead}}
\savestack\fZZigarrowtextstyle{\fXarrowtextstyle{\ZigZagtextstyle}{\RRightarrowhead}}
\savestack\fllinearrowtextstyle{\fXarrowtextstyle{\onelinetextstyle}{\rrightarrowhead}}
\savestack\fLLinearrowtextstyle{\fXarrowtextstyle{\twolinetextstyle}{\RRightarrowhead}}
% SUPPORT FOR ALL MATH STYLES
%  - CYCLE BLOCKS
\newcommand\zigzag{\scalerel*{\zigzagtextstyle}{()}}
\newcommand\ZigZag{\scalerel*{\ZigZagtextstyle}{()}}
\newcommand\oneline{\scalerel*{\onelinetextstyle}{()}}
\newcommand\twoline{\scalerel*{\twolinetextstyle}{()}}
%  - ARROW HEADS
\newcommand\fzigarrow{\scalerel*{\fzigarrowtextstyle}{()}}
\newcommand\fZigarrow{\scalerel*{\fZigarrowtextstyle}{()}}
\newcommand\flinearrow{\scalerel*{\flinearrowtextstyle}{()}}
\newcommand\fLinearrow{\scalerel*{\fLinearrowtextstyle}{()}}
\newcommand\fzzigarrow{\scalerel*{\fzzigarrowtextstyle}{()}}
\newcommand\fZZigarrow{\scalerel*{\fZZigarrowtextstyle}{()}}
\newcommand\fllinearrow{\scalerel*{\fllinearrowtextstyle}{()}}
\newcommand\fLLinearrow{\scalerel*{\fLLinearrowtextstyle}{()}}
% ARROW-EXTENDING USER MACROS
\newcommand\zigzagarrow[1][]{\Xarrow{\zigzag}{\fzigarrow}{#1}{-.65}}
\newcommand\ZigZagarrow[1][]{\Xarrow{\ZigZag}{\fZigarrow}{#1}{-.40}}
\newcommand\linearrow[1][]{\Xarrow{\oneline}{\flinearrow}{#1}{-.65}}
\newcommand\Linearrow[1][]{\Xarrow{\twoline}{\fLinearrow}{#1}{-.40}}
\newcommand\zzigzagarrow[1][]{\Xarrow{\zigzag}{\fzzigarrow}{#1}{-.65}}
\newcommand\ZZigZagarrow[1][]{\Xarrow{\ZigZag}{\fZZigarrow}{#1}{-.40}}
\newcommand\llinearrow[1][]{\Xarrow{\oneline}{\fllinearrow}{#1}{-.65}}
\newcommand\LLinearrow[1][]{\Xarrow{\twoline}{\fLLinearrow}{#1}{-.40}}
% \Xarrow EXTENDING-ARROW MACRO
% #1 - arrow building block
% #2 - arrowhead
% #3 - overtext
% #4 - overset distance multiplier wrt top of paren
\newcommand\Xarrow[4]{\ThisStyle{\mathrel{\Xarrowhelp{#1#2}{#3}{#1}{#4}}}}
\newcommand\Xarrowhelp[4]{%
  \setbox0=\hbox{$\SavedStyle#1$}%
  \setbox2=\hbox{$\SavedStyle_{\,\,#2\,\,}$}%
  \ifdim\wd0<\wd2\relax\Xarrowhelp{#3#1}{#2}{#3}{#4}%
  \else\stackengine{#4\LMex}{\copy0}{\copy2\,}{O}{c}{F}{T}{S}\fi%
}
\begin{document}
\setstackEOL{\\}
\Longunderstack{
3 cycles:
\\
$\zigzagtextstyle\zigzagtextstyle\zigzagtextstyle$
\setbox0=\hbox{\zigzagtextstyle}\the\wd0
\\
$\ZigZagtextstyle\ZigZagtextstyle\ZigZagtextstyle$
\setbox0=\hbox{\zigzagtextstyle}\the\wd0
\\
$\onelinetextstyle\onelinetextstyle\onelinetextstyle$
\setbox0=\hbox{\onelinetextstyle}\the\wd0
\\
$\twolinetextstyle\twolinetextstyle\twolinetextstyle$
\setbox0=\hbox{\twolinetextstyle}\the\wd0
}
\qquad\Longunderstack{
Arrowheads:
\\
\rightarrowhead\setbox0=\hbox{\rightarrowhead}\the\wd0
\\
\Rightarrowhead\setbox0=\hbox{\Rightarrowhead}\the\wd0
\\
\rrightarrowhead\setbox0=\hbox{\rrightarrowhead}\the\wd0
\\
\RRightarrowhead\setbox0=\hbox{\RRightarrowhead}\the\wd0
}
\qquad\Longunderstack{
Half-cycle + arrowheads
\\
\fzigarrowtextstyle \quad \fzzigarrowtextstyle
\\
\fZigarrowtextstyle \quad \fZZigarrowtextstyle
\\
\flinearrowtextstyle \quad \fllinearrowtextstyle
\\
\fLinearrowtextstyle \quad \fLLinearrowtextstyle
}

Examples:

\Shortunderstack{
$A \zigzagarrow B \zigzagarrow[T=298K] C$
\\
$A \ZigZagarrow B \ZigZagarrow[T=298K] C$
\\
$A \linearrow B \linearrow[T=298K] C$
\\
$A \Linearrow B \Linearrow[T=298K] C$
\\
$A \zzigzagarrow B \zzigzagarrow[T=298K] C$
\\
$A \ZZigZagarrow B \ZZigZagarrow[T=298K] C$
\\
$A \llinearrow B \llinearrow[T=298K] C$
\\
$A \LLinearrow B \LLinearrow[T=298K] C$
}
\quad
\Shortunderstack{
$\scriptstyle A \zigzagarrow B \zigzagarrow[T=298K] C$
\\
$\scriptstyle A \ZigZagarrow B \ZigZagarrow[T=298K] C$
\\
$\scriptstyle A \linearrow B \linearrow[T=298K] C$
\\
$\scriptstyle A \Linearrow B \Linearrow[T=298K] C$
\\
$\scriptstyle A \zzigzagarrow B \zzigzagarrow[T=298K] C$
\\
$\scriptstyle A \ZZigZagarrow B \ZZigZagarrow[T=298K] C$
\\
$\scriptstyle A \llinearrow B \llinearrow[T=298K] C$
\\
$\scriptstyle A \LLinearrow B \LLinearrow[T=298K] C$
}
\quad
\Shortunderstack{
$\scriptscriptstyle A \zigzagarrow B \zigzagarrow[T=298K] C$
\\
$\scriptscriptstyle A \ZigZagarrow B \ZigZagarrow[T=298K] C$
\\
$\scriptscriptstyle A \linearrow B \linearrow[T=298K] C$
\\
$\scriptscriptstyle A \Linearrow B \Linearrow[T=298K] C$
\\
$\scriptscriptstyle A \zzigzagarrow B \zzigzagarrow[T=298K] C$
\\
$\scriptscriptstyle A \ZZigZagarrow B \ZZigZagarrow[T=298K] C$
\\
$\scriptscriptstyle A \llinearrow B \llinearrow[T=298K] C$
\\
$\scriptscriptstyle A \LLinearrow B \LLinearrow[T=298K] C$
}
\end{document}

enter image description here

As mentioned earlier, the key is achieving uniformly sized building blocks (cycles and arrowheads, respectively). For 12pt font, here are tweaks required to achieve that:

\savestack\onelinetextstyle{\vphantom{()}\scalebox{.97196}[1]{\kern-1.1pt{$-$}\kern-1.1pt}}
\savestack\twolinetextstyle{\vphantom{()}\scalebox{.97121}[1]{\kern-1pt{$=$}\kern-1pt}}

\newcommand\rrightarrowhead{\clipbox{6.5pt -2pt 0pt -2pt}{$\rightarrow\kern-9.5pt\rightarrow$}}
\newcommand\RRightarrowhead{\clipbox{6.5pt -2pt 0pt -2pt}{$\Rightarrow\kern-9.5pt\Rightarrow$}}

enter image description here

4
  • Thanks for your detailed and really useful answer. I find the space above the label to be insufficient, as witness by the following example: \begin{tabular}{c} t\\ \hline $ B \zigzagarrow[T=298K] C$ \end{tabular}. I don't manage to patch it myself, do you have any suggestion?
    – Clément
    Commented Sep 21, 2016 at 20:30
  • @Clément One easy workaround is \addstackgap[3pt]{$ B \zigzagarrow[T=298K] C$} or [4pt]. I'll try to think of an automated fix. Commented Sep 22, 2016 at 0:16
  • @Clément If you add $\mathstrut$ just before \copy2 in the last line of the \Xarrowhelp definition, this will add extra vertical space needed. However, the -.65 and -.40 parameters in the \zigzagarrow et al definitions may have to be larger in magnitude, for example -.85 and -.60. Commented Sep 22, 2016 at 0:31
  • 1
    I vote...up for your excellent zig zag.
    – Sebastiano
    Commented Jul 8, 2021 at 12:21
1

I have used Steven B. Segletes's answer for many year and value it. Unfortunately, it is quite computationally expensive, and made some simple document that I had take 26 seconds to compile.

I have re-written my own solution, without the double-lined (d) variations: this is not exactly an answer to this question, but I thought it could be helpful nevertheless.

\documentclass{article}
\usepackage{tikz}
\usepackage{mathtools}
\usetikzlibrary{calc,decorations.pathmorphing,shapes}

\newcounter{sarrow}
\NewDocumentCommand{\xrsquigarrow}{m}{ % Backward arrow
    \stepcounter{sarrow}%
    \mathrel{\begin{tikzpicture}[baseline= {( $(current bounding box.south)$ )}]
            \node[inner sep=.5ex, font=\scriptsize] (\thesarrow) {$#1$};
            \path[draw,->,decorate,
            %decoration={zigzag,amplitude=0.7pt,segment length=1.2mm,pre=lineto,pre length=4pt}
            decoration={snake,
                amplitude=-1pt, segment length =1.5mm, pre=lineto, post length=2pt%, post length=3pt
            }
            ] 
            (\thesarrow.south west) -- (\thesarrow.south east);
    \end{tikzpicture}}%
}

\newcommand{\tootip}{\mathrel{\tikz[anchor=base]{\draw[->](0,0)--(.01,0);}}} % https://tex.stackexchange.com/a/645771/34551

% https://tex.stackexchange.com/a/260581/34551
\newcommand{\rightarrowdbl}{\mathrel{\ooalign{ % https://tex.stackexchange.com/a/22375/34551
            $\rightarrow$\cr %
            \hidewidth\raise.23em\hbox{$\tootip\mkern4mu$}}}
        }

\newcommand{\xrightarrowdbl}[1]{%
    \mathrel{\ooalign{%
            $\xrightarrow{#1}$\cr%
            \hidewidth\raise.23em\hbox{$\tootip\mkern4mu$}}}
}

\ExplSyntaxOn
\RenewDocumentCommand{\r}{m o o} 
% m = direction (b, f, bf)
% o = label
% o = key
% Assumption is that you will never have a key without a label.
{
    \str_case:nnF { #1 }
    {
        {f}{% Forward case
            \IfValueTF{#2} % We test if the label is here.
            {
                \IfValueTF{#3}% We test if the key is here.
                    {\xrightarrow{#2[#3]} } % Forward,  label and key
                    {\xrightarrow{#2}} % Forward, a label but no key
            }
            { \rightarrow } % Forward, no label nor key
        }
        {b}{% backward case
            \IfValueTF{#2} % We test if the label is here.
            {
                \IfValueTF{#3}% We test if the key is here.
                {\xrsquigarrow{#2[#3]}} % Backward,  label and key
                {\xrsquigarrow{#2}} % Backward, a label but no key
            }
            { \xrsquigarrow{\mkern10mu} } % Backward, no label nor key
        }
        {fb}{ % Forward and backward case
        \IfValueTF{#2} % We test if the label is here.
            {
            \IfValueTF{#3}% We test if the key is here.
            { \xrightarrowdbl{#2[#3]}} % Backward, label and key
            { \xrightarrowdbl{#2}} % Backward, a label but no key
        }
        {\rightarrowdbl} % Forward and backward, no label nor key
        }
    }
    { #1 }
}
\ExplSyntaxOff

\begin{document}
\begin{align*}
    \Aboxed{P \r{f} Q} & \Aboxed{P \r{f}[\alpha] Q} & \Aboxed{P \r{f}[\alpha][k] Q}\\
    \Aboxed{P \r{b} Q} & \Aboxed{P \r{b}[\alpha] Q} & \Aboxed{P \r{b}[\alpha][k] Q} \\
    \Aboxed{P \r{fb} Q} & \Aboxed{P \r{fb}[\alpha] Q} & \Aboxed{P \r{fb}[\alpha][k] Q} \\
\end{align*}
\end{document}

gives:

enter image description here

My document went from 26 seconds to 6 seconds using this alternative way of generating arrows…

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .