
私のソフトウェアは、いくつかのドキュメントを自動的に生成します。ドキュメントに追加している要素の 1 つは、ここで説明するスタイルのツリーです。
ツリーが大きくなると、複数のページにまたがることがあります。ここで、ツリーにページを分割する場所を手動で指示する方法があることがわかりました。
適切な垂直方向の位置合わせで森林ツリーを 2 つのツリーに分割するにはどうすればよいですか?
しかし、私が本当に望んでいるのは、ツリーを自動的にページ区切りにすることです。Tikz Forest でこれを実現する方法はありますか?
森林コードの例は以下のとおりです。
\documentclass[english,10pt,oneside,table,xcdraw]{book}
\usepackage{lmodern}
\usepackage[lgr,T1]{fontenc}
\usepackage{geometry}
\usepackage[utf8]{inputenc}
\usepackage{forest}
\usepackage{longtable}
\usepackage{tikz}
\usetikzlibrary{calc,shapes,shapes.arrows,arrows,trees,shadows,backgrounds,positioning}
\forestset{
nodeStyle/.style={
before typesetting nodes={
edge=#1,
for ancestors={
edge=#1,
#1,
},
#1,
}
},
my edge label/.style={
edge label={
node [midway, fill=white, font=\scriptsize] {#1}
}
}
}
\begin{document}
\newcolumntype{C}[1]{>{\centering}p{#1}}
\newcolumntype{M}{>{\begin{varwidth}{4cm}}l<{\end{varwidth}}} %M is for Maximal column
\definecolor{tempColor}{rgb}{0.2,1,0.2}
\begin{figure}
\begin{center}
\par\medskip
{
\footnotesize
\begin{forest}
for tree=
{
if level=0{align=center}
{
% allow multi-line text and set alignment
align={@{}C{50mm}@{}},
},
grow'=0,
font=\sffamily\bfseries,
edge path=
{
\noexpand\path [draw, \forestoption{edge}] (!u.south west) +(7.5pt,0) |- (.child anchor) \forestoption{edge label};
},
before typesetting nodes=
{
if n=1
{
insert before={[,phantom]}
}
{}
},
parent anchor=south,
child anchor=west,
anchor=west,
calign=first,
inner xsep=7pt,
edge={ultra thick, rounded corners=2pt},
fill=white,
rounded corners=2pt,
fit=band,
before computing xy={l=15pt},
}
[{TypeData}
[{ModelType}
]
[{StationType},drop shadow
[{StationConfig},fill=lightgray
]
[{StationConfig},drop shadow,fill=lightgray[child, font=\sffamily, align=left][child, font=\sffamily, align=left][child, font=\sffamily, align=left]
]
[{StationConfig},drop shadow,fill=lightgray[child, font=\sffamily, align=left][child, font=\sffamily, align=left][child, font=\sffamily, align=left]
]
[{StationConfig},drop shadow,fill=lightgray[child, font=\sffamily, align=left][child, font=\sffamily, align=left][child, font=\sffamily, align=left]
]
[{StationConfig},drop shadow,fill=lightgray[child, font=\sffamily, align=left][child, font=\sffamily, align=left][child, font=\sffamily, align=left]
]
[{StationConfig},drop shadow,fill=lightgray[child, font=\sffamily, align=left][child, font=\sffamily, align=left][child, font=\sffamily, align=left]
]
[{StationConfig}
]
[{StationConfig}
]
[{StationConfig}
]
[{StationConfig}
]
[{StationConfig}
]
[{StationConfig}
]
[{StationConfig}
]
[{StationConfig}
]
[{StationConfig}
]
[{StationConfig}
]
[{StationConfig}
]
[{StationConfig}
]
[{StationConfig},drop shadow,fill=lightgray[child, font=\sffamily, align=left][child, font=\sffamily, align=left][child, font=\sffamily, align=left]
]
[{StationConfig},drop shadow,fill=lightgray[child, font=\sffamily, align=left][child, font=\sffamily, align=left][child, font=\sffamily, align=left]
]
[{StationConfig},drop shadow,fill=lightgray[child, font=\sffamily, align=left][child, font=\sffamily, align=left][child, font=\sffamily, align=left]
]
[{StationConfig},drop shadow,fill=lightgray[child, font=\sffamily, align=left][child, font=\sffamily, align=left][child, font=\sffamily, align=left]
]
[{StationConfig},drop shadow,fill=lightgray[child, font=\sffamily, align=left][child, font=\sffamily, align=left][child, font=\sffamily, align=left]
]
[{StationConfig},drop shadow,fill=lightgray[child, font=\sffamily, align=left][child, font=\sffamily, align=left][child, font=\sffamily, align=left]
]
[{StationConfig},drop shadow,fill=lightgray[child, font=\sffamily, align=left][child, font=\sffamily, align=left][child, font=\sffamily, align=left]
]
[{StationConfig},drop shadow,fill=lightgray[child, font=\sffamily, align=left][child, font=\sffamily, align=left][child, font=\sffamily, align=left]
]
[{StationConfig}
]
[{StationConfig}
]
[{StationConfig}
]
]
[{SecondType}
]
]
\end{forest}
}
\caption{ }
\end{center}
\end{figure}
\end{document}
答え1
問題がコンパイル エラーでない限り、常に完全で動作するコードを投稿してください。コードをコンパイルするように説得するだけでは、推測の作業になります。このため、私が気付かないうちにコードを変更している可能性があります。
あなたの序文についての私の推測は次のとおりです:
\documentclass[a4paper]{article}
\usepackage{geometry,array}
\usepackage{forest}
\usetikzlibrary{shadows}
\newcolumntype{C}[1]{@{}>{\centering\arraybackslash}m{#1}@{}}
明らかに、これはおそらくあなたのものと同じではないでしょう。それは推測する限り当然のことです。以下の回答は、これがあなたの前文であるという仮定に基づいています。
問題が自動化だけの問題であれば、あなたの例は手動の方法を示しているはずです。疑い深いあなたは、たとえ理由がわからなくても、そうではないことはわかっていると思います。
もちろん、これらはどれもあまり役に立ちません。質問に費やせるはずの時間が、どのパッケージをロードするかを判断したり、 の定義を考え出したりするなど、他の多くのことに費やされることになりますC
。
わかりました。リンクした質問の回答で示されているコードは、下向きに成長する木を分割するためにのみ使用できます。あなたの木は東向きに成長します。
さらに、少なくともいくつかの例 (例: mind) は、コンソール上の TeX のプロンプトに応答するか、 を変更しなければ、現在の Forest ではコンパイルできませんforest.sty
。
したがって、プロンプトを無視できるように、コマンド ラインでコンパイルする必要があるでしょう。エラーはありません。TeX は、\show
さらにヘルプを要求した場合に通知されるように、単に何かを実行しているだけです。したがって、ここではエラーを無視することについて心配する必要はありません。表示したいものを表示させるだけでよいのです。
次のコードは、次のことを行います。
- Forest 2 のスタイルを使用するようにコードが更新され
folder
、このスタイルのツリーの描画が容易になります。 - それは、
dir tree
そのスタイルの特定の味のためのスタイルを提供します。 split dir here=<text>
リンクされたスタイルと同様に、現在のノードでツリーを分割するスタイルを提供しますsplit here=<text>
。
スタイル定義は、リンクされた質問への回答に含まれており、以下のコメントに記載されているように、Sašo のコードを私が改変したものに基づいています。
制限事項:
- 何も自動的に分割されません。
- 奇妙なことをすると分割は正しく機能しません。ここでの「奇妙な」とは、おおよそ「分割が正しく機能しなくなる原因となること」を意味します。
- それほど奇妙なことを行わない場合でも、分割後にタイプセットされた現在のノードの兄弟に描画されたすべてのエッジが分割によって失われます。
- ツリーごとに 1 つの分割のみがサポートされます (ただし、複数分割バージョンについては以下を参照してください)。
- 分割はノードのデフォルトの「線形順序」に依存するため、
next node
線形順序内のノードは分割後に描画される最初のノードであり、分割後には、その順序内のそのノードとそれ以降のノードだけが描画されます。
コード:
\documentclass[a4paper]{article}
\usepackage{geometry,array}
\usepackage[edges]{forest}
\usetikzlibrary{shadows}
\newcolumntype{C}[1]{@{}>{\centering\arraybackslash}m{#1}@{}}
\forestset{%
dir tree/.style={%
for tree={%
folder,
grow'=0,
if level=0{align=center}
{
align={C{50mm}},
},
font=\sffamily\bfseries\footnotesize,
inner xsep=7pt,
edge={ultra thick, rounded corners=2pt},
fill=white,
rounded corners=2pt,
drop shadow,
},
},
}
% addaswyd o gôd Sašo Živanović: https://tex.stackexchange.com/a/296771/
\def\hiddenparcommand{\par}
\forestset{%
declare toks register={split here interject},
declare toks register={split here node},
declare toks register={split resume here node},
split here interject={},
split here node={},
split resume here node={},
to widest/.style={
tikz+={\path (\forestregister{tempdima}, \forestoption{y}) -- (\forestregister{tempdimb}, \forestoption{y});},
},
split dir here/.style={%
split here node/.option=name,
split here interject={#1},
split dir tree,
delay={
for next node={split dir resume here},
},
},
split dir resume here/.style={%
split resume here node/.option=name,
},
split dir tree/.code={%
\forestset{%
draw tree stage/.style={
for root'={
tempdima/.min={x()+min_x()}{tree},
tempdimb/.max={x()+max_x()}{tree},
for tree={%
to widest,
if name/.wrap pgfmath arg={{####1}{label={[text=gray, anchor=north, font=\scriptsize]below:{[cont.]}}}{}}{split_here_node},
if name/.wrap pgfmath arg={{####1}{edge={densely dotted, gray}, label={[font=\scriptsize, anchor=south, text=gray]above:{[cont.]}}}{}}{split_resume_here_node},
},
},
for nodewalk/.wrap pgfmath arg={{draw tree processing order/.style={name=####1,preceding nodes}}{}}{split_here_node},
for root'={draw tree},
TeX/.wrap pgfmath arg={\hiddenparcommand ####1\hiddenparcommand}{split_here_interject},
for nodewalk/.wrap pgfmath arg={{draw tree processing order/.style={name=####1,following nodes}}{}}{split_resume_here_node},
for root'={draw tree},
},
}
}
}
\begin{document}
\begin{forest}
dir tree,
before drawing tree={
for tree={
tikz+/.wrap 2 pgfmath args={\node [anchor=west, font=\footnotesize, text=red] at (.east) {L:#1; n:#2};}{level()}{n()}
}
}
[TypeData
[ModelData
]
[StationData
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig, split dir here=continued
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
]
[StationData
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
]
]
\end{forest}
\end{document}
結果:
これらの変更を適用するのはそれほど難しいことではないと思う。複数の分割を可能にする実験的なコードこれは、上記が私が書いたコードを変更して、Sašo の単一分割戦略を様式化したものであるのと同じです。
例えば(後ほど編集) 以下は、マルチ分割の実験的な概念実証コードの修正版です (分割には特に意味がないため、必ずしもページ区切りが伴うわけではありません)。
% ateb: https://tex.stackexchange.com/a/339790/ addaswyd o gwestiwn DiB: https://tex.stackexchange.com/q/339669/
\documentclass[a4paper]{article}
\usepackage{geometry,array}
\usepackage[edges]{forest}
\usetikzlibrary{shadows}
\newcolumntype{C}[1]{@{}>{\centering\arraybackslash}m{#1}@{}}
\forestset{%
dir tree/.style={%
for tree={%
folder,
grow'=0,
if level=0{align=center}
{
align={C{50mm}},
},
font=\sffamily\bfseries\footnotesize,
inner xsep=7pt,
edge={ultra thick, rounded corners=2pt},
fill=white,
rounded corners=2pt,
drop shadow,
},
},
}
% addaswyd o gôd Sašo Živanović: https://tex.stackexchange.com/a/296771/
\def\hiddenparcommand{\par}
\newcommand\otherhiddenparcommand{\par\noindent}
\newcommand\hiddencommacommand{, }
\forestset{%
declare keylist register={split here ids},
declare keylist register={split here interjects},
declare toks register=split here toks,
split here ids={},
split here interjects={},
to widest/.style={
tikz+={\path (\forestregister{tempdima}, \forestoption{y}) -- (\forestregister{tempdimb}, \forestoption{y});},
},
hide commas/.style={%
split here toks+={\hiddencommacommand},
split here toks+={#1},
},
split dir here/.style={%
split dir tree pre,
delay={%
!next.split dir tree post,
},
before drawing tree={%
split here ids+/.option=id,
!next.split resume here ids+/.option=id,
},
split={#1}{,}{split here toks,hide commas},
split here interjects/.register=split here toks,
split dir tree
},
split dir tree pre/.style={%
label={[text=gray, anchor=north, font=\scriptsize]below:{[cont.]}{}},
},
split dir tree post/.style={%
label={[font=\scriptsize, anchor=south, text=gray]above:{[cont.]}{}},
},
split dir tree/.code={%
\forestset{%
draw tree stage/.style={
for root'={
tempdima/.min={x()+min_x()}{tree},
tempdimb/.max={x()+max_x()}{tree},
for tree={%
to widest,
},
},
tempcountb'=-1,
do until={%
strequal((split_here_ids),"")
}{%
tempkeylistb'={},
tempkeylista'={},
split register={split here ids}{,}{tempcounta,tempkeylistb+},
split register={split here interjects}{,}{temptoksa,tempkeylista+},
split here ids'/.register=tempkeylistb,
split here interjects'/.register=tempkeylista,
% Sašo Živanović: http://chat.stackexchange.com/transcript/message/28484520#28484520
for nodewalk/.wrap 2 pgfmath args={%
{%
draw tree processing order/.style={%
filter={tree}{(id()<=########1)&&(id()>########2)}%
}%
}{}%
}{tempcounta}{tempcountb},
for root'={draw tree},
TeX/.wrap pgfmath arg={\otherhiddenparcommand ########1\hiddenparcommand}{temptoksa},
tempcountb'/.register=tempcounta,
},
for nodewalk/.wrap pgfmath arg={%
{%
draw tree processing order/.style={%
filter={tree}{id()>=####1}%
}%
}{}%
}{(tempcountb)+1},
for root'={draw tree},
},
}
},
}
\begin{document}
\begin{forest}
dir tree,
[TypeData
[ModelData
]
[StationData
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig, split dir here=continued
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
]
[StationData
[StationConfig
]
[StationConfig, split dir here=more to come
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig, split dir here=last part coming up
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
]
]
\end{forest}
\end{document}
私はあなたを思うかもしれないその後、自動化できるようになります。ツリーが常に新しいページで開始され、y
各ノードをテキスト ブロックの高さと比較できると簡単になります。そのため、それがオプションである場合は、それを調べることをお勧めします。それ以外の場合は、テキスト ブロックがどれだけ残っているかを判断する方法を検索できます。しかし、それは Forest の問題ではないように思われます。
しかし、失われたエッジを復元する簡単な方法はおそらくないと思いますが、シミュレートすることは可能です。エッジとして復元するには、ツリーの構造に干渉する必要があると思います。シミュレートするには、x
親の値を保存して、Tiで座標を作成するだけでよいかもしれません。けZ で分割後そこから描画します。
それでもこれをやりたいのであれば、段階的に取り組むことをお勧めします。エッジを復元する必要がある場合 (これはよくわかりません)、最初にそれを実行してから、複数の分割が必要な場合は、マルチ分割ケースを変更してみてください。その後で初めて、自動化を検討し、最終的にはそれを回避したい場合でも (これもよくわかりません)、最初はツリーごとに新しいページを作成するシナリオに焦点を当てます。これらすべて (該当する場合は、新しいページがない部分を除く) は Forest で実行できると思いますが、もちろん、これはある程度の知識に基づいた推測に過ぎません。 (Sašo なら確実に言えるでしょうし、おそらくもっと良いアイデアを持っているでしょう。)
EIDT
これにより、ツリーが自動的に分割され、失われたエッジが復元されます。edge
復元されたエッジを持つ異なるノードに異なるオプションを使用することが禁止され、指定された が無視されることに注意してくださいedge label
。
バグ修正 2023-12-04これで、必要に応じて 2 つのツリーを描くことができ、それほど多くのエッジ ケースでエラーが発生することはなくなります。
% ateb: https://tex.stackexchange.com/a/326884/ i gwestiwn Amir: https://tex.stackexchange.com/q/326875/
% agenir y fersiwn hwn Forest 2017/02/02 v2.1.4
\documentclass[a4paper]{article}
\usepackage{geometry,array}
\usepackage[edges]{forest}
\usetikzlibrary{shadows}
\newcolumntype{C}[1]{@{}>{\centering\arraybackslash}m{#1}@{}}
\forestset{%
dir tree/.style={%
for tree={%
folder,
grow'=0,
if level=0{align=center}
{
align={C{50mm}},
},
font=\sffamily\bfseries\footnotesize,
inner xsep=7pt,
edge={ultra thick, rounded corners=2pt},
fill=white,
rounded corners=2pt,
drop shadow,
},
},
}
% addaswyd o gôd Sašo Živanović: https://tex.stackexchange.com/a/296771/
\def\hiddenparcommand{\par}
\newcommand\otherhiddenparcommand{\par\noindent}
\newcommand\hiddencommacommand{, }
\forestset{%
declare keylist register={split here ids},
split here ids={},
declare keylist register={split here interjects}, the tree parts
split here interjects={},
declare keylist={split here auto siblings}{},
declare toks register=split here toks,
declare dimen register=tmpdima,
tmpdima'=0pt,
declare dimen register=tmpdimb,
tmpdimb'=0pt,
declare dimen register=tmpdimc,
tmpdimc'=0pt,
to widest/.style={
tikz+={\path (\forestregister{tempdima}, \forestoption{y}) -- (\forestregister{tempdimb}, \forestoption{y});},
},
hide commas/.style={%
split here toks+={\hiddencommacommand},
split here toks+={#1},
},
split dir tree pre/.style={%
label={[text=gray, anchor=north, font=\scriptsize]below:{[cont.]}{}},
},
split dir tree post/.style={%
label={[font=\scriptsize, anchor=south, text=gray]above:{[cont.]}{}},
},
split dir tree auto post/.style={
split dir tree post,
tempkeylistc'={},
tmpdimb/.option=y,
for nodewalk={
while={
> ORw2+d _+d < On=! & {y}{tmpdimb}{##2-##1} {\textheight-#1} {n'}{1}%
}{
next,
tempkeylistc/.option=name
}%
}{},
split here auto siblings/.register=tempkeylistc,
tikz+/.process={
OOw2{edge}{id}
{
\path [##1] (!u.parent anchor |- .north) ++(\forestregister{folder indent},1ex) coordinate (before ##2) |- (.child anchor);
\edef\tempa{\foresteoption{split here auto siblings}}
\foreach \i in \tempa \path [##1] (before ##2) |- ({forest cs:\i.child anchor});
}
},
},
split dir tree/.code={
\forestset{
draw tree stage/.style={
for root'={
tempdima/.min={
>OOw2+d{x}{min x}{####1+####2}%
}{tree},
tempdimb/.max={
>OOw2+d{x}{max x}{####1+####2}%
}{tree},
for tree={
to widest,
},
},
tempcountb'=-1,
until={
strequal((split_here_ids),"")
}{
tempkeylistb'={},
tempkeylista'={},
split register={split here ids}{,}{tempcounta,tempkeylistb+},
split register={split here interjects}{,}{temptoksa,tempkeylista+},
split here ids'/.register=tempkeylistb,
split here interjects'/.register=tempkeylista,
for nodewalk={
draw tree processing order/.style={
filter={tree}{> ORw+n< OR> & {id}{tempcounta}{########1+1}{id}{tempcountb}}%
}
}{},
for root'={draw tree},
TeX/.process={Rw{temptoksa}{\otherhiddenparcommand ####1\hiddenparcommand}},
tempcountb'/.register=tempcounta,
},
for nodewalk={
draw tree processing order/.style={
filter={tree}{>OR>{id}{tempcountb}}
}
}{},
for root'={draw tree},
},
}%
},
split dir here auto/.style n args=2{
split dir tree pre,
!next node.split dir tree auto post=#2,
split here ids+/.option=id,
split={#1}{,}{split here toks,hide commas},
split here interjects/.register=split here toks,
},
split dir tree auto/.style={
split dir tree,
before drawing tree={
tempdima/.max={y}{tree},
tempdimc/.register=tempdima,
tempdimd/.min={y}{tree},
tempdima-/.register=tempdimd,
tempdimb'=\textheight,
tmpdima'=10ex,
tmpdimc'=\pagetotal,
while={
>RR>{tempdima}{tempdimb}
}{
for nodewalk={
root',
until={
> ROw2+d RRw2+d > {tempdimc}{y}{##1-##2} {tmpdima}{tmpdimc}{\textheight-##2-##1}
}{next node},
if nodewalk valid={previous node}{%
previous node,
split dir here auto/.process={R_w2{tmpdima}{continued}{{##2}{##1}}},
next node,
}{},
tempdima/.option=y,
tempdimc/.register=tempdima,
tempdima-/.register=tempdimd,
tmpdima'=15ex,
tmpdimc'=0pt
}{},
},
},
},
}
\begin{document}
\begin{forest}
dir tree,
split dir tree auto,
[TypeData
[ModelData
]
[StationData
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
]
[StationData
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
]
[StationData
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
[StationConfig
]
]
]
\end{forest}
\end{document}