3

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!)

  • Welcome! It would be easier to typeset the tree using one of the methods for typesetting trees as such e.g. trees and/or the built-in stuff, tikz-qtree or forest (or the graph stuff for trees provided by TikZ). – cfr Jul 21 '17 at 13:57
  • Note that \tikzstyle is deprecated. – cfr Jul 21 '17 at 13:58
3

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.

  • Thanks, it seems that forest indeed makes it easier to layout the tree itself! However, one still has to manually determine the widest node here, the thing I wanted to made easier. And I don't yet see the advantage of defining the braces inline if one has to refer to two out of three nodes by hand anyway. – Mike Gebirge Jul 21 '17 at 14:50
  • @MikeGebirge You could pass a node walk instead. See above for an example. But you have to tell the programme where you want the decoration to start and finish somehow. How else can it know unless you specify the the first and last node you want covered? If there's a consistent pattern, obviously you could use that to automate it. But that is not obvious from just one example. – cfr Jul 21 '17 at 17:11
  • @MikeGebirge The inline specification is just because I'm too lazy to name them and keep track, and then to write everything out in full each time. – cfr Jul 21 '17 at 17:15

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.