我想绘制由dot
程序生成的以下 png 图形。经过一些快速研究后,我决定尝试矩阵节点。使用它我成功创建了第一个 Pipeline 块。不幸的是,当尝试嵌套 BandPassFilter 时,我收到一条编译错误消息,提示:
alchemy.tex:541:包 pgf 错误:您还不能嵌套 pgfmatrix 环境。
我在这里寻找绘制此类图表的正确方向。值得注意的是,在这种情况下深度只有 2,但我需要任何深度的解决方案。
注意:我不是在寻找绘制此图形的 tikz 代码,而只是寻求入门帮助。
答案1
以下是关于如何绘制此类图表的简短教程。每个步骤都有很多种方法可以实现,我只是展示一下一的方法。请注意,这捕捉了我如何制作这类图表,但绝不意味着这一定是最好的的方式,并且针对那些刚接触的人tikz
。
1.绘制椭圆:
最好不要尝试这样的图表,直到我们至少能弄清楚如何绘制椭圆。好吧,幸运的是,这已经内置在tikz
:
\documentclass[border=2pt]{standalone}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
\draw [blue, ultra thick] (0, 0) ellipse (1.5cm and 1cm);
\end{tikzpicture}
\end{document}
效果很好。但是,在这种情况下,省略号有一个关联的文本,因此使用以下代码可能\node
更好:
\node [draw=blue, ultra thick, ellipse] at (0, 0) {text};
以上需要使用\usetikzlibrary{shapes}
。由于我们有几个这样的椭圆,我们确实应该定义一种样式:
\tikzset{my ellipse/.style={draw=blue, ultra thick, ellipse}}
因此,现在使用\node [my ellipse] at (0, 0) {text};
我们得到:
看起来我们已经准备好开始实际的图表。
2. 定位椭圆
第一步是确定如何定位椭圆?对我来说,显而易见的选择是使用相对位置来定位它们。但在定位它们之前,我们需要能够绘制至少一个椭圆。
好的,让我们完成前两个。我们可以将Int Source
节点放置在(0,0)
:
\node [my ellipse] (Int Source) at (0, 0) {Int Source};
我们不妨使用文本Int Source
来提供此节点的名称,(Int Source)
以便在引用它们时更容易阅读。放置duplicate
节点的一个明显位置是(Int Source.east)
:
\node [my ellipse] (Int Source) at (0, 0) {Int Source};
\node [my ellipse] (duplicate) at (Int Source.east) {duplicate};
得出的结果是:
并不是我们想要的。那么,为什么(duplicate)
不像 (Int Source)
我们指定的那么东。仔细观察,似乎节点的中心(duplicate.center)
确实位于(Int Source.east)
。所以,我们只需要指定anchor
要使用什么。所以:
\node [my ellipse, anchor=west] (duplicate) at (Int Source.east) {duplicate};
让我们得到一些至少可读的东西:
还没到那儿,因为我们确实想将其向右移动一点。为此,我们可以指定应用xshift=
:
\node [my ellipse,anchor=west,xshift=1.0cm] (duplicate) at (Int Source.east) {duplicate};
很好地移动了节点:
3.建立连接:
好的,在放置所有椭圆之前,让我们确保它们可以连接起来。所以:
\draw [->] (Int Source.east) -- (duplicate.west);
看起来工作正常。但我们知道我们可能想要改变所有的箭头,我们最好为它们定义一个样式:
\tikzset{my arrow/.style={-latex, thick}}
现在,\draw [my arrow] (Int Source.east) -- (duplicate.west);
收益为:
4.放置其他节点:
现在我们可以继续放置其他节点。对于High Pass Filter
节点,我们需要使用yshift
将其向上移动。所以,到目前为止,我们有:
\node [my ellipse] (Int Source) at (0, 0) {Int Source};
\node [my ellipse, anchor=west, xshift=1.0cm] (duplicate) at (Int Source.east) {duplicate};
\node [my ellipse, anchor=west, xshift=1.0cm, yshift=1.0cm] (High Pass Filter) at (duplicate.east) {High Pass Filter};
\draw [my arrow] (Int Source.east) -- (duplicate.west);
现在我们放置了另一个节点,我们看到了一些重复。也许我们应该将添加anchor=west, xshift=1.0cm
到节点样式中,这将使代码更容易维护。对于这个特定的图表,这种放置方法似乎有效:
\tikzset{my ellipse/.style={draw=blue, ultra thick, ellipse, anchor=west, xshift=1.0cm}}
\tikzset{my arrow/.style={-latex, thick}}
所以,现在我们继续放置其他节点并通过连接各个锚点来绘制箭头:
\documentclass[border=2pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{shapes}
\begin{document}
\tikzset{my ellipse/.style={draw=blue, ultra thick, ellipse, anchor=west, xshift=1.0cm}}
\tikzset{my arrow/.style={-latex, thick}}
\begin{tikzpicture}
\node [my ellipse] (Int Source) at (0, 0) {Int Source};
\node [my ellipse] (duplicate) at (Int Source.east) {duplicate};
\node [my ellipse, yshift=+1.0cm] (High Pass Filter) at (duplicate.east) {High Pass Filter};
\node [my ellipse, yshift=-1.0cm] (Low Pass Filter) at (duplicate.east) {Low Pass Filter};
\node [my ellipse, yshift=-1.0cm] (roundrobin) at (High Pass Filter.east) {roundrobin(1,1)};
\node [my ellipse] (IntPrinter) at (roundrobin.east) {IntPrinter};
\draw [my arrow] (Int Source.east) -- (duplicate.west);
\draw [my arrow] (duplicate.east) -- (High Pass Filter.west);
\draw [my arrow] (duplicate.east) -- (Low Pass Filter.west);
\draw [my arrow] (High Pass Filter.east) -- (roundrobin.west);
\draw [my arrow] (Low Pass Filter.east) -- (roundrobin.west);
\draw [my arrow] (roundrobin.east) -- (IntPrinter.west);
\end{tikzpicture}
\end{document}
并获得了看起来很有希望的结果:
5.调整边缘:
嗯,上面的边并不完全是我们想要的对角线中间部分。所以我们需要调整连接这些节点的方式。幸运的是,我们可以访问节点的边缘,并使之与规范形成任何角度<node name>.<angle>
。因此,使用:
\draw [my arrow] (duplicate.10) -- (High Pass Filter.-175);
\draw [my arrow] (duplicate.-10) -- (Low Pass Filter.175);
\draw [my arrow] (High Pass Filter.-10) -- (roundrobin.175);
\draw [my arrow] (Low Pass Filter.10) -- (roundrobin.-175);
6 处理嵌套节点:
嗯,这实际上比使用fit
tikz 库时看起来要容易得多。我们只需要定义一个适合现有节点的新虚拟节点。幸运的是,我们命名了所有节点,所以这很容易。只需使用
\node [draw=red, ultra thick,
fit=(duplicate) (High Pass Filter) (Low Pass Filter) (roundrobin)
] {};
我们获得一个围绕指定节点的良好拟合矩形:
嗯,这与其中一个箭头重叠,所以我们只需要使用 TikZ fit 包时留出一些额外的空间通过使用节点左侧的点(duplicate)
。此时,我们也可以调整红色矩形的右侧:
\node [draw=red, ultra thick,
fit={($(duplicate.west)-(0.4cm,0)$)
(High Pass Filter) (Low Pass Filter)
([xshift=0.4cm]roundrobin.east)}
] {};
请注意,我展示了两种不同的方法来调整上述要点。一种使用 tikz 包的语法calc
,另一种应用[xshift=]
语法。
所以,这看起来相当不错,现在我们只需要添加外框:
\node [draw=red, ultra thick,
fit={($(duplicate.west)-(0.4cm,0)$)
(High Pass Filter) (Low Pass Filter)
([xshift=0.4cm]roundrobin.east)}
] {};
\node [draw=brown, ultra thick,
fit=(Int Source) (High Pass Filter) (Low Pass Filter) (IntPrinter)
] {};
好吧,看起来我们fit
也需要调整这里的节点:
\node [draw=red, ultra thick,
fit={($(duplicate.west)-(0.4cm,0)$)
(High Pass Filter)
(Low Pass Filter)
([xshift=0.4cm]roundrobin.east)}
] {};
\node [draw=brown, ultra thick,
fit={(Int Source)
([yshift=0.4cm]High Pass Filter.north)
([yshift=-0.4cm]Low Pass Filter.south)
(IntPrinter)}
] {};
7. 标记嵌套节点:
不幸的是,我忘记放置这些嵌套节点的标签。因此,上面的代码似乎扩展的将需要向北延伸一点,以便为节点文本腾出空间。放置这些的最佳方法是使用north
使用点嵌套节点。
代码:
\documentclass[border=2pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\usetikzlibrary{fit}
\usetikzlibrary{shapes}
\begin{document}
\tikzset{my ellipse/.style={
draw=blue,
ultra thick,
ellipse,
anchor=west,
xshift=1.0cm},
}
\tikzset{my arrow/.style={-latex, thick}}
\begin{tikzpicture}
\node [my ellipse] (Int Source) at (0, 0) {Int Source};
\node [my ellipse] (duplicate) at (Int Source.east) {duplicate};
\node [my ellipse, yshift=+1.0cm] (High Pass Filter) at (duplicate.east) {High Pass Filter};
\node [my ellipse, yshift=-1.0cm] (Low Pass Filter) at (duplicate.east) {Low Pass Filter};
\node [my ellipse, yshift=-1.0cm] (roundrobin) at (High Pass Filter.east) {roundrobin(1,1)};
\node [my ellipse] (IntPrinter) at (roundrobin.east) {IntPrinter};
\draw [my arrow] (Int Source.east) -- (duplicate.west);
\draw [my arrow] (duplicate.10) -- (High Pass Filter.-175);
\draw [my arrow] (duplicate.-10) -- (Low Pass Filter.175);
\draw [my arrow] (High Pass Filter.-10) -- (roundrobin.175);
\draw [my arrow] (Low Pass Filter.10) -- (roundrobin.-175);
\draw [my arrow] (roundrobin.east) -- (IntPrinter.west);
\node [draw=red, ultra thick,
fit={($(duplicate.west)-(0.5cm,0)$)
([yshift=0.40cm]High Pass Filter.north)
(Low Pass Filter)
([xshift=0.4cm]roundrobin.east)}
] (Band Pass Filter) {};
\node [draw=brown, ultra thick,
fit={(Int Source)
([yshift=0.9cm]High Pass Filter.north)
([yshift=-0.4cm]Low Pass Filter.south)
(IntPrinter)}
] (Pipeline) {};
\node [anchor=north, font=\bfseries] at (Band Pass Filter.north) {Band Pass Filter};
\node [anchor=north, font=\bfseries] at (Pipeline.north) {Pipeline};
\end{tikzpicture}
\end{document}
答案2
为了清楚起见,也许其他用户也一样,正如@percusse 在问题的评论中所建议的那样,一个很好的答案是使用fit
如下库:
\node[fit=(nodenames) (that) (I want to) (be covered)] {};