跟进我之前的问题,我想知道如何自动且稳健地创建以下信号流图的并行形式,而无需手动绘制线条/节点:
考虑一个没有重复极点的传递函数
每个项都是以 R(s) 为输入的一阶子系统,输出C(s)
可以视为三个项之和
因此,这导致了以下期望信号流图的并行形式
关于我的以下代码,我有以下疑问:
1-为什么foreach
我的代码不起作用?
2-如何微调amark
标签的位置,使它们稍微远离曲线?
3-如何才能在没有手动绘制线条/节点的情况下自动完成绘制图表的过程?
4-下面的代码是否有使用tikz-cd
、matrix of nodes
或的更简化的代码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}
答案1
这些只是一些快速修复方法。
foreach
不起作用,因为没有\path
命令。- 您可以使用
edge label
来实现更好的定位。
第 3 项和第 4 项需要更多思考。但如果清楚这些图表的整体结构,这将更有意义。这将使人们能够判断哪种策略可能是最合适的。
\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}
这是一个根据一些输入绘制图形的程序,比如
\def\GraphInput{12/-2,-24/-3,12/-4}
我尝试添加一些解释。不幸的是,解析边in
和out
边时也存在 pgf 问题。此代码还将边标签放置得更紧密。
\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}
答案2
使用这个tiz-cd
包(它专门用于绘制此类图表)可以避免这些问题。代码相对简单明了,而且更短:
\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}
注意,如果您想改变节点之间的距离,那么您只需要添加tikzcd
选项:
column sep=<desired distance between columns>,
row sep=<desired distance between rows>,