我正在尝试将这片森林的分支 1 和 2 与字幕 1 和 2 下的垂直线对齐,但目前还无法做到这一点。您知道该怎么做吗?
该图是使用此代码制作的。在序言中:
\usepackage[linguistics]{forest}
\makeatletter
\csdef{forest@compute@node@boundary@rounded rectangle}{%
\forest@mt{east}%
\forest@lt{north east}%
\forest@lt{north}%
\forest@lt{north west}%
\forest@lt{west}%
\forest@lt{south west}%
\forest@lt{south}%
\forest@lt{south east}%
\forest@lt{east}%
}
\makeatother
并在文件中:
\vspace{0.2cm}
\begin{center}
\begin{forest}
t1/.style={style={draw=black,text height=2.3ex,text depth=.85ex},rounded corners},
t2/.style={style={draw=black,text height=4.5ex,text depth=3ex},rounded corners},
t3/.style={style={draw=black,text height=6.6ex,text depth=5.0ex},rounded corners},
for tree={edge path={\noexpand\path[\forestoption{edge}] (\forestOve{\forestove{@parent}}{name}.parent anchor) -- +(0,-8pt)-| (\forestove{name}.child anchor)\forestoption{edge label};}
}
[Title, s sep=8mm,t1
[Subtitle 1, s sep=8mm,t1
[$\begin{array}{l} \bullet \\ \bullet \\ \bullet \end{array}$, s sep=8mm,t3
[Branch 1, s sep=8mm,t1
[$\begin{array}{l} \bullet \\ \bullet \end{array}$, s sep=8mm,t2]
]
[Branch 2, s sep=8mm,t1
[$\begin{array}{l} \bullet \\ \bullet \end{array}$, s sep=8mm,t2]
]
]
]
[Subtitle 2, s sep=8mm,t1
[$\begin{array}{l} \bullet \\ \bullet \end{array}$, s sep=8mm,t2]
]
]
\end{forest}
\end{center}
答案1
一个近似解决方案非常简单:添加calign=1
到包含“Branches”的三重项目符号节点。此键将节点与第一个(因为1
)子节点对齐;在我们的例子中,它将垂直对齐“Branch 1”与三重项目符号节点(因此是“Subtitle 1”)。如果“Subtitle”节点与“Branch”节点的宽度相同,这将完美地工作。由于情况并非如此,“Branch 2”看起来有点错位,所以我们需要开发另一个解决方案。
子节点相对于父节点的位置存储在s
(给定树的生长方向,稍后将转换为坐标x
)。因此,我们的想法是s
从“字幕”节点复制到“分支”节点 — 这会将“字幕”之间的“水平距离”转移到“分支” — 然后从所有“分支”的中减去(s-
)s
“字幕 1”的s
— 这将使s
“分支 1”为零,使其与父节点对齐,而所有其他“分支”节点将与其对应的“字幕”节点对齐。
我们先手动操作一下,相关代码可以在before computing xy
“分支 1”和“分支 2”中找到。
\documentclass{article}
\usepackage[linguistics]{forest}
\makeatletter
\csdef{forest@compute@node@boundary@rounded rectangle}{%
\forest@mt{east}%
\forest@lt{north east}%
\forest@lt{north}%
\forest@lt{north west}%
\forest@lt{west}%
\forest@lt{south west}%
\forest@lt{south}%
\forest@lt{south east}%
\forest@lt{east}%
}
\makeatother
\begin{document}
\vspace{0.2cm}
\begin{center}
\begin{forest}
t1/.style={style={draw=black,text height=2.3ex,text depth=.85ex},rounded corners},
t2/.style={style={draw=black,text height=4.5ex,text depth=3ex},rounded corners},
t3/.style={style={draw=black,text height=6.6ex,text depth=5.0ex},rounded corners},
for tree={edge path={\noexpand\path[\forestoption{edge}] (\forestOve{\forestove{@parent}}{name}.parent anchor) -- +(0,-8pt)-| (\forestove{name}.child anchor)\forestoption{edge label};}
}
[Title, s sep=8mm,t1
[Subtitle 1, s sep=8mm,t1
[$\begin{array}{l} \bullet \\ \bullet \\ \bullet \end{array}$, s sep=8mm,t3,
[Branch 1, s sep=8mm,t1,
before computing xy={s/.option={!r1.s}, s-/.option={!r1.s}},
[$\begin{array}{l} \bullet \\ \bullet \end{array}$, s sep=8mm,t2]
]
[Branch 2, s sep=8mm,t1,
before computing xy={s/.option={!r2.s}, s-/.option={!r1.s}},
[$\begin{array}{l} \bullet \\ \bullet \end{array}$, s sep=8mm,t2]
]
]
]
[Subtitle 2, s sep=8mm,t1
[$\begin{array}{l} \bullet \\ \bullet \end{array}$, s sep=8mm,t2]
]
]
\end{forest}
\end{center}
\end{document}
before computing xy
是时间传播子将密钥的执行延迟到树已经打包好的时刻(即,森林已经确定了每个节点的放置位置并将这个决定存储在节点的s
和l
中),但在树布局转移到纸张坐标x
和之前y
。
.option
是密钥处理程序告诉 Forest 给定的值应解释为选项名称,并且是相对选项名称。读!r1.s
作:!
表示“我们去散步”;r
= 让我们去根节点;1
= 让我们去第一个子节点;点将相对节点名称与选项名称分开;s
是我们想要获取的选项。因此,s/.option={!r1.s}
指示 Forest 采用s
根节点的第一个子节点的选项并将其存储在s
当前节点的选项中。
当然,当我们有许多节点需要处理时,手动方法使用起来很麻烦。下面,我们s
一次调整所有“分支”的选项。相关代码可以在 中找到before computing xy
,但现在这个时间传播器出现在三重子弹节点中,它是“分支”的父节点。
\documentclass{article}
\usepackage[linguistics]{forest}
\makeatletter
\csdef{forest@compute@node@boundary@rounded rectangle}{%
\forest@mt{east}%
\forest@lt{north east}%
\forest@lt{north}%
\forest@lt{north west}%
\forest@lt{west}%
\forest@lt{south west}%
\forest@lt{south}%
\forest@lt{south east}%
\forest@lt{east}%
}
\makeatother
\begin{document}
\vspace{0.2cm}
\begin{center}
\begin{forest}
t1/.style={style={draw=black,text height=2.3ex,text depth=.85ex},rounded corners},
t2/.style={style={draw=black,text height=4.5ex,text depth=3ex},rounded corners},
t3/.style={style={draw=black,text height=6.6ex,text depth=5.0ex},rounded corners},
for tree={edge path={\noexpand\path[\forestoption{edge}] (\forestOve{\forestove{@parent}}{name}.parent anchor) -- +(0,-8pt)-| (\forestove{name}.child anchor)\forestoption{edge label};}
}
[Title, s sep=8mm,t1
[Subtitle 1, s sep=8mm,t1
[$\begin{array}{l} \bullet \\ \bullet \\ \bullet \end{array}$, s sep=8mm,t3,
before computing xy={
for children={
s/.process=Ow+O{n}{!r#1.s},
s-/.option={!r1.s},
},
},
[Branch 1, s sep=8mm,t1,
[$\begin{array}{l} \bullet \\ \bullet \end{array}$, s sep=8mm,t2]
]
[Branch 2, s sep=8mm,t1,
[$\begin{array}{l} \bullet \\ \bullet \end{array}$, s sep=8mm,t2]
]
]
]
[Subtitle 2, s sep=8mm,t1
[$\begin{array}{l} \bullet \\ \bullet \end{array}$, s sep=8mm,t2]
]
]
\end{forest}
\end{center}
\end{document}
空间传播器for children
是不言自明的:它对每个子节点使用给定的选项。其中s-
的第二行for children
也很熟悉:它将s
从根节点的第一个子节点中 减去s
。第一行调用参数处理器需要.process
解释——但要真正了解它是如何工作的,您还需要查阅手册第 3.13 节。
的值.process
由两部分组成:指令(OwO
)和参数({n}{!r#1.s}
)。
第一条指令,
O
,告诉森林查找一个选项。哪个选项?它的名称在第一个参数中给出:n
,该选项保存节点的子编号(“分支 1”和“字幕 1”有n=1
;“分支 2”和“字幕 2”有n=2
)。结果(的值n
)。结果(当前节点处理争论。第二条指令是
w
:将第一个剩余参数包裹在最后处理的参数周围。在我们的例子中,第一个剩余参数是!r#1.s
。它被视为宏定义,并被#1
替换为最后处理的参数。假设我们处于“分支 2”:那么该指令的结果是!r2.s
成为最后处理的参数。指令
+
使最后处理的参数成为第一个剩余的参数。O
再次查找第一个剩余参数中命名的选项;在我们的例子中是!r2.s
。我们已经知道这是什么:s
根的第二个子节点的,即s
“Subtitle 2”的。然后,该值将成为最后处理的参数。没有其他指令,因此最后处理的参数,即
s
“字幕 2”的 ,存储在s
当前节点的 中(因为s/.process
)。(通常,所有处理过的参数加上剩余的参数都会传递给调用选项。)
简而言之,简短而复杂(至少在你习惯它之前:)。