4

I am trying to draw an automaton using TikZ with the automata package that I am modifying from slide to slide with overlay-beamer-styles. One of the things I am changing is which nodes are marked as accepting states. However, changing this causes everything to shift around slightly, which looks bad when changing from slide to slide. How can I fix this? I would like all of the edges, node labels, etc to stay at the same places and only have the border of the nodes change, ideally by adding/removing the inner line.

Here is a minimum working example:

\documentclass{beamer}

\usepackage{tikz}
\usetikzlibrary{
    automata,
    overlay-beamer-styles}
    
\tikzset{
    accepting on/.style={alt=#1{accepting}{}}
}


\begin{document}

\begin{frame}{}
    \begin{tikzpicture}[initial text=,auto,state/.append style={minimum size=1.5cm}]
        \node[state,initial,accepting on=<2>] (z0) at (0,0) {$z_0$};
        \node[state,accepting on=<1>] (z1) at (2,0) {$z_1$};
        
        \path[->] (z0) edge [loop above] node {$a$} (z0)
                       edge node {$b$} (z1)
                  (z1) edge [loop above] node {$a,b$} (z1);
    \end{tikzpicture}
\end{frame}

\end{document}

and here is the output:

enter image description here

2

2 Answers 2

4

The automata style accepting by double is defined as

\tikzset{accepting by double/.style= {double,outer sep=.5\pgflinewidth+.3pt}} % .3pt is half double width distance

This means that not only a double line is added, but also that the size of the node changes due to the different outer sep.

You can avoid the node from jumping between overlays, if you increase the outer sep also on the non-accepted overlays:

\documentclass{beamer}

\usepackage{tikz}
\usetikzlibrary{
    automata,
    overlay-beamer-styles}
    
\tikzset{
    accepting on/.style={alt=#1{accepting}{accepting, double=none}}
}

\begin{document}

\begin{frame}
    \begin{tikzpicture}[initial text=,auto,state/.append style={minimum size=1.5cm}]
        \node[state,initial,accepting on=<2>] (z0) at (0,0) {$z_0$};
        \node[state,accepting on=<1>] (z1) at (2,0) {$z_1$};
        
        \path[->] (z0) edge [loop above] node {$a$} (z0)
                       edge node {$b$} (z1)
                  (z1) edge [loop above] node {$a,b$} (z1);
    \end{tikzpicture}
\end{frame}

\end{document}

enter image description here

6
3

The doubleing changes the node size which lead to the typical problem of the whole TikZ picture being a bit wider and higher (first instance of jumping) but in this case it will also change the loops and the nodes placed a long the loop a bit (second instance of jumping).

While samcarter's answer compensates this by making the non-accepting state as big as the accepting one (which will lead to a bit whitespace between the node's border and the arrow) this answer tries to solve this by making the accepting one as big as the non-accepting one:

  1. By not doubleing the path but by offsetting it via the nfold library.

    For circles this works perfectly. For other shapes, this might not work as neatly. The rectangle shape isn't handled correctly by nfold (yet?) and the offset needs to be inverted since the rectangle is constructed in the other direction.

    But we could add a boolean to flip/flop the offset easily.

  2. By adding another circle node that's only drawn and has no text.

    This is actually my preferred version if your states all had the same size and shape otherwise you would need to adapt this a bit depending on how your states vary. (Though, we could easily add a nice interface to do this.)

    In its current form it's very inflexible and uses hard-coded numbers. (We could measure the original node and make the label's size dependent on that.)

    Though, when you start using colors, transformation or other fancy stuff this might break very easily.

  3. By the original doubleing via accepting by double but by making the node a bit smaller.

    This only works if the node is big enough, i.e. the minimum width/height is not exceeded because the text is too big.

Overall I'd prefer the solution via nfold, especially if you only use circles since it has the least interference in case you customize your states.

Code

\documentclass{beamer}
\usepackage{tikz}
\usetikzlibrary{automata,overlay-beamer-styles,nfold}
\makeatletter
\tikzset{offset/.code=\tikz@addmode{\pgfgetpath\tikz@temp\pgfsetpath\pgfutil@empty\pgfoffsetpath\tikz@temp{#1}}}
\makeatother
\tikzset{
  accepting by offset/.style={postaction={path only, draw, offset=#1}},
  accepting by label/.style={label={[circle,path only, draw, minimum size={1.5cm-(#1)}]center:}},
  accepting by smaller/.style={
    accepting by double,
    minimum width/.expanded={\pgfkeysvalueof{/pgf/minimum width}-(\pgfkeysvalueof{/pgf/outer xsep})-.5\pgflinewidth},
    minimum height/.expanded={\pgfkeysvalueof{/pgf/minimum height}-(\pgfkeysvalueof{/pgf/outer ysep})-.5\pgflinewidth}},
  set accepting on/offset/.style={accepting on/.style={alt={##1{accepting by offset={#1}}{}}}},
  set accepting on/label/.style={accepting on/.style={alt={##1{accepting by label={#1}}{}}}},
  set accepting on/smaller/.style={accepting on/.style={alt={##1{accepting by smaller}{}}}},
  set accepting on/offset=+1pt, % just a default
}
\begin{document}
\begin{frame}{Accepting by offset}
\begin{tikzpicture}[initial text=, auto, state/.append style={minimum size=1.5cm}]
  \node[state,initial,accepting on=<2>] (z0) at (0,0) {$z_0$};
  \node[state,accepting on=<1>] (z1) at (2,0) {$z_1$};
  \path[->] (z0) edge [loop above] node {$a$} (z0)
                 edge node {$b$} (z1)
            (z1) edge [loop above] node {$a,b$} (z1);
\end{tikzpicture}
\end{frame}
\begin{frame}{Accepting by label}
\tikzset{set accepting on/label=2pt}
\begin{tikzpicture}[initial text=, auto, state/.append style={minimum size=1.5cm}]
  \node[state,initial,accepting on=<2>] (z0) at (0,0) {$z_0$};
  \node[state,accepting on=<1>] (z1) at (2,0) {$z_1$};
  \path[->] (z0) edge [loop above] node {$a$} (z0)
                 edge node {$b$} (z1)
            (z1) edge [loop above] node {$a,b$} (z1);
\end{tikzpicture}
\end{frame}
\begin{frame}{Accepting by smaller}
\tikzset{set accepting on/smaller=.3pt+\pgflinewidth}
\begin{tikzpicture}[initial text=, auto, state/.append style={minimum size=1.5cm}]
  \node[state,initial,accepting on=<2>] (z0) at (0,0) {$z_0$};
  \node[state,accepting on=<1>] (z1) at (2,0) {$z_1$};
  \path[->] (z0) edge [loop above] node {$a$} (z0)
                 edge node {$b$} (z1)
            (z1) edge [loop above] node {$a,b$} (z1);
\end{tikzpicture}
\end{frame}
\end{document}
1

You must log in to answer this question.

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