Problema

Problema

Problema

No TikZ, pode-se controlar a direção de crescimento de um ramo (e seus sub-ramos) usando a grow=<direction>tecla. Em forest, no entanto, a growchave controla apenas a direção crescente dosub-ramosmas não seu ramo pai.

Como faço para que galhos no mesmo nível de uma forestárvore cresçam em direções diferentes?No MWE a seguir, por exemplo, como posso child 2estender horizontalmente a rootpartir da árvore 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}

insira a descrição da imagem aqui

Responder1

O efeito desejado pode ser alcançado movendo child 2manualmente a subárvore do nó. Posso ver duas maneiras de fazer isso.

  1. Altere a posição relativa ( le s) child 2imediatamente antes do estágio compute xy. Observe que child 2são le ssão coordenadas no rootsistema de coordenadas ls (consulte a documentação para opções le s). Como essas coordenadas são relativas ao pai, só é necessário alterá-las para a raiz da subárvore, child 2.

  2. Altere a posição absoluta ( xe y) detodos os nós na child 2subárvore depouco antes do palco draw tree.

    Uma nota. No exemplo, yé ajustado para que child 2fique alinhado verticalmente com root: como isso é conseguido calculando a diferença entre root's e child 2's y, o nó child 2deve ser movido após seus descendentes.

Observe que em ambas as abordagens, forestprimeiro posiciona os filhos da raiz na direção padrão de crescimento de -90 graus, o que pode, em princípio, influenciar tanto a posição (observe que child 1fica à esquerda da raiz) quanto a estrutura interna das subárvores.

\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 sincero, não considero nenhuma das duas abordagens elegante. Meu primeiro pensamento foi incorporar dois forestambientes, um para cada subárvore, em um tikzpictureambiente. Como forestfunciona cuspindo tikzcódigo, raciocinei que deveria ser possível posicionar os nós raiz manualmente usando tikzos mecanismos de ( at, right of, etc.). (Observe que begin drawe end drawdeve ser esvaziado para que tenha alguma chance de sucesso.) Porém, o resultado não foi o esperado... Vou investigar os motivos e tentar corrigir o problema em alguma versão futura do forest.

Responder2

Esta é uma questão antiga, mas como essa funcionalidade não está incorporada forest, pensei em oferecer uma solução geral.

Uma nova opção multiple directionsé definida em um arquivo \forestset. A ideia básica é que para cada direção em que você deseja que uma subárvore cresça, um nó raiz duplicado seja criado e colocado no local do nó raiz real. Então cada subárvore pode crescer em sua própria direção, já que tecnicamente possui seu próprio pai.

O mesmo \forestsetdefine grow subtreee grow' subtreequais requerem uma direção (ângulo ou direção da bússola). Estes seguem as mesmas regras de grow=e grow'=, ou seja, grow' subtreeinvertem a ordem dos nós na subárvore. Não há nada de especial: grow subtree=é apenas uma sintaxe alternativa parafor tree={grow=}

A única alteração de formatação da sintaxe de floresta padrão é que um nó vazio para cada direção deve ser criado. Aqui está um exemplo:

insira a descrição da imagem aqui

O código desta árvore é:

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

A opção multiple directionstambém atua como for treee aceita opções como visto no próximo exemplo.

Nota: forked edgerequer \useforestlibrary{edges}.

insira a descrição da imagem aqui

O código é:

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

Um exemplo adicional com os nós desenhados:

insira a descrição da imagem aqui

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

Observe que o nó raiz original é um phantom, portanto, se você quiser alterar a aparência do nó por nível, será necessário aumentar o nível em 1. Assim, o nó raiz (visível) (que na verdade é uma cópia) está no nível 1 e seus filhos estão no nível 2. Por exemplo:

insira a descrição da imagem aqui

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

Também é possível usar multiple directionsem uma subárvore:

insira a descrição da imagem aqui

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

Observe que devido à forma como multiple directionsoculta o nó raiz, é necessário configurá-lo phantom=falsepara esse uso.

Aqui está o código completo, incluindo alguns dos exemplos:

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

Responder3

Nenhuma solução para este problema pode ser perfeitamente geral. Automatizar o estilo não é necessariamente melhor (ou pior) do queA excelente estruturação manual das árvores de Sandy G. Esta resposta pretende ser mais automática, um pouco mais robusta e evitar pequenos problemas de formatação, mas não há garantia de que não comerá seu chocolate ou roubará sua meia.

Emptor de advertência


Esta é uma modificação deExcelente resposta de Sandy G. A ideia fundamental é a mesma, com as seguintes alterações.

  1. O estilo é mais automatizado.
  2. Ele evita a composição de múltiplas cópias do mesmo conteúdo do nó, o que é responsável pelo efeito de negrito falso em nós que geram subárvores em múltiplas direções. (Mas existem maneiras mais fáceis, de longe, se esta for sua única preocupação.)
  3. Ele tenta ser potencialmente um pouco mais robusto integrando uma das mudanças noA resposta ainda mais excelente de Sašo Živanović. (Isso não significa que funcionará com folderestilo - eu nemtestadoisto!)

A saída deve ser a mesma da resposta de Sandy G (módulo peso aparente da fonte). Apenas a especificação das árvores difere.

produção sem ousadia falsa em pontos de crescimento divergentes

Apenas duas chaves são realmente de interesse na especificação das árvores.

subtree grow=<growth direction>
subtree grow'=<growth direction>

Pretende-se que sejam precisamente análogos a grow subtreee grow subtree'.

A principal diferença é que isso é tudo que precisamos fazer. Não há necessidade de inserir nós adicionais ou especificar qualquer estilo para a árvore como um todo. subtree growe subtree grow'ative o estilo necessário, que insere quaisquer nós adicionais necessários.

Tomando os exemplos de Sandy G para fins de demonstração, o primeiro pode ser especificado simplesmente 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}

É claro que há uma desvantagem nessa simplicidade. Em algumas árvores, temos que digitar com mais precisão porque não temos um nó falso adicional para definir a direção de crescimento de uma subárvore inteira. Em vez disso, devemos especificar a direção de crescimento para cada filho que não deve crescer na direção padrão determinada pelos pais. Portanto, a segunda árvore de Sandy G requerseisusos de subtree grow/ subtree grow', enquanto exigia apenasdoisde grow subtree/ grow subtree'.

[Se o exemplo anterior foram as oscilações, presumo que já estamos nas rotatórias.]

\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 o terceiro exemplo, precisamos de dois usos de subtree grow', mas como não precisamos inserir nós extras ou adicionar nós ao preâmbulo, isso ainda é um pouco mais 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}

Da mesma forma, o quarto exemplo envolve um bom equilíbrio entre oscilações e rotundas, mas a recompensa para múltiplas especificações de crescimento é uma estrutura mais clara (pelo menos ao nível da sintaxe de entrada - a árvore real é uma questão 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}

Sob o capô, ambos subtree growchamam subtree grow'um estilo chamado wild branching.

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

wild branchingnão faz nada que você não pudesse fazer sozinho com o código de Sandy G e alguns ajustes. Ele usa uma variedade de opções e registros personalizados de Floresta (para aqueles que não estão familiarizados com esta terminologia, uma 'opção' é uma configuração para um nó em uma árvore, enquanto um 'registro' se aplica a toda a árvore).

Opções:

  declare boolean={wild children}{0},
  declare boolean={wild child}{0},
  declare keylist={tame ones}{},
  declare keylist={wild siblings}{},
  declare boolean={wild leader}{0},

O argumento final é o valor inicial para todos os nós em todas as árvores.

Registros:

  declare boolean register={wild tree},
  wild tree=0,

Também precisamos de alguns 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,
  },

e usei uma etapa personalizada

  define long step={wild children by growth}{}{%
    sort by={>O{grow}},sort'={filter={children}{>O{wild child}}}
  },

Dividi o estilo em partes, pois estava ficando um pouco longo. wild branchingchamadas do tamenesse do wildnessconforme apropriado.

do tamenessinsere um nó adicional para subárvores que crescem na direção padrão, quando um de seus irmãos usa subtree growou 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 wildnessdescobre quando criar um novo nó adicional para uma nova direção de crescimento e quando a subárvore deve ser adicionada a uma adição existente. É aqui que a etapa personalizada definida acima é usada: ela garante que visitaremos os irmãos na ordem de suas direções de crescimento, o que facilita a compilação de listas de quais deles pertencem um ao outro.

  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 cria mais nós que o de Sandy G, mas os extras são excluídos antes que a árvore seja compactada. No entanto, a estrutura da árvore final difere porque wild branchingé alterada lantes sdo nó pai ser compactado. Teoricamente, isso deveria levar a menos situações que requerem intervenção manual. Mas a teoria não é, como todos sabemos, prática.

  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={},

Observe que subtree grow/ subtree grow'não foi projetado para ser definido para o nó raiz. Se estiverem, wild branchingsimplesmente aplica growou grow'à árvore, sem invocar nada de especial.

wild branchingprovavelmente não deveria ter uma .defaultconfiguração, mas por algum motivo que não consigo lembrar, defini uma.

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}

informação relacionada