5

I have a LaTeX document with several trees made with the forest package and would like to define a decoration style for these trees in a way that avoids code duplication. In other words, I would like to put the tree decoration settings (i.e: for tree= {...}) in a unique place—it would be more readable and easier to modify this way. My code looks like this:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{arrows.meta,angles,quotes}
\colorlet{linecol}{black!75}
\tikzset{
  my rounded corners/.append style={rounded corners=2pt},
}
\usepackage{forest}

\begin{document}

% ================= FIRST TREE
\begin{forest}
  for tree={
    line width=1pt,
    if={level()<2}{
      my rounded corners,
      draw=linecol,
    }{},
    edge={color=linecol, >={Triangle[]}, ->},
    if level=0{%
      l sep+=1.5cm,
      align=center,
      parent anchor=south,
      tikz={
        \path (!1.child anchor) coordinate (A) -- () coordinate (B) -- (!l.child anchor) coordinate (C) pic [draw, angle radius=20mm, every node/.append style={fill=white}, "based on"] {angle};
      },
    }{%
      if level=1{%
        parent anchor=south west,
        child anchor=north,
        tier=parting ways,
        align=center,
        font=\bfseries,
        for descendants={
          child anchor=west,
          parent anchor=west,
          anchor=west,
          align=left,
        },
      }{
        if level=2{
          shape=coordinate,
          no edge,
          grow'=0,
          calign with current edge,
          xshift=20pt,
          for descendants={
            parent anchor=south west,
            l sep+=-20pt
          },
          for children={
            edge path={
              \noexpand\path[\forestoption{edge}] (!to tier=parting ways.parent anchor) |- (.child anchor)\forestoption{edge label};
            },
            font=\bfseries,
            for descendants={
              no edge,
            },
          },
        }{},
      },
    }%
  },
  [Concept
    [sub-concept
      [
        [method
        [submethod]
        ]
        [method
        [submethod\\details]
        ]
        [method
        ]
      ]
    ]
    [sub-concept
      [
        [method
        [submethod]
        ]
        [method
        [submethod\\details]
        ]
        [method
        ]
      ]
    ]
    [sub-concept
      [
        [method
        [submethod]
        ]
        [method
        [submethod\\details]
        ]
        [method
        ]
      ]
    ]
  ]
\end{forest}

%================= SECOND TREE
\begin{forest}
  for tree={
    line width=1pt,
    if={level()<2}{
      my rounded corners,
      draw=linecol,
    }{},
    edge={color=linecol, >={Triangle[]}, ->},
    if level=0{%
      l sep+=1.5cm,
      align=center,
      parent anchor=south,
      tikz={
        \path (!1.child anchor) coordinate (A) -- () coordinate (B) -- (!l.child anchor) coordinate (C) pic [draw, angle radius=20mm, every node/.append style={fill=white}, "based on"] {angle};
      },
    }{%
      if level=1{%
        parent anchor=south west,
        child anchor=north,
        tier=parting ways,
        align=center,
        font=\bfseries,
        for descendants={
          child anchor=west,
          parent anchor=west,
          anchor=west,
          align=left,
        },
      }{
        if level=2{
          shape=coordinate,
          no edge,
          grow'=0,
          calign with current edge,
          xshift=20pt,
          for descendants={
            parent anchor=south west,
            l sep+=-20pt
          },
          for children={
            edge path={
              \noexpand\path[\forestoption{edge}] (!to tier=parting ways.parent anchor) |- (.child anchor)\forestoption{edge label};
            },
            font=\bfseries,
            for descendants={
              no edge,
            },
          },
        }{},
      },
    }%
  },
  [Concept
    [sub-concept
      [
        [method
        [submethod]
        ]
        [method
        [submethod\\details]
        ]
        [method
        ]
      ]
    ]
    [sub-concept
      [
        [method
        [submethod]
        ]
        [method
        [submethod\\details]
        ]
        [method
        ]
      ]
    ]
    [sub-concept
      [
        [method
        [submethod]
        ]
        [method
        [submethod\\details]
        ]
        [method
        ]
      ]
    ]
  ]
\end{forest}
\end{document}

Is there a way to achieve such a thing ? Thanks.

7

I know two ways to do this. The first way puts the customizations inside a style, therefore they will be applied to every forest environment for which you specify this style; the second way applies the given customizations to all forest environments inside a given TeX group—possibly the whole document.

Customizations applied using a style

