
Problema
En TikZ, se puede controlar la dirección de crecimiento de una rama (y sus subramas) usando la grow=<direction>
tecla. En forest
, sin embargo, la grow
clave sólo controla la dirección creciente de lasubramaspero no su rama matriz.
¿Cómo hago para que las ramas que están al mismo nivel en un forest
árbol crezcan en diferentes direcciones?En el siguiente MWE, por ejemplo, ¿cómo puedo child 2
extenderme horizontalmente desde root
el árbol TikZ?
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}
Respuesta1
El efecto deseado se puede lograr moviendo el subárbol del nodo child 2
manualmente. Puedo ver dos formas de hacer esto.
Cambie la posición relativa (
l
ys
) dechild 2
justo antes del escenariocompute xy
. Tenga en cuenta quechild 2
'sl
ys
son coordenadas en elroot
sistema de coordenadas ls de ' (consulte la documentación para conocer las opcionesl
ys
). Dado que estas coordenadas son relativas al padre, solo es necesario cambiarlas para la raíz del subárbolchild 2
.Cambiar la posición absoluta (
x
yy
) detodos los nodos enchild 2
el subárbolJusto antes del escenariodraw tree
.Una nota. En el ejemplo,
y
se ajusta para quechild 2
esté alineado verticalmente conroot
: dado que esto se logra calculando la diferencia entreroot
's ychild 2
'sy
, el nodochild 2
debe moverse después de sus descendientes.
Tenga en cuenta que en ambos enfoques, forest
primero coloca a los hijos de la raíz en la dirección de crecimiento predeterminada de -90 grados, lo que en principio puede influir tanto en la posición (tenga en cuenta que child 1
está a la izquierda de la raíz) como en la estructura interna de los subárboles.
\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}
Para ser honesto, ninguno de los dos enfoques me parece elegante. Lo primero que pensé fue integrar dos forest
entornos, uno para cada subárbol, en un tikzpicture
entorno. Dado que forest
funciona escupiendo tikz
código, razoné que debería ser posible posicionar los nodos raíz manualmente usando tikz
los mecanismos de ( at
,, right of
etc.). (Tenga en cuenta que begin draw
y end draw
debe vaciarse para que esto tenga alguna posibilidad de éxito). Sin embargo, el resultado no fue el esperado... Investigaré los motivos e intentaré solucionar el problema en alguna versión futura de forest
.
Respuesta2
Esta es una vieja pregunta, pero como esta funcionalidad no está integrada forest
, pensé en ofrecer una solución general.
multiple directions
Se define una nueva opción en un archivo \forestset
. La idea básica es que para cada dirección en la que desee que crezca un subárbol, se crea un nodo raíz duplicado y se coloca en la ubicación del nodo raíz real. Entonces cada subárbol puede crecer en su propia dirección ya que técnicamente tiene su propio padre.
Lo mismo \forestset
define grow subtree
y grow' subtree
cuáles requieren una dirección (ángulo o dirección de la brújula). Estos siguen las mismas reglas que grow=
y grow'=
, es decir, que grow' subtree
invierten el orden de los nodos en el subárbol. Estos no son nada especiales: grow subtree=
es solo una sintaxis alternativa parafor tree={grow=}
El único cambio de formato con respecto a la sintaxis del bosque estándar es que se debe crear un nodo vacío para cada dirección. Aquí hay un ejemplo:
El código de este árbol es:
\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}
La opción multiple directions
también actúa como for tree
y acepta opciones como se ve en el siguiente ejemplo.
Nota: forked edge
requiere \useforestlibrary{edges}
.
El código es:
\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}
Un ejemplo adicional con los nodos dibujados:
\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}
Tenga en cuenta que el nodo raíz original es un phantom
, por lo que si desea cambiar la apariencia del nodo por nivel, debe aumentar el nivel en 1. Por lo tanto, el nodo raíz (visible) (que en realidad es una copia) está en el nivel 1 y sus hijos están en el nivel 2. Por ejemplo:
\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}
También es posible utilizar multiple directions
en un subárbol:
\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}
Tenga en cuenta que debido a la forma en que multiple directions
se oculta el nodo raíz, es necesario configurarlo phantom=false
para este uso.
Aquí está el código completo incluyendo algunos de los ejemplos:
\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}
Respuesta3
Ninguna solución a este problema puede ser perfectamente general. Automatizar el estilo no es necesariamente mejor (o peor) queLa excelente estructuración manual de los árboles de Sandy G.. Esta respuesta pretende ser más automática, un poco más robusta y evitar problemas menores de formato, pero no hay garantía de que no se coma el chocolate ni le robe el calcetín.
Advertencia emptor
Esta es una modificación deExcelente respuesta de Sandy G.. La idea fundamental es la misma, con los siguientes cambios.
- El estilo es más automatizado.
- Evita componer múltiples copias del mismo contenido de nodo, lo cual es responsable del efecto de negrita falsa en los nodos que generan subárboles en múltiples direcciones. (Pero hay formas mucho más fáciles si esta es su única preocupación).
- Intenta ser potencialmente un poco más robusto al integrar uno de los cambios enLa respuesta aún más excelente de Sašo Živanović. (Esto no significa que funcionará con
folder
estilo; ni siquiera lo heintentó¡él!)
El resultado debe ser el mismo que si se utilizara la respuesta de Sandy G (peso de fuente aparente del módulo). Sólo difiere la especificación de los árboles.
Sólo dos claves son realmente interesantes para especificar los árboles.
subtree grow=<growth direction>
subtree grow'=<growth direction>
Estos pretenden ser precisamente análogos a grow subtree
y grow subtree'
.
La principal diferencia es que esto es todo lo que necesitamos hacer. No es necesario insertar nodos adicionales ni especificar ningún estilo para el árbol en su conjunto. subtree grow
y subtree grow'
active el estilo requerido, que inserta los nodos adicionales necesarios.
Tomando los ejemplos de Sandy G con fines de demostración, el primero se puede especificar simplemente como
\begin{forest}
[root
[child 1
[a][b]
]
[child 2, subtree grow=150
[c][d][e]
]
[child 3, subtree grow'=30
[f][g]
]
]
\end{forest}
Por supuesto, esta simplicidad tiene un inconveniente. En algunos árboles, tenemos que escribir con mayor precisión porque no tenemos un nodo falso adicional para establecer la dirección de crecimiento de un subárbol completo. En cambio, debemos especificar la dirección de crecimiento para cada niño que no debería crecer en la dirección predeterminada determinada por los padres. Por lo tanto, el segundo árbol de Sandy G requiereseisusos de subtree grow
/ subtree grow'
, mientras que solo requeríadosde grow subtree
/ grow subtree'
.
[Si el ejemplo anterior fueron los columpios, supongo que ya estamos en las rotondas.]
\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}
Para el tercer ejemplo, necesitamos dos usos de subtree grow'
, pero como no tenemos que insertar nodos adicionales ni agregar nada al preámbulo, esto sigue siendo un poco más conciso.
\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}
De manera similar, el cuarto ejemplo implica un buen equilibrio de cambios y rotondas, pero la recompensa por especificaciones de crecimiento múltiple es una estructura más clara (al menos en el nivel de sintaxis de entrada; el árbol real es un asunto diferente).
\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}
Debajo del capó, ambos subtree grow
y subtree grow'
llaman a un estilo llamado wild branching
.
subtree grow/.style={%
wild branching={grow=#1},
},
subtree grow'/.style={%
wild branching={grow'=#1},
},
wild branching
no hace nada que no puedas hacer por ti mismo con el código de Sandy G y un par de ajustes. Utiliza una variedad de registros y opciones de bosque personalizados (para aquellos que no están familiarizados con esta terminología, una "opción" es una configuración para un nodo en un árbol, mientras que un "registro" se aplica a todo el árbol).
Opciones:
declare boolean={wild children}{0},
declare boolean={wild child}{0},
declare keylist={tame ones}{},
declare keylist={wild siblings}{},
declare boolean={wild leader}{0},
El argumento final es el valor inicial de todos los nodos de todos los árboles.
Registros:
declare boolean register={wild tree},
wild tree=0,
También necesitamos algunos estilos simples:
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,
},
y utilicé un paso personalizado
define long step={wild children by growth}{}{%
sort by={>O{grow}},sort'={filter={children}{>O{wild child}}}
},
Dividí el estilo en partes porque se estaba volviendo un poco largo. wild branching
llamadas do tameness
y do wildness
según corresponda.
do tameness
inserta un nodo adicional para los subárboles que crecen en la dirección predeterminada, cuando uno de sus hermanos usa subtree grow
o 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
averigua cuándo crear un nuevo nodo adicional para una nueva dirección de crecimiento y cuándo se debe agregar el subárbol a una adición existente. Aquí es donde se utiliza el paso personalizado definido anteriormente: garantiza que visitemos a los hermanos en orden según su dirección de crecimiento, lo que facilita la compilación de listas de cuáles pertenecen juntos.
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},
},
},
},
},
}{},
},
Este método crea más nodos que el de Sandy G, pero los extras se eliminan antes de que se empaquete el árbol. No obstante, la estructura del árbol final difiere porque wild branching
se modifica l
y s
antes de que se empaquete el nodo principal. En teoría, esto debería dar lugar a menos situaciones que requieran intervención manual. Pero la teoría no es, como todos sabemos, práctica.
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={},
Tenga en cuenta que subtree grow
/ subtree grow'
no está diseñado para configurarse para el nodo raíz. Si lo son, wild branching
simplemente se aplica grow
o grow'
al árbol, sin invocar nada especial.
wild branching
Probablemente no debería tener una .default
configuración, pero por alguna razón que ahora no recuerdo, he definido una.
Código:
\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}