2

I am trying to automate the generation of a multiline block diagram. So far with help (old post), I was able to generate a single sequence of block diagrams by using TikZ chains. It is great for short processes, however, I would like to expand it to longer sequences of blocks and make it multiline. My idea to solve this would be to generate some fixed length (of 5 blocks for example) horizontal chains with vertical offset and later connect them via the chain connect option. Ideally, I would also try to flip the direction of the chain so that it goes like a zig-zag (or lawnmower pattern). I tried to split my chain into a few lines, but I got stuck while trying to create a list for iteration over rows. In short I would like to ask if there is some option or what would be the best way to achieve block diagram "wrapping" in TikZ.

\documentclass[10pt]{report}

\usepackage{blindtext}
\usepackage[a4paper, margin={1in}]{geometry}

\usepackage{tikz}
\usetikzlibrary{chains, arrows.meta, quotes}

\tikzset{block/.style={draw, minimum width=2cm, minimum height=1cm}}

\ExplSyntaxOn
\newcommand\createlist[1]{
  \int_step_function:nnN{0}{\int_eval:#1 - 1}{\int_eval:n}
}
\ExplSyntaxOff

\newcommand{\process}[1]{
    \newcounter{row_count}
    \foreach \blockstr/\outputstr/\descriptionstr [count=\n] in {#1}{
        \addtocounter{row_count}{1}
    }
    
    % five blocks per page should be ok
    % \{\pgfmathparse{int(ceil(\value{len} / 5))}\pgfmathresult}
    \def\nrows{{\pgfmathparse{int(ceil(\value{row_count} / 5))}\pgfmathresult}} 
    
    % \nrows
    % \def\mylist{{{0,...,\nrows}}}
    % \mylist
    % \def\mylist{\createlist{\nrows}}
    
    % \foreach \row [count=\i] in {0,...,\nrows}
    %     {
    %        \row
    %        }
        
    \begin{tikzpicture}
        \begin{scope}[start chain=diagram_chain going right, node distance=1mm and 1cm, 
                      every join/.style={thick, -Stealth}]
            \foreach \blockstr/\outputstr/\descriptionstr [count=\j] in {#1}{
                \node[on chain=diagram_chain, block, join=by "\outputstr"]{\blockstr};
            }
        \end{scope}
    \end{tikzpicture}
    
    \begin{description}
    \foreach \blockstr/\outputstr/\descriptionstr [count=\k] in {#1}{
        \item [\blockstr] output: \outputstr \ \descriptionstr
    }
    
    \end{description}
}   

\begin{document}

\process{
    n0/$s(0)$/Long description of what n0 does,
    n1/$s(1)$/Long description of what n1 does,
    n2/$s(2)$/Long description of what n2 does,
    n3/$s(3)$/Long description of what n4 does,
    n4/$s(4)$/Long description of what n5 does,
    n5/$s(5)$/Long description of what n6 does,
    n6/$s(6)$/Long description of what n7 does
}



\end{document}

The result so far: enter image description here

The target structure: enter image description here

1 Answer 1

2

It seems that only four columns will fit but this will depend heavily on the edge labels on the special joins.

All the smart things are happening in the evaluate part of the \foreach loop.

The key is that we can set the direction of the chain for each node on the chain separately: So for n1, n2, n3 we want it to be going right, for n4 we need going below, then n5, n6 and n7 are going left and n8 is going below again. The direction set with start chain is irrelevant (could just as well be empty).

The edge label will be placed via the join key from the chains library but we delay this for the next iteration because you specify the “output” label (i.e. the edge to the next node), though join describes the edge to a previous node. Thus, the remember key is used.

Based on the value of process columns, let's call it n, and each node i we determine

  • the \Direction of the next node:

    • below if mod(i, n) is zero (i.e. it's the 0th, nth, 2nth, … node on the chain) ,
    • right if mod(i, 2n) is less then n (i.e. it is the 2kn+1st, 2kn+2nd, …, 2kn + 2kn + (n−1)th node on the chain)
    • left otherwise.
  • whether we need a \SpecialJoin (and what kind) as well as

  • the \AutoDirection so that the labels along the edges are always on top or to the outer side.


We could have TikZ or TeX measure things and maybe adjust the number of columns, at least for the next compilation run but I believe this to be a good start.

Code

\documentclass[10pt]{report}
\usepackage{blindtext}
\usepackage[a4paper, margin={1in}]{geometry}
\usepackage{tikz}
\usetikzlibrary{arrows.meta, chains, ext.paths.ortho}
\tikzset{
  every process diagram/.style={
    start chain=processes going right,
    every on chain/.append style={process node},
    every join/.append style={-Stealth, thick},
    node distance=3mm and 1cm,
    left join/.style ={to path={r-rl(\tikztotarget)\tikztonodes}},
    right join/.style={to path={r-lr(\tikztotarget)\tikztonodes}},
  },
  process node/.style={draw, minimum width=2cm, minimum height=1cm},
  process columns/.initial=4}
\newcommand*\process[2][]{%
  \begin{center}
  \begin{tikzpicture}[every process diagram]
  \foreach[
    count=\i from 0,
    remember=\Output as \PrevOutput,
    evaluate={
      \NotVerticalLR=mod(\i,   \pgfkeysvalueof{/tikz/process columns} );
      \NotVerticalL =mod(\i,2*(\pgfkeysvalueof{/tikz/process columns}));
      \Direction=ifthenelse(\NotVerticalLR, ifthenelse(\NotVerticalL<
          \pgfkeysvalueof{/tikz/process columns}, "right", "left"), "below");
      \SpecialJoin=ifthenelse(not(\NotVerticalLR),
        ifthenelse(\NotVerticalL, "left join", "right join"), "");
      \AutoDirection=ifthenelse(\NotVerticalL>
        \pgfkeysvalueof{/tikz/process columns}||\NotVerticalL==0,"right","left");
      }] \Text/\Output/\throwaway in {#2}
    \node[on chain=going \Direction,
          join=by {\SpecialJoin, auto=\AutoDirection, edge label=\PrevOutput}]{\Text};
  \end{tikzpicture}
  \end{center}
  \begin{description}
  \foreach\Text/\Output/\Item in{#2}{\item[\Text] output: \Output\par\Item}
  \end{description}}
\begin{document}
\blindtext
\process{
    n0/$s(0)$/Long description of what n0 does,
    n1/$s(1)$/Long description of what n1 does,
    n2/$s(2)$/Long description of what n2 does,
    n3/$s(3)$/Long description of what n3 does,
    n4/$s(4)$/Long description of what n4 does,
    n5/$s(5)$/Long description of what n5 does,
    n6/$s(6)$/Long description of what n6 does,
    n7/$s(7)$/Long description of what n7 does,
    n8/$s(8)$/Long description of what n8 does,
    n9/$s(9)$/Long description of what n9 does,
    n10/$s(10)$/Long description of what n10 does,
    n11/$s(11)$/Long description of what n11 does,
    n12/$s(12)$/Long description of what n12 does
}
\blindtext
\end{document}

Output

enter image description here

2
  • If the nodes won't be uniform, i.e. they don't have the same width, it's going to be much more complex and measure intensive. Aug 12, 2023 at 0:56
  • Thank you this is a really great solution and code! Aug 14, 2023 at 7:31

You must log in to answer this question.

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