You can define a style with \forestset (the style is named my forest in the following example) and specify its name in the particular forest environments where you want it to be active:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{arrows.meta,angles,quotes}
\colorlet{linecol}{black!75}
\tikzset{
  my rounded corners/.append style={rounded corners=2pt},
}
\usepackage{forest}

\forestset{
  my forest/.style={
    for tree={
    line width=1pt,
    if={level()<2}{
      my rounded corners,
      draw=linecol,
    }{},
    edge={color=linecol, >={Triangle[]}, ->},
    if level=0{%
      l sep+=1.5cm,
      align=center,
      parent anchor=south,
      tikz={
        \path (!1.child anchor) coordinate (A) -- () coordinate (B) -- (!l.child anchor) coordinate (C) pic [draw, angle radius=20mm, every node/.append style={fill=white}, "based on"] {angle};
      },
    }{%
      if level=1{%
        parent anchor=south west,
        child anchor=north,
        tier=parting ways,
        align=center,
        font=\bfseries,
        for descendants={
          child anchor=west,
          parent anchor=west,
          anchor=west,
          align=left,
        },
      }{
        if level=2{
          shape=coordinate,
          no edge,
          grow'=0,
          calign with current edge,
          xshift=20pt,
          for descendants={
            parent anchor=south west,
            l sep+=-20pt
          },
          for children={
            edge path={
              \noexpand\path[\forestoption{edge}] (!to tier=parting ways.parent anchor) |- (.child anchor)\forestoption{edge label};
            },
            font=\bfseries,
            for descendants={
              no edge,
            },
          },
        }{},
      },
    }%
  }}}

\begin{document}

\section{First tree}

\begin{forest}
  my forest
  [Concept
    [sub-concept
      [
        [method
        [submethod]
        ]
        [method
        [submethod\\details]
        ]
        [method
        ]
      ]
    ]
    [sub-concept
      [
        [method
        [submethod]
        ]
        [method
        [submethod\\details]
        ]
        [method
        ]
      ]
    ]
    [sub-concept
      [
        [method
        [submethod]
        ]
        [method
        [submethod\\details]
        ]
        [method
        ]
      ]
    ]
  ]
\end{forest}

\section{Second tree}

\begin{forest}
  my forest
  [Concept
    [sub-concept
      [
        [method
        [submethod]
        ]
        [method
        [submethod\\details]
        ]
        [method
        ]
      ]
    ]
    [sub-concept
      [
        [method
        [submethod]
        ]
        [method
        [submethod\\details]
        ]
        [method
        ]
      ]
    ]
    [sub-concept
      [
        [method
        [submethod]
        ]
        [method
        [submethod\\details]
        ]
        [method
        ]
      ]
    ]
  ]
\end{forest}
\end{document}

Screenshot

Customizations applied to all forest environments within a given TeX group

Using

\forestset{
  default preamble={...}
}

you can apply the ... settings to all forest environments in the current TeX group. If used in the preamble, this will be applied to all forest environments in the document.

Example with the settings in the preamble:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{arrows.meta,angles,quotes}
\colorlet{linecol}{black!75}
\tikzset{
  my rounded corners/.append style={rounded corners=2pt},
}
\usepackage{forest}

\forestset{
  default preamble={
    for tree={
    line width=1pt,
    if={level()<2}{
      my rounded corners,
      draw=linecol,
    }{},
    edge={color=linecol, >={Triangle[]}, ->},
    if level=0{%
      l sep+=1.5cm,
      align=center,
      parent anchor=south,
      tikz={
        \path (!1.child anchor) coordinate (A) -- () coordinate (B) -- (!l.child anchor) coordinate (C) pic [draw, angle radius=20mm, every node/.append style={fill=white}, "based on"] {angle};
      },
    }{%
      if level=1{%
        parent anchor=south west,
        child anchor=north,
        tier=parting ways,
        align=center,
        font=\bfseries,
        for descendants={
          child anchor=west,
          parent anchor=west,
          anchor=west,
          align=left,
        },
      }{
        if level=2{
          shape=coordinate,
          no edge,
          grow'=0,
          calign with current edge,
          xshift=20pt,
          for descendants={
            parent anchor=south west,
            l sep+=-20pt
          },
          for children={
            edge path={
              \noexpand\path[\forestoption{edge}] (!to tier=parting ways.parent anchor) |- (.child anchor)\forestoption{edge label};
            },
            font=\bfseries,
            for descendants={
              no edge,
            },
          },
        }{},
      },
    }%
  }}}

