![使用 LaTeX 创建此树结构](https://linux22.com/image/460219/%E4%BD%BF%E7%94%A8%20LaTeX%20%E5%88%9B%E5%BB%BA%E6%AD%A4%E6%A0%91%E7%BB%93%E6%9E%84.png)
我想轻松创建类似树状结构
D─┬───────────┬───A
R─┬───A─┬─A R─┐
R─┐ R R
R
D 是顶部根节点。它从右侧开始一条水平线,代表“同一级别”。这条水平线可以A
在任何位置穿入更多字母,这些字母没有额外的语义含义。这些字母A
总是有一条从左侧进入的线。
更有趣的特性是,水平线可以向下分裂为子(基)节点R
,该节点始终有一条线从顶部进入。这种分裂可以在水平基线上的多个位置发生。子节点递归地开始一个新的虚拟块,因此通常由向右的一条水平线继续。- 因此,下一个节点R
实际上在自己的“边界框”(此处标记为)D
中重新开始整个过程。+
D─┬───────────────┬───A
+++++++++++ R─┐
+R─┬───A─┬─A+ R
+ R─┐ R +
+ R +
+++++++++++
如果某个节点的右侧没有其他节点连接,则不会显示水平输出线。当然,需要避免树/文本重叠,即在开始新的子节点之前,一些水平线需要延伸得足够远。类型A
节点也应该只在其左侧所有先前的子块都完成后才出现。
在 LaTeX 中创建这种树状图的最佳方法是什么?
我已经找到了\usetikzlibrary{trees}
朝这个方向发展的方法,但我不确定如何翻转示例,如何在水平线上制作多个字母,如何控制文本重叠等......
关于如何重新创建该图表,有什么好的建议吗?
有一个可行的答案,可以手动定位所有内容,但我希望有另一个解决方案,例如 tikz-tree,它可以自动为我计算定位,这样我只需要指定子节点关系。
使用任意更长的标签:
D─┬───────────────────────G─┬─────C
Cc─┬─────────Aa─┬──Aaaa Eee─┐
Bbb─┐ Dd A
Aaaaa
答案1
带标签参数的变体
改编
- 请参阅下面的原始版本以获取更多描述
\nodeD
,\nodeR
并且\nodeA
每个现在都有一个参数来设置文本。- 这里我使用了相对定位,而不是绝对定位。因此,考虑了不同的文本长度。
- 我
base node/.style={draw, dashed}
只是为了澄清
结果
代码
\documentclass[border=2mm]{standalone}
\usepackage{etoolbox}
\usepackage{tikz}
\usetikzlibrary{positioning}
\def\nodedistance{5mm}
\def\treeY{0}
\newcounter{treeRnode}
\setcounter{treeRnode}{0}
\newcounter{treeAnode}
\setcounter{treeAnode}{0}
\def\lastXnode{}
\newcommand{\nodeD}[1]{%
\node[D] (D0) at (0,0) {#1};
\csdef{treeLastY\treeY}{D0}
\xdef\lastXnode{D0}
}
\newcommand{\nodeR}[1]{%
\pgfmathtruncatemacro\lastY{\treeY}
\pgfmathtruncatemacro\treeY{\treeY+1}
\ifcsdef{treeLastY\treeY}{%
\node[R, anchor=west] (R\arabic{treeRnode}) at ([xshift=\nodedistance] \csuse{treeLastY\treeY}.east-|\lastXnode.east) {#1};
}{%
\node[R, below right=of \csuse{treeLastY\lastY}] (R\arabic{treeRnode}) {#1};
}
\draw (\csuse{treeLastY\lastY}) -| (R\arabic{treeRnode});
\csxdef{treeLastY\treeY}{R\arabic{treeRnode}}
\xdef\lastXnode{R\arabic{treeRnode}}
\stepcounter{treeRnode}
}
\newcommand{\nodeA}[1]{%
\node[A, anchor=west] (A) at ([xshift=\nodedistance] \csuse{treeLastY\treeY}.east-|\lastXnode.east) (A\arabic{treeAnode}) {#1};
\draw (\csuse{treeLastY\treeY}) -- (A\arabic{treeAnode});
\csxdef{treeLastY\treeY}{A\arabic{treeAnode}}
\xdef\lastXnode{A\arabic{treeAnode}}
\stepcounter{treeAnode}
}
\newcommand{\branchend}{%
\pgfmathtruncatemacro\treeY{\treeY-1}
}
\begin{document}
\begin{tikzpicture}[
node distance=\nodedistance,
base node/.style={draw, dashed},
D/.style={base node},
R/.style={base node},
A/.style={base node},
]
\nodeD{$D_1$ with longer text}
\nodeR{$R_1$}
\nodeR{$R_2$ with longer text}
\nodeR{$R_3$}
\branchend
\branchend
\nodeA{$A_1$}
\nodeR{$R_4$}
\branchend
\nodeA{$A_2$ with longer text}
\branchend
%
\nodeR{$R_5$}
\nodeR{$R_6$}
\branchend
\branchend
\nodeA{$A_3$}
\end{tikzpicture}
\end{document}
无参数的变体(原始)
描述
我定义了一些可以在 tikz 中使用的命令:
\nodeD
以及不同的节点\nodeR
类型\nodeA
\branchend
停止一个分支并上升一级。
我使用计数器treeRnode
和treeAnode
来命名节点和变量\treeX
并\treeY
保存当前位置。此外,\treeLastY<y>
保存层中的最后一个节点<y>
。
因此,给定树的代码符合我的语法:
\nodeD
\nodeR
\nodeR
\nodeR
\branchend
\branchend
\nodeA
\nodeR
\branchend
\nodeA
\branchend
%
\nodeR
\nodeR
\branchend
\branchend
\nodeA
源代码中的缩进只是为了便于阅读。因此,这将是相同的:
\nodeD\nodeR\nodeR\nodeR\branchend\branchend\nodeA\nodeR\branchend\nodeA\branchend\nodeR\nodeR\branchend\branchend\nodeA
结果
代码
\documentclass{article}
\usepackage{etoolbox}
\usepackage{tikz}
\begin{document}
\def\treeX{0}
\def\treeY{0}
\newcounter{treeRnode}
\setcounter{treeRnode}{0}
\newcounter{treeAnode}
\setcounter{treeAnode}{0}
\newcommand{\nodeD}{%
\node[D] (D0) at (0,0) {D};
\csdef{treeLastY\treeY}{D0}
}
\newcommand{\nodeR}{%
\pgfmathtruncatemacro\treeX{\treeX+1}
\pgfmathtruncatemacro\lastY{\treeY}
\pgfmathtruncatemacro\treeY{\treeY+1}
\node[R] (R\arabic{treeRnode}) at (\treeX, -\treeY) {R};
\draw (\csuse{treeLastY\lastY}) -| (R\arabic{treeRnode});
\csxdef{treeLastY\treeY}{R\arabic{treeRnode}}
\stepcounter{treeRnode}
}
\newcommand{\nodeA}{%
\pgfmathtruncatemacro\treeX{\treeX+1}
\node[A] (A) (A\arabic{treeAnode}) at (\treeX, -\treeY) {A};
\draw (\csuse{treeLastY\treeY}) -- (A\arabic{treeAnode});
\csxdef{treeLastY\treeY}{A\arabic{treeAnode}}
\stepcounter{treeAnode}
}
\newcommand{\branchend}{%
\pgfmathtruncatemacro\treeY{\treeY-1}
}
\begin{tikzpicture}[
base node/.style={},
D/.style={base node},
R/.style={base node},
A/.style={base node},
]
\nodeD
\nodeR
\nodeR
\nodeR
\branchend
\branchend
\nodeA
\nodeR
\branchend
\nodeA
\branchend
%
\nodeR
\nodeR
\branchend
\branchend
\nodeA
\end{tikzpicture}
\end{document}
答案2
TikZ 是一个多用途绘图系统,带有\draw
和\node
(acoordinate
是特殊的node
)。这是我的建议。代码是不言自明的(稍微注意+
和++
)。
\documentclass[tikz,border=5mm]{standalone}
\begin{document}
\begin{tikzpicture}
\path
(0,0) node[left] (D) {D}
(5,0) node[right] (A) {A}
(4,0) coordinate (P1)
++(0,-.5) node (P2) {R}
++(.5,-.5) node (P3) {R}
(.5,0) coordinate (T1)
++(0,-.5) node (T2) {R}
++(.5,-.5) node (T3) {R}
++(.5,-.5) node (T4) {R}
(T2)
+(2.5,0) node (A2) {A}
+(1.5,0) node (A1) {A}
+(2,-.5) node (A3) {R}
;
\draw[thick,magenta] (D)--(A)
(P1)--(P2)-|(P3)
(T1)--(T2)-|(T3)-|(T4)
(T2)--(A1)--(A2)-|(A3)
;
\end{tikzpicture}
\end{document}
答案3
以下是我的方法forest
。
规则:
一個空的根。
同一视觉级别的节点是兄弟节点。
节点是否应与其先前的兄弟节点连接
connect left
。如果您必须自定义所有边或者想要沿这些边放置节点,则可能需要做更多的工作。
笔记:
connect left
当连接到具有其自身子项的兄弟项时,某些线段会被绘制多次。在合适的 PDF 查看器中,这不应该被注意到,在纸上更是如此。(不要相信我添加到答案中的图片。)
该gere tree
风格安装了几件事:
父级是一个坐标而不是一个节点(不是真的必要,但何必呢)。
其子元素的边缘被隐藏,并且其子元素没有任何
l
意义,因为第一个元素上方没有垂直空间视觉的排。第一个子元素(第一行除外)会获得一个隐藏的上一个兄弟元素。这会将第一的 孩子向右移动,不要弄乱ls坐标系。
fit = rectangle
实现你的当然,需要避免树/文本重叠,即在开始新的子节点之前,一些水平线需要延伸得足够远。类型
A
节点也应该只在其左侧所有先前的子块完成后才出现。在
edge path'
for tree
edge path'={ ([xshift={\pgfkeysvalueof{/pgf/inner xsep}+.4em}].north west) |- (!u)}
将所有边设置为一条
(child) |- (parent)
路径(意味着从子节点向上,然后与父节点正交,父节点始终在左边)。现在设置的方式是将子元素大致连接到第一个(大写)字母上方。要达到第一个字母的真正水平中心,还需要做更多的工作。
如果该线应该击中整个宽度的中心子元素,则只需使用
edge path' = {() |- (!u)}
代码
\documentclass[tikz,convert]{standalone}
\usepackage{forest}
\forestset{
connect left/.style={% don't connect to parent but to previous sibling
tikz+={\draw()--(!p);}, no edge},
gere tree/.style={
shape=coordinate, % parent hidden
for tree={
calign=first, fit=rectangle,
edge path'={
([xshift={\pgfkeysvalueof{/pgf/inner xsep}+.4em}].north west)|-(!u)}},
for children={
no edge, % parent hidden
before computing xy={l=+0pt}, % parent hidden
where n children>={0}{prepend={[,phantom]}}{},% invisible sibling at first
}
}
}
\begin{document}
\begin{forest} gere tree
[
[D
[Cc
[Bbb
[Aaaaa]]]
[Aa, connect left
[Dd]]
[Aaaa, connect left]
]
[G, connect left
[Eee
[A]]
]
[C, connect left]
]
\end{forest}
\end{document}