2

Following up on my previous question, I would like to know how I can automatically and robustly create the parallel form of a signal-flow diagram for the following without manual drawing of the lines/nodes:

Consider a transfer function without repeated poles

enter image description here

Each term is a first-order subsystem with R(s) as the input, and the output C(s) can be considered the sum of the three terms

enter image description here

Therefore, this leads to the following desired parallel form of the signal-flow diagram

enter image description here

My questions about my code below are:

1- why doesn't foreach work in my code?

2- How to fine-tune the position of amark labels to make them move away a bit from the curved lines?

3- How can I robustly automate the process of drawing the diagram without the manual drawing of lines/nodes?

4- Is there a more simplified code of the code below using tikz-cd, matrix of nodes, or chains?

\documentclass[tikz,border=5mm]{standalone}
\usetikzlibrary{calc,decorations.markings,positioning,arrows.meta,matrix}
\newif\iflabrev
\begin{document}
    \begin{tikzpicture}[
        node distance = 15mm and 15mm, 
        relative = false,
        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,alias=ln,circle,inner sep=2pt,label={#1:#2}},
        ]
        \path
        node[terminal={left}{$R(s)$}] (R) {}
        node[above right=of R, terminal={}{}] (sX-1) {}
        node[right=of sX-1, terminal={}{}] (X-1) {}
        node[right=of R, terminal={}{}] (sX-2) {}
        node[right=of sX-2, terminal={}{}] (X-2) {}
        node[right=of X-2, terminal={right}{$C(s)$}] (C) {}
        node[below right=of R, terminal={}{}] (sX-3) {}
        node[right=of sX-3, terminal={}{}] (X-3) {}
        ;
        %
        \foreach \X in {1,...,3}{
            (sX-\X) edge[amark=1/s] (X-\X)
        };
    \path (R) edge[out=90,in=180,amark=12] (sX-1) (X-1) edge[out=-90,in=-90,amark=-2] (sX-1) edge[out=0,in=90,amark=1] (C);
    \path (R) edge[amark=-24] (sX-2) (X-2) edge[out=-90,in=-90,amark=-3] (sX-2) edge[amark=1] (C);
    \path (R) edge[out=-90,in=180,amark=12,label revd] (sX-3) (X-3) edge[out=-90,in=-90,amark=-4] (sX-3) edge[out=0,in=-90,amark=1,label revd] (C);
    \end{tikzpicture}
\end{document}
Diaa
  • 9,599

2 Answers2

3

These are just some quick fixes.

  1. foreach does not work because there is no \path command.
  2. You can use an edge label to achieve an arguably better positioning.

Items 3 and 4 would require more thought. But this would make more sense if it was clear what the overall structure of these diagrams is. This would allow one to gauge which strategy may be the most appropriate one.

\documentclass[tikz,border=5mm]{standalone}
\usetikzlibrary{calc,decorations.markings,positioning,arrows.meta,matrix}
\makeatletter
\pgfmathdeclarefunction{Dim}{1}{%
  \begingroup%
    \pgfmath@count=0\relax
    \edef\pgfutil@tmpb{#1}%
    \pgfutil@for\pgfutil@tmpa:={\pgfutil@tmpb}\do{%
      \advance\pgfmath@count by1\relax}%
    \edef\pgfmathresult{\the\pgfmath@count}% 
    \pgfmath@smuggleone\pgfmathresult% 
  \endgroup}  
\makeatother
\begin{document}
    \begin{tikzpicture}[
        node distance = 15mm and 15mm, 
        relative = false,
        amark/.style = {
            decoration={             
                markings,   
                mark=at position {0.5} with { 
                    \arrow{stealth},
                }
            },
            postaction={decorate},
            edge label={#1}
        },
        amark'/.style = {
            decoration={             
                markings,   
                mark=at position {0.5} with { 
                    \arrow{stealth},
                }
            },
            postaction={decorate},
            edge label'={#1}
        },
    terminal/.style 2 args={draw,alias=ln,circle,inner sep=2pt},
        ]
        % input
        \def\GraphInput{12/-2,-24/-3,12/-4}
        %\def\GraphInput{12/-2,-24/-3,12/-4,5/-7}
        %\def\GraphInput{12/-2,-24/-3,12/-4,5/-7,3/-3}
        % number of insertion
        \pgfmathtruncatemacro{\mydim}{Dim("\GraphInput")}
        % local bounding box is a trick that all distances derive from node distance
        \path[local bounding box=graph] 
         foreach \X/\Y [count=\Z] in \GraphInput {
         % draw the inner nodes in a loop
          \ifnum\Z=1
          node[terminal] (sX-\Z){} node[right=of sX-\Z,terminal] (X-\Z){}
          \else
          node[terminal,below=of sX-\the\numexpr\Z-1] (sX-\Z){} 
          node[right=of sX-\Z,terminal] (X-\Z){}
          \fi
        }
        % draw R and S nodes
        node[left=of graph,terminal,label=left:{$R(s)$}] (R){}
        node[right=of graph,terminal,label=right:{$C(s)$}] (C){}
        % loop for connections
        foreach \X/\Y [count=\Z] in \GraphInput {
        % bug in pgf, out does not get parsed properly
        [/utils/exec=\pgfmathsetmacro{\myout}{90-(\Z-1)*180/(\mydim-1)}]
        (sX-\Z) edge[amark={$1/s$}] (X-\Z)
        (X-\Z) edge[amark'={$\Y$},out=-90,in=-90] (sX-\Z)
        \ifnum\Z>\numexpr\mydim/2
         (R) edge[amark'={$\X$},out=\myout,in=180] (sX-\Z)
         (X-\Z) edge[amark'={$1$},out=0,in=180-\myout] (C)
        \else
         (R) edge[amark={$\X$},out=\myout,in=180] (sX-\Z)
         (X-\Z) edge[amark={$1$},out=0,in=180-\myout] (C)
        \fi
        };
 \end{tikzpicture}
\end{document}

enter image description here

And this is a routine that draws the graph from some input like

\def\GraphInput{12/-2,-24/-3,12/-4}

I tried to add some explanations. Unfortunately there is also a pgf issue with the parsing of in and out in edges. This code also places the edge labels more tightly.

\documentclass[tikz,border=5mm]{standalone}
\usetikzlibrary{calc,decorations.markings,positioning,arrows.meta,matrix}
\makeatletter
\pgfmathdeclarefunction{Dim}{1}{%
  \begingroup%
    \pgfmath@count=0\relax
    \edef\pgfutil@tmpb{#1}%
    \pgfutil@for\pgfutil@tmpa:={\pgfutil@tmpb}\do{%
      \advance\pgfmath@count by1\relax}%
    \edef\pgfmathresult{\the\pgfmath@count}% 
    \pgfmath@smuggleone\pgfmathresult% 
  \endgroup}  
\makeatother
\begin{document}
    \begin{tikzpicture}[
        node distance = 15mm and 15mm, 
        relative = false,
        amark/.style = {
            decoration={             
                markings,   
                mark=at position {0.5} with { 
                    \arrow{stealth},
                }
            },
            postaction={decorate},
            nodes={inner xsep=0pt},
            edge label={#1}
        },
        amark'/.style = {
            decoration={             
                markings,   
                mark=at position {0.5} with { 
                    \arrow{stealth},
                }
            },
            postaction={decorate},
            nodes={inner xsep=0pt},
            edge label'={#1}
        },
    terminal/.style 2 args={draw,alias=ln,circle,inner sep=2pt},
        ]
        % input
        %\def\GraphInput{12/-2,-24/-3,12/-4}
        \def\GraphInput{12/-2,-24/-3,12/-4,5/-7}
        % number of insertions
        \pgfmathtruncatemacro{\mydim}{Dim("\GraphInput")}
        % local bounding box is a trick that all distances derive from node distance
        \path[local bounding box=graph] 
         foreach \X/\Y [count=\Z] in \GraphInput {
         % draw the inner nodes in a loop
          \ifnum\Z=1
          node[terminal] (sX-\Z){} node[right=of sX-\Z,terminal] (X-\Z){}
          \else
          node[terminal,below=of sX-\the\numexpr\Z-1] (sX-\Z){} 
          node[right=of sX-\Z,terminal] (X-\Z){}
          \fi
        }
        % draw R and S nodes
        node[left=of graph,terminal,label=left:{$R(s)$}] (R){}
        node[right=of graph,terminal,label=right:{$C(s)$}] (C){}
        % loop for connections
        foreach \X/\Y [count=\Z] in \GraphInput {
        % bug in pgf, out does not get parsed properly
        [/utils/exec=\pgfmathsetmacro{\myout}{90-(\Z-1)*180/(\mydim-1)}]
        (sX-\Z) edge[amark={$1/s$}] (X-\Z)
        (X-\Z) edge[amark'={$\Y$},out=-90,in=-90] (sX-\Z)
        \ifnum\Z>\numexpr\mydim/2
         (R) edge[amark'={$\X$},out=\myout,in=180] (sX-\Z)
         (X-\Z) edge[amark'={$1$},out=0,in=180-\myout] (C)
        \else
         (R) edge[amark={$\X$},out=\myout,in=180] (sX-\Z)
         (X-\Z) edge[amark={$1$},out=0,in=180-\myout] (C)
        \fi
        };
 \end{tikzpicture}
\end{document}

enter image description here

  • Thanks for your answer. Is it possible to have an automatic/robust way to do it like this answer by just providing the numerators and denominators of each term without having to manually place the nodes and draw the lines regardless the number of the terms? – Diaa Nov 13 '20 at 23:00
  • @Diaa It certainly is if it is clear what the structure of the input is. I take that the number of factors is variable, i.e. not necessarily equal to 3. Is that right? And the 1 on the arrows on the right seem to be redundant, or is there a possibility they change, and if so, how? –  Nov 13 '20 at 23:06
  • The labels of the right arrows are always ones. The number of the branches/terms is problem-dependent and doesn't have to be three. You expect an input from me to be the signed numerical numerator and the signed number added/subtracted to/from S in the denominator of each term. – Diaa Nov 13 '20 at 23:37
  • BTW, why do you have amark' and amark'? Aren't they the same? – Diaa Nov 13 '20 at 23:44
  • @Diaa I added such a routine. amark and amark' are distinguished by edge label vs edge label', which swaps the position of the edge label. –  Nov 14 '20 at 01:19
  • When having \def\GraphInput{12/-2,-24/-3,12/-4,5/-7} the output looks weird https://i.ibb.co/sy26sNY/image.png instead of being symmetric with two parallel branches on each side of the centerlne connecting R(s) and C(s). Additionally, this \def\GraphInput{12/-2,-24/-3,12/-4,5/-7,3/-3} should have one middle straight (of 12/-4) branch with two symmetrical ones on each side not like this https://i.ibb.co/JBgp23K/image.png – Diaa Nov 20 '20 at 00:08
  • @Diaa Yes, you are right. Sorry about this. I shouldn't have used the function dim (which got advertised recently on this site...) but should have checked carefully. Fixed it. –  Nov 20 '20 at 00:21
  • One final question please: is it possible to make the arrow label of -24 not overlap the neighbor branch as shown here https://i.ibb.co/Vx2hKJ9/image.png? – Diaa Nov 20 '20 at 17:10
  • I mean is it possible to add some conditional ifnum\X=-24 then another label option is activated to make separation around the label be reduced or move it closer to its respective branch? – Diaa Nov 20 '20 at 17:21
  • In this case I would just place the edge labels more closely. I added that to the second code. –  Nov 20 '20 at 17:33
2

With use of the tiz-cd package (which is designed to draw such diagrams), you will avoid your problems. Code is relative simple and clear, and shorter:

enter image description here

\documentclass[border=5mm]{standalone}
\usepackage{tikz-cd}
\usetikzlibrary{arrows.meta,
                decorations.markings}
\newif\iflabrev

\begin{document} \begin{tikzcd}[ cells={nodes={circle, draw, inner sep=2pt}}, every arrow/.append style = {decoration={markings, mark=at position .5 with {\arrow{Straight Barb[scale=0.5]}}}, postaction={decorate}, - }, every label/.append style = {font=\footnotesize}
] & \ar[r,"1/s"] & \ar[l,"-2",bend left=60] \ar[rd,"1",bend left] & \ |[label=left:R(s)]| \ar[ru,"12",bend left] \ar[rd,"12",bend right] \ar[r,"-24"]
& \ar[r,"1/s"] & \ar[l,"-3", bend left=60] \ar[r,"1"] & |[label=right:C(s)]| \ & \ar[r,"1/s"] & \ar[l,"-4",bend left=60] \ar[ru,"1",bend right] & \ \end{tikzcd} \end{document}

Note, if you like to change distances between node, than you only need to add to tikzcd options:

column sep=<desired distance between columns>,
row sep=<desired distance between rows>,
Zarko
  • 296,517
  • Thanks for your answer. Is it possible to have an automatic/robust way to do it like this answer by just providing the numerators and denominators of each term without having to manually place the nodes and draw the lines regardless the number of the terms? – Diaa Nov 13 '20 at 23:00