\begin{document}

\section{First tree}

\begin{forest}
  [Concept
    [sub-concept
      [
        [method
        [submethod]
        ]
        [method
        [submethod\\details]
        ]
        [method
        ]
      ]
    ]
    [sub-concept
      [
        [method
        [submethod]
        ]
        [method
        [submethod\\details]
        ]
        [method
        ]
      ]
    ]
    [sub-concept
      [
        [method
        [submethod]
        ]
        [method
        [submethod\\details]
        ]
        [method
        ]
      ]
    ]
  ]
\end{forest}

\section{Second tree}

\begin{forest}
  [Concept
    [sub-concept
      [
        [method
        [submethod]
        ]
        [method
        [submethod\\details]
        ]
        [method
        ]
      ]
    ]
    [sub-concept
      [
        [method
        [submethod]
        ]
        [method
        [submethod\\details]
        ]
        [method
        ]
      ]
    ]
    [sub-concept
      [
        [method
        [submethod]
        ]
        [method
        [submethod\\details]
        ]
        [method
        ]
      ]
    ]
  ]
\end{forest}
\end{document}

The output is identical as in the previous example.

Example where we do the settings inside a group, output one tree from this group (thus using the custom settings), then leave the group and output a second tree which therefore uses the default settings:

\documentclass{article}
\usepackage[landscape]{geometry} % the tree in section 2 is rather wide
\usepackage{tikz}
\usetikzlibrary{arrows.meta,angles,quotes}
\colorlet{linecol}{black!75}
\tikzset{
  my rounded corners/.append style={rounded corners=2pt},
}
\usepackage{forest}

\begin{document}

\section{First tree}

{% \forestset settings local to the current TeX group
\forestset{
  default preamble={
    for tree={
    line width=1pt,
    if={level()<2}{
      my rounded corners,
      draw=linecol,
    }{},
    edge={color=linecol, >={Triangle[]}, ->},
    if level=0{%
      l sep+=1.5cm,
      align=center,
      parent anchor=south,
      tikz={
        \path (!1.child anchor) coordinate (A) -- () coordinate (B) -- (!l.child anchor) coordinate (C) pic [draw, angle radius=20mm, every node/.append style={fill=white}, "based on"] {angle};
      },
    }{%
      if level=1{%
        parent anchor=south west,
        child anchor=north,
        tier=parting ways,
        align=center,
        font=\bfseries,
        for descendants={
          child anchor=west,
          parent anchor=west,
          anchor=west,
          align=left,
        },
      }{
        if level=2{
          shape=coordinate,
          no edge,
          grow'=0,
          calign with current edge,
          xshift=20pt,
          for descendants={
            parent anchor=south west,
            l sep+=-20pt
          },
          for children={
            edge path={
              \noexpand\path[\forestoption{edge}] (!to tier=parting ways.parent anchor) |- (.child anchor)\forestoption{edge label};
            },
            font=\bfseries,
            for descendants={
              no edge,
            },
          },
        }{},
      },
    }%
  }}}
%
% Tree in the same TeX group: it uses the settings we just set
\begin{forest}
  [Concept
    [sub-concept
      [
        [method
        [submethod]
        ]
        [method
        [submethod\\details]
        ]
        [method
        ]
      ]
    ]
    [sub-concept
      [
        [method
        [submethod]
        ]
        [method
        [submethod\\details]
        ]
        [method
        ]
      ]
    ]
    [sub-concept
      [
        [method
        [submethod]
        ]
        [method
        [submethod\\details]
        ]
        [method
        ]
      ]
    ]
  ]
\end{forest}

}% Leave the group where we used custom \forestset{default preamble={...}}
 % settings.

\section{Second tree}

% This tree uses the default settings.
\begin{forest}
  [Concept
    [sub-concept
      [
        [method
        [submethod]
        ]
        [method
        [submethod\\details]
        ]
        [method
        ]
      ]
    ]
    [sub-concept
      [
        [method
        [submethod]
        ]
        [method
        [submethod\\details]
        ]
        [method
        ]
      ]
    ]
    [sub-concept
      [
        [method
        [submethod]
        ]
        [method
        [submethod\\details]
        ]
        [method
        ]
      ]
    ]
  ]
\end{forest}
\end{document}

Screenshot

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.