![森林:当子节点缺失时,父节点与孙节点之间的定制边](https://linux22.com/image/379669/%E6%A3%AE%E6%9E%97%EF%BC%9A%E5%BD%93%E5%AD%90%E8%8A%82%E7%82%B9%E7%BC%BA%E5%A4%B1%E6%97%B6%EF%BC%8C%E7%88%B6%E8%8A%82%E7%82%B9%E4%B8%8E%E5%AD%99%E8%8A%82%E7%82%B9%E4%B9%8B%E9%97%B4%E7%9A%84%E5%AE%9A%E5%88%B6%E8%BE%B9.png)
考虑这棵树,其中节点 C1 缺失。缺失节点可能出现在树的任何层级,并且节点可能出现在缺失节点下方的一个或多个层级。
绘制 B1 -> D1 和 B1 -> D2 的边的语法是什么,如红色所示,以使这些边的垂直部分与节点 C2 下方的边的垂直部分对齐?键fork sep
指定l
父锚点和分叉之间的距离,但我不知道如何使用它。
这是 MWE:
\documentclass[12pt,crop=true,border=1cm]{standalone}
\usepackage[edges]{forest}
\begin{document}
\begin{forest}
for tree={
grow'=0,
draw,
forked edges,
text width=10mm,
minimum height=5mm,
parent anchor=east,
child anchor=west,
text centered,
}
[A,tier=level1
[B1,tier=level2,name=B1
% [C1 would be here, but it is missing
[D1,tier=level4,name=D1
[E1,tier=level5]
[E2,tier=level5]
]
[D2,tier=level4,name=D2
[E3,tier=level5]
[E4,tier=level5]
]
% ]
]
[B2,tier=level2
[C2,tier=level3
[D3,tier=level4
[E5,tier=level5]
[E6,tier=level5]
]
[D4,tier=level4
[E7,tier=level5]
[E8,tier=level5]
]
]
]
]
% Draw the edges with the fork at the desired level.
% The fork sep key specifies the "The l-distance between the parent anchor and the fork."
% See Forest manual version July 14, 2017, p81
\draw[red,thick] (B1.parent anchor) -- +(57pt,0) |- (D1.child anchor);
\draw[red,thick] (B1.parent anchor) -- +(57pt,0) |- (D2.child anchor);
\end{forest}
\end{document}
更新
使用 @marmot 的代码测试扩展的 MWEfork sep
发现了一些不足之处
由于 处有两个节点(C1
和C3
) ,并且缺少tier=level3
第三个节点,因此边缘形状类似于文件夹结构,而不是分叉。C2
删除C2
并D5
导致树不再正确表示结构。
虽然我预计自动化解决方案将依赖于使用密钥fork sep
,但@marmot 的解决方案看起来很接近,但可能仍然缺少一些东西。
@Zarko 发布的解决方法使用正交坐标在祖父母节点和子节点之间绘制边来定义分叉的位置。这是一种稳健且通用的方法。@Zarko,请回复您的答案。
更新2
这可能是一个强大的解决方案。它使用 (a) @marmot 的fork sep
代码,(b) 将缺失的节点标识为坐标而不绘制节点形状,即[,draw=none,name=C2
(c) 使用\draw
命令连接到node.child anchor
。node.parent anchor
例如:\draw (C2.child anchor) -- (C2.parent anchor);
\documentclass[12pt,crop=true,border=1cm]{standalone}
\usepackage[edges]{forest}
\forestset{
declare dimen={my fork sep}{0.5em},
my forked edge'/.style={
edge={rotate/.option=!parent.grow},
edge path'={let \noexpand\p1=($(.child anchor)-(!u.parent anchor)$) in
(!u.parent anchor) -- ++(\noexpand\x1-\forestoption{my fork sep},0) |- (.child anchor)},
},
my forked edge/.style={
on invalid={fake}{!parent.parent anchor=children},
child anchor=parent,
my forked edge',
},
my forked edges/.style={for nodewalk={#1}{my forked edge}},
my forked edges/.default=tree,
}
\begin{document}
\begin{forest}
for tree={
grow'=0,
draw,
my forked edges,my fork sep=8pt,
text width=10mm,
minimum height=5mm,
parent anchor=east,
child anchor=west,
text centered,
}
[A,tier=level1
[B1,tier=level2,name=B1
[C1% would be here, but it is missing
[D1,tier=level4,name=D1a
[E1,tier=level5]
[E2,tier=level5]
]
[D2,tier=level4,name=D2a
[E3,tier=level5]
[E4,tier=level5]
]
[D3,tier=level4,name=D3a
[E5,tier=level5]
[E6,tier=level5]
]
]
[,draw=none,name=C2
[D4,tier=level4,name=D1b
[E7,tier=level5]
[E8,tier=level5]
]
[,draw=none,name=D5
[E9,tier=level5]
[E10,tier=level5]
]
]
]
[B2,tier=level2
[C3,tier=level3
[D6,tier=level4
[E11,tier=level5]
[E12,tier=level5]
]
[D7,tier=level4
[E13,tier=level5]
[E14,tier=level5]
]
]
]
]
\draw (C2.child anchor) -- (C2.parent anchor);
\draw (D5.child anchor) -- (D5.parent anchor);
\end{forest}
\end{document}
答案1
我不知道这个问题的扩展版本是什么,因为现在的问题是一个答案而不是一个问题。
因此,我的目标是做两件事:
回答非扩展问题;
在扩展答案问题中重现结果。
基本上,我想提出一些更简单的东西,它不需要绘制任何东西,不需要样式的变体forked edges
(尽管它确实使用了样式原始版本的包装器)并且让您将缺失的节点指定为空节点。
此外,tier
s 的分配是自动化的,并且相对锚点的使用使代码更加灵活。
包装器样式的名字很无趣,my tree
定义如下:
\forestset{
my tree/.style={
forked edges,
注意forked edges
应该不是在 的范围内。要么在该范围内for tree
使用,要么像这里一样,保持在范围之外。forked edge
forked edges
for tree={
grow'=0,
draw,
text width=10mm,
minimum height=5mm,
text centered,
tier/.option=level,
这使级别自动化,因为级别完全决定了所需的层级。
},
delay={
where content={}{content=\phantom{X},draw=none, child anchor=children}{}
如果您想忽略某个节点,请将该节点留空。然后,此代码将添加一个幻影X
(以避免费力地尝试找出正确的高度 - 显然其他节点超过了最小值5mm
)。该节点将具有标准分配的宽度,但不会被绘制。child anchor
但是,更改 会产生使其看起来好像没有节点的效果。本质上,这会将分支从父节点一直延伸到节点的中心,如果您在远端有一个坐标,而不是常规宽度节点,就会发生这种情况。
在某些情况下,您需要一个坐标,但在这里,不走这条路更容易。如果您真的想要真正的coordinate
s 而不是相似的 s,我建议稍后在 中移动坐标before drawing tree
。
},
},
}
就是这样。
现在我们可以写
\begin{forest}
my tree
[A
[B1
[
[D1
[E1]
[E2]
]
[D2
[E3]
[E4]
]
]
]
[B2
[C2
[D3
[E5]
[E6]
]
[D4
[E7]
[E8]
]
]
]
]
\end{forest}
\begin{forest}
my tree,
[A
[B1
[
[D1
[E1]
[E2]
]
[D2
[E3]
[E4]
]
[D3
[E5]
[E6]
]
]
[
[D4
[E7]
[E8]
]
[
[E9]
[E10]
]
]
]
[B2
[C3
[D6
[E11]
[E12]
]
[D7
[E13]
[E14]
]
]
]
]
\end{forest}
生产
\documentclass[12pt,border=10pt]{standalone}
\usepackage[edges]{forest}
\forestset{
my tree/.style={
forked edges,
for tree={
grow'=0,
draw,
text width=10mm,
minimum height=5mm,
text centered,
tier/.option=level,
},
delay={
where content={}{content=\phantom{X},draw=none, child anchor=children}{}
},
},
}
\begin{document}
\begin{forest}
my tree
[A
[B1
[
[D1
[E1]
[E2]
]
[D2
[E3]
[E4]
]
]
]
[B2
[C2
[D3
[E5]
[E6]
]
[D4
[E7]
[E8]
]
]
]
]
\end{forest}
\begin{forest}
my tree,
[A
[B1
[
[D1
[E1]
[E2]
]
[D2
[E3]
[E4]
]
[D3
[E5]
[E6]
]
]
[
[D4
[E7]
[E8]
]
[
[E9]
[E10]
]
]
]
[B2
[C3
[D6
[E11]
[E12]
]
[D7
[E13]
[E14]
]
]
]
]
\end{forest}
\end{document}
答案2
我不是森林专家,但我可以在森林手册中查找分叉边缘的定义,并用它来定义一个“新”版本,其中分叉到子级(而不是父级)的距离是固定的。编辑:已将 移出my forked edges
,for tree
非常感谢@cfr!
\documentclass[12pt,crop=true,border=1cm]{standalone}
\usepackage[edges]{forest}
%\usetikzlibrary{calc}
\forestset{
declare dimen={my fork sep}{0.5em},
my forked edge'/.style={
edge={rotate/.option=!parent.grow},
edge path'={let \noexpand\p1=($(.child anchor)-(!u.parent anchor)$) in
(!u.parent anchor) -- ++(\noexpand\x1-\forestoption{my fork sep},0) |- (.child anchor)},
},
my forked edge/.style={
on invalid={fake}{!parent.parent anchor=children},
child anchor=parent,
my forked edge',
},
my forked edges/.style={for nodewalk={#1}{my forked edge}},
my forked edges/.default=tree,
}
\begin{document}
\begin{forest}
my forked edges,
for tree={
grow'=0,
draw,
my fork sep=8pt,
text width=10mm,
minimum height=5mm,
parent anchor=east,
child anchor=west,
text centered,
}
[A,tier=level1
[B1,tier=level2,name=B1
% [C1 would be here, but it is missing
[D1,tier=level4,name=D1
[E1,tier=level5]
[E2,tier=level5]
]
[D2,tier=level4,name=D2
[E3,tier=level5]
[E4,tier=level5]
]
% ]
]
[B2,tier=level2
[C2,tier=level3
[D3,tier=level4
[E5,tier=level5]
[E6,tier=level5]
]
[D4,tier=level4
[E7,tier=level5]
[E8,tier=level5]
]
]
]
]
\end{forest}
\end{document}