使用以 tikz 代码扩展的宏作为森林的内容

使用以 tikz 代码扩展的宏作为森林的内容

我正在尝试生成一棵树,将每个节点的内容设置为一个表。

我的尝试是这样的

\documentclass{article}

\usepackage{forest}

\tikzset{
table/.style={%
  rectangle split, rectangle split parts=#1,
  draw, minimum width=1cm,
  rectangle split part align={center}
},
}
% Test with this, got an '! Missing \cr inserted.' error
\newcommand{\vals}[3]{#1\nodepart{two}#2\nodepart{three}#3}
% I suspected of a expansion problem, try this
% Now it compiles, but it doesn't produce the parts of the node
\newcommand{\valsnx}[3]{#1\noexpand\nodepart{two}#2\noexpand\nodepart{three}#3}

\begin{document}
\begin{forest}
for tree={
  parent anchor=south, child anchor=north,
  font=\ttfamily,
  align=center,
  align=center,
  node options={table=3},
},
[
%  [{a\nodepart{two}b\nodepart{three}c}]% doesn't work either
%  [\vals{a}{b}{c}]% doesn't work
  [\valsnx{a}{b}{c}]% works but doesn't produce the \nodepart structure I'm looking for
  []
]
\end{forest}
\end{document}

我正在使用rectangle split来生成类似表格的节点。但是,当我尝试生成使用 拆分的内容时,\nodepart我得到了! Missing \cr inserted.

我怀疑是扩展问题。有些行中的内容表明,在解析森林时,我的宏\vals被扩展,然后\nodeparts在随后解析森林时被扩展,因此产生了错误。尝试\noexpand在宏中放置一个(以及放在宏本身上,如\noexpand\vals),但没有成功。

有人可以解释一下我遗漏了什么吗?我该如何解决?

答案1

此代码是为森林版本 1. 它无需修改就可以与版本 2 一起编译,并且输出相同(据我所知)。


为什么要给自己找麻烦?align设置表格标题。多行节点只是表格中的森林树。centerleftright设置一个适当的表格标题。

如果你不想使用默认选项,你可以使用其他选项。例如,我定义了一个新的森林风格,forest table

\forestset{
  forest table/.style={
    align={|C{#1}|},
    inner sep=0pt,
    before typesetting nodes={
      if content={}{
        shape=coordinate
      }{},
    },
    draw
  },
}

这里的关键设置是align使用包中C定义的自定义列类型列:\newcolumntype{}{}array

\newcolumntype{C}[1]{>{\centering\arraybackslash}p{#1}}

如果你总是想要相同的宽度,你可以说

  forest table/.style={
    align={|C{10mm}|},
    ...

或者其他任何形式。

其余设置只是为了确保绘制的分支符合表格边界,从而使输出更漂亮(在我看来):

    inner sep=0pt,
    ...
    draw

并且空节点平滑地集成到绘制的边中:

    before typesetting nodes={
      if content={}{
        shape=coordinate
      }{},
    },

然后,您可以在节点中使用\hline等来创建水平线,并将命令定义为值的包装器,该包装器仅插入行结尾和规则。例如,我使用了

\newcommand{\vals}[3]{\hline#1\\\hline#2\\\hline#3\\\hline}

然后

\begin{forest}
  for tree={
    forest table=10mm
  }
  [
      [\vals{a}{b}{c}]
  ]
\end{forest}

将产生一棵树如下:

树节点中的表格

如果您希望节点中有一个空表,同时还允许真正为空的节点,则此方法可以正常工作:

\begin{forest}
  for tree={
    parent anchor=south,
    child anchor=north,
    font=\ttfamily,
    forest table=25mm,
    edge path={
      \noexpand\path [\forestoption{edge}] (!u.parent anchor) -- +(0,-5pt) -| (.child anchor)\forestoption{edge label};
    }
  },
  [\vals{}{}{}
    [\vals{a}{b}{c}
      [
        [\vals{Node above}{deliberately}{left empty}]
      ]
    ]
    [\vals{d}{e}{f}]
  ]
\end{forest}

生产

空表和空节点

虽然你的问题没有提到这一点,但你在评论中建议你希望空节点成为空表。这相对简单。我们可以用forest tables=<width>与样式相同的方式定义样式forest table=<width>。事实上,我们可以使用后者来定义前者。

像前面一样指定列的宽度,并将其传递给样式forest table

  forest tables/.style={
    forest table=#1,

如果节点没有内容,无论如何都在这里创建一个表格:

    delay={
      if content={}{
        content={\vals{}{}{}},
      }{}
    }

delay确保如果节点确实有内容,它不会被空表覆盖。

  }

然后

\begin{forest}
  for tree={
    parent anchor=south,
    child anchor=north,
    font=\ttfamily,
    forest tables=10mm,
    edge path={
      \noexpand\path [\forestoption{edge}] (!u.parent anchor) -- +(0,-5pt) -| (.child anchor)\forestoption{edge label};
    }
  },
  [
    [\vals{a}{b}{c}
      [
        [\vals{Node above}{auto-}{filled}]
      ]
    ]
    [\vals{d}{e}{f}]
  ]
\end{forest}

将产生

空节点是空表

完整代码:

\documentclass[tikz,multi,border=5pt]{standalone}
\usepackage{array,forest}
\newcolumntype{C}[1]{>{\centering\arraybackslash}p{#1}}
\newcommand{\vals}[3]{\hline#1\\\hline#2\\\hline#3\\\hline}
\forestset{
  forest table/.style={
    align={|C{#1}|},
    inner sep=0pt,
    before typesetting nodes={
      if content={}{
        shape=coordinate
      }{},
    },
    draw
  },
  forest tables/.style={
    forest table=#1,
    delay={
      if content={}{
        content={\vals{}{}{}},
      }{}
    }
  }
}
\begin{document}
\begin{forest}
  for tree={
    forest table=10mm
  }
  [
      [\vals{a}{b}{c}]
  ]
\end{forest}
\begin{forest}
  for tree={
    parent anchor=south,
    child anchor=north,
    font=\ttfamily,
    forest table=25mm,
    edge path={
      \noexpand\path [\forestoption{edge}] (!u.parent anchor) -- +(0,-5pt) -| (.child anchor)\forestoption{edge label};
    }
  },
  [\vals{}{}{}
    [\vals{a}{b}{c}
      [
        [\vals{Node above}{deliberately}{left empty}]
      ]
    ]
    [\vals{d}{e}{f}]
  ]
\end{forest}
\begin{forest}
  for tree={
    parent anchor=south,
    child anchor=north,
    font=\ttfamily,
    forest tables=10mm,
    edge path={
      \noexpand\path [\forestoption{edge}] (!u.parent anchor) -- +(0,-5pt) -| (.child anchor)\forestoption{edge label};
    }
  },
  [
    [\vals{a}{b}{c}
      [
        [\vals{Node above}{auto-}{filled}]
      ]
    ]
    [\vals{d}{e}{f}]
  ]
\end{forest}
\end{document}

相关内容