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

A.Ellett
  • 50,533
  • 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. – Qrrbrbirlbel Oct 30 '13 at 00:34
  • What is \usetikzlibrary{arrows.meta}? Tex complains about it when I try to typeset your code... – Alenanno Oct 30 '13 at 00: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 '13 at 00:40
  • Another small suggestion: rather than creating a separate tikzpicture environment for each instance of \aemarkup, have a single one and have \aemarkup just input some TikZ code. You can use a scope to adjust the vertical spacing, and a custom counter to move each level down the appropriate distance. That also means the rows of vertical bars can’t shift slightly (as in the penultimate and final lines). – alexwlchan Oct 30 '13 at 00:41
  • 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 [tag:tikz-external]/the external library. – Qrrbrbirlbel Oct 30 '13 at 00:42
  • @alexwlchan Unfortunately, I intend to have text between the pictures. – A.Ellett Oct 30 '13 at 00:42
  • @A.Ellett: Fair enough. – alexwlchan Oct 30 '13 at 00:43
  • @Qrrbrbirlbel I was having difficulty getting the natural anchors to behave as I wanted them to. Also regarding your earlier comment, I'm not entirely sure how extensively it should be applied. – A.Ellett Oct 30 '13 at 00:43
  • @Qrrbrbirlbel Could you explain what you mean by "more PGF instead of TikZ"? – A.Ellett Oct 30 '13 at 01:08
  • @A.Ellett That was related to the node stuff. Nodes use (except for the path usage itself) only PGF and thus should be faster than drawing the rectangle manually and placing pseudo-anchors. If you add text width=\widthof{M}, text depth=\depthof{j}, text height=\heightof{M} (or simply the widest, deepest and highest character you’re going to use) to the nodes, the should have the same disregarding their content. – Qrrbrbirlbel Oct 30 '13 at 01:18
  • The little lines could be drawn as an append after command to the node, he nodes themselves might just be placed on a chain. This all would make it possible to store m, x, o and f entirely in styles. (Simpler code, probably not really faster.) – Qrrbrbirlbel Oct 30 '13 at 01:20
  • @Qrrbrbirlbel How do you know what is TikZ and what is PGF? I would have thought nodes where just TikZ stuff. I've been searching the documentation and I don't see how one is able to discern this difference. – A.Ellett Oct 30 '13 at 01:22
  • 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? – Qrrbrbirlbel Oct 30 '13 at 01:30

1 Answers1

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}
Qrrbrbirlbel
  • 119,821
  • 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 '13 at 02:33
  • pgf code is awesome!!! – A.Ellett Oct 30 '13 at 02: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. :) – Qrrbrbirlbel Oct 30 '13 at 02:54