
Мое программное обеспечение автоматически создает некоторую документацию. Одним из элементов, которые я добавляю в документацию, является дерево в стиле, описанном здесь:
Создание (простого) дерева каталогов
Поскольку дерево может быть большим, оно будет охватывать несколько страниц. Я вижу здесь, что есть способ вручную указать дереву, где разбить страницы:
Как разделить лесное дерево на два дерева с правильным вертикальным выравниванием?
Но что я действительно хочу, так это чтобы дерево автоматически разрывало страницу. Есть ли способ сделать это с 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) не могут быть скомпилированы с помощью текущего Forest без ответа на запросы TeX на консоли или без изменения forest.sty
.
Поэтому вам, вероятно, придется скомпилировать их в командной строке, чтобы вы могли отклонить запросы. Ошибки нет. TeX просто \show
что-то вам сообщает, так как он скажет вам, если вы попросите о дальнейшей помощи. Поэтому здесь нет никаких проблем с игнорированием ошибок. Вам просто нужно позволить ему показать вам то, что он хочет вам показать.
Следующий код выполняет следующие действия:
- он обновляет ваш код для использования стиля Forest 2
folder
, что упрощает рисование этого стиля дерева; - он обеспечивает
dir tree
стиль, соответствующий вашему особому вкусу этого стиля; - он предоставляет
split dir here=<text>
стиль для разделения дерева в текущем узле, аналогичный связанномуsplit here=<text>
стилю.
Определения стилей основаны на моей адаптации кода Сашо, как указано в ответах на связанный вопрос и отмечено в комментариях ниже.
Ограничения:
- ничего не делится автоматически;
- разделение не будет работать правильно, если вы делаете странные вещи, где «странные» примерно означает «вещи, которые заставят разделение работать неправильно»;
- даже если вы сделаете менее странные вещи, разделение потеряет все ребра, проведенные к родственным узлам текущего узла, набранного после разделения;
- поддерживается только одно разделение на дерево (но см. ниже версию с несколькими разделениями);
- Разделение(я) зависит(ют) от «линейного порядка» узлов по умолчанию, так что
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}
Результат:
Я думаю, что не должно быть слишком сложно применить эти изменения.к моему экспериментальному коду, допускающему множественные разделения, таким же образом, как приведенный выше код изменяет написанный мной код для стилизации стратегии одного разделения Сашо.
Например (позжередактировать) вот модифицированная версия этого экспериментального кода для проверки концепции множественных разделений (обратите внимание, что разделение не имеет особого смысла, поэтому не обязательно подразумевает разрыв страницы):
% 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, хотя, очевидно, это не более чем несколько обоснованное предположение. (Сашо мог бы сказать наверняка и, вероятно, имел бы лучшую идею в любом случае.)
EIDT
Это автоматически разбивает дерево и пытается восстановить отсутствующие ребра. Обратите внимание, что это исключает использование различных edge
опций для разных узлов с восстановленными ребрами и игнорирует любые указанные edge label
.
Исправление ошибки 2023-12-04Теперь вы можете нарисовать два таких дерева, если у вас хватит фантазии, и вы не должны получать ошибок в столь многих крайних случаях.
% 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}