1

Adopting this answer, the following signal-flow diagram has been created by representing the following set of state equations:

enter image description here

I would like to:

1- know how my code can be optimized and generalized to be more robust when the number of nodes and their connecting signals differ without having to manually edit it.

2- make the current output cleaner if possible.

enter image description here

\documentclass[border=5mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations.markings,positioning,arrows.meta}
\newif\iflabrev
\begin{document}
    \begin{tikzpicture}
        [
        relative = false,
        node distance = 15 mm,
        label revd/.is if=labrev,
        %label revd/.default=true,
        amark/.style = {
            decoration={             
                markings,   
                mark=at position {0.5} with { 
                    \arrow{stealth},
                    \iflabrev \node[below] {#1};\else \node[above] {#1};\fi
                }
            },
            postaction={decorate}
        },
        terminal/.style 2 args={draw,circle,inner sep=2pt,label={#1:#2}},
        ]
        %
        %Place the nodes
        \node[terminal={left}{$R(S)$}] (a) at (0,0) {};
        \node[terminal={below right}{$sX_3(s)$}] (b) [right=of a] {};
        \node[terminal={below right}{$X_3(S)$}] (c) [right=of b] {};
        \node[terminal={[xshift=-4mm]below right}{$sX_2(s)$}] (d) [right=of c] {};
        \node[terminal={[xshift=-4mm]below right}{$X_2(s)$}] (e) [right=of d] {};
        \node[terminal={[xshift=-4mm]below right}{$sX_1(s)$}] (f) [right=of e] {};
        \node[terminal={[xshift=-4mm]below right}{$X_1(s)$}] (g) [right=of f] {};
        \node[terminal={right}{$Y(s)$}] (h) [right=of g] {};
        %
        %Draw the connections
        \foreach \target/\bend/\text/\loose in {b/0/7/0, d/90/5/1.5, f/90/2/1.5}
        \draw[amark=\text] (a) to[bend left=\bend,looseness=\loose] (\target);
    \draw[amark=1/s] (b) to (c);
    \foreach \source/\text/\loose in {c/-4/2, e/-3/1.5, g/1/1.5}
    \draw[amark=\text] (\source) to[bend left=90, looseness=\loose] (b);

    \foreach \target/\text/\loose in { d/-6/1.5, f/2/2}
    \draw[amark=\text] (g) to[bend left=90, looseness=\loose] (\target);

    \foreach \source/\text in {c/9, e/6}
    \draw[amark=\text] (\source) to[bend left=90, looseness=1.5] (h);

    \draw[amark=2] (c) to (d);
    \draw[amark=3] (c) to[bend right=90, looseness=1.5] (f);
    \draw[amark=1/s] (d) to (e);
    \draw[amark=-5] (e) to (f);
    \draw[amark=-2,label revd] (e) to[bend left=90, looseness=2] (d);
    \draw[amark=1/s] (f) to (g);
    \draw[amark=-4] (g) to (h);
\end{tikzpicture}

\end{document}

Diaa
  • 9,599
  • It is certainly possible to make things more automatic. However, this requires that the rules of the game are clear. Getting the nodes in a row: easy, use a chain. But what determines the edge labels? Likewise, what determines which nodes are connected by the semicircles? –  Nov 07 '20 at 01:25
  • @anonymous That's a good question. The answers can be found in this set of equations https://i.ibb.co/rtcPPww/image.png. I apologize that I don't know your specialization, but I simply represent the previously mentioned state equations by this graph. – Diaa Nov 07 '20 at 01:50
  • There is no need to apologize to an anonymous non-user for not knowing their specialization. ;-) But I take that the graph can be generated from some matrix. If you provide the rules how this matrix translates into some the graph you may get some more automatic options. –  Nov 07 '20 at 01:55
  • @anonymous The rules are stated in this screenshot https://i.ibb.co/QkHfLbH/image.png (source: Control Systems Engineering). I would be grateful if you only could give some help in making my current code cleaner and minimal. – Diaa Nov 07 '20 at 02:00
  • It is hard to read this excerpt since the article says "as shown in figure blablabla" but your screen shot does not show that figure. –  Nov 07 '20 at 02:18
  • @anonymous I am sorry. Here are the rest: https://i.ibb.co/Cs2Qpvj/image.png, https://i.ibb.co/mCLtK6V/image.png and https://i.ibb.co/kgV8qSQ/image.png – Diaa Nov 07 '20 at 02:31
  • https://topanswers.xyz/tex?q=1494 – Diaa Nov 09 '20 at 11:57

1 Answers1

1

This post does not address the question of achieving a better layout. The purpose is to generate the graph from a matrix automatically. You can define a matrix via

\edef\mmat{{2,-5,3,2},{-6,-2,2,5},{1,-3,-4,7},{-4,6,9,0}}

and then the graph gets created via foreach loops. There are some annotations in the code. The perhaps most important new ingredients are the pmark style which gets the labels from the matrix entries, i.e. its arguments are the row and column index, and the semicircle style which inserts a semicircle that can be used in edges.

\documentclass[tikz,border=5mm]{standalone}
\usetikzlibrary{calc,decorations.markings,positioning,arrows.meta}
\newif\iflabrev
\begin{document}
    \begin{tikzpicture}[node distance = 15 mm,
        label revd/.is if=labrev,
        label revd/.default=true,
        amark/.style = {
            decoration={             
                markings,   
                mark=at position {0.5} with { 
                    \arrow{stealth},
                    \iflabrev \node[below] {#1};\else \node[above] {#1};\fi
                }
            },
            postaction={decorate}
        }, % make the mark an entry of the \mmat matrix
        pmark/.style n args={2}{amark={$%
            \pgfmathparse{int({\mmat}[\numexpr#1][\numexpr#2])}%
                \pgfmathresult$}},
        terminal/.style 2 args={draw,alias=ln,circle,inner sep=2pt,label={#1:#2}},
        % semicircle path
        semicircle/.style={to path={let \p1=($(\tikztotarget)-(\tikztostart)$)
            in \ifdim\x1>0pt
              (\tikztostart.north) arc[start angle=180,end angle=0,radius=0.5*\x1]
            \else
              (\tikztostart.south) arc[start angle=0,end angle=-180,radius=-0.5*\x1]
            \fi}}
        ]
        % define the  matrix 
        \edef\mmat{{2,-5,3,2},{-6,-2,2,5},{1,-3,-4,7},{-4,6,9,0}}
        \pgfmathtruncatemacro{\dimy}{dim({\mmat})} % number of rows
        \pgfmathtruncatemacro{\dimx}{dim({\mmat}[0])} % number of columns
        % create the graph
        \path % R node
        node[terminal={left}{$R(S)$},alias={X-\dimy}] (R) {}
        % loop over matrix entries
        foreach \Y [evaluate=\Y as \X using {int(\dimy-\Y)}]
        in {1,...,\numexpr\dimy-1}
        {node[right=of ln,terminal={below right}{% sX_i node
            $sX_{\X}(s)$}](sX-\X){}
        node[right=of ln,terminal={below right}{% X_i node
            $X_{\X}(s)$}](X-\X){}
        % ege from sX_i to X_i  
        (sX-\X) edge[amark={$\frac{1}{s}$}] (X-\X)
        % edge from X_{i+1} to X_i  (R had an alias)
        (X-\the\numexpr\X+1) edge[pmark={\X-1}{\X}] (sX-\X)
        % semicircle edge from X_i to sX_i
        (X-\X) edge[semicircle,label revd,pmark={\X-1}{\X-1}] (sX-\X)
        % various semicircles
        \ifnum\Y>1
         (R) edge[semicircle,pmark={\X-1}{\dimx-1}] (sX-\X) 
         foreach \Z in {1,...,\numexpr\Y-1} {
          (X-\X) edge[semicircle,pmark={\X+\Z-1}{\X-1}] (sX-\the\numexpr\X+\Z)
         }
        \fi
        }% the Y node
        node[right=of ln,terminal={right}{$Y(s)$}](Y){}
        (X-1) edge[pmark={\dimy-1}{0}] (Y)
        % semicircles goint to Y
        foreach \Y [evaluate=\Y as \X using {int(\dimy-\Y)}]
         in {1,...,\numexpr\dimy-2}
        {(X-\X) edge[semicircle,pmark={\dimy-1}{\X-1}] (Y)};
    \end{tikzpicture}
\end{document}

enter image description here

Diaa
  • 9,599
  • This is so clean and awesome :). Many thanks for your help and I hope you don't mind if I get back for some questions. – Diaa Nov 07 '20 at 09:02
  • I know it will be annoying for you but I hope you can add explanations of your algorithm for these code snippets to learn something new: \pmark, semicircle style. I would like to know what they actually are doing. – Diaa Nov 07 '20 at 14:58
  • @Diaa I added some short explanations. pmark takes two arguments, the row and column index of the entry of the matrix which should become an edge label. semicircle just inserts a semicircle. It measures the distance between start and target so that it "knows" the radius, and also distinguishes between the cases target right or left of start to either draw the semicircle in the upper or lower half-plane. –  Nov 07 '20 at 15:43