构建树

构建树

有人知道怎样用 TeX 构建这样的树吗?

在此处输入图片描述

答案1

下次您提问时,请记住先自己努力一下,然后发布您已完成的工作,即使工作量不大。至少,您应该提供文档的结构 ( \documentclass ... \end{document}) 和树节点的内容,这样人们就不必从头开始来帮助您。我恰好对了解更多信息感兴趣,forest否则我不会尝试回答您的问题。如果您不依赖像我这样的拖延者的古怪兴趣,您更有可能得到答案!

构建树

步骤 1:基本树

第一步是使用forest的符号来设置基本树:

\documentclass[tikz]{standalone}
\usepackage{forest}

\begin{document}

  \begin{forest}
    [Language workbench
    [Notation
        [Textual
          [Symbols]
        ]
        [Graphical]
        [Tabular]
      ]
      [Semantics
        [Translational
          [Model to text]
          [Model to model
            [Concrete syntax]
          ]
        ]
        [Interpretative]
      ]
      [Editor
      [Editing mode
          [Free form]
          [Projectional]
        ]
        [Syntactic services
          [Highlighting]
          [Outline]
          [Folding]
          [Syntactic completion]
          [Diff]
          [Auto formatting]
        ]
        [Semantic services
          [Reference resolution]
          [Semantic completion]
          [Redo factoring]
          [Error marking]
          [Quick fixes]
          [Origin tracking]
          [Live translation]
        ]
      ]
      [Validation
        [Structural]
        [Semantic
          [Naming]
          [Types]
          [Programmatic]
        ]
      ]
      [Testing, optional
        [DSL testing]
        [DSL debugging]
        [More DSL debugging]
      ]
      [Composition
        [Syntax/views]
        [Validation]
        [Semantics]
        [Editor services]
      ]
    ]
  \end{forest}

\end{document}

这将产生以下内容:

初始树

第 2 步:修复层级对齐

正如 Kevin C 对您的问题的评论中所指出的,我们可以使用以下方法使“编辑器”节点与“具体语法”节点对齐tier=

