森林:将文件夹子树插入任意节点

森林:将文件夹子树插入任意节点

我正在尝试构建一棵水平生长的树,这样就可以引入具有“文件夹”方面的列表,以指定与该节点相关的一系列主题。

在我所附的代码中,有两个我无法解决的问题:

  1. 子节点为文件夹的节点与文件夹节点的第一个元素之间的距离大于其余子节点的其他距离。

  2. 属于该文件夹的节点与树的其余部分重叠。

我已经就类似主题提出了一个问题(森林文件夹中的分叉边树间距问题),但在这种其他情况下,我无法利用所建议的方法。

为了使子树朝不同方向生长的节点,我尝试使用此问题中的建议:

森林:同一层树枝的生长方向不同

有什么方法可以解决这些问题吗?

梅威瑟:

\documentclass[varwidth=20cm,tikz]{standalone}

\usepackage[edges]{forest}

\forestset{
  rounded forked tree/.style={
    for tree={
      grow'=east,
      fit=band,
      rounded corners,
      line width=1.6pt,
      font=\sffamily\bfseries\Large,
      inner xsep=5mm,
      inner ysep=5mm,
      outer ysep=0pt,
      edge+={line width=1.6pt, rounded corners=2mm},
      if n children=1{
        for first={
          before typesetting nodes={
            edge+={rounded corners=0mm},
          }
        }
      }{},
      align=center,
      l sep'=20mm,
      s sep'=7mm,
      fork sep'=8mm,
      draw,
      minimum height=15mm,
      minimum width=20mm,
      anchor=west,
    },
    forked edges,
  },
  folder subtree/.style={
    for tree={
      folder,
      s sep'=5mm,
      l sep'=10mm,
      minimum height=5mm,
    },
    folder indent'=5mm,
    anchor=west,
    for descendants={
      align=left,
      anchor=base west,
      base=top,
      inner xsep=2mm,
      inner ysep=1mm,
      before typesetting nodes={
        edge+={rounded corners=2mm},
      },
      edge path'/.expanded={
        ([xshift=\forestregister{folder indent}]!u.parent anchor) |- ([yshift=.6ex].base west)
      },
    },
  },
  phantom node/.style={
    !c.content/.pgfmath=content("!parent"),
    text opacity=0,
    draw=none,
    no edge,
    before computing xy={
      l=0,
      s=0
    }
  },
  grow subtree/.style={
    for tree={grow=#1}
  }, 
  grow' subtree/.style={
    for tree={grow'=#1}
  },
}

\begin{document}

\begin{forest}
  rounded forked tree,
  [Root
    [Node first line\\(Node second line)
      [, phantom node, folder subtree
        [Folder element]
        [Folder element]
        [Folder element
          [Folder element child]
          [Folder element child]
        ]
        [Folder element first line\\(Folder element second line)]
        [Folder element]
        [Folder element]
      ]
      [, phantom node, grow' subtree=east,
        [Node]
        [Node]
      ]
    ]
    [Node
      [Node first line\\(Node second line)]
      [Node]
    ]
  ]
\end{forest}

\end{document}

答案1

让我先解决 (2)。子树重叠是因为幻像节点的l和在错误的时间被设置为零。在 时,树已经打包,对节点坐标的任何手动更改都可能导致重叠。解决方案是在打包父节点后立即更改幻像节点的和: 。这实质上取消了打包幻像父节点的效果,但至关重要的是,祖先节点的后续打包将使用包含幻像节点的子树的正确(即手动更改)轮廓。sbefore computing xylsafter packing node={for children={l=0,s=0}}

(1) 的问题在于folder依赖于其自身的 的设置,对于文件夹父级及其子级,都anchor必须将其设置为。然而,OP 的代码将其更改为/ 。理由很充分:如果幻影副本要绘制在与原始副本相同的位置,则它们必须具有相同的锚点。因此,如果我们删除应用于幻影节点的样式 中的和,并绘制这些节点(更改为),我们可以看到它们相对于原始节点发生了偏移。parent firstwestbase westanchor=westanchor=base westfolder subtreedraw=nonedraw

解决方案是删除有问题的anchor调用,但通过手动移动l和来模拟它们的效果s。从这些坐标中减去所需的锚点(上图,west)应该可以解决问题。但我们如何获得锚点坐标?这时事情就变得非常低级和棘手了。

那些深入研究过 PGF 的人知道如何访问节点锚点的坐标:使用\pgfpointanchor。好吧,Forest 有一个相关的私有宏,,\forest@pointanchor我差点就公开了,我最初想在这种情况下使用它。然而,事实证明这个宏是不完整的,因为它只接受锚点选项((parent/child) anchor),但不接受正常的 TikZ 锚点名称,如north,或 Forest 的锚点,如parent first。(似乎锚点选项是内部唯一需要的用例。)下面,我定义了\forestpointanchor,它可以解决问题,并且会在测试后将其放入包中。(我认为在这里解释它的工作原理没有多大意义。)

