[Tex/LaTex] TikZ: Brace around nodes

bracesdecorationstikz-pgftikz-styles

So I want to put a horizontal brace around a part of the tikzpicture.

Alternate approaches I found but don't seem to apply too well here:

In both cases, there already is a node at the right-most (left-most) position,
allowing for easy calculation of the brace-endpoints via -| or equivalent.

But there's not always such a node already; for example, when trying to put braces of part of a tree (see MWE).
I'm remotely aware that technically I could probably use calc and compute minimal/maximal x in custom code,
but I expect that there's a simpler way I don't know about.

\documentclass[tikz]{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations.pathreplacing,positioning}
\begin{document}
\tikzstyle{edge} =[draw,thick,-latex]
\begin{tikzpicture}[node distance=0.8cm]
% Root trunk
\node (a1) at (0,0) {Sometimes};
\node[below=of a1] (a2) {One};
\path[edge] (a1) -- (a2);
\node[below=of a2] (a99) {Thing};
\path[edge] (a2) -- (a99);
% Right leaf
\node[below right=0.8cm and 0.5cm of a99] (r1) {Goes};
\path[edge] (a99) -- (r1);
\node[below=of r1] (r2) {On};
\path[edge] (r1) -- (r2);
\node[below=of r2] (r3) {And};
\path[edge] (r2) -- (r3);
\node[below=of r3,rectangle,draw] (r99) {On};
\path[edge] (r3) -- (r99);
% Left leaf
\node[below left=0.8cm and 0.5cm of a99] (l1) {Leads};
\path[edge] (a99) -- (l1);
\node[below=of l1,align=center] (l2) {In wide, verbose, even \\ multi-lined ways};
\path[edge] (l1) -- (l2);
\node[below=of l2,rectangle,draw] (l99) {To another};
\path[edge] (l2) -- (l99);
% Mapping to (sub)sections
\draw[decorate,decoration={brace,amplitude=10pt,mirror}]
(a1.north west) -- (a99.south west) node [midway,xshift=-0.4cm,left] {Section foo};
\draw[decorate,decoration={brace,amplitude=10pt,mirror}]
(l1.north west) -- (l99.south west) node [midway,xshift=-0.4cm,left] {Section bar};
\draw[decorate,decoration={brace,amplitude=10pt}]
(r1.north east) -- (r99.south east) node [midway,xshift=0.4cm,right] {Section baz};
\end{tikzpicture}
\end{document}

Rendered MWE

I'd like to have a more sensible position of the braces, specifically:

  • Don't get so close to the rectangles of the leafs by moving further away.
  • No intersection with wide "internal" nodes, by moving the whole brace away.
  • Braces should be vertical, but slightly sloped might look good, too.

Here's what it would look like with brace-end-points calculated by manually-chosen -| and |-:

Positive example with exactly-vertical braces

So how do I get a vertical brace at a good position,
without doing manual work?
Or more specifically, assuming this is a good idea: How do I get an extreme corner (e.g. right-most and top-most) of a subset of nodes?

(Also: general suggestions and recommendations about my TeX and post style are very welcome!)

Best Answer

I would use one of the packages or libraries designed for drawing trees. Forest is my favourite because it means you don't have to figure out the positioning manually: the package automatically figures out how much space to allow for the nodes and places them accordingly. It is also very powerful and flexible, allowing substantial customisation and automation with very concise tree specifications.

Here's a version using fit to. To specify the curl's span, we pass a node walk as the argument. The label goes in curl label. mirror curl triggers mirroring.

\documentclass[border=10pt]{standalone}
\usepackage{forest}
\usetikzlibrary{decorations.pathreplacing,arrows.meta}
\begin{document}
\forestset{
  declare toks={curl label}{},
  declare toks={curl mirror}{},
  mirror curl/.style={curl mirror=mirror},
  curl/.style n args=5{
    tikz+={
      \node (n) [fit to=#1, inner sep=0pt] {};
      \draw [decorate, decoration={brace, amplitude=10pt, #3}] (n.north #2) -- (n.south #2) node [midway, #4] {#5};
    }
  },
  curl left/.style={
    delay={
      curl/.process={OOw2 {curl label}{curl mirror}{ {#1}{west}{##2}{left, xshift=-10pt}{##1}}  }
    },
  },
  curl right/.style={
    delay={
      curl/.process={OOw2 {curl label}{curl mirror}{ {#1}{east}{##2}{right, xshift=10pt}{##1}}  }
    },
  },
}
\begin{forest}
  for tree={
    edge+={-Latex,thick},
    align=center,
  },
  where n children=0{draw}{},
  [Sometimes
    [One
      [Thing, curl left=cur, curl label=Section foo, mirror curl
        [Leads, curl left=tree, curl label=Section bar, mirror curl
          [{In wide, verbose, even\\multi-lined ways}
            [To Another]
          ]
        ]
        [Goes, curl right=tree, curl label=Section baz
          [On
            [And
              [On]
            ]
          ]
        ]
      ]
    ]
  ]
\end{forest}
\end{document}

Forest solution

Or you can specify the start and end coordinates for the span:

\documentclass[border=10pt]{standalone}
\usepackage{forest}
\usetikzlibrary{decorations.pathreplacing,arrows.meta}
\begin{document}
\forestset{
  declare toks={curl label}{},
  declare toks={curl mirror}{},
  mirror curl/.style={curl mirror=mirror},
  curl/.style n args=5{
    tikz+={
      \draw [decorate, decoration={brace, amplitude=10pt, #3}] #1 -- #2 node [midway, #4] {#5};
    }
  },
  curl left/.style args={from #1 to #2}{
    delay={
      curl/.process={OOw2 {curl label}{curl mirror}{ {#1}{#2}{##2}{left, xshift=-10pt}{##1}}  }
    },
  },
  curl right/.style args={from #1 to #2}{
    delay={
      curl/.process={OOw2 {curl label}{curl mirror}{ {#1}{#2}{##2}{right, xshift=10pt}{##1}}  }
    },
  },
}
\begin{forest}
  for tree={
    edge+={-Latex,thick},
    align=center,
  },
  where n children=0{draw}{},
  [Sometimes
    [One
      [Thing, curl left=from (.south -| !r.west) to (!r.north west), curl label=Section foo
        [Leads
          [{In wide, verbose, even\\multi-lined ways}, curl left=from (!u.north -| .west) to (!1.south -| .west), curl label=Section bar, mirror curl
            [To Another]
          ]
        ]
        [Goes, curl right=from (.north east) to (.east |- !F.south), curl label=Section baz
          [On
            [And
              [On]
            ]
          ]
        ]
      ]
    ]
  ]
\end{forest}    
\end{document}

Note that the ( -| ) coordinate specifications could be used with your original code, too. This is an easy way to make sure your curly brackets are aligned as you wish.

If you have a subset of nodes, a, b, c, say, then (<widest node>.west |- <topmost node>.north) will give the top left corner of a tight box just big enough to contain them, (<widest node>.east |- <bottommost node>.south) will give the bottom right corner and so on.