我有许多“决策树”类型的图表,我想动态地制作它们(而不是在 Illustrator 或 OmniGraffle 中)。每个图表包含几百个节点。由于节点数量众多,能够使用围绕根节点的 360 度空间(很像思维导图或圆形系统发育树)将特别有用。
我对每个节点的确切位置没有强烈的偏好。因此,LaTeX 的动态布局可能性很大(我不想手动放置数百个节点)。但如果我有时可以“微调”父节点和子节点之间的角度,布局将更有意义,也更美观。
我已经看到有一种方法可以在 TikZ 中做我想做的事情,grow cyclic
但我不知道如何将其合并到用 Forest 制作的树中(我是 LaTeX 的新手,但我的理解是 Forest 是基于 TikZ 的,所以我的直觉是这应该是可能的)。
有没有办法在 Forest 中径向布局树木,或者我应该使用其他工具?
作为一个快速(且非常简单)的示例,此代码:
\documentclass[tikz,border=5pt]{standalone}
\usepackage{forest}
\usepackage[LGR,T1]{fontenc} % these three lines added
\usepackage[utf8]{inputenc} % so that greek symbols will show
\newcommand{\greek}[1]{\begingroup\fontencoding{LGR}\selectfont#1\endgroup}
% write greek like this --> \greek{α}
\begin{document}
\begin{forest}
for tree={
rectangle,
rounded corners=0.3em,
draw,
minimum width=2.5em,
l sep+=1.5em,
s sep+=1em,
anchor=center,
grow=0,
},
delay={
,
}
[kansas city
[west
[los angeles,
[santa monica]
[west hollywood]
]
[san francisco,
[the castro]
]
]
[south
[texas]
[mexico]
]
[east
[north carolina]
[south carolina]
]
[north
[wisconsin]
[minnesota]
]
]
\end{forest}
\end{document}
但实际上,为了让观看者更有意义,第一级节点(东、西等)应该围绕根节点呈放射状分布,有点像这样极其粗鲁小样 ...
...如果任何给定节点有 8 个以上的子节点,那么使用超过 180 度的弧度来定位这些子节点确实很有意义,在我看来。例如,看看这里的“l”节点有 9 个子节点:
我还要指出的是,我知道,calign
但使用它将子节点对齐到相距超过 179 度的任何位置都会导致 Package PGF 出现“除以零”错误。任何超过 160 度的节点看起来都很糟糕,因为小角度问题会导致节点连接显著延长,但它确实可以达到 179 度。示例代码片段:
\begin{forest}
calign=fixed edge angles,
calign primary angle=-89,calign secondary angle=89,
for tree={
rectangle,
如果需要,我可以切换到 TikZ 或其他东西,只是我在 Forest 上花费了沉没成本,而且它似乎非常适合我的用途。(我能想到的唯一解决方法是将每个图表实际上变成 4 个“分数图表”,其中每个图表覆盖一个象限,然后合并输出。但这似乎是如此糟糕的做法,我想是时候在这里问了。)
答案1
它绘制了五棵树,分别代表north
、east
、south
和城市组。每棵树都使用 保存west
。每棵树都有定制的生长方向和锚点以适应其设计。例如,树使用,等等。对于向西北生长的树,可能需要使用锚点来获得节点和边的首选排列。然后使用 绘制各个树,并使用库语法定位。每棵树都使用命令连接到根节点。northwest
\savebox
north
grow'=north, child anchor=south, parent anchor=north
\node
positioning
Kansas City
\draw
结果如下:
这是 MWE:
% http://tex.stackexchange.com/questions/213770/how-to-combine-a-top-down-and-bottom-up-binary-tree-in-one-picture
\documentclass[12pt,crop=true,border=1cm]{standalone}
\usepackage[edges]{forest}
\usepackage[none]{hyphenat}
\usetikzlibrary{shapes,positioning,arrows.meta,calc}
\tikzset{
mynodes/.style={inner sep=0pt,draw=none}
}
\forestset{
mytree/.style={
forked edges,
for tree={
draw,
rounded corners,
node options={align=center},
text width=2cm
}
}
}
\newsavebox\North
\newsavebox\East
\newsavebox\South
\newsavebox\West
\newsavebox\NorthWest
\savebox\North{\begin{forest}
mytree,
for tree={
child anchor=south,
parent anchor=north,
grow'=north,
anchor=south,
}
[North
[Minnesota]
[Wisconsin]
]
\end{forest}
}
\savebox\East{\begin{forest}
mytree,
for tree={
child anchor=west,
parent anchor=east,
grow'=east,
anchor=west,
}
[East
[South Carolina]
[North Carolina]
]
\end{forest}
}
\savebox\South{\begin{forest}
mytree,
for tree={
child anchor=north,
parent anchor=south,
grow'=south,
anchor=north,
}
[South
[Texas]
[Mexico]
]
\end{forest}
}
\savebox\West{\begin{forest}
mytree,
for tree={
child anchor=east,
parent anchor=west,
grow'=west,
anchor=east,
}
[West
[Los Angeles
[Santa Monica]
[West Hollywood]
]
[San Francisco
[The Castro]
]
]
\end{forest}
}
\savebox\NorthWest{\begin{forest}
mytree,
for tree={
child anchor=south east,
parent anchor=north west,
grow'=north west,
anchor=east,
}
[North West
[Portland]
[Seattle
[Redmond]
]
]
\end{forest}
}
\begin{document}
\begin{tikzpicture}[node distance=2cm]
\node (Kansas) at (0,0) [shape=ellipse,x radius=2cm,draw,align=center] {Kansas City};
\node (North) [mynodes,above=of Kansas] {\usebox\North};
\node (East) [mynodes,right=of Kansas] {\usebox\East};
\node (South) [mynodes,below=of Kansas] {\usebox\South};
\node (West) [mynodes,left=of Kansas,xshift=3mm,yshift=-3mm] {\usebox\West};
\node (NorthWest) [mynodes,above left=1.85cm and 2.0cm of Kansas] {\usebox\NorthWest};
%
\draw[thick] (Kansas.north) -- (North.south);
\draw[thick] (Kansas.east) -- (East.west);
\draw[thick] (Kansas.south) -- (South.north);
\draw[thick] (Kansas.west) -- ++(-1.85,0) ;% (West.east);
\draw[thick] (Kansas.north west) -- ++(-2.2,1.9) ;% (NorthWest.south east);
\end{tikzpicture}
\end{document}
答案2
从概念上来说非常类似于罗斯的精彩回答但我不使用保存箱Ulrike Fischers 子节点技巧。这允许连接节点而不是保存箱。仍然需要进行一些手动调整。编辑:grow
用“北卡罗来纳”替换“s” grow'
,使得“北卡罗来纳”位于“南卡罗来纳”以北,并且添加了 makeboxes 以节省一些空间。
\documentclass[border=5pt]{standalone}
\usepackage{forest}
\usetikzlibrary{tikzmark} % subnode trick from https://tex.stackexchange.com/a/393656/121799
\usepackage[LGR,T1]{fontenc} % these three lines added
\usepackage[utf8]{inputenc} % so that greek symbols will show
\begin{document}
\forestset{universal/.style={rounded corners=0.3em,
draw,rectangle,
minimum width=2.5em,
l sep+=1.5em,
s sep+=1em,
anchor=center,}
}
$
\begin{array}{ccc}
&\makebox[3.5cm][c]{\begin{forest}
for tree={
universal,
grow'=north
},
[\subnode{north}{north}
[wisconsin]
[minnesota]
]
\end{forest}} & \\
\vcenter{\hbox{\begin{forest}
for tree={
universal,
grow'=west
},
[\subnode{west}{west}
[los angeles,
[santa monica]
[west hollywood]
]
[san francisco,
[the castro]
]
]
\end{forest}}}
&
\vcenter{\hbox{\begin{forest}
for tree={
universal,
grow'=0
},
[\subnode{kansas}{kansas city}
]
\end{forest}}} &
\vcenter{\hbox{\begin{forest}
for tree={
universal,
grow'=east
},
[\subnode{east}{east}
[north carolina]
[south carolina]
]
\end{forest}}} \\
&
\makebox[3em][c]{\begin{forest}
for tree={
universal,
grow'=south
},
[\subnode{south}{south}
[texas]
[mexico]
]
\end{forest}}
&
\end{array}$
\begin{tikzpicture}[remember picture,overlay]
\draw (kansas) -- (north);
\draw (kansas) -- (west);
\draw (kansas) -- (south);
\draw (kansas) -- (east);
\end{tikzpicture}
\end{document}