我还定义了此宏的公共接口 style get point anchor。此 style 接受三个参数:#1是要检索的锚点的名称(它可以是普通的 TikZ 锚点,也可以是 Forest 锚点之一,例如parent first,或者是锚点选项parent anchor);#2和是将分别接收锚点的和坐标的#3键。如果您想在后续计算中使用它们,将它们存储到和 中是个好主意。下面,我们只需将它们传递给和,这实际上是从和中减去接收到的和。xytempdimxtempdimyl-s-xyls

顺便说一句,!c.content在原始phantom node样式中相当于普通的content,因此!c.(代表“移动到当前节点”)可以被删除。否则,我会说 OP 的代码是这个网站上发布过的最干净的 Forest 代码。赞!

\documentclass[varwidth=20cm,tikz]{standalone}

\usepackage[edges]{forest}

\forestset{
  rounded forked tree/.style={
    for tree={
      grow'=east,
      fit=band,
      rounded corners,
      line width=1.6pt,
      font=\sffamily\bfseries\Large,
      inner xsep=5mm,
      inner ysep=5mm,
      outer ysep=0pt,
      edge+={line width=1.6pt, rounded corners=2mm},
      if n children=1{
        for first={
          before typesetting nodes={
            edge+={rounded corners=0mm},
          }
        }
      }{},
      align=center,
      l sep'=20mm,
      s sep'=7mm,
      fork sep'=8mm,
      draw,
      minimum height=15mm,
      minimum width=20mm,
      anchor=west,
    },
    forked edges,
  },
  folder subtree/.style={
    for tree={
      folder,
      s sep'=5mm,
      l sep'=10mm,
      minimum height=5mm,
    },
    folder indent'=5mm,
    % anchor=west,
    for descendants={
      align=left,
      % anchor=base west,
      base=top,
      inner xsep=2mm,
      inner ysep=1mm,
      before typesetting nodes={
        edge+={rounded corners=2mm},
      },
      edge path'/.expanded={
        ([xshift=\forestregister{folder indent}]!u.parent anchor) |- ([yshift=.6ex].base west)
      },
    },
  },
  phantom children/.style={
    delay={
      for children={
        content/.pgfmath=content("!parent"),
        text opacity=0,
        draw,%=none,
        no edge,
      },
    },
    after packing node={
      for children={
        l=0,
        s=0,
        get point anchor={west}{l-}{s-},
      },
    },
  },
  grow subtree/.style={
    for tree={grow=#1}
  }, 
  grow' subtree/.style={
    for tree={grow'=#1}
  },
}

\makeatletter
% Analogous to \pgfpointanchor, but also allows forest anchors ("parent first"
% etc.)  and forest anchor options ("parent anchor" etc.) as #1.
\def\forestpointanchor#1{% #1 = anchor
  \forestanchortotikzanchor{#1}\forest@temp@anchor
  \forestoifdefined{@box}{%
    \forestoget{@box}\forest@temp
    \ifdefempty\forest@temp{%
      \forestpointanchor@callpgfpointanchor{name}%
    }{%
      \setbox0\hbox{%
        \begin{pgfpicture}%
          \forestpointanchor@callpgfpointanchor{later@name}%
        \end{pgfpicture}%
      }%
    }%
  }{%
    \forestpointanchor@callpgfpointanchor{name}%
  }%
  \forest@global@marshal
}
\def\forestpointanchor@callpgfpointanchor#1{% #1 = (later@)name
  \pgfpointanchor{\forestove{#1}}{\forest@temp@anchor}%
  \xdef\forest@global@marshal{%
    \global\pgf@x=\the\pgf@x\relax
    \global\pgf@y=\the\pgf@y\relax\relax
  }%
}
\csdef{forest@anchor@@}{\def\forest@temp@anchor{center}}%
\def\forestPointanchor#1#2{% #1 = node id, #2 = anchor
  \forest@fornode{#1}{\forestpointanchor{#2}}%
}
\forestset{
  % Get the coordinates of anchor #1
  % and assign the resulting x and y to keys #2 and #3, respectively.
  get point anchor/.code n args=3{%
    \forestpointanchor{#1}%
    \forestset{#2=\pgf@x,#3=\pgf@y}%
  },
}
\makeatother

\begin{document}

\begin{forest}
  rounded forked tree,
  [Root
    [Node first line\\(Node second line), phantom children
      [, folder subtree
        [Folder element]
        [Folder element]
        [Folder element
          [Folder element child]
          [Folder element child]
        ]
        [Folder element first line\\(Folder element second line)]
        [Folder element]
        [Folder element]
      ]
      [, grow' subtree=east,
        [Node]
        [Node]
      ]
    ]
    [Node
      [Node first line\\(Node second line)]
      [Node]
    ]
  ]
\end{forest}

\end{document}

编译结果

相关内容