在 TikZ 图表中不要重复太多

在 TikZ 图表中不要重复太多

我正在编写一份定义网络协议的文档。我用如下图表来说明关键的消息交换:

两条垂直线分别标记为 C 和 S,中间有一组水平箭头来回穿梭。每条水平箭头都标有一个数学表达式。

该图的 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线,然后进行“内部”循环,其中坐标节点沿着这条线放置。该选项允许您访问C0Cncount=\name指数 列表中项目的。因此,\n\name将为您提供C1C2等等。


您还要求提供一种将箭头端点沿线定位到绝对距离的方法。可能有几种方法可以做到这一点,以下使用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一定距离处。当然,这种方法不适用于非垂直(或非水平)线。\yC0S0

答案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 错误,比如在非常糟糕的位置定义事物等等。此外,数字有点手动调整。蹩脚的变量名是为了避免冲突。我没有花更多时间在这上面,因为我甚至不确定这是否是你想要的。如果你想使用,请告诉我哪部分需要调整或重新制作。

相关内容