自动将森林分支长度缩放为节点内容的值

自动将森林分支长度缩放为节点内容的值

我正在构建一个系统发育树,它始于这个问题。每个节点都有关联的年龄。我想根据两个连续节点之间的年龄差异来缩放分支长度。

我已经想出了如何为每个分支执行此操作,方法是从较老的年龄中减去较年轻的年龄,然后按一个因子缩放(参见 MWE)。这需要我为时间线输入一次年龄,为分支长度输入一次年龄。如果我需要调整年龄,那么我必须记住调整两次。

问题 1:有没有办法通过从前一个节点的年龄中减去一个节点的年龄来自动将分支长度缩放到时间?

还请注意,在下面的示例图中,年龄相近的人会重叠。出于这个和其他原因,我希望可以选择在左侧的时间刻度上打印任何年龄。

我声明了一个名为的布尔值print age,如果给定节点的 ,它将打印年龄和节点的虚线print age=1。我所能做的就是打印所有年龄或不打印年龄,这取决于print age首次声明时 设置为 0 还是 1。

问题2:如何使用布尔值仅打印选定的年龄?对于这个问题,我认为我需要做到以下几点才能实现我的目标:

答:我决定是否按照时间尺度显示年龄。

B. 如果显示年龄,请确定虚线是否存在(或颜色是灰色还是白色)。这样我就可以沿着时间尺度选择年龄,而不是打印会重叠的年龄(例如示例中的 290 和 305),并且仅显示虚线以突出显示感兴趣的节点。

\documentclass[border=10pt]{article}
\usepackage{tikz}
\usetikzlibrary{backgrounds, positioning, arrows, arrows.meta}
\usepackage{forest}
\forestset{
  declare toks register={timeline scale},
  timeline scale=0.2,
  declare boolean={print age}{0}, % Default to no ages printed
  declare dimen register={timeline offset},
  timeline offset'=10mm,
  declare toks register={timeline target},
  timeline target=,
  declare boolean={tree node}{0},
  mytree/.style={
    for tree={
      edge+={thick},
      edge path'={
        (!u.parent anchor) -| (.child anchor)
      },
      grow=north,
      parent anchor=children,
      child anchor=parent,
      anchor=base,
      % l sep=1cm,
      s sep=3mm,
      if n children=0{tier=word, align=center, base=bottom, not tree node}{coordinate, tree node}
    }
  },
%
  age/.style={
% if print age={ %This prints all or no ages depending on declared boolean value above.
    tikz+={
      \begin{scope}[on background layer] 
            \draw [gray, thick, dotted] () -- ( -| timeline base) node[black,anchor=east]{#1}; 
      \end{scope} 
    }
%   }{} end if print age
  },
%
  timeline/.style={
    before drawing tree={
      timeline target/.option=name,
      tempdima/.option=y,
      for tree={
        if={>OR>{y}{tempdima}}{timeline target/.option=name}{},
      }
    },
    tikz+={
      \begin{scope}[on background layer]
        \draw ([xshift=-\foresteregister{timeline offset}]current bounding box.west |- .parent anchor) coordinate (timeline base) -- (\foresteregister{timeline target}.child anchor -| timeline base) node [above] { extsc{mya}};
      \end{scope}
    },
  },
}

\begin{document}
\begin{forest}
  mytree,
  timeline
    [, name=ancestor, age=454,
        [, age=354, l+={\forestregister{timeline scale}*(454-354)}
      [fishes]
          [, age=325, l+={\forestregister{timeline scale}*(354-325)}, 
             [, age=305, l+={\forestregister{timeline scale}*(325-305)}
               [frogs]
              [salamanders]
             ]
              [,age=290,l+={\forestregister{timeline scale}*(325-290)}, print age=1 %Print the age for this node.
                [
                  [mammals]
                ]
                [,age=206, l+={\forestregister{timeline scale}*(290-206)}
                  [reptiles, l+={\forestregister{timeline scale}*(206)}] %Scale the top-most branches.
                  [birds] 
                ]
              ]
            ]
        ]
    ]
\end{forest}
\end{document}

在此处输入图片描述

答案1

Forest 以循环方式工作。如果您需要使用树规范中设置的值,则需要延迟至少一个循环,以便 Forest 解析树并设置这些值。否则,它将仅使用第一个循环期间应用的值,这些值将是默认值。

我会进行age计数,以便您可以对其进行操作,然后在将格式应用于树时使用它。然后您可以使用它来打印年龄、设置级别距离等。

出于某种原因,我用自动化方法得到了一棵稍短的树,但将比例设置为0.3近似于问题中的例子。(0.2根据 Okular 的说法,原始代码为 108 毫米,而不是 109 毫米,其中包括边框。)

\documentclass[border=10pt,tikz]{standalone}
\usetikzlibrary{backgrounds}
\usepackage{forest}
\forestset{
  declare toks register={timeline scale},
  timeline scale=0.2,
  declare boolean={print age}{0}, % Default to no ages printed
  declare dimen register={timeline offset},
  timeline offset'=10mm,
  declare toks register={timeline target},
  timeline target=,
  declare boolean={tree node}{0},
  declare count={age}{0},
  mytree/.style={
    for tree={
      edge+={thick},
      edge path'={
        (!u.parent anchor) -| (.child anchor)
      },
      grow=north,
      parent anchor=children,
      child anchor=parent,
      anchor=base,
      s sep=3mm,
      if n children=0{tier=word, align=center, base=bottom, not tree node}{coordinate, tree node}
    }
  },
  timeline/.style={
    before typesetting nodes={
      where level=0{}{
        if={
          > O_=! O_=! & {age}{0} {!u.age}{0}
        }{
          l+/.process={ ROOw3+n {timeline scale}{age}{!u.age}{##1*(##3-##2)} }
        }{},
        if print age={
          tikz+/.process={
            Ow{age}%
            {
              \begin{scope}[on background layer]
                \draw [gray, thick, dotted] () -- ( -| timeline base) node[black,anchor=east]{##1};
              \end{scope}
            }%
          },
        }{},
      },
    },
    before drawing tree={
      timeline target/.option=name,
      tempdima/.option=y,
      for tree={
        if={>OR>{y}{tempdima}}{timeline target/.option=name}{},
      }
    },
    tikz+={
      \begin{scope}[on background layer]
        \draw ([xshift=-\foresteregister{timeline offset}]current bounding box.west |- .parent anchor) coordinate (timeline base) -- (\foresteregister{timeline target}.child anchor -| timeline base) node [above] { extsc{mya}};
      \end{scope}
    },
  },
}

\begin{document}
\begin{forest}
  mytree,
  timeline,
  timeline scale=0.3
  [, name=ancestor, age=454,
    [, age=354,
      [fishes]
      [, age=325,
        [, age=305,
          [frogs]
          [salamanders]
        ]
        [, age=290, print age
          [
            [mammals]
          ]
          [, age=206,
            [reptiles, l+={\forestregister{timeline scale}*(206)}] %Scale the top-most branches.
            [birds]
          ]
        ]
      ]
    ]
  ]
\end{forest}
\end{document}

自动树

相关内容