...
            [Concrete syntax, tier=edits]
          ]
        ]
        [Interpretative]
      ]
      [Editor, tier=edits
...

这样可以正确对齐节点,但是存在一个问题:

问题

这是因为tier对齐将“编辑器”节点移得离其通常的位置“太远”,并且forest无法跟踪正在发生的事情。(我相信有更技术复杂的解释,但这就是我的想法。)

我们可以forest通过使用phantom键来创建一些不可见的节点来提供帮助。这些节点不会被绘制,但它们有助于留出空间。我们将基于“验证子树”创建一个幻影子树,并将其插入到“编辑器”子树之前:

...
      [Semantics
        [Translational
          [Model to text]
          [Model to model
            [Concrete syntax, tier=edits]
          ]
        ]
        [Interpretative]
      ]
  [Validation, phantom
    [Structural, phantom]
    [Semantic, phantom
      [Naming, phantom]
      [Types, phantom]
      [Programmatic, phantom]
    ]
  ]
      [Editor, tier=edits
...

这样更好,因为我们现在有空间容纳所有东西了:

具有幻像子树的树

步骤3:节点外观

现在该考虑一下节点的外观了。我们希望在每个节点周围画一个框,并且使用无衬线字体,因此我们在环境的开头添加了以下内容forest

for tree={
  font=\sffamily,
  node options={draw=black!25},
}

这给了我们:

方框和字体

但是,我们希望节点的高度相同,即使特定节点中没有上升部和下降部,也能容纳它们。也就是说,我们希望同一层上的盒子都具有相同的高度。

我们增加

  anchor=base,
  align=center,
  base=b,

通过树选项来实现这一点。

步骤 4:节点定位和锚点

然而事情还不太对劲:

尚存问题

直观来看,节点之间没有足够的空间,因此事物会相互交叉。此外,通向子节点的线不是从一个点辐射出去的,而且考虑到它们的宽度,这些层似乎太靠近了。

我们可以添加

  parent anchor=south,
  child anchor=north,

使线条从单个点辐射出去。这确保了从父节点到子节点的线条始终离开south父节点的锚点并进入north子节点的锚点。

为了增加层之间的距离,我们添加

  l sep+=20pt,

至此我们的树看起来好一些了:

已调整节点锚点和位置的树

步骤 5:加粗线条

为了加粗节点周围的线条,我们可以添加

  line width=1pt,

但为了加粗父母与孩子之间的界线,我们需要调整edge path

  edge path={\noexpand\path[line width=1pt, \forestoption{edge}](!u.parent anchor)--(.child anchor)\forestoption{edge label};},

我们现在有:

带粗线的树

步骤 6:设置节点样式

我们需要为节点设置 4 种不同的样式:我们已经实现的普通默认样式;北锚点处的“强制”实心圆;北锚点处的“可选”白色中心绘制圆;南锚点处的实心角。实心角可以与强制/可选样式共存,因此我们要确保附加样式选项而不是覆盖它们。

for tree因此,我们在环境开始时添加以下样式定义forest

  mandatory/.append style={edge label={node [circle, fill=black!80] {}}},
  optional/.append style={edge label={node [circle, draw=black!80, fill=white] {}}},
  or/.append style={for first={disjunct}},
  disjunct/.append style={
    tikz={\begin{scope}\clip (!u.south) -- (!u1.north) -- (!ul.north) -- cycle; \node [circle, fill=black!80, minimum width=15pt] at (!u.south) {};\end{scope}}
  },

需要将styleor选项赋予相关节点的孩子们而不是作为选项传递给节点本身。(也许可以避免这种情况,但我不知道该怎么做。)optionalmandatory样式传递给相关节点本身。例如,这是“语义”子树:

...
  [Semantics, mandatory, or
    [Translational, or
      [Model to text]
      [Model to model
        [Concrete syntax, optional, tier=edits]
      ]
    ]
    [Interpretative, or]
  ]
...

其结果为:

具有样式节点的子树

步骤 7:添加图例

剩下的就是添加“图例”。为此,我们将使用两个tikz库,positioningcalc。我们还将定义一个legend样式以方便使用:

\tikzset{
  legend/.append style={line width=1pt, font=\sffamily, align=left},
}

先前的设置仅适用于所以我们在定义中重复它们。

我们现在命名其中一个节点,以便我们可以将图例框相对于该节点定位:

...
    [Syntax/views, name=my node]
...

现在我们可以创建节点来保存图例文本和符号:

\node (legend title) [below=of my node, legend] {Legend:};
\node (legend mandatory) [below=5pt of legend title, legend, circle, fill=black!80] {}  ;
\node (legend mandatory text) [right=5pt of legend mandatory.east, legend] {Mandatory};
\node (legend optional) [below=10pt of legend mandatory, legend, circle, draw=black!80, fill=white] {};
\node (legend optional text) [right=5pt of legend optional.east, legend] {Optional};
\node (legend or) [below=5pt of legend optional, legend] {};
\node (legend or text) [right=5pt of legend or.east, legend, yshift=-5pt, xshift=1pt] {Or};
\begin{scope}
  \draw [draw, line width=1pt] ($(legend or) + (5pt,-10pt)$) coordinate (A) -- (legend or.center) coordinate (B)  -- ($(legend or) - (5pt,10pt)$) coordinate (C);
  \clip (A) -- (B) -- (C) -- cycle;
  \node [circle, fill=black!80, minimum width=15pt] at (B) {};
\end{scope}

最后,我们在图例周围画出方框:

\draw [line width=1pt] (legend title.north west) -- (legend title.north west -| legend mandatory text.north east) -- (legend mandatory text.north east |- legend or text.south east) -- (legend or text.south east -| legend title.north west) -- cycle;

我们完成了!

最终代码

\documentclass[tikz]{standalone}
\usepackage{forest}
\usetikzlibrary{positioning, calc}

\begin{document}

  }
  \begin{forest}
    for tree={
      mandatory/.append style={edge label={node [circle, fill=black!80] {}}},
      optional/.append style={edge label={node [circle, draw=black!80, fill=white] {}}},
      or/.append style={for first={disjunct}},
      disjunct/.append style={
        tikz={\begin{scope}\clip (!u.south) -- (!u1.north) -- (!ul.north) -- cycle; \node [circle, fill=black!80, minimum width=15pt] at (!u.south) {};\end{scope}}
      },
      font=\sffamily,
      parent anchor=south,
      child anchor=north,
      node options={draw=black!25},
      edge path={\noexpand\path[line width=1pt, \forestoption{edge}](!u.parent anchor)--(.child anchor)\forestoption{edge label};},
      anchor=base,
      align=center,
      base=b,
      l sep+=20pt,
      line width=1pt,
    }
    [Language workbench,
    [Notation, mandatory, or
        [Textual
          [Symbols, optional]
        ]
        [Graphical]
        [Tabular]
      ]
      [Semantics, mandatory, or
        [Translational, or
          [Model to text]
          [Model to model
            [Concrete syntax, optional, tier=edits]
          ]
        ]
        [Interpretative, or]
      ]
      [Validation, optional, phantom
        [Structural, phantom]
        [Semantic, phantom
          [Naming, phantom]
          [Types, phantom]
          [Programmatic, phantom]
        ]
      ]
      [Editor, mandatory, tier=edits
      [Editing mode, mandatory, or
          [Free form]
          [Projectional]
        ]
        [Syntactic services, optional, or
          [Highlighting]
          [Outline]
          [Folding]
          [Syntactic completion]
          [Diff]
          [Auto formatting]
        ]
        [Semantic services, optional, or
          [Reference resolution]
          [Semantic completion]
          [Redo factoring]
          [Error marking]
          [Quick fixes]
          [Origin tracking]
          [Live translation]
        ]
      ]
      [,phantom
        [,phantom
          [,phantom]
        ]
      ]
      [Validation, optional, or
        [Structural]
        [Semantic, or
          [Naming]
          [Types]
          [Programmatic]
        ]
      ]
      [Testing, optional, or
        [DSL testing]
        [DSL debugging]
        [More DSL debugging]
      ]
      [Composition, optional, or
        [Syntax/views, name=my node]
        [Validation]
        [Semantics]
        [Editor services]
      ]
    ]
    \tikzset{
      legend/.append style={line width=1pt, font=\sffamily, align=left},
    }
    \node (legend title) [below=of my node, legend] {Legend:};
    \node (legend mandatory) [below=5pt of legend title, legend, circle, fill=black!80] {}  ;
    \node (legend mandatory text) [right=5pt of legend mandatory.east, legend] {Mandatory};
    \node (legend optional) [below=10pt of legend mandatory, legend, circle, draw=black!80, fill=white] {};
    \node (legend optional text) [right=5pt of legend optional.east, legend] {Optional};
    \node (legend or) [below=5pt of legend optional, legend] {};
    \node (legend or text) [right=5pt of legend or.east, legend, yshift=-5pt, xshift=1pt] {Or};
    \begin{scope}
      \draw [draw, line width=1pt] ($(legend or) + (5pt,-10pt)$) coordinate (A) -- (legend or.center) coordinate (B)  -- ($(legend or) - (5pt,10pt)$) coordinate (C);
      \clip (A) -- (B) -- (C) -- cycle;
      \node [circle, fill=black!80, minimum width=15pt] at (B) {};
    \end{scope}
    \draw [line width=1pt] (legend title.north west) -- (legend title.north west -| legend mandatory text.north east) -- (legend mandatory text.north east |- legend or text.south east) -- (legend or text.south east -| legend title.north west) -- cycle;
  \end{forest}

\end{document}

最后一棵树

最终树

相关内容