如何绘制嵌套节点?

如何绘制嵌套节点?

我想绘制由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 处理嵌套节点:

嗯,这实际上比使用fittikz 库时看起来要容易得多。我们只需要定义一个适合现有节点的新虚拟节点。幸运的是,我们命名了所有节点,所以这很容易。只需使用

\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)] {};

相关内容