9

I'm still a bit of a novice at TikZ.

The following MWE seems fairly simple and straight forward, but it compiles much more slowly than it seems to me it should. Could someone explain what's going on? Or possibly make suggestions which will still result in the same image but compile more quickly?

Initially, I thought it might be the calc library which was slowing things down. The MWE shows how I rewrote things to avoid using calc. The old approach is merely commented out.

\documentclass{article}
\usepackage[margin=0.5in]{geometry}
\usepackage{tikz}
%%\usetikzlibrary{calc}
\usetikzlibrary{arrows.meta}

\makeatletter

\def\@aem(#1)#2;{\node[anchor=base] at (#1) {#2}; 
                 \draw (#1/nw) rectangle (#1/se);
                 \path (#1) + (0,6ex) coordinate (#1/t)
                            + (0,2.5ex) coordinate (#1/h);
                 %%\coordinate (#1/t) at ($(#1)+(0,6ex)$); 
                 %%\coordinate (#1/h) at ($(#1)+(0,2ex)$); 
                 \draw[line width=2pt,arrows=-{Stealth[length=2ex,width=1.5ex]}] (#1/t) -- (#1/h); }
\def\@aeo(#1)#2;{\node[inner sep=1ex,anchor=base] at (#1) {#2}; \draw (#1/nw) rectangle (#1/se);}
\def\@aex(#1)#2;{\node[anchor=base] at (#1) {#2};}
\def\@aef(#1)#2;{\node[anchor=base] at (#1) {\textcolor{gray!20}{#2}};}

\def\ae#1#2#3{\csname @ae#1\endcsname(p#3){#2};}

\makeatother

\def\aemarkup#1{%%
  \begin{tikzpicture}
    \foreach \x in {0,1,2,...,14}
      {
        \path (0,0) --  (\x*0.45cm,0) coordinate (p\x)
                     +  (115:3ex)     coordinate (p\x/nw)
                     +  (-20:1.5ex)   coordinate (p\x/se);
        \draw (p\x) ++(0,-0.5ex) -- ++(0,-2ex);
        %%\coordinate (p\x)    at ($(0,0)+\x*(0.45cm,0)$);
        %%\coordinate (p\x/nw) at ($(p\x)+(115:3ex)$);
        %%\coordinate (p\x/se) at ($(p\x)+(-20:1.5ex)$);
        %%\draw (p\x) -- ($(p\x)-(0,2ex)$);
      }
    \foreach \y [count=\yi from 0] in {#1}
      { \expandafter\ae\y\yi}  
  \end{tikzpicture}\par}

\begin{document}

Diagram:

\aemarkup{mx , ox , ox , ox , ox , ox , ox , oM , ox , ox , ox , oa , ox , ob , oc}
\aemarkup{fx , ox , mx , ox , ox , ox , ox , oM , ox , ox , ox , oa , ox , ob , oc}
\aemarkup{fx , ox , fx , ox , mx , ox , ox , oM , ox , ox , ox , oa , ox , ob , oc}
\aemarkup{fx , ox , fx , ox , fx , ox , mx , oM , ox , ox , ox , oa , ox , ob , oc}
\aemarkup{fx , ox , fx , ox , fx , ox , fx , oM , mx , ox , ox , oa , ox , ob , oc}
\aemarkup{fx , ox , fx , ox , fx , ox , fx , oM , fx , ox , mx , oa , ox , ob , oc}
\aemarkup{fx , ox , fx , ox , fx , ox , fx , oM , fx , ox , fx , oa , mx , ob , oc}
\aemarkup{fx , ox , fx , ox , fx , ox , fx , oM , fx , ox , fx , oa , fx , ob , mc}
\aemarkup{mx , ox , ox , oM , ox , oa , ob , xc}
\aemarkup{fx , ox , mx , oM , ox , oa , ob , xc}
\aemarkup{fx , ox , fx , oM , mx , oa , ob , xc}
\aemarkup{fx , ox , fx , oM , fx , oa , mb , xc}
\aemarkup{mx , oM , oa , xb , xc}
\aemarkup{fx , oM , ma , xb , xc}
\aemarkup{mM , xa , xb , xc}

\end{document}

enter image description here

13
  • 1
    Use the + notation for PGFmath input so that it doesn’t parse the argument but simply uses TeX assignments (faster, see manual and my answer). Instead of text width=1.5ex use text width=+1.5ex. Also, instead of (0,2.5ex) use (+0pt,+2.5ex): the + and no additional calculation of a vector (it does (0,0) + (0pt, 2.5ex) otherwise). — And, if you use that repeating code so often, you might just be coding it (or at least parts of it) in PGF, less parsing, more macro-ing. The loops are pretty simple, maybe replace them with a basic one. Oct 30, 2013 at 0:34
  • What is \usetikzlibrary{arrows.meta}? Tex complains about it when I try to typeset your code...
    – Alenanno
    Oct 30, 2013 at 0:39
  • @Alenanno I have a fairly CSV version of TikZ. It's a new library that's been available since the last week of September.
    – A.Ellett
    Oct 30, 2013 at 0:40
  • 1
    Why do you use a node, but then you draw a rectangle around it and add auxiliary coordinates (and stuff, to be honest, I haven’t followed your MWE to the dot). You can just use a drawn node with its anchors here. (Also more PGF instead of TikZ.) And, as a last resort, there is always tikz-external/the external library. Oct 30, 2013 at 0:42
  • 1
    All you can see in your code is TikZ (and then some). TikZ is only the top-layer to PGF and parses and translates the \path …; into PGF code and thus even more (there is for example no direct equivalent to nodes along paths (think pos, sloped, etc.). There are some relatedbut no direct “What is PGF? What is TikZ?” questions: Is there an advantage in using the pgf Basic Layer over tikz? and Conversion of pgf code into TikZ code? Oct 30, 2013 at 1:30

1 Answer 1

9

I would make the code easier.

  • Use the chains library.
  • Use the full power of nodes:
    • text * keys so that all nodes have the same size,
    • append after command helps to place the arrow and the little lines at the bottom.
  • Use styles.

I also have used the node contents key that has been introduced with the CVS version (relatively easy to implement but very much helpful). Note that there is no text group for the node anymore ({<text>} is missing).

The \aemarkup needs a third (second mandatory) argument that represents the maximum number of lines/nodes. It is used to fill up the line if less then #3 loop elements are given. This was your hard-coded 14.

Code

\documentclass[varwidth]{standalone}
\usepackage{tikz} \usetikzlibrary{chains,arrows.meta}
\tikzset{
  my node/.style 2 args={#1 style/.try, node contents={#2}},
  x style/.style={text width=\widthof{M}, text depth=+0pt, text height=\heightof{M}+2pt,
    align=center, inner xsep=+0.5pt, inner ysep=+1pt,
    append after command={(\tikzlastnode.south) edge ++ (down:+1.5ex)}},
  f style/.style={x style, text=gray},
  o style/.style={x style, draw},
  m style/.style={o style,
    append after command={% the arrow from above
      (\tikzlastnode.north) edge
        [line width=+2pt, arrows={Stealth[length=+2ex,width=+1.5ex]}-] ++ (up:+3.5ex)}}}
\newcommand*\aemarkup[3][]{
\tikzpicture[start chain=going mid right,node distance=+.1em,#1]
  \foreach \aeI[count=\cnt from 2,remember=\cnt] in {#2}% \cnt will be global after this
    \node[on chain,my node/.expand once=\aeI];
  \ifnum\cnt>#3\else
    \foreach \aeI in {\cnt,...,#3}\node[on chain, my node={x}{}];
  \fi
\endtikzpicture\par}
\begin{document}
Diagram:

\aemarkup{mx , ox , ox , ox , ox , ox , ox , oM , ox , ox , ox , oa , ox , ob , oc}{15}
\aemarkup{fx , ox , mx , ox , ox , ox , ox , oM , ox , ox , ox , oa , ox , ob , oc}{15}
\aemarkup{fx , ox , fx , ox , mx , ox , ox , oM , ox , ox , ox , oa , ox , ob , oc}{15}
\aemarkup{fx , ox , fx , ox , fx , ox , mx , oM , ox , ox , ox , oa , ox , ob , oc}{15}
\aemarkup{fx , ox , fx , ox , fx , ox , fx , oM , mx , ox , ox , oa , ox , ob , oc}{15}
\aemarkup{fx , ox , fx , ox , fx , ox , fx , oM , fx , ox , mx , oa , ox , ob , oc}{15}
\aemarkup{fx , ox , fx , ox , fx , ox , fx , oM , fx , ox , fx , oa , mx , ob , oc}{15}
\aemarkup{fx , ox , fx , ox , fx , ox , fx , oM , fx , ox , fx , oa , fx , ob , mc}{15}
\aemarkup{mx , ox , ox , oM , ox , oa , ob , xc}{15}
\aemarkup{fx , ox , mx , oM , ox , oa , ob , xc}{15}
\aemarkup{fx , ox , fx , oM , mx , oa , ob , xc}{15}
\aemarkup{fx , ox , fx , oM , fx , oa , mb , xc}{15}
\aemarkup{mx , oM , oa , xb , xc}{15}
\aemarkup{fx , oM , ma , xb , xc}{15}
\aemarkup{mM , xa , xb , xc}{15}
\end{document}

Output

enter image description here

Code (PGF)

For comparison, a quick and dirty implementation with only PGF (and pgffor).

The effect of the keys text width, text depth and text height (which are TikZ keys) are reproduced with a rule (a strut essentially) and LaTeX’s \makebox. The keys draw and text that sets the stroke and text color are implemented with rudimentary keys that set TeX macros. The same is true for the arrow.

The effect of the chains library is hard-coded with the \ifnum\cnt conditional and the \pgftransformshift.

TikZ really does a lot under the hood, doesn’t it?


\documentclass[varwidth]{standalone}
\usepackage{pgf,pgffor} \usepgflibrary{arrows.meta}
\pgfset{draw path/.code=\def\aeusepath{stroke},
        none path/.code=\def\aeusepath{discard},
        gray text/.code=\def\aenodesetupcol{\color{gray}}}
\let\arrowpath\relax
\let\aenodesetupcol\relax
\def\Arrowpath{%
  \edef\linewidth{\the\pgflinewidth}
  \pgfsetlinewidth{+2pt}
  \pgfsetstrokecolor{black}
  \pgfpathmoveto{\pgfpointanchor{chain-\cnt}{north}}
  \pgfpathlineto{\pgfpointadd{\pgfpointanchor{chain-\cnt}{north}}{\pgfqpoint{0pt}{15pt}}}
  \pgfsetarrowsstart{Stealth[length=+2ex,width=+1.5ex]}
  \pgfusepath{stroke}
  \pgfsetlinewidth{\linewidth}}
\pgfset{
  x style/.style={inner xsep=+0.5pt, inner ysep=+1pt, none path},
  f style/.style={x style, gray text},
  o style/.style={x style, draw path},
  m style/.style={o style, /utils/exec=\let\arrowpath\Arrowpath}}
\newcommand*\aenodesetup[1]{%
  \vrule height1emdepth0ptwidth0pt\relax
  \makebox[1em][c]{\aenodesetupcol#1}}
\newcommand*\aeaux[2]{%
\begingroup
  \pgfset{#1 style}
  \ifnum\cnt=1
    \pgfnode{rectangle}{base}{\aenodesetup{#2}}{chain-\cnt}{\pgfusepath{\aeusepath}}
  \else
    \begingroup
      \pgftransformshift{
        \pgfpointadd{\pgfpointanchor{chain-\the\numexpr\cnt-1\relax}{base east}}
                    {\pgfqpoint{2pt}{0pt}}}
      \pgfnode{rectangle}{base west}{\aenodesetup{#2}}{chain-\cnt}
        {\pgfusepath{\aeusepath}}
    \endgroup
  \fi
  \pgfsetstrokecolor{black}
  \pgfpathmoveto{\pgfpointanchor{chain-\cnt}{south}}
  \pgfpathlineto{\pgfpointadd{\pgfpointanchor{chain-\cnt}{south}}{\pgfqpoint{0pt}{-6pt}}}
  \pgfusepath{stroke}
  \arrowpath
\endgroup}
\newcommand*\aemarkup[3][]{
\pgfpicture[#1]
  \foreach \aeI[count=\cnt,remember=\cnt] in {#2}% \cnt will be global after this
    {\expandafter\aeaux\aeI}
  \ifnum\cnt=#3\else
    \edef\Cnt{\the\numexpr\cnt+1\relax}
    \foreach \cnt in {\Cnt,...,#3}{\aeaux{x}{}}
  \fi
\endpgfpicture\par}
\begin{document}
Diagram:

\aemarkup{mx , ox , ox , ox , ox , ox , ox , oM , ox , ox , ox , oa , ox , ob , oc}{15}
\aemarkup{fx , ox , mx , ox , ox , ox , ox , oM , ox , ox , ox , oa , ox , ob , oc}{15}
\aemarkup{fx , ox , fx , ox , mx , ox , ox , oM , ox , ox , ox , oa , ox , ob , oc}{15}
\aemarkup{fx , ox , fx , ox , fx , ox , mx , oM , ox , ox , ox , oa , ox , ob , oc}{15}
\aemarkup{fx , ox , fx , ox , fx , ox , fx , oM , mx , ox , ox , oa , ox , ob , oc}{15}
\aemarkup{fx , ox , fx , ox , fx , ox , fx , oM , fx , ox , mx , oa , ox , ob , oc}{15}
\aemarkup{fx , ox , fx , ox , fx , ox , fx , oM , fx , ox , fx , oa , mx , ob , oc}{15}
\aemarkup{fx , ox , fx , ox , fx , ox , fx , oM , fx , ox , fx , oa , fx , ob , mc}{15}
\aemarkup{mx , ox , ox , oM , ox , oa , ob , xc}{15}
\aemarkup{fx , ox , mx , oM , ox , oa , ob , xc}{15}
\aemarkup{fx , ox , fx , oM , mx , oa , ob , xc}{15}
\aemarkup{fx , ox , fx , oM , fx , oa , mb , xc}{15}
\aemarkup{mx , oM , oa , xb , xc}{15}
\aemarkup{fx , oM , ma , xb , xc}{15}
\aemarkup{mM, xa, xb, xc}{15}
\end{document}
3
  • Thanks for the answer; I understand your comments now quite a bit better. There's a lot here that I don't know yet: chain, edge, and handler/.try. Your my node is very illustrative and clean. Speed is marginally better. Let me think on this a bit.
    – A.Ellett
    Oct 30, 2013 at 2:33
  • pgf code is awesome!!!
    – A.Ellett
    Oct 30, 2013 at 2:52
  • @A.Ellett The edge simply creates a separate path with a to path (default: --), this makes it possible to use things like different stroke/fill color, different arrow tips on one path. This is only really necessary where we have both the little line at the bottom and the arrow at one node (which is all done in one \path). The node paths themselves are always on a separate path. The .try handler is only used because I didn’t know which #1 styles are to be expected and it suppressed error messages. It should be safe to remove it. For the chains library see the manual and TeX.sx. :) Oct 30, 2013 at 2:54

You must log in to answer this question.

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