Problem

Problem

Problem

In TikZ kann man die Wachstumsrichtung eines Zweigs (und seiner Unterzweige) mit der grow=<direction>Taste steuern. In steuert forestdie Taste jedoch grownur die Wachstumsrichtung desUnterzweigeaber nicht deren übergeordneter Zweig.

Wie kann ich erreichen, dass Äste auf derselben Höhe eines forestBaumes in unterschiedliche Richtungen wachsen?Wie kann ich beispielsweise im folgenden MWE child 2eine horizontale Erweiterung von „ rootwie 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}

Bildbeschreibung hier eingeben

Antwort1

Der gewünschte Effekt kann durch manuelles Verschieben des Knoten-Teilbaums erzielt werden child 2. Ich sehe hierfür zwei Möglichkeiten.

  1. Ändern Sie die relative Position ( lund s) von child 2direkt vor der Stufe compute xy. Beachten Sie, dass die Koordinaten child 2von lund sim rootls-Koordinatensystem von sind (siehe die Dokumentation zu den Optionen lund s). Da diese Koordinaten relativ zum übergeordneten Element sind, müssen sie nur für die Wurzel des Unterbaums, geändert werden child 2.

  2. Ändern Sie die absolute Position ( xund y) vonalle Knoten im child 2Teilbaum vonkurz vor der Bühne draw tree.

    Ein Hinweis. Im Beispiel ywird so angepasst, dass child 2vertikal ausgerichtet ist an : Da dies durch die Berechnung der Differenz zwischen 's und 's rooterreicht wird , muss der Knoten hinter seine Nachkommen verschoben werden.rootchild 2ychild 2

Beachten Sie, dass bei beiden Ansätzen forestdie untergeordneten Elemente der Wurzel zuerst in der Standardwachstumsrichtung von -90 Grad positioniert werden, was im Prinzip sowohl die Position (beachten Sie, dass sie child 1links 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 forestUmgebungen, eine für jeden Teilbaum, in eine tikzpictureUmgebung einzubetten. Da dies forestdurch das Ausspucken von tikzCode funktioniert, dachte ich, es müsste möglich sein, die Stammknoten manuell mithilfe tikzder Mechanismen von ( at, right of, usw.) zu positionieren. (Beachten Sie, dass begin drawund end drawgeleert 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 directionswird 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 \forestsetDefinitionen grow subtreeund grow' subtreeerfordern eine Richtung (Winkel oder Kompassrichtung). Diese folgen denselben Regeln wie grow=und grow'=, nämlich dass grow' subtreedie 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:

Bildbeschreibung hier eingeben

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 directionsfungiert auch als for treeund akzeptiert Optionen, wie im nächsten Beispiel zu sehen.

Hinweis: forked edgeerfordert \useforestlibrary{edges}.

Bildbeschreibung hier eingeben

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:

Bildbeschreibung hier eingeben

\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:

Bildbeschreibung hier eingeben

\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 directionsEs ist auch möglich , Folgendes auf einem Teilbaum anzuwenden :

Bildbeschreibung hier eingeben

\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 directionsfestgelegt 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.

  1. Der Stil ist eher automatisiert.
  2. 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.)
  3. 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.

Ausgabe ohne Fake-Fettdruck an divergierenden Wachstumspunkten

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 subtreeund 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 growund 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 growals auch subtree grow'einen Stil namens wild branching.

  subtree grow/.style={%
    wild branching={grow=#1},
  }, 
  subtree grow'/.style={%
    wild branching={grow'=#1},
  },

wild branchingtut 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 branchingAnrufe do tamenessund do wildnessnach Bedarf.

do tamenessfügt einen zusätzlichen Knoten für Teilbäume ein, die in die Standardrichtung wachsen, wenn einer ihrer Geschwister subtree growoder 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 wildnessermittelt, 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, sbevor 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 branchingeinfach auf den Baum angewendet, ohne dass etwas Besonderes aufgerufen wird.growgrow'

wild branchingsollte wahrscheinlich keine .defaultEinstellung 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}

verwandte Informationen