我需要在 LaTeX 中绘制如下所示的 PQ 树:
我尝试在 tikz 中使用树,但遇到了两个问题:
- 部分阴影矩形
- 如果父级是矩形,则使用从子级到父级的垂直指针
我想知道 tikz-trees 是否是最简单的方法,或者您是否建议使用其他方法?
答案1
该解决方案使用forest
包用于放置节点。这样做的好处是它们可以自动定位,不会重叠。
对于节点我定义了三种样式:
tria
对于三角形节点,它使用形状isosceles triangle
(可以指定宽度和高度);rect
对于矩形节点,作为参数,它需要其子节点的数量(包括节点…
),以便它可以正确地拉伸并覆盖其子节点的宽度;circ
为圆形节点;dots
对于包含的节点\ldots
,通过键(一个键)\ldots
给出。content
forest
此外,
键
patt
用于通过选项仅对形状的一部分进行图案化path picture
;当我使用 TikZ 版本(坐标@aux1
和@aux2
位于默认中心)遇到问题时,此版本的(更快更方便)PGF 版本有效。参数是<start of the pattern>:<end of the pattern>
,即水平比率。用于
*|
仅绘制一条垂直线到父节点(这实际上可以包含在样式中rect
)。normal width
和键的值normal height
用于节点的大小。
在rect
风格上可以找到
minimum width=
(#1 )*(\pgfkeysvalueof{/forest/normal width})
+ (#1+1)*(\forestove{s sep})
- (#1 )*(\pgfkeysvalueof{/pgf/outer xsep})
效果很好,尽管我最初的设置不同
当rect
样式获得子元素的数量时#1
,宽度设置为#1 * width
占子元素总宽度的数。到目前为止一切顺利。我最初在这里乘以#1 - 1
(s sep
六个子元素之间有五个空格),虽然#1 + 1
看起来更正确,否则子元素或多或少会突出角度的水平边rect
(或者rect
不如其子元素宽)。
节点之间的间距似乎还有更多问题。如果设置不同的normal width
和normal height
值,则间距会受到影响。我怀疑节点的高度会以某种方式影响s sep
节点之间的水平间距。
进一步改进……
…可以通过自动化流程来实现。三角形节点只与它们之间的点成对出现,然后以相同的方式形成图案。
另外,forest
可能还具有提前读取孩子的数量的功能,以便rect
自动设置角度的宽度。
代码
\documentclass[tikz,convert=false]{standalone}
\usepackage{forest}
\usetikzlibrary{shapes.geometric,patterns}
\forestset{
*|/.style={
parent anchor=south,
for descendants={
edge path={
\noexpand\path[\forestoption{edge}]
(!u.parent anchor-|.child anchor) -- (.child anchor)\forestoption{edge label};
}
}
},
normal width/.initial=.5cm,
normal height/.initial=.5cm,
every forest node/.style={
draw,
minimum width=\pgfkeysvalueof{/forest/normal width},
minimum height=\pgfkeysvalueof{/forest/normal height},
inner sep=+0pt,
anchor=south,
},
tria/.style={
every forest node,
shape=isosceles triangle,
shape border rotate=90,
isosceles triangle apex angle=60,
% isosceles triangle stretches=true
},
rect/.style={
every forest node,
shape=rectangle,
minimum width=(#1)*(\pgfkeysvalueof{/forest/normal width})+(#1+1)*(\forestove{s sep})-(#1)*(\pgfkeysvalueof{/pgf/outer xsep}),
},
rect/.default=3,
circ/.style={
every forest node,
shape=circle
},
dots/.style={
no edge,
content=\ldots,
every forest node,
shape=rectangle,
draw=none,
},
patt/.style args={#1:#2}{
node options={
/tikz/path picture={
\pgfsetfillpattern{north east lines}{black}
\pgfpathrectanglecorners
{\pgfpointlineattime{#1}
{\pgfpointanchor{path picture bounding box}{north west}}
{\pgfpointanchor{path picture bounding box}{north east}}}
{\pgfpointlineattime{#2}
{\pgfpointanchor{path picture bounding box}{south west}}
{\pgfpointanchor{path picture bounding box}{south east}}}
\pgfusepath{fill}
% \draw[green,ultra thick] (path picture bounding box.north west) -- coordinate[pos={#1}] (@aux1) (path picture bounding box.north east)
% (path picture bounding box.south west) -- coordinate[pos={#2}] (@aux2) (path picture bounding box.south east);
% \fill[draw,pattern=north east lines] (@aux1) rectangle (@aux2);
%% \fill[blue] (@aux1) circle (2pt);
%% \fill[red] (@aux2) circle (1pt);
}
}
},
patt/.default=0:1
}
\begin{document}
\begin{forest} for tree={child anchor=north}
[,circ,
[, tria]
[, dots]
[, tria]
[, rect=6, patt=.5:1, *|, alias=XY
[, tria]
[, dots]
[, tria]
[, tria, patt]
[, dots]
[, tria, patt, alias=XX]
]
[, tria, patt]
[, dots]
[, tria, patt]
[, rect=6, patt=0:.5, *|
[, tria, patt]
[, dots]
[, tria, patt]
[, tria]
[, dots]
[, tria]
]
]
% just to show that they are aligned correctly
\draw[opacity=.5] (XY.south east) -- (XX.right corner);
\end{forest}
\end{document}