1

I want to create a tree, where the children at the last parent node of each branch are arranged as a folder. Based on some other posts I created the example shown below. However, my problem with this example is that the folders in the center (B.2.2.2, B.2.2.1, and B.2.1) have a weird large horizontal distance and thus the nodes overlap.

Is there some way to adjust this space or is there maybe even a more elegant way to create folders on each relative last level (with proper alignment)?

\documentclass[border=10pt,multi,tikz]{standalone}
\usepackage[edges]{forest}
\usetikzlibrary{arrows.meta,shadows.blur,positioning,fit} 
\begin{document}
\begin{forest}
        forked edges,
        basic/.style = {draw, thin, drop shadow, font=\sffamily},
        upper style/.style = {basic, rounded corners=6pt, edge+={-Stealth, thin}, fill=black!6, text width=10.5em},
          lower style/.style = {basic, rounded corners=0pt, edge+={-, line width=.4pt}, fill=black!10, text width=9em},
          where n children=0{%
            lower style
          }{%
            upper style,
          },
          where level<=1{%
            parent anchor=children,
            child anchor=parent,
            if={isodd(n_children())}{%
              calign=child edge,
              calign primary child/.process={
                O+nw+n{n children}{(#1+1)/2}
              },
            }{%
              calign=edge midpoint,
            },
          }{
            grow'=0,
          },
           [{Root},fill=parent,
            [{A}, folder, grow'=0, for children={lower style},
              before drawing tree={
                tempdima/.option=!r2.max y,
                tempdima-/.option=max y,
                for tree={
                  y+/.register=tempdima,
                },
              }
                [A.1]
                [A.2]
                [...]
            ]
            [{B}, 
              [{B.1},   folder,   before drawing tree={
                tempdima/.option=!r2.max y,
                tempdima-/.option=max y,
                for tree={
                  y+/.register=tempdima,
                },
              }
                [B.1.1]
                [B.1.2]
              ]
              [{B.2},  grow'=south,  parent anchor=children,
            child anchor=parent,
            if={isodd(n_children())}{%
              calign=child edge,
              calign primary child/.process={
                O+nw+n{n children}{(#1+1)/2}
              },
            }{%
              calign=edge midpoint,
            },
                [{B.2.1},  folder,   before drawing tree={
                tempdima/.option=!r2.max y,
                tempdima-/.option=max y,
                for tree={
                  y+/.register=tempdima,
                },
              }
                    [B.2.1.2]
                    [B.2.1.3]
                    [B.2.1.4]
                    [B.2.1.5]
                    ]
              [{B.2.2}, grow'=south,parent anchor=children,
            child anchor=parent,calign=edge midpoint,
                [{B.2.2.1},  folder,   before drawing tree={
                tempdima/.option=!r2.max y,
                tempdima-/.option=max y,
                for tree={
                  y+/.register=tempdima,
                },
              }
                    [B.2.2.1.1]
                    [B.2.2.1.2]
                    [B.2.2.1.3]
                ]
                [{B.2.2.2},  folder,   before drawing tree={
                tempdima/.option=!r2.max y,
                tempdima-/.option=max y,
                for tree={
                  y+/.register=tempdima,
                },
              }
                    [B.2.2.2.1]
                    [B.2.2.2.2]
                    [B.2.2.2.3]
                ]
              ]]
              [{B.3},  folder,   before drawing tree={
                tempdima/.option=!r2.max y,
                tempdima-/.option=max y,
                for tree={
                  y+/.register=tempdima,
                },
              }  
                [B.3.1]
                [B.3.2]
                [B.3.3]
              ]
            ]
            [{C}, folder, grow'=0,
              before drawing tree={
                tempdima/.option=!r2.max y,
                tempdima-/.option=max y,
                for tree={
                  y+/.register=tempdima,
                },
              }
                [C.1]
                [C.2]
            ]
          ] 
\end{forest}
\end{document}

Example tree

0
0

The short answer: change grow'=south (set on nodes B.2 and B.2.2) to grow=south (without the apostrophe).

Prettified (and compilable) code (with some comments):

\documentclass{standalone}

\usepackage[edges]{forest}
\usetikzlibrary{arrows.meta,shadows.blur}

\forestset{
  calign to midchild or edge midpoint/.style={
    if={isodd(n_children())}{
      calign=child edge,
      calign primary child/.process={
        O+nw+n{n children}{(##1+1)/2}
      },
    }{
      calign=edge midpoint,
    },
  },
}

\begin{document}
\begin{forest}
  forked edges,
  basic/.style = {draw, thin, drop shadow, font=\sffamily},
  upper style/.style = {basic, rounded corners=6pt, edge+={-Stealth, thin}, fill=black!6, text width=10.5em},
  lower style/.style = {basic, rounded corners=0pt, edge+={-, line width=.4pt}, fill=black!10, text width=9em},
  my fork/.style={
    grow=south, calign to midchild or edge midpoint,
  },
  my folder/.style={
    % This does not work but it should. The following release of Forest will fix the issue by changing "s+=tempdims()*(reversed()-0.5)*2," to "s+=tempdims()*(reversed("!parent")-0.5)*2," in the definition of style "folder".
    % grow'=0,
    % The workaround is to set "grow'" for the entire subtree.
    for tree={grow'=0},
    folder,
    % This code lifts the entire folder a little bit, but.
    % (a) It could do so more elegantly, by changing "l" before computing xy,
    % instead of changing "y" after computing xy.
    % (b) It relies on some accidental properties of the tree. "!r2" is the
    % second child of the root, which is "anchor"ed at the base by default, so
    % "max y" is the non-zero. The folder nodes are "anchor"ed on top, so their
    % "max y" is zero.  "tempdima" thus results in some, rather arbitrary
    % shift.
    % 
    % before drawing tree={
    %   tempdima/.option=!r2.max y,
    %   tempdima-/.option=max y,
    %   for tree={
    %     y+/.register=tempdima,
    %   },
    % }
    % 
    % A better way to do this is to explicitly shift the folder nodes by
    % changing their "l" at the right time.  Note that while this will work for
    % this tree, it could result in overlapping nodes in general (but the same
    %   was true for the approach above).
    before computing xy={l-=2.5ex},
  },
  for tree={
    parent anchor=children,
    child anchor=parent,
    if n children=0{
      lower style,
    }{
      upper style,
      % By default, we set "my fork" for all branching nodes.  We can do this
      % as everything it does is overriden by "my folder" (which is set
      %   manually for selected nodes).
      my fork,
    },
  },
  [Root,
    [A, my folder,
      [A.1]
      [A.2]
      [\dots]
    ]
    [B, 
      [B.1, my folder,
        [B.1.1]
        [B.1.2]
      ]
      [B.2,
        [B.2.1, my folder,
          [B.2.1.2]
          [B.2.1.3]
          [B.2.1.4]
          [B.2.1.5]
        ]
        [B.2.2,
          [B.2.2.1, my folder,
            [B.2.2.1.1]
            [B.2.2.1.2]
            [B.2.2.1.3]
          ]
          [B.2.2.2, my folder,
            [B.2.2.2.1]
            [B.2.2.2.2]
            [B.2.2.2.3]
          ]
        ]]
      [B.3, my folder,
        [B.3.1]
        [B.3.2]
        [B.3.3]
      ]
    ]
    [C, my folder, 
      [C.1]
      [C.2]
    ]
  ]
\end{forest}
\end{document}

the result

The long answer. The big question is how can using grow instead of grow' on the parent of the folder root influence how the folder looks like?!

First, unlike grow, grow' sets reversed to true (note that in OPs picture, B.2.1 and B.2.2are reversed, and so are B.2.2.1 and B.2.2.2).

Style folder sets the anchor of the parent node (i.e. the folder root) to parent first. This is crucial, because the style then shifts the children with this reference point in mind to produce the visual effect of a folder. However, the parent first anchor depends on the reversed (and grow) of the parent of the node it is used at --- in this case, the parent of the folder. (I will have to think about whether this is a (design) bug or not.) So what was really happening here was that the parent node (and not the children) got thrown to a wrong place.

1
  • Thank you very much for your detailed answer and the provided explanations! Also, thank you for the development of the forest package.
    – Christian
    Sep 14 '21 at 12:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.