如何在乳胶中将我的图形(文件夹目录树)分成两列,如屏幕截图所示?

如何在乳胶中将我的图形(文件夹目录树)分成两列,如屏幕截图所示?

所需图形

\documentclass[tikz, border=12pt, a4paper]{standalone}
\usepackage{tikz}
\usetikzlibrary{trees}
\usepackage[edges]{forest}
\usepackage{sectsty}
\usepackage{array}
\usepackage{multicol}
\usepackage{graphicx}
\definecolor{folderborder}{RGB}{110,144,169}
\definecolor{folderbg}{rgb}{0.91, 0.84, 0.42}
\usepackage[T1]{fontenc}    
\usepackage[utf8]{inputenc} 
\usepackage{floatrow}
\newlength\Size
\setlength\Size{4pt}
\tikzset{%
  folder/.pic={%
    \filldraw [draw=folderborder, top color=folderbg!50, bottom color=folderbg] (-1.05*\Size,0.2\Size+5pt) rectangle ++(.75*\Size,-0.2\Size-5pt);
    \filldraw [draw=folderborder, top color=folderbg!50, bottom color=folderbg] (-1.15*\Size,-\Size) rectangle (1.15*\Size,\Size);
  },
  file/.pic={%
    \filldraw [draw=folderborder, top color=folderborder!5, bottom color=folderborder!10] (-\Size,.4*\Size+5pt) coordinate (a) |- (\Size,-1.2*\Size) coordinate (b) -- ++(0,1.6*\Size) coordinate (c) -- ++(-5pt,5pt) coordinate (d) -- cycle (d) |- (c) ;
  },
}
\forestset{%
  declare autowrapped toks={pic me}{},
  pic dir tree/.style={%
    for tree={%
      folder,
      font=\ttfamily,
      grow'=0,
    },
    before typesetting nodes={%
      for tree={%
        edge label+/.option={pic me},
      },
    },
  },
  pic me set/.code n args=2{%
    \forestset{%
      #1/.style={%
        inner xsep=2\Size,
        pic me={pic {#2}},
      }
    }
  },
  pic me set={directory}{folder},
  pic me set={file}{file},
}
\begin{document}
\begin{forest}
  pic dir tree,
  where level=0{}{% folder icons by default; override using file for file icons
    directory,
  },
[
[OpenFOAM,
        [system
                 [\fname{controlDict.sim}{}, file]
                 [\fname{controlDict.moveDyn}{}, file]
                 [\fname{controlDict}{}, file]
                 [\fname{createPathDict.ami}{}, file]
                 [\fname{decomposeParDict}{}, file]
                 [\fname{topoSetDict}{}, file]
                 [\fname{...}{}, file]

        ]
        [Constant
                [polyMesh]
                [\fname{dynamicMeshDict}{}, file]
                [\fname{...}{}, file]
        ]
        [0
               [\fname{pointDisplacement}{}, file]
               [\fname{U}{}, file]
               [\fname{...}{}, file]
        ]

        [MeshFactory
                   [\fname{...}{}, file]
        ]
        [postProcessing]
        [log]
]
[\fname{runMesh.sh}{}, file]
[\fname{runMoveDyn.sh}{}, file]
[\fname{runBatchJob.Job}{}, file]
]
\end{forest}
\end{document}

答案1

这是一个相当复杂的情况。下面的解决方案主要依赖于 Forest 仅绘制树的一部分的能力(通过设置)draw tree processing order,以及输出多个 TikZ 图片(通过修改为多次draw tree stage调用) 。draw tree

下面,我们定义 style draw part of tree,它有两个参数,即要绘制的起始节点和最终节点;这两个节点被指定为从根节点开始的节点行走。然后,在重新定义 时使用此 style 来draw tree stage输出树的块。

绘制连续点和箭头时会出现更多复杂情况。每当最后绘制的节点不是树中的最后一个节点时,draw part of tree就会调用 style to be continued(并将第一个和最后一个绘制的节点传递给此 style)。此 style 是可自定义的;下面,在调用 时dots below,它需要知道最后绘制的节点,而arrow to next tree则不需要参数。

dots below获取y最后绘制节点的坐标,并为每个未绘制子节点的节点绘制垂直线(后跟垂直点);这些是第一个未绘制节点的祖先。

arrow to next tree绘制箭头,仅参考当前边界框。它依赖于begin drawbaseline树块垂直对齐到顶部。

在输出树块之后(但在关闭内部之前),to be continued从内部调用Style 。要获取作为延续的树块中未绘制父节点的边(以及这些边上方的垂直点),方法是修改这些“未绘制”父节点draw treetikzpicture绘制树。这发生在样式中prepare fake ancestors,由调用draw part of tree

prepare fake ancestors最终调用两种可自定义的样式。init prepare fake parent在第一个绘制节点的上下文中调用;在下面,它记住了y该节点的坐标。prepare fake parent然后将未绘制的父节点放在记住的坐标的稍上方y- 这样就可以从“未绘制”的父节点到它们的绘制的子节点获得正确的边。prepare fake parent还将未绘制的父节点变成坐标节点,移除它们的边缘并在它们上方放置垂直点。

当然,必须实际绘制“未绘制”的父节点(否则 Forest 不会绘制其子节点的边缘),但要绘制它们,它们必须出现在 中。我通过要求 的第一个参数必须从根节点逐个祖先地走到第一个绘制的节点来draw tree processing order简化实现。例如,下面的第二次调用将遍历根节点、“OpenFAM”和“0”节点。如果我们直接遍历“0”,例如通过命名它,事情就不会奏效。draw part of treedraw part of tree

正如我所说,这里的解决方案相当复杂,要完全理解它需要阅读手册,特别是第 3.4 节“工作流程”,但如果出现特定问题,我当然很乐意更详细地解释。

\documentclass[a4paper]{article}
\usepackage{tikz}
\usetikzlibrary{trees}
\usepackage[edges]{forest}
\usepackage{sectsty}
\usepackage{array}
\usepackage{multicol}
\usepackage{graphicx}
\definecolor{folderborder}{RGB}{110,144,169}
\definecolor{folderbg}{rgb}{0.91, 0.84, 0.42}
\usepackage[T1]{fontenc}    
\usepackage[utf8]{inputenc} 
\usepackage{floatrow}
\newlength\Size
\setlength\Size{4pt}
\tikzset{%
  folder/.pic={%
    \filldraw [draw=folderborder, top color=folderbg!50, bottom color=folderbg] (-1.05*\Size,0.2\Size+5pt) rectangle ++(.75*\Size,-0.2\Size-5pt);
    \filldraw [draw=folderborder, top color=folderbg!50, bottom color=folderbg] (-1.15*\Size,-\Size) rectangle (1.15*\Size,\Size);
  },
  file/.pic={%
    \filldraw [draw=folderborder, top color=folderborder!5, bottom color=folderborder!10] (-\Size,.4*\Size+5pt) coordinate (a) |- (\Size,-1.2*\Size) coordinate (b) -- ++(0,1.6*\Size) coordinate (c) -- ++(-5pt,5pt) coordinate (d) -- cycle (d) |- (c) ;
  },
}
\forestset{%
  declare autowrapped toks={pic me}{},
  pic dir tree/.style={%
    for tree={%
      folder,
      font=\ttfamily,
      grow'=0,
    },
    before typesetting nodes={%
      for tree={%
        edge label+/.option={pic me},
      },
    },
  },
  pic me set/.code n args=2{%
    \forestset{%
      #1/.style={%
        inner xsep=2\Size,
        pic me={pic {#2}},
      }
    }
  },
  pic me set={directory}{folder},
  pic me set={file}{file},
}
\newcommand\fname[2]{#1}

\forestset{
  arrow to next tree/.code={
    \draw[dashed] (current bounding box.south east) -- ++(1.5em,0);
    \draw[->](current bounding box.south east) ++(0.25em,0) -- ++(1.5em,0) -- (current bounding box.north east) -- ++(1.5em,0);
  },
  dots below/.style={
    for group={#1}{
      tempdima/.option=y,
      for next node={
        for ancestors={
          TeX={%
            \draw([xshift=\forestregister{folder indent}].parent anchor)--([xshift=\forestregister{folder indent}]\forestoption{x},\forestregister{tempdima}) node[anchor=north]{\vdots};
          },
        },
      },
    },
  },
  prepare fake ancestors/.style={
    for group={#1}{
      init prepare fake parent,
      prepare fake ancestors',
    },
  },
  prepare fake ancestors'/.style={
    if nodewalk valid={parent}{
      for parent={
        prepare fake parent,
        prepare fake ancestors',
      },
    }{},
  },
  init prepare fake parent/.style={
    tempdima/.option=y,
  },
  prepare fake parent/.style={
    y/.register=tempdima, y+=10pt,
    coordinate, edge label={}, no edge,
    tikz={\node at ()[anchor=south,xshift=\forestregister{folder indent}]{\vdots};},
    typeset node,
  },
  draw part of tree/.style 2 args={
    draw tree processing order/.nodewalk style={
      for nodewalk={#2,tempcounta/.option=id}{},
      #1,
      do until={id()==tempcounta()}{next node},
    },
    for root'={
      post draw tree hook/.style={},
      for group={#2}{
        if nodewalk valid={next node}{
          post draw tree hook/.append style={
            to be continued={#1}{#2},
          },
        }{}
      },
      prepare fake ancestors={#1},
      draw tree,
    },
  },
}

\begin{document}
\begin{forest}
  pic dir tree,
  for descendants={% folder icons by default; override using file for file icons
    directory,
  },
  draw tree method/.append style={post draw tree hook},
  begin draw/.append code={[baseline=(current bounding box.north)]},
  to be continued/.style 2 args={
    dots below={#2},
    arrow to next tree,
  },
  draw tree stage/.style={
    draw part of tree={current}{n=1,n=2,last leaf},
    draw part of tree={current,n=1,n=3}{last leaf},
  },
  [
    [OpenFOAM,
      [system
        [\fname{controlDict.sim}{}, file]
        [\fname{controlDict.moveDyn}{}, file]
        [\fname{controlDict}{}, file]
        [\fname{createPathDict.ami}{}, file]
        [\fname{decomposeParDict}{}, file]
        [\fname{topoSetDict}{}, file]
        [\fname{...}{}, file]
      ]
      [Constant
        [polyMesh]
        [\fname{dynamicMeshDict}{}, file]
        [\fname{...}{}, file]
      ]
      [0
        [\fname{pointDisplacement}{}, file]
        [\fname{U}{}, file]
        [\fname{...}{}, file]
      ]
      [MeshFactory
        [\fname{...}{}, file]
      ]
      [postProcessing]
      [log]
    ]
    [\fname{runMesh.sh}{}, file]
    [\fname{runMoveDyn.sh}{}, file]
    [\fname{runBatchJob.Job}{}, file]
  ]
\end{forest}
\end{document}

在此处输入图片描述

更新:在评论中,OP 询问如何确定分割点。这是通过的参数完成的,它出现在树序言中draw part of tree的重新定义中。采用两个参数,即要绘制的第一个节点和最后一个节点。两个节点都由draw tree stagedraw part of tree节点行走(参见手册第 3.8 节“Nodewalks”)从根节点开始。

  • 在第一次调用 时draw tree stage,第一个参数是current。由于 nodewalk 是在根节点的上下文中求值的,因此其求值为根节点本身。第二个参数是n=1,n=2,last leaf:从根节点开始,我们首先转到第一个子节点(“OpenFAM”),然后转到此节点的第二个子节点(“Constant”),然后转到此节点的最后一个叶子节点(终端节点)(“Constant”文件夹中的“...”)。

  • 在第二次调用 时draw tree stage,第一个参数是current,n=1,n=3。我们首先踏入当前节点,即根节点;然后踏入其第一个子节点(“OpenFAM”),然后踏入此节点的第三个子节点(“0”),从而成为第一个绘制的节点。第二个参数是last leaf;由于它是在根节点的上下文中进行评估的,因此这是树的最后一个节点(“runBatchJob.Job”)。

不可否认,这种确定分割点的方式很麻烦,尤其是因为我们必须小心地踩到第一个绘制节点的祖先(如上所述)。下面,您可以找到代码(产生相同的结果),大大简化了用户界面。只需在draw linear split tree树的前言中宣布分割,然后在树本身中用标记分割点split here;用标记的节点split here将开始一个新的块。(最后一个块是自动绘制的。)

\documentclass[a4paper]{article}
\usepackage{tikz}
\usetikzlibrary{trees}
\usepackage[edges]{forest}
\usepackage{sectsty}
\usepackage{array}
\usepackage{multicol}
\usepackage{graphicx}
\definecolor{folderborder}{RGB}{110,144,169}
\definecolor{folderbg}{rgb}{0.91, 0.84, 0.42}
\usepackage[T1]{fontenc}    
\usepackage[utf8]{inputenc} 
\usepackage{floatrow}
\newlength\Size
\setlength\Size{4pt}
\tikzset{%
  folder/.pic={%
    \filldraw [draw=folderborder, top color=folderbg!50, bottom color=folderbg] (-1.05*\Size,0.2\Size+5pt) rectangle ++(.75*\Size,-0.2\Size-5pt);
    \filldraw [draw=folderborder, top color=folderbg!50, bottom color=folderbg] (-1.15*\Size,-\Size) rectangle (1.15*\Size,\Size);
  },
  file/.pic={%
    \filldraw [draw=folderborder, top color=folderborder!5, bottom color=folderborder!10] (-\Size,.4*\Size+5pt) coordinate (a) |- (\Size,-1.2*\Size) coordinate (b) -- ++(0,1.6*\Size) coordinate (c) -- ++(-5pt,5pt) coordinate (d) -- cycle (d) |- (c) ;
  },
}
\forestset{%
  declare autowrapped toks={pic me}{},
  pic dir tree/.style={%
    for tree={%
      folder,
      font=\ttfamily,
      grow'=0,
    },
    before typesetting nodes={%
      for tree={%
        edge label+/.option={pic me},
      },
    },
  },
  pic me set/.code n args=2{%
    \forestset{%
      #1/.style={%
        inner xsep=2\Size,
        pic me={pic {#2}},
      }
    }
  },
  pic me set={directory}{folder},
  pic me set={file}{file},
}
\newcommand\fname[2]{#1}

\forestset{
  arrow to next tree/.code={
    \draw[dashed] (current bounding box.south east) -- ++(1.5em,0);
    \draw[->](current bounding box.south east) ++(0.25em,0) -- ++(1.5em,0) -- (current bounding box.north east) -- ++(1.5em,0);
  },
  dots below/.style={
    for group={#1}{
      tempdima/.option=y,
      for next node={
        for ancestors={
          TeX={%
            \draw([xshift=\forestregister{folder indent}].parent anchor)--([xshift=\forestregister{folder indent}]\forestoption{x},\forestregister{tempdima}) node[anchor=north]{\vdots};
          },
        },
      },
    },
  },
  prepare fake ancestors/.style={
    for group={#1}{
      init prepare fake parent,
      prepare fake ancestors',
    },
  },
  prepare fake ancestors'/.style={
    if nodewalk valid={parent}{
      for parent={
        prepare fake parent,
        prepare fake ancestors',
      },
    }{},
  },
  init prepare fake parent/.style={
    tempdima/.option=y,
  },
  prepare fake parent/.style={
    y/.register=tempdima, y+=10pt,
    coordinate, edge label={}, no edge,
    tikz={\node at ()[anchor=south,xshift=\forestregister{folder indent}]{\vdots};},
    typeset node,
  },
  draw part of tree/.style 2 args={
    for nodewalk={#1,tempcounta/.option=id}{},
    for nodewalk={#2,tempcountb/.option=id}{},
    draw@part@of@tree/.process=RR{tempcounta}{tempcountb},
  },
  draw@part@of@tree/.style 2 args={
    draw tree processing order/.nodewalk style={
      id=#1,
      ancestors,
      fake={id=#1},
      do until={id()==#2}{next node},
    },
    for root'={
      post draw tree hook/.style={},
      for id={#2}{
        if nodewalk valid={next node}{
          post draw tree hook/.append style={
            to be continued={id=#1}{id=#2},
          },
        }{}
      },
      prepare fake ancestors={id=#1},
      draw tree,
    },
  },
  declare count register=current first node,
  draw linear split tree/.style={
    draw tree method/.append style={post draw tree hook},
    begin draw/.append code={[baseline=(current bounding box.north)]},
    draw tree stage/.style={},
    for root'={current first node/.option=id},
    before typesetting nodes/.process=Ow{id}{
      draw tree stage/.append style={
        draw part of tree={id/.register=current first node}{root',last leaf},
      },
    },
  },
  split here/.style={
    draw tree stage/.append style/.process=ROw2{current first node}{id}{
      draw part of tree={id=##1}{id=##2,previous node},
    },
    current first node/.option=id,
  },
}

\begin{document}
\begin{forest}
  pic dir tree,
  for descendants={% folder icons by default; override using file for file icons
    directory,
  },
  draw linear split tree,
  to be continued/.style 2 args={
    dots below={#2},
    arrow to next tree,
  },
  [
    [OpenFOAM,
      [system
        [\fname{controlDict.sim}{}, file]
        [\fname{controlDict.moveDyn}{}, file]
        [\fname{controlDict}{}, file]
        [\fname{createPathDict.ami}{}, file]
        [\fname{decomposeParDict}{}, file]
        [\fname{topoSetDict}{}, file]
        [\fname{...}{}, file]
      ]
      [Constant
        [polyMesh]
        [\fname{dynamicMeshDict}{}, file]
        [\fname{...}{}, file]
      ]
      [0, split here
        [\fname{pointDisplacement}{}, file]
        [\fname{U}{}, file]
        [\fname{...}{}, file]
      ]
      [MeshFactory
        [\fname{...}{}, file]
      ]
      [postProcessing]
      [log]
    ]
    [\fname{runMesh.sh}{}, file]
    [\fname{runMoveDyn.sh}{}, file]
    [\fname{runBatchJob.Job}{}, file]
  ]
\end{forest}
\end{document}

相关内容