我正在使用森林来绘制数据结构(特别是 AVL- / (a,b)-树)。我想自动绘制所有叶子之间的连接线(到目前为止,我已经手动命名它们并使用 foreach 语句绘制线条,这有点烦人)。
以下方法有些有效:
for tree={edge={->},if n children=0{}{treenode}},
for leaves={leafnode,tier=list,if={id()!=id("!rL")}{tikz={\draw[<->,thick,gray] () -- (!>);}}{}},
for root={rootnode}
[5 & 19,align=cc
[5] [19] [$\infty$]
]
(这id()!=id("!rL")
是我能想到的唯一方法来检测当前叶子是否不是最后一片,因为没有!>
定义)
但是,我还想在不改变布局的情况下对节点的插入/删除进行动画处理,即添加幻影节点
for tree={edge={->},if n children=0{}{treenode}},
for leaves={leafnode,tier=list,if={id()!=id("!rL")}{tikz={\draw[<->,thick,gray] () -- (!>);}}{}},
for root={rootnode}
[5,align=c
[5] [,phantom] [$\infty$]
]
现在,幻影节点仍然被视为下一个(后来不存在的)兄弟节点,但我想直接从节点 5 到 $\infty$ 节点画一条线。
我能以某种方式搜索下一个非幻像(或非空内容)兄弟吗?我无法理解森林的while
/until
构造,而且例子似乎很少。
编辑:抽象地说,我认为问题可以归结为“如何获取满足 nodewalk 中条件的第一个节点的句柄”
编辑2:MWE
\documentclass{article}
\usepackage{tikz,forest}
\begin{document}
\tikzset{box/.style={draw,rectangle,minimum height=0.7cm,minimum width=1cm}}
\begin{forest}
for tree={edge={->},anchor=north,box},
for leaves={tier=list,if={id()!=id("!rL")}{tikz={\draw[<->] () -- (!>);}}{}},
[x
[a]
[b]
[c]
]
\end{forest}
\end{document}
答案1
这里有两种方法可以做到这一点。 两者都采用了相同的基本思想:我们访问非幻影叶,使用alias'=last
记住先前访问的节点,并使用处理程序将id
前一个节点的 放入代码中。(我们只在指向有效节点时绘制,这由 检查,我们在开始时初始化为无效节点。)tikz
.process
last
if nodewalk valid
last
第一种方法用于where
访问非幻像叶子节点。第一个参数是一个表达式。和pgfmath
的组合也可以。(请注意,从技术上讲,这不是节点行走。)where n children
if phantom
where
\documentclass{article}
\usepackage{forest}
\begin{document}
\tikzset{box/.style={draw,rectangle,minimum height=0.7cm,minimum width=1cm}}
\begin{forest}
for tree={edge={->},anchor=north,box},
% We delay to process the "phantom"s in the tree first:
delay={
% Set name "last" to an invalid node. Argh, cumbersome:
for nodewalk={on invalid={fake}{id=0,alias'=last}}{},
% Visit the non-phantom leaves:
where={n_children==0 && !phantom}{
if nodewalk valid={name=last}{% Do we have the "last" node?
% Yes. Identifying the current node inside \draw is not a problem:
% "()". Possibly the best way to refer to the last node inside TikZ
% code (which is executed much much later) is to paste its "id" there.
% In ".process", "O" gets the option (name.option), and "w" injects
% that into the code as "#1".
tikz+/.process=Ow {last.id}{
\draw[red, thick]()--(!id=#1);
},
}{% The "false" branch of "if nodewalk valid".
},
% Name the current node as last.
alias'=last,
}{% The "false" branch of "where".
},
},
[x
[a]
[b]
[,phantom]
[c]
]
\end{forest}
\end{document}
第二种方式使用真实的 nodealk。我们使用表达式进行filter
nodewalk 。leaves
pgfmath
!phantom
\documentclass{article}
\usepackage{forest}
\begin{document}
\tikzset{box/.style={draw,rectangle,minimum height=0.7cm,minimum width=1cm}}
\begin{forest}
for tree={edge={->},anchor=north,box},
delay={
for nodewalk={on invalid={fake}{id=0,alias'=last}}{},
% Filtering: walk the leaves of the tree, and step on those that are not phantom.
for filter={leaves}{!phantom}{
if nodewalk valid={name=last}{
tikz+/.process=Ow {last.id}{
\draw[red, thick]()--(!id=#1);
},
}{},
alias'=last,
},
},
[x
[a]
[b]
[,phantom]
[c]
]
\end{forest}
\end{document}
答案2
发布后,我偶然发现了define long step
森林文档中的示例。这似乎有效:
\documentclass{article}
\usepackage{tikz,forest}
\begin{document}
\tikzset{box/.style={draw,rectangle,minimum height=0.7cm,minimum width=1cm}}
\forestset{
define long step={nextleaf}{n args=0}{do while={>{O}{phantom}}{next on tier}},
connect leaves/.style={delay={if={>{O!P&}{phantom}{id()!=id("!rL")}}{tikz={\draw[-] () -- (!nextleaf);}}{}}}
}
\begin{forest}
for tree={anchor=north,box},
for leaves={tier=list,connect leaves}
[x
[a]
[b]
[c]
]
\end{forest}
\end{document}
但我仍然想不出更好的方法来检查当前节点是否位于树的末端。