我正在编写一份定义网络协议的文档。我用如下图表来说明关键的消息交换:
该图的 TikZ 源代码是:
\documentclass{article}
\usepackage{tikz}
\usepackage{amsmath}
\usetikzlibrary{arrows}
\begin{document}
\begin{tikzpicture}
\node (C0) at (0.2,7.7) {$C$} ;
\node [coordinate] (Cn) at (0.2,0) {} ;
\node (S0) at (4.8,7.7) {$S$} ;
\node [coordinate] (Sn) at (4.8,0) {} ;
\begin{scope}[shape=coordinate]
\draw (C0) -- (Cn)
node[pos=.10] (C1) {}
node[pos=.19] (C2) {}
node[pos=.32] (C3) {}
node[pos=.45] (C4) {}
node[pos=.58] (C5) {}
node[pos=.67] (C6) {}
node[pos=.80] (C7) {}
;
\draw (S0) -- (Sn)
node[pos=.10] (S1) {}
node[pos=.19] (S2) {}
node[pos=.32] (S3) {}
node[pos=.45] (S4) {}
node[pos=.58] (S5) {}
node[pos=.67] (S6) {}
node[pos=.80] (S7) {}
;
\end{scope}
\begin{scope}[above,->,>=angle 60] \footnotesize
\draw (C1) -- node {$C^\prime_\alpha[\alpha,99,l_r,,\cdots]$} (S1) ;
\draw (C2) -- node {$C^\prime_\alpha[\alpha,100,l_s,\text{\textsc{fin}},\cdots]$}
(S2) ;
\draw (S3) -- node{$S^\prime_\alpha[\alpha,60,l_t,,\cdots]$} (C3) ;
\draw (C4) -- node{$C^\prime_\alpha[\alpha,101,0,,]$} (S4) ;
\draw (S5) -- node{$S^\prime_\alpha[\alpha,61,l_u,,\cdots]$} (C5) ;
\draw (S6) -- node{$S^\prime_\alpha[\alpha,62,l_v,,\cdots]$} (C6) ;
\draw (C7) -- node{$C^\prime_\alpha[\alpha,102,0,,]$} (S7) ;
\end{scope}
\end{tikzpicture}
\end{document}
这已经让人感觉有点重复了,但更糟糕的是,这些图表有六张,每张的来源几乎相同,除了箭头上的标签、箭头数量以及箭头指向哪个方向。(其中两张图表的箭头不仅仅是水平线——它们会转弯并与另一支箭头交叉——但如果我必须对它们做一些特别的事情,那也没关系。)
请提出一些减少图表内和连续图表之间重复性的方法。我还可以使用一种定位箭头端点的方法,即绝对沿相关线的距离,而不是相对的距离;对所有图表进行这样的编码后,发现它们都太高了,但如果不更改所有注释,我就无法切掉底部的额外空间[pos=X]
。
答案1
这里你甚至不需要calc
库。因为你可以在序列\foreach
内部使用\draw
:
\node (C0) at (0.2,0) {$C$};
\draw (C0)
\foreach [count=\name] \y in {0.77, 0.693, 1.001, 1.001, 1.001, 0.693, 1.001, 1.54}
{
-- ++(0,-\y) coordinate (C\name)
};
[count=\name]
我从中学到的语法Torbjørn T.(谢谢!)。好处是你不必计算整个长度,因为你只给出两个坐标之间的差异。现在我们可以轻松地将此代码包装成宏:
\newcommand{\drawparties}[1]{
\node (C0) at (0.2,0) {$C$} ;
\draw (C0)
\foreach [count=\name] \y in {#1}
{
-- ++(0,-\y) coordinate (C\name)
};
\node (S0) at (4.8,0) {$S$};
\draw (S0)
\foreach [count=\name] \y in {#1}
{
-- ++(0,-\y) coordinate (S\name)
};
}
注意:此宏不依赖于绘制节点的数量。如果箭头代码确实像示例中那样重复,我们还可以将其缩短为:
\newcommand{\drawsend}[2]{\draw[->,>=angle 60] (C#1) -- node[above, font={\footnotesize}] {$C^\prime_\alpha[\alpha,#2]$} (S#1);}
\newcommand{\drawreceive}[2]{\draw[<-,>=angle 60] (S#1) -- node[above, font={\footnotesize}]{$S^\prime_\alpha[\alpha,#2]$} (C#1);}
然后整个代码归结为:
\begin{tikzpicture}
\drawparties{0.77, 0.693, 1.001, 1.001, 1.001, 0.693, 1.001, 1.54}
\drawsend{1}{99,l_r,,\cdots}
\drawsend{2}{100,l_s,\text{\textsc{fin}},\cdots}
\drawreceive{3}{60,l_t,,\cdots}
\drawsend{4}{101,0,,}
\drawreceive{5}{61,l_u,,\cdots}
\drawreceive{6}{62,l_v,,\cdots}
\drawsend{7}{102,0,,}
\end{tikzpicture}
我个人不喜欢使用宏(如上面的宏),因为tikz
它们感觉就像是 中的外星人tikzpicture
。但在这种情况下,如果你多次使用它,你确实可以节省一些输入。
答案2
您可以使用 for 循环在 TikZ 中重复执行操作。第 56 章对此进行了描述,重复的事情:Foreach 语句 在手册中。
例如,你可以绘制垂直线并使用
\foreach \x/\y/\n in {C0/Cn/C, S0/Sn/S}
{\draw (\x) -- (\y)
\foreach [count=\name] \p in {0.10,0.19,0.32,0.45,0.58,0.67,0.8}
{node[pos=\p] (\n\name) {}};
};
这将对每个组合进行循环。因此,第一次循环将绘制从到 的\x/\y/\n
线,然后进行“内部”循环,其中坐标节点沿着这条线放置。该选项允许您访问C0
Cn
count=\name
指数 列表中项目的。因此,\n\name
将为您提供C1
,C2
等等。
您还要求提供一种将箭头端点沿线定位到绝对距离的方法。可能有几种方法可以做到这一点,以下使用calc
库进行坐标计算:
\foreach [count=\name] \y in {1,2,...,7} {
\coordinate (C\name) at ($(C0) + (0,-\y)$);
\coordinate (S\name) at ($(S0) + (0,-\y)$);
};
1,2,...,7
与 相同。这会将坐标置于和节点下方1,2,3,4,5,6,7
一定距离处。当然,这种方法不适用于非垂直(或非水平)线。\y
C0
S0
答案3
我认为你可以使用如下 TikZ 矩阵:
\documentclass{article}
\usepackage{amsmath,tikz}
\usetikzlibrary{arrows,matrix}
\begin{document}
\begin{tikzpicture}[>=angle 60,every left delimiter/.style={xshift=1.5ex},%
every right delimiter/.style={xshift=-1.5ex}]
\matrix[matrix of math nodes,nodes in empty cells,row sep={9mm,between origins},%
nodes={minimum width=4cm},left delimiter=|,right delimiter=|,%
label={north east:$S$},label={north west:$C$}] (listmat) at (0,4)
{\\
C^\prime_\alpha[\alpha,99,l_r,,\cdots]\\
C^\prime_\alpha[\alpha,100,l_s,\text{\textsc{fin}},\cdots]\\
S^\prime_\alpha[\alpha,60,l_t,,\cdots] \\
C^\prime_\alpha[\alpha,101,0,,]\\
S^\prime_\alpha[\alpha,61,l_u,,\cdots]\\
S^\prime_\alpha[\alpha,62,l_v,,\cdots]\\
C^\prime_\alpha[\alpha,102,0,,]\\
\\
};
\def\alef{<-}
\def\arig{->}
\foreach \x/\ddir in {2/\arig,3/\arig,4/\alef,5/\arig,6/\alef,7/\alef,8/\arig}{
\draw[\ddir] (listmat-\x-1.south west) -- (listmat-\x-1.south east);
}
\end{tikzpicture}
\end{document}
结果如下
我犯了一些 TeX 错误,比如在非常糟糕的位置定义事物等等。此外,数字有点手动调整。蹩脚的变量名是为了避免冲突。我没有花更多时间在这上面,因为我甚至不确定这是否是你想要的。如果你想使用,请告诉我哪部分需要调整或重新制作。