
Problem
In TikZ kann man die Wachstumsrichtung eines Zweigs (und seiner Unterzweige) mit der grow=<direction>
Taste steuern. In steuert forest
die Taste jedoch grow
nur die Wachstumsrichtung desUnterzweigeaber nicht deren übergeordneter Zweig.
Wie kann ich erreichen, dass Äste auf derselben Höhe eines forest
Baumes in unterschiedliche Richtungen wachsen?Wie kann ich beispielsweise im folgenden MWE child 2
eine horizontale Erweiterung von „ root
wie es im TikZ-Baum“ funktioniert erreichen?
MWE
\documentclass{article}
\usepackage{tikz,forest}
\begin{document}
\texttt{grow} used in a Ti\textit{k}Z tree
\begin{tikzpicture}
\node{root}
child{node{child 1}}
child[grow=east]{node{child 2}
child child child
}
;
\end{tikzpicture}
\vskip20pt
\texttt{grow} used in a Forest tree
\begin{forest}
[root
[child 1]
[child 2,grow=east
[][][]
]
]
\end{forest}
\end{document}
Antwort1
Der gewünschte Effekt kann durch manuelles Verschieben des Knoten-Teilbaums erzielt werden child 2
. Ich sehe hierfür zwei Möglichkeiten.
Ändern Sie die relative Position (
l
unds
) vonchild 2
direkt vor der Stufecompute xy
. Beachten Sie, dass die Koordinatenchild 2
vonl
unds
imroot
ls-Koordinatensystem von sind (siehe die Dokumentation zu den Optionenl
unds
). Da diese Koordinaten relativ zum übergeordneten Element sind, müssen sie nur für die Wurzel des Unterbaums, geändert werdenchild 2
.Ändern Sie die absolute Position (
x
undy
) vonalle Knoten imchild 2
Teilbaum vonkurz vor der Bühnedraw tree
.Ein Hinweis. Im Beispiel
y
wird so angepasst, dasschild 2
vertikal ausgerichtet ist an : Da dies durch die Berechnung der Differenz zwischen 's und 'sroot
erreicht wird , muss der Knoten hinter seine Nachkommen verschoben werden.root
child 2
y
child 2
Beachten Sie, dass bei beiden Ansätzen forest
die untergeordneten Elemente der Wurzel zuerst in der Standardwachstumsrichtung von -90 Grad positioniert werden, was im Prinzip sowohl die Position (beachten Sie, dass sie child 1
links von der Wurzel liegt) als auch die interne Struktur der Teilbäume beeinflussen kann.
\documentclass{article}
\usepackage{forest}
\begin{document}
% the ls way
\begin{forest}
[root
[child 1
[][][]
]
[child 2, for tree={grow=0},
before computing xy={l=0,s=2cm}
[][][]
]
]
\end{forest}
% the xy way
\begin{forest}
[root
[child 1
[][][]
]
[child 2, for tree={grow=0},
before drawing tree={for descendants=
{x+=1cm, y+=y("!r")-y("!r2")},
x+=1cm, y+=y("!r")-y("!r2")
}
[][][]
]
]
\end{forest}
\end{document}
Ehrlich gesagt finde ich keinen der beiden Ansätze elegant. Mein erster Gedanke war, zwei forest
Umgebungen, eine für jeden Teilbaum, in eine tikzpicture
Umgebung einzubetten. Da dies forest
durch das Ausspucken von tikz
Code funktioniert, dachte ich, es müsste möglich sein, die Stammknoten manuell mithilfe tikz
der Mechanismen von ( at
, right of
, usw.) zu positionieren. (Beachten Sie, dass begin draw
und end draw
geleert werden müssen, damit dies überhaupt eine Chance auf Erfolg hat.) Das Ergebnis war jedoch nicht wie erwartet ... Ich werde die Gründe untersuchen und versuchen, das Problem in einer zukünftigen Version von zu beheben forest
.
Antwort2
Dies ist eine alte Frage, aber da diese Funktionalität nicht integriert ist forest
, dachte ich, ich würde eine allgemeine Lösung anbieten.
Eine neue Option multiple directions
wird in einem definiert \forestset
. Die Grundidee besteht darin, dass für jede Richtung, in die ein Teilbaum wachsen soll, ein doppelter Stammknoten erstellt und an der Position des tatsächlichen Stammknotens platziert wird. Dann kann jeder Teilbaum in seine eigene Richtung wachsen, da er technisch gesehen seinen eigenen übergeordneten Knoten hat.
Dieselben \forestset
Definitionen grow subtree
und grow' subtree
erfordern eine Richtung (Winkel oder Kompassrichtung). Diese folgen denselben Regeln wie grow=
und grow'=
, nämlich dass grow' subtree
die Reihenfolge der Knoten im Teilbaum umgekehrt wird. Diese sind nichts Besonderes: grow subtree=
ist nur eine alternative Syntax fürfor tree={grow=}
Die einzige Formatierungsänderung gegenüber der Standard-Forest-Syntax besteht darin, dass für jede Richtung ein leerer Knoten erstellt werden muss. Hier ist ein Beispiel:
Der Code für diesen Baum lautet:
\begin{forest}
multiple directions
[root
[
[child 1 [a][b]]
]
[, grow subtree=150
[child 2 [c][d][e]]
]
[, grow' subtree=30
[child 3 [f][g]]
]
]
\end{forest}
Die Option multiple directions
fungiert auch als for tree
und akzeptiert Optionen, wie im nächsten Beispiel zu sehen.
Hinweis: forked edge
erfordert \useforestlibrary{edges}
.
Der Code lautet:
\begin{forest}
multiple directions={minimum height=4ex, anchor=center, forked edge}
[R
[, grow' subtree=east
[1 [a][b]]
[2 [c][d]]
[3 [e][f]]]
[, grow subtree=west
[4 [g][h]]
[5 [i][j]]
[6 [k][l]]
]
]
\end{forest}
Ein weiteres Beispiel mit eingezeichneten Knoten:
\begin{forest}
multiple directions={minimum width=2.5em, anchor=center, circle, draw}
[c
[, grow' subtree=north
[a[a1][a2]]
[b[b1][b2]]]
[
[d[d1][d2]]
[e[e1][e2]]]
]
\end{forest}
Beachten Sie, dass der ursprüngliche Stammknoten ein ist phantom
. Wenn Sie also das Erscheinungsbild des Knotens ebenenweise ändern möchten, müssen Sie die Ebene um 1 erhöhen. Somit befindet sich der (sichtbare) Stammknoten (der eigentlich eine Kopie ist) auf Ebene 1 und seine untergeordneten Knoten auf Ebene 2. Beispiel:
\begin{forest}
multiple directions={
text width=20mm,
if level=1{fill=gray!80}{fill=gray!10},
if level=2{fill=gray!40}{},
forked edge,
s sep=5mm, l sep=5mm,
fork sep=2.5mm
}
[Root
[, grow subtree=west
[West of root[Far west]]
]
[
[A[B]]
[C[D[E]]]
[F[G]]
]
[, grow subtree=east
[East of root[Far east]]
]
]
\end{forest}
multiple directions
Es ist auch möglich , Folgendes auf einem Teilbaum anzuwenden :
\begin{forest}
multiple directions={anchor=center}, forked edges
[0
[, grow subtree=west
[1[1a][1b]][2[2a][2b]]]
[, grow' subtree=east
[3[3a][3b]][4[4a][4b, multiple directions, phantom=false
[, grow' subtree=east[x[x1][x2]]]
[, grow subtree=south[y[y1][y2]]]
]]]
]
\end{forest}
Beachten Sie, dass aufgrund der Art und Weise , wie der Stammknoten ausgeblendet wird, diese Verwendung multiple directions
festgelegt werden muss .phantom=false
Hier ist der vollständige Code einschließlich einiger Beispiele:
\documentclass{article}
\usepackage{forest}
\useforestlibrary{edges}
\forestset{multiple directions/.style={for tree={#1}, phantom, for relative level=1{no edge, delay={!c.content/.pgfmath=content("!u")}, before computing xy={l=0,s=0}}},
multiple directions/.default={},
grow subtree/.style={for tree={grow=#1}},
grow' subtree/.style={for tree={grow'=#1}}}
\begin{document}
\begin{forest}
multiple directions
[root
[
[child 1 [a][b]]
]
[, grow subtree=150
[child 2 [c][d][e]]
]
[, grow' subtree=30
[child 3 [f][g]]
]
]
\end{forest}
\vspace{1cm}
\begin{forest}
multiple directions={minimum height=4ex, anchor=center, forked edge}
[R
[, grow' subtree=east
[1 [a][b]]
[2 [c][d]]
[3 [e][f]]]
[, grow subtree=west
[4 [g][h]]
[5 [i][j]]
[6 [k][l]]
]
]
\end{forest}
\vspace{1cm}
\begin{forest}
multiple directions={minimum width=2.5em, anchor=center, circle, draw}
[c
[, grow' subtree=north
[a[a1][a2]]
[b[b1][b2]]]
[
[d[d1][d2]]
[e[e1][e2]]]
]
\end{forest}
\vspace{1cm}
\begin{forest}
multiple directions={anchor=center}, forked edges
[0
[, grow subtree=west
[1[1a][1b]][2[2a][2b]]]
[, grow' subtree=east
[3[3a][3b]][4[4a][4b, multiple directions, phantom=false
[, grow' subtree=east[x[x1][x2]]]
[, grow subtree=south[y[y1][y2]]]
]]]
]
\end{forest}
\end{document}
Antwort3
Keine Lösung für dieses Problem kann vollkommen allgemeingültig sein. Die Automatisierung des Stils ist nicht unbedingt besser (oder schlechter) alsSandy G's hervorragende manuelle Strukturierung der Bäume. Diese Antwort soll automatischer und etwas robuster sein und kleinere Formatierungsprobleme vermeiden, aber es gibt keine Garantie, dass sie nicht Ihre Schokolade isst oder Ihre Socke stiehlt.
Vorbehaltlicher Käufer
Dies ist eine Modifikation vonSandy Gs ausgezeichnete Antwort. Die Grundidee ist dieselbe, mit den folgenden Änderungen.
- Der Stil ist eher automatisiert.
- Dadurch wird vermieden, dass mehrere Kopien desselben Knoteninhalts gesetzt werden, was für den Fake-Bold-Effekt in Knoten verantwortlich ist, die in mehrere Richtungen Unterbäume sprießen lassen. (Wenn dies jedoch Ihr einziges Anliegen ist, gibt es bei weitem einfachere Möglichkeiten.)
- Es versucht, potenziell etwas robuster zu sein, indem es eine der Änderungen inSašo Živanovićs noch bessere Antwort. (Das heißt nicht, dass es mit Stil funktioniert
folder
- ich habe noch nicht einmalversuchtEs!)
Die Ausgabe sollte dieselbe sein wie bei Verwendung der Antwort von Sandy G (Modulo der scheinbaren Schriftstärke). Nur die Spezifikation der Bäume unterscheidet sich.
Für die Spezifikation der Bäume sind nur zwei Schlüssel wirklich interessant.
subtree grow=<growth direction>
subtree grow'=<growth direction>
Diese sollen genau analog zu grow subtree
und sein grow subtree'
.
Der Hauptunterschied besteht darin, dass dies alles ist, was wir tun müssen. Es ist nicht erforderlich, zusätzliche Knoten einzufügen oder einen Stil für den gesamten Baum anzugeben. subtree grow
und subtree grow'
aktivieren Sie den erforderlichen Stil, wodurch alle erforderlichen zusätzlichen Knoten eingefügt werden.
Wenn wir Sandy Gs Beispiele zu Demonstrationszwecken nehmen, kann das erste wie folgt angegeben werden:
\begin{forest}
[root
[child 1
[a][b]
]
[child 2, subtree grow=150
[c][d][e]
]
[child 3, subtree grow'=30
[f][g]
]
]
\end{forest}
Diese Einfachheit hat natürlich auch einen Nachteil. In manchen Bäumen müssen wir genauer typisieren, weil wir keinen zusätzlichen Fake-Knoten haben, um die Wachstumsrichtung für einen ganzen Teilbaum festzulegen. Stattdessen müssen wir die Wachstumsrichtung für jedes Kind angeben, das nicht in die vom Elternteil festgelegte Standardrichtung wachsen soll. Daher erfordert Sandy Gs zweiter BaumsechsVerwendungen von subtree grow
/ subtree grow'
, während es nur erforderlich war,zweivon grow subtree
/ grow subtree'
.
[Wenn es sich bei dem vorherigen Beispiel um die Schaukeln handelte, gehe ich davon aus, dass wir uns jetzt im Karussell befinden.]
\begin{forest}
for tree={minimum height=4ex, anchor=center},
forked edges,
[R
[1, subtree grow'=east
[a][b]
]
[2, subtree grow'=east
[c][d]
]
[3, subtree grow'=east
[e][f]
]
[4, subtree grow=west
[g][h]
]
[5, subtree grow=west
[i][j]
]
[6, subtree grow=west
[k][l]
]
]
\end{forest}
Für das dritte Beispiel benötigen wir zwei Verwendungen von subtree grow'
, aber da wir keine zusätzlichen Knoten einfügen oder der Präambel etwas hinzufügen müssen, ist es dennoch etwas prägnanter.
\begin{forest}
for tree={minimum width=2.5em, anchor=center, circle, draw},
[c
[a, subtree grow'=north
[a1][a2]
]
[b, subtree grow'=north
[b1][b2]
]
[d
[d1][d2]
]
[e
[e1][e2]
]
]
\end{forest}
Auch das vierte Beispiel weist eine gute Balance zwischen Vor- und Nachteilen auf, der Lohn für mehrere Wachstumsspezifikationen ist jedoch eine klarere Struktur (zumindest auf der Ebene der Eingabesyntax – der eigentliche Baum ist eine andere Sache).
\begin{forest}
for tree={anchor=center},
forked edges,
[0
[1, subtree grow=west
[1a][1b]
]
[2, subtree grow=west
[2a][2b]
]
[3, subtree grow'=east
[3a][3b]
]
[4, subtree grow'=east
[4a]
[4b
[x, subtree grow'=east[x1][x2]]
[y, subtree grow=south[y1][y2]]
]
]
]
\end{forest}
Unter der Haube haben sowohl subtree grow
als auch subtree grow'
einen Stil namens wild branching
.
subtree grow/.style={%
wild branching={grow=#1},
},
subtree grow'/.style={%
wild branching={grow'=#1},
},
wild branching
tut nichts, was Sie nicht mit Sandy Gs Code und ein paar Optimierungen selbst tun könnten. Es verwendet eine Vielzahl benutzerdefinierter Forest-Optionen und -Register (für diejenigen, die mit dieser Terminologie nicht vertraut sind: Eine „Option“ ist eine Einstellung für einen Knoten in einem Baum, während ein „Register“ für den gesamten Baum gilt).
Optionen:
declare boolean={wild children}{0},
declare boolean={wild child}{0},
declare keylist={tame ones}{},
declare keylist={wild siblings}{},
declare boolean={wild leader}{0},
Das letzte Argument ist der Anfangswert für alle Knoten in allen Bäumen.
Register:
declare boolean register={wild tree},
wild tree=0,
Wir brauchen auch ein paar einfache Stile:
append me/.style={append={!id=#1},do dynamics},
prepend me/.style={prepend={!id=#1},do dynamics},
wild phantom/.style={%
node options/.option=!u.node options,
content/.process={Ow{!u.content}{\phantom{##1}}},
child anchor/.option=!1.child anchor,
anchor/.option=!1.anchor,
parent anchor/.option=!1.parent anchor,
opacity=0,
no edge,
},
und ich habe einen benutzerdefinierten Schritt verwendet
define long step={wild children by growth}{}{%
sort by={>O{grow}},sort'={filter={children}{>O{wild child}}}
},
Ich habe das Styling in Teile aufgeteilt, da es etwas lang wurde. wild branching
Anrufe do tameness
und do wildness
nach Bedarf.
do tameness
fügt einen zusätzlichen Knoten für Teilbäume ein, die in die Standardrichtung wachsen, wenn einer ihrer Geschwister subtree grow
oder verwendet subtree grow'
.
do tameness/.style={%
where wild children={%
tempboola=0,
for children={%
if wild child={}{%
if tempboola={%
!u.tame ones+/.option=id,
}{%
tempboola,
replace by={%
[,
append,
delay={%
if={>O_={!u.tame ones}{}}{}{%
split option={!u.tame ones}{,}{append me},
},
wild phantom,
},
]%
},
},
},
},
}{},
},
do wildness
ermittelt, wann ein neuer zusätzlicher Knoten für eine neue Wachstumsrichtung erstellt werden soll und wann der Teilbaum zu einem vorhandenen hinzugefügt werden soll. Hier kommt der oben definierte benutzerdefinierte Schritt zum Einsatz: Er stellt sicher, dass wir die Geschwister in der Reihenfolge ihrer Wachstumsrichtungen besuchen, was es einfacher macht, Listen darüber zu erstellen, welche zusammengehören.
do wildness/.style={%
where wild children={%
tempcounta'=9999,
for wild children by growth={%
if={>OR= {grow}{tempcounta} }
{%
tempkeylista'=,
for children={%
tempkeylista+/.option=id,
},
for nodewalk={%
until={>O{wild leader}}{next},
if wild siblings={}{%
wild siblings/.register=tempkeylista
}{%
wild siblings+/.register=tempkeylista
}%
}{%
},
before packing={remove},
}{%
wild leader,
tempcounta/.option=grow,
before packing={%
if wild siblings={}{}{%
split option={wild siblings}{,}{prepend me},
},
},
},
},
}{},
},
Diese Methode erstellt mehr Knoten als die von Sandy G, aber die zusätzlichen werden gelöscht, bevor der Baum gepackt wird. Dennoch unterscheidet sich die Struktur des endgültigen Baums, weil wild branching
Änderungen vorgenommen werden l
, s
bevor der übergeordnete Knoten gepackt wird. Theoretisch sollte dies zu weniger Situationen führen, die manuelle Eingriffe erfordern. Aber Theorie ist, wie wir alle wissen, nicht gleich Praxis.
wild branching/.style={%
if id=1{for tree={#1}}{%
!u.wild children,
delay={%
replace by={%
[,
wild child,
append,
delay={%
wild phantom,
for tree={#1},
},
]%
},
},
if wild tree={}{%
wild tree,
!root.before typesetting nodes={%
do tameness,
do wildness,
},
!root.before packing={%
delay={
where wild children={%
after packing node={%
for children={l'=0pt,s'=0pt},
},
}{},
},
},
},
},
},
wild branching/.default={},
Beachten Sie, dass subtree grow
/ subtree grow'
nicht für den Stammknoten vorgesehen sind. Wenn dies der Fall ist, wird oder wild branching
einfach auf den Baum angewendet, ohne dass etwas Besonderes aufgerufen wird.grow
grow'
wild branching
sollte wahrscheinlich keine .default
Einstellung haben, aber aus irgendeinem Grund, an den ich mich jetzt nicht erinnern kann, habe ich eine definiert.
Code:
\documentclass[a4paper,landscape]{article}
\usepackage[scale=.8]{geometry}
% ateb: https://tex.stackexchange.com/a/705635/ addaswyd o ateb Sandy G: https://tex.stackexchange.com/a/643061/
\usepackage{forest}
\useforestlibrary{edges}
\forestset{% https://tex.stackexchange.com/a/705635/
declare boolean={wild children}{0},
declare boolean={wild child}{0},
declare keylist={tame ones}{},
declare keylist={wild siblings}{},
declare boolean={wild leader}{0},
declare boolean register={wild tree},
wild tree=0,
append me/.style={append={!id=#1},do dynamics},
prepend me/.style={prepend={!id=#1},do dynamics},
wild phantom/.style={%
node options/.option=!u.node options,
content/.process={Ow{!u.content}{\phantom{##1}}},
child anchor/.option=!1.child anchor,
anchor/.option=!1.anchor,
parent anchor/.option=!1.parent anchor,
opacity=0,
no edge,
},
define long step={wild children by growth}{}{%
sort by={>O{grow}},sort'={filter={children}{>O{wild child}}}
},
do tameness/.style={%
where wild children={%
tempboola=0,
for children={%
if wild child={}{%
if tempboola={%
!u.tame ones+/.option=id,
}{%
tempboola,
replace by={%
[,
append,
delay={%
if={>O_={!u.tame ones}{}}{}{%
split option={!u.tame ones}{,}{append me},
},
wild phantom,
},
]%
},
},
},
},
}{},
},
do wildness/.style={%
where wild children={%
tempcounta'=9999,
for wild children by growth={%
if={>OR= {grow}{tempcounta} }
{%
tempkeylista'=,
for children={%
tempkeylista+/.option=id,
},
for nodewalk={%
until={>O{wild leader}}{next},
if wild siblings={}{%
wild siblings/.register=tempkeylista
}{%
wild siblings+/.register=tempkeylista
}%
}{%
},
before packing={remove},
}{%
wild leader,
tempcounta/.option=grow,
before packing={%
if wild siblings={}{}{%
split option={wild siblings}{,}{prepend me},
},
},
},
},
}{},
},
wild branching/.style={%
if id=1{for tree={#1}}{%
!u.wild children,
delay={%
replace by={%
[,
wild child,
append,
delay={%
wild phantom,
for tree={#1},
},
]%
},
},
if wild tree={}{%
wild tree,
!root.before typesetting nodes={%
do tameness,
do wildness,
},
!root.before packing={%
delay={
where wild children={%
after packing node={%
for children={l'=0pt,s'=0pt},
},
}{},
},
},
},
},
},
wild branching/.default={},
subtree grow/.style={%
wild branching={grow=#1},
},
subtree grow'/.style={%
wild branching={grow'=#1},
},
}
\pagestyle{empty}
\begin{document}
\centering
\begin{forest}
[root
[child 1
[a][b]
]
[child 2, subtree grow=150
[c][d][e]
]
[child 3, subtree grow'=30
[f][g]
]
]
\end{forest}
\begin{forest}
for tree={minimum height=4ex, anchor=center},
forked edges,
[R
[1, subtree grow'=east
[a][b]
]
[2, subtree grow'=east
[c][d]
]
[3, subtree grow'=east
[e][f]
]
[4, subtree grow=west
[g][h]
]
[5, subtree grow=west
[i][j]
]
[6, subtree grow=west
[k][l]
]
]
\end{forest}
\begin{forest}
for tree={minimum width=2.5em, anchor=center, circle, draw},
[c
[a, subtree grow'=north
[a1][a2]
]
[b, subtree grow'=north
[b1][b2]
]
[d
[d1][d2]
]
[e
[e1][e2]
]
]
\end{forest}
\begin{forest}
for tree={anchor=center},
forked edges,
[0
[1, subtree grow=west
[1a][1b]
]
[2, subtree grow=west
[2a][2b]
]
[3, subtree grow'=east
[3a][3b]
]
[4, subtree grow'=east
[4a]
[4b
[x, subtree grow'=east[x1][x2]]
[y, subtree grow=south[y1][y2]]
]
]
]
\end{forest}
\end{document}