Best method to animate a step by step state space diagram construction in TikZ

animatediagramstikz-pgf

Introduction

A long time ago I started learning how to use TikZ and I created some not very well made diagrams. One of those diagrams is a state space representation of linear dynamical systems. When I discovered I could perform animations with TikZ I tried my best to animate that diagram as an explanation of one should think step by step to draw and to understand the diagram.

Recently I redesigned some old diagrams with new methods I learned and decided to create some animations, but I noticed I should also revise how to animate them, since they were different from the originals draws I made.

Question

And now comes the decision-making point. What would be the best method to animate these diagrams, creating step by step, in diagrams where I create the nodes and then the connections between them (MWE 2 below)? How to create such step by step animation after a already read TikZ diagram?

I know asking for "The Best " is a little bit subjective and opinion based, but in this case, it just lacks me information to decide how to proceed and it would take a long time to learn by trial and error which method is more suitable.

Criteria for "The Best".

  1. Less changes in the original TikZ drawn
  2. Allowing step by step of the diagram in (almost) any order
  3. Compiling fast (or not so long)

Background

I've seen some possibilities with uncover, multido, creating multiple pages pdf and using \animategraphics, but I don't know which one would fit the best my needs.

As fas as I've read, I guess uncover seems to better fill my criteria.

Original diagram and animation – MWE 1

This is the first method I created the animation. It is clumsy, I used \ifthenelse to define what is presented in ecah step, the code is long, I didn't know how to keep what have already been draw. It is a mess. (I don't even remember where I find the example I based this code).

MWE 1 follows:

\documentclass[border=5pt]{standalone}
\usepackage{amsmath,xifthen,tikz,animate}
\usetikzlibrary{calc}
\begin{document}
\begin{animateinline}[poster=last, controls]{1}
  \multiframe{8}{ii=0+1}{% ii=0,1,...99 ; rj=9.9,9.8,...,0.0
    \begin{tikzpicture}[very thick, every node/.style={font=\LARGE}];
      \definecolor{dkg}{rgb}{0,0.5,0};
      % ==========
      \useasboundingbox (-6,-3) rectangle ++(20,8);
      \node[blue, right] at (9.5,-0.5) {\Huge Step:\ii};

      \draw (2.5,0) rectangle ++(2,2) coordinate(G)  node[pos=0.5] {\Huge{$\int$}};

      \node at (-1,-1.5) {
        \begin{tabular}{l}
          $\dot{x} = A x + B u$ \\
          $y = C x + D u$
        \end{tabular}
      };

      \ifthenelse{\ii < 1}{
        \draw [-latex, teal] (G)++(0,-1) -- ++(1,0) coordinate(X) node[above]{$x$} -- ++(1,0);
      }{
        \ifthenelse{\ii < 2}{
          \draw [-latex, teal] (G)++(0,-1) -- ++(1,0) coordinate(X) node[above]{$x$} -- ++(1,0);
          \draw (1,1) coordinate(S) circle (1/2);
          \draw [-latex, orange] (S)++(1/2,0) -- ++(1,0) coordinate(E) node[midway, above]{$\dot{x}$};

        }{
          \ifthenelse{\ii < 3}{
            \draw [-latex, teal] (G)++(0,-1) -- ++(1,0) coordinate(X) node[above]{$x$} -- ++(1,0);
            \draw (1,1) coordinate(S) circle (1/2);
            \draw [-latex, orange] (S)++(1/2,0) -- ++(1,0) coordinate(E) node[midway, above]{$\dot{x}$};

            \draw (2.5,-2.5) coordinate(A) rectangle ++(2*1,2*1) node[pos=0.5]{$A$};
            \draw [-latex, teal] (X) -- ++(0,-2.5) -- ++(-1,0);

          }{
            \ifthenelse{\ii < 4}{
              \draw [-latex, teal] (G)++(0,-1) -- ++(1,0) coordinate(X) node[above]{$x$} -- ++(1,0);
              \draw (1,1) coordinate(S) circle (1/2);
              \draw [-latex, orange] (S)++(1/2,0) -- ++(1,0) coordinate(E) node[midway, above]{$\dot{x}$};

              \draw (2.5,-2.5) coordinate(A) rectangle ++(2*1,2*1) node[pos=0.5]{$A$};
              \draw [-latex, teal] (X) -- ++(0,-2.5) -- ++(-1,0);

              \draw [-latex, cyan] ($(A)+(0,1)$) -| ($(S)+(0,-0.5)$) node[below right]{$+$};
            }{
              \ifthenelse{\ii < 5}{
                \draw [-latex, teal] (G)++(0,-1) -- ++(1,0) coordinate(X) node[above]{$x$} -- ++(1,0);
                \draw (1,1) coordinate(S) circle (1/2);
                \draw [-latex, orange] (S)++(1/2,0) -- ++(1,0) coordinate(E) node[midway, above]{$\dot{x}$};

                \draw (2.5,-2.5) coordinate(A) rectangle ++(2*1,2*1) node[pos=0.5]{$A$};
                \draw [-latex, teal] (X) -- ++(0,-2.5) -- ++(-1,0);

                \draw [-latex, cyan] ($(A)+(0,1)$) -| ($(S)+(0,-0.5)$) node[below right]{$+$};

                \draw (-2.5,0) rectangle ++(2*1,2*1) coordinate(B) node[pos=0.5]{$B$};
                \draw [-latex, red] (B)++(0,-1) -- ++(1,0) node[above left]{$+$};
                \draw [-latex, dkg] (-4,1) coordinate(u) node[above]{$u$} |- ++(1.5,0);

              }{
                \ifthenelse{\ii < 6}{
                  \draw [-latex, teal] (G)++(0,-1) -- ++(1,0) coordinate(X) node[above]{$x$} -- ++(1,0);
                  \draw (1,1) coordinate(S) circle (1/2);
                  \draw [-latex, orange] (S)++(1/2,0) -- ++(1,0) coordinate(E) node[midway, above]{$\dot{x}$};

                  \draw (2.5,-2.5) coordinate(A) rectangle ++(2*1,2*1) node[pos=0.5]{$A$};
                  \draw [-latex, teal] (X) -- ++(0,-2.5) -- ++(-1,0);

                  \draw [-latex, cyan] ($(A)+(0,1)$) -| ($(S)+(0,-0.5)$) node[below right]{$+$};

                  \draw (-2.5,0) rectangle ++(2*1,2*1) coordinate(B) node[pos=0.5]{$B$};
                  \draw [-latex, red] (B)++(0,-1) -- ++(1,0) node[above left]{$+$};
                  \draw [-latex, dkg] (-4,1) coordinate(u) node[above]{$u$} |- ++(1.5,0);

                  \draw (6.5,0) coordinate(C) rectangle ++(2*1,2*1) node[pos=0.5]{$C$};
                  \draw (10.5,1) coordinate(y) circle (1/2);
                  \draw [-latex, violet] (y)++(0.5,0) -- ++(1,0) node[above]{$y$};
                  \draw [-latex, olive] ($(C)+(2*1,1)$) -- ($(y)+(-0.5,0)$) node[above left]{$+$};

                }{
                  \ifthenelse{\ii < 7}{
                    \draw [-latex, teal] (G)++(0,-1) -- ++(1,0) coordinate(X) node[above]{$x$} -- ++(1,0);
                    \draw (1,1) coordinate(S) circle (1/2);
                    \draw [-latex, orange] (S)++(1/2,0) -- ++(1,0) coordinate(E) node[midway, above]{$\dot{x}$};

                    \draw (2.5,-2.5) coordinate(A) rectangle ++(2*1,2*1) node[pos=0.5]{$A$};
                    \draw [-latex, teal] (X) -- ++(0,-2.5) -- ++(-1,0);

                    \draw [-latex, cyan] ($(A)+(0,1)$) -| ($(S)+(0,-0.5)$) node[below right]{$+$};

                    \draw (-2.5,0) rectangle ++(2*1,2*1) coordinate(B) node[pos=0.5]{$B$};
                    \draw [-latex, red] (B)++(0,-1) -- ++(1,0) node[above left]{$+$};
                    \draw [-latex, dkg] (-4,1) coordinate(u) node[above]{$u$} |- ++(1.5,0);

                    \draw (6.5,0) coordinate(C) rectangle ++(2*1,2*1) node[pos=0.5]{$C$};
                    \draw (10.5,1) coordinate(y) circle (1/2);
                    \draw [-latex, violet] (y)++(0.5,0) -- ++(1,0) node[above]{$y$};
                    \draw [-latex, olive] ($(C)+(2*1,1)$) -- ($(y)+(-0.5,0)$) node[above left]{$+$};

                    \draw (2.5,2.5) coordinate(D) rectangle ++(2*1,2*1) coordinate(B)  node[pos=0.5]{$D$};
                    \draw [-latex, dkg] ($(u)+(0.5*1,0)$) |- ($(D)+(0,1)$);
                    \draw [-latex, cyan] ($(D)+(2*1,1)$) -| ($(y)+(0,0.5)$) node[above right]{$+$};
                  }{
                    \draw [-latex, teal] (G)++(0,-1) -- ++(1,0) coordinate(X) node[above]{$x$} -- ++(1,0);
                    \draw (1,1) coordinate(S) circle (1/2);
                    \draw [-latex, orange] (S)++(1/2,0) -- ++(1,0) coordinate(E) node[midway, above]{$\dot{x}$};

                    \draw (2.5,-2.5) coordinate(A) rectangle ++(2*1,2*1) node[pos=0.5]{$A$};
                    \draw [-latex, teal] (X) -- ++(0,-2.5) -- ++(-1,0);

                    \draw [-latex, cyan] ($(A)+(0,1)$) -| ($(S)+(0,-0.5)$) node[below right]{$+$};

                    \draw (-2.5,0) rectangle ++(2*1,2*1) coordinate(B) node[pos=0.5]{$B$};
                    \draw [-latex, red] (B)++(0,-1) -- ++(1,0) node[above left]{$+$};
                    \draw [-latex, dkg] (-4,1) coordinate(u) node[above]{$u$} |- ++(1.5,0);

                    \draw (6.5,0) coordinate(C) rectangle ++(2*1,2*1) node[pos=0.5]{$C$};
                    \draw (10.5,1) coordinate(y) circle (1/2);
                    \draw [-latex, violet] (y)++(0.5,0) -- ++(1,0) node[above]{$y$};
                    \draw [-latex, olive] ($(C)+(2*1,1)$) -- ($(y)+(-0.5,0)$) node[above left]{$+$};

                    \draw (2.5,2.5) coordinate(D) rectangle ++(2*1,2*1) coordinate(B)  node[pos=0.5]{$D$};
                    \draw [-latex, dkg] ($(u)+(0.5*1,0)$) |- ($(D)+(0,1)$);
                    \draw [-latex, cyan] ($(D)+(2*1,1)$) -| ($(y)+(0,0.5)$) node[above right]{$+$};

                    \draw [very thick, red] (-3,-3) coordinate(eq) rectangle ++(12,8);
      }}}}}}}
    \end{tikzpicture}
  }
\end{animateinline}
\end{document}

The result is as follows.

enter image description here

New diagram to animated – MWE 2

This new diagram uses some personal local defined /.styles and is much better organized. Nodes are defined first and then connections are made. The code is clean, easy to change, much better than the older one. But I don't how the best way to animate it.

MWE 2 follows:

\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\tikzset{addCross/.style n args={6}{
    minimum size={#5 mm}, %minimum height=10mm,
    path picture={
      \draw[#6]
      (path picture bounding box.south east) -- (path picture bounding box.north west)
      (path picture bounding box.south west) -- (path picture bounding box.north east);
      \node at ($(path picture bounding box.south)!0.4!(path picture bounding box.center)$) {#1};
      \node at ($(path picture bounding box.west)!0.4!(path picture bounding box.center)$)  {#2};
      \node at ($(path picture bounding box.north)!0.4!(path picture bounding box.center)$) {#3};
      \node at ($(path picture bounding box.east)!0.4!(path picture bounding box.center)$)  {#4};
    }
  },
  addCross/.default={}{}{}{}{10}{}
}
\tikzset{mySimpleArrow/.style n args={2}{
    >={latex[#1]},
    every path/.style={draw=#2}
  },
  mySimpleArrow/.default={black}{black}
}
\tikzset{myBlockOpacity/.style n args={4}{
    every node/.style={rectangle,draw, text=black,
      minimum width=#1, minimum height=#2,
      fill opacity=#3, text opacity=#4}
  },
  myBlockOpacity/.default={1cm}{1cm}{0.4}{1}
}

\begin{document}
\begin{tikzpicture}[very thick]
  \begin{scope}
    \node[circle,draw,addCross={$+$}{$+$}{}{}{10}{black}] (N1) at (1,-0.5) {};
  \end{scope}

  \begin{scope}[shift={(3.0,-0.5)},myBlockOpacity]
    \node[fill=red] (A) at (0,-1.5) {$A$};
    \node[fill=green!50!black] (B) at (-4,0) {$B$};
    \node[fill=yellow!75!black] (C) at (3,0) {$C$};
    \node (I) at (0,0) {$\displaystyle \int$};
  \end{scope}
  
  \begin{scope}[mySimpleArrow={blue}{cyan}]
    \path[->] (-2.5,-0.5) node[above]{$u$} -- (B);
    \path[->] (B) -- (N1);
    \path[->] (N1) -- (I) node[midway, above] {$\dot{x}$};
    
    \path[->] (I) -- (4.5,-0.5) coordinate(x) node[above]{$x$} -- (C);
    \path[->] (x) |- (A) -| (N1);
    \path[->] (C) -- ++(1.5,0) node[above]{$y$};
  \end{scope}
\end{tikzpicture}
\end{document}

which produces

enter image description here

Best Answer

An off-beamerclass \uncover command is defined, that specifies the frame number at which an object in a tikzpicture is to be revealed.

%\uncover{<current>}{<when>}{...<commands>...}
\newcommand\uncover[3]{\ifnum#1<#2\phantom{#3}\else#3\fi}

It relies on the \phantom{...}command which prevents the object from being drawn, yet evaluates coordinate definitions and even TikZ's automatic bounding box calculations. This allows us to reference node coordinates of hidden objects without changing too much of the original code.

enter image description here

\documentclass[border=10pt]{standalone}
\usepackage{animate}
\usepackage{tikz}
\usetikzlibrary{calc}
\tikzset{addCross/.style n args={6}{
    minimum size={#5 mm}, %minimum height=10mm,
    path picture={
      \draw[#6]
      (path picture bounding box.south east) -- (path picture bounding box.north west)
      (path picture bounding box.south west) -- (path picture bounding box.north east);
      \node at ($(path picture bounding box.south)!0.4!(path picture bounding box.center)$) {#1};
      \node at ($(path picture bounding box.west)!0.4!(path picture bounding box.center)$)  {#2};
      \node at ($(path picture bounding box.north)!0.4!(path picture bounding box.center)$) {#3};
      \node at ($(path picture bounding box.east)!0.4!(path picture bounding box.center)$)  {#4};
    }
  },
  addCross/.default={}{}{}{}{10}{}
}
\tikzset{mySimpleArrow/.style n args={2}{
    >={latex[#1]},
    every path/.style={draw=#2}
  },
  mySimpleArrow/.default={black}{black}
}
\tikzset{myBlockOpacity/.style n args={4}{
    every node/.style={rectangle,draw, text=black,
      minimum width=#1, minimum height=#2,
      fill opacity=#3, text opacity=#4}
  },
  myBlockOpacity/.default={1cm}{1cm}{0.4}{1}
}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%\uncover{<current>}{<when>}{...}
\newcommand\uncover[3]{\ifnum#1<#2\phantom{#3}\else#3\fi}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{document}
\begin{animateinline}[step,controls=step]{1}
\multiframe{13}{i=0+1}{  
  \begin{tikzpicture}[very thick]
    \begin{scope}
      \uncover{\i}{4}{\node[circle,draw,addCross={$+$}{$+$}{}{}{10}{black}] (N1) at (1,-0.5) {};}
    \end{scope}
  
    \begin{scope}[shift={(3.0,-0.5)},myBlockOpacity]
      \uncover{\i}{9}{\node[fill=red] (A) at (0,-1.5) {$A$};}
      \uncover{\i}{2}{\node[fill=green!50!black] (B) at (-4,0) {$B$};}
      \uncover{\i}{11}{\node[fill=yellow!75!black] (C) at (3,0) {$C$};}
      \uncover{\i}{6}{\node (I) at (0,0) {$\displaystyle \int$};}
    \end{scope}
    
    \begin{scope}[mySimpleArrow={blue}{cyan}]
      \uncover{\i}{1}{\path[->] (-2.5,-0.5) node[above]{$u$} -- (B);}
      \uncover{\i}{3}{\path[->] (B) -- (N1);}
      \uncover{\i}{5}{\path[->] (N1) -- (I) node[midway, above] {$\dot{x}$};}
      
      \uncover{\i}{7}{\path[->] (I) -- (4.5,-0.5) coordinate(x) node[above]{$x$} -- (C);}
      \uncover{\i}{8}{\path[->] (x) |- (A);}
      \uncover{\i}{10}{\path[->] (A) -| (N1);}
      \uncover{\i}{12}{\path[->] (C) -- ++(1.5,0) node[above]{$y$};}
    \end{scope}
  \end{tikzpicture}
}
\end{animateinline}
\end{document}
Related Question