5

I've created a block diagram and want to add a legend to it which should be positioned in the very top left corner.

I've setup a custom environment for the legend according to the answer to this question. Without the legend, this is my result: Without legend

However, when adding the legend at current bounding box.north west the whole thing gets shifted for some reason: enter image description here

This is the code:

\documentclass[tikz]{standalone}
  \usepackage{pgfplots}
  \usetikzlibrary{positioning}

  % setup the custom legend stuff
  % argument #1: any options
\makeatletter
\newenvironment{customlegend}[1][]{%
    \begingroup
    % inits/clears the lists (which might be populated from previous
    % axes):
    \pgfplots@init@cleared@structures
    \pgfplotsset{#1}%
}{%
    % draws the legend:
    \pgfplots@createlegend
    \endgroup
}%

% makes \addlegendimage available (typically only available within an
% axis environment):
\def\addlegendimage{\pgfplots@addlegendimage}
\makeatother

  \begin{document}
  \begin{tikzpicture}
    [
    node distance=50pt
    ]
    \newcommand{\nodesize}{12pt}
    \linespread{0.8}

    \node (S0) [circle,draw,inner sep=\nodesize,very thick]
    {$$\mbox{\Large $0$}$$};
    \node (S1) [circle,draw,inner sep=\nodesize, right =of S0 ,very thick] 
    {$$\mbox{\Large $1$}$$};
    \node(S2A) [circle,draw,inner sep=\nodesize, above right =of S1 ,very thick]
    {$$\mbox{\Large $2^a$}$$};
    \node(S2B) [circle,draw,inner sep=\nodesize, below right =of S1 ,very thick]
    {$$\mbox{\Large $2^b$}$$};
    \node (S3) [circle,draw,inner sep=\nodesize, below right =of S2A,very thick]
    {$$\mbox{\Large $3$}$$};
    \node (S4) [circle,draw,inner sep=\nodesize, right =of S3,very thick]
    {$$\mbox{\Large $4$}$$};

    \draw[->, gray] (S0.10) -- (S1.170);
    \draw[->, gray] (S1.190) -- (S0.350);
    \draw[->, gray] (S1.90) -- (S2A.180);
    \draw[->, gray] (S1.290)      --  (S2B.160);
    \draw[->, gray] (S2B.180)     --  (S1.270);
    \draw[->, gray] (S2A.260)     --  (S2B.100);
    \draw[->, gray] (S2B.80)      --  (S2A.280);
    \draw[->, gray] (S2A.0)       --  (S3.90);
    \draw[->, gray] (S2B.20)      --  (S3.250);
    \draw[->, gray] (S3.270)      --  (S2B.0);
    \draw[->, gray] (S4.190)    --  (S3.350);
    \draw[->, gray] (S0) to [in=80,out=30,looseness=4.5] (S0);
    \draw[->, gray] (S1) to [in=100,out=150,looseness=4.5] (S1);
    \draw[->, gray] (S2B) to [in=350,out=300,looseness=4.5] (S2B);
    \draw[->, gray] (S3) to [in=80,out=30,looseness=4.5] (S3);
    \draw[->, gray] (S1) to [in=280,out=260,looseness=2.3] (S3);
    \draw[->, gray] (S3) to [in=240,out=300,looseness=2.7] (S1);

    \draw[->] (S3.10)    edge  node[sloped, anchor=center, above,align=center] 
    {$t_m$ \\ $$\mbox{\tiny $\mathtt{[0,0,-,1]}$}$$} (S4.170);

    \draw[->] (S4) to [in=80,out=30,looseness=4.5] node [above,xshift=-7pt,yshift=2pt,align=center] {$t_{m+1}...t_{n-1}$ \\ $$\mbox{\tiny $\mathtt{[0,0,-,-]}$}$$} (S4);

    \draw[->, dashed]
    (S2A.200)  edge  node[sloped, anchor=center, below,align=center] {$t_n$ \\ $$\mbox{\tiny $\mathtt{[1,0,-,-]}$}$$}     (S1.70)
    (S3.110)      edge  node[sloped, anchor=center, below,align=center] {$t_m$ \\ $$\mbox{\tiny $\mathtt{[0,0,-,-]}$}$$}     (S2A.340)
    (S2A) to [in=80,out=30,looseness=4.5] node [right,xshift=2pt,align=center] {$t_{m+1}...t_{n-1}$ \\ $$\mbox{\tiny $\mathtt{[0,0,-,-]}$}$$} (S2A);

% the next few lines add the legend:
\begin{customlegend}[legend style={at={(current bounding box.north west)}}, anchor=north west, legend entries={\textbf{Transitions}, valid, invalid, corrected}]
    \addlegendimage{empty legend}
    \addlegendimage{black}
    \addlegendimage{dotted}
    \addlegendimage{dashed}
\end{customlegend}

\end{tikzpicture}
\end{document}

Why is adding the custom legend changing the bounding box and how can I position the legend at the top left corner of the first image? I know I could fiddle around with legend style={at={x,y)} but I feel like there should be a more elegant solution which actually lines up the legend with my diagram.

Dominic
  • 170

2 Answers2

5

The problem seems to be that you're setting the anchor of the legend outside the legend style, meaning that it doesn't actually apply to the legend. Move it inside the legend style, and it works as expected.

Unrelated: you can avoid some code repetition for example by the use of a scope environment as demonstrated below.

May I also ask why you're using the construct $$\mbox{$..$}$$? First of all, $$ ... $$ shouldn't be used in LaTeX at all (Why is \[ ... \] preferable to $$ ... $$?), and second you switch back to text mode with \mbox and then to inline math mode again with $.. $. Why not use just $ .. $ in this case?

\documentclass[tikz]{standalone}
  \usepackage{pgfplots}
  \usetikzlibrary{positioning}

  % setup the custom legend stuff
  % argument #1: any options
\makeatletter
\newenvironment{customlegend}[1][]{%
    \begingroup
    % inits/clears the lists (which might be populated from previous
    % axes):
    \pgfplots@init@cleared@structures
    \pgfplotsset{#1}%
}{%
    % draws the legend:
    \pgfplots@createlegend
    \endgroup
}%

% makes \addlegendimage available (typically only available within an
% axis environment):
\def\addlegendimage{\pgfplots@addlegendimage}
\makeatother

  \begin{document}
  \begin{tikzpicture}
    [
    node distance=50pt
    ]
    \newcommand{\nodesize}{12pt}
    \linespread{0.8}

    \begin{scope}[every node/.style={circle,draw,very thick,font=\Large,inner sep=\nodesize}]

    \node (S0) {$0$};
    \node (S1) [right=of S0] {$1$};
    \node(S2A) [above right =of S1] {$2^a$};
    \node(S2B) [below right =of S1] {$2^b$};
    \node (S3) [below right =of S2A] {$3$};
    \node (S4) [right =of S3] {$4$};
    \end{scope}


    \draw[->, gray] (S0.10) -- (S1.170);
    \draw[->, gray] (S1.190) -- (S0.350);
    \draw[->, gray] (S1.90) -- (S2A.180);
    \draw[->, gray] (S1.290)      --  (S2B.160);
    \draw[->, gray] (S2B.180)     --  (S1.270);
    \draw[->, gray] (S2A.260)     --  (S2B.100);
    \draw[->, gray] (S2B.80)      --  (S2A.280);
    \draw[->, gray] (S2A.0)       --  (S3.90);
    \draw[->, gray] (S2B.20)      --  (S3.250);
    \draw[->, gray] (S3.270)      --  (S2B.0);
    \draw[->, gray] (S4.190)    --  (S3.350);
    \draw[->, gray] (S0) to [in=80,out=30,looseness=4.5] (S0);
    \draw[->, gray] (S1) to [in=100,out=150,looseness=4.5] (S1);
    \draw[->, gray] (S2B) to [in=350,out=300,looseness=4.5] (S2B);
    \draw[->, gray] (S3) to [in=80,out=30,looseness=4.5] (S3);
    \draw[->, gray] (S1) to [in=280,out=260,looseness=2.3] (S3);
    \draw[->, gray] (S3) to [in=240,out=300,looseness=2.7] (S1);

    \draw[->] (S3.10)    edge  node[sloped, anchor=center, above,align=center] 
    {$t_m$ \\ \tiny $\mathtt{[0,0,-,1]}$} (S4.170);

    \draw[->] (S4) to [in=80,out=30,looseness=4.5] node [above,xshift=-7pt,yshift=2pt,align=center] {$t_{m+1}...t_{n-1}$ \\ \tiny $\mathtt{[0,0,-,-]}$} (S4);

    \draw[->, dashed]
    (S2A.200)  edge  node[sloped, anchor=center, below,align=center] {$t_n$ \\ \tiny $\mathtt{[1,0,-,-]}$}     (S1.70)
    (S3.110)      edge  node[sloped, anchor=center, below,align=center] {$t_m$ \\ \tiny $\mathtt{[0,0,-,-]}$}     (S2A.340)
    (S2A) to [in=80,out=30,looseness=4.5] node [right,xshift=2pt,align=center] {$t_{m+1}...t_{n-1}$ \\ \tiny $\mathtt{[0,0,-,-]}$} (S2A);

% the next few lines add the legend:
\begin{customlegend}[
 legend style={
    at={(current bounding box.north west)},
    anchor=north west % <-- inside the legend style
  },
 legend entries={\textbf{Transitions}, valid, invalid, corrected}
]
    \addlegendimage{empty legend}
    \addlegendimage{black}
    \addlegendimage{dotted}
    \addlegendimage{dashed}
\end{customlegend}

\end{tikzpicture}
\end{document}
Torbjørn T.
  • 206,688
  • Thanks, that was an easy solution and thanks for the scope suggestion. I just started using LaTeX a week ago. Regarding the ugly $$...$$ construct, I initially tried $...$ but this did not apply \tiny. I found this construct with \mbox somewhere on tex.stackexchange which allows me to change text size inside the math environment. – Dominic Mar 30 '20 at 14:52
  • @Dominic You can't use \tiny inside $ .. $, but if you place it before it changes the font size. See the code in my answer, where I changed $$\mbox{\tiny $ .. $}$$ to \tiny $ .. $. (In fact, if you did $$ \tiny a $$ then that wouldn't work either, as that would be using the font size macro in math mode, and they only work in text mode, I think.) – Torbjørn T. Mar 30 '20 at 14:59
  • I've seen the changes, thanks a lot. It's working perfectly fine now. – Dominic Mar 30 '20 at 15:01
3

Anchor of customlegend should be part of legend style:

\begin{customlegend}[legend style={at={(current bounding box.north west)}, anchor=north west},
                     legend entries={\textbf{Transitions}, valid, invalid, corrected}]

enter image description here

Off-topic: With using TikZ library quotes, defining nodes, edge and edge quotes styles your MWE code become quite shorter:

\documentclass[tikz, margin=3mm]{standalone}
\usepackage{pgfplots}
\usetikzlibrary{positioning,
                quotes}

  % setup the custom legend stuff
  % argument #1: any options
\makeatletter
\newenvironment{customlegend}[1][]{%
    \begingroup
    % inits/clears the lists (which might be populated from previous
    % axes):
    \pgfplots@init@cleared@structures
    \pgfplotsset{#1}%
}{%
    % draws the legend:
    \pgfplots@createlegend
    \endgroup
}%

% makes \addlegendimage available (typically only available within an
% axis environment):
\def\addlegendimage{\pgfplots@addlegendimage}
\makeatother

\begin{document}
\begin{tikzpicture}[auto,
    node distance = 50pt,
     state/.style = {circle, draw, very thick, inner sep=\nodesize, 
                    font=\Large},
every edge/.style = {draw=gray, semithick, ->},
every edge quotes/.style = {font=\small\linespread{0.6}\selectfont, align=center, sloped}
                    ]
    \newcommand{\nodesize}{12pt}
%
    \begin{scope}[nodes={state}]
\node (S0)                      {$0$};
\node (S1)  [right=of S0]       {$1$};
\node (S2A) [above right=of S1] {$2^a$};
\node (S2B) [below right=of S1] {$2^b$};
\node (S3)  [below right=of S2A]    {$3$};
\node (S4)  [right=of S3]       {$4$};
    \end{scope}
%
\draw   (S0.10)     edge (S1.170) 
        (S1.190)    edge (S0.350)
        (S1.90)     edge (S2A.180) 
        (S1.290)    edge (S2B.160) 
        (S2B.180)   edge (S1.270)
        (S2A.260)   edge (S2B.100)
        (S2B.80)    edge (S2A.280)
        (S2A.0)     edge (S3.90)
        (S2B.20)    edge (S3.250)
        (S3.270)    edge (S2B.0)
        (S4.190)    edge (S3.350)
        (S0)        edge[in=80, out=30, looseness=4.5] (S0)
        (S1)        edge[in=100,out=150,looseness=4.5] (S1)
        (S2B)       edge[in=350,out=300,looseness=4.5] (S2B)
        (S3)        edge[in=80, out=30, looseness=4.5] (S3)
        (S1)        edge[in=280,out=260,looseness=2.3] (S3)
        (S3)        edge[in=240,out=300,looseness=2.7] (S1);
\draw[dashed]
        (S3.10)     edge["$t_m$ \\ \tiny {$[0,0,-,1]$}"]                    (S4.170)
        (S4)        edge[in=80,out=30,looseness=4.5,
                         "$t_{m+1}\dots t_{n-1}$ \\ \tiny {$[0,0,-,-]$}"]   (S4)
        (S2A.200)   edge["$t_n$ \\ \tiny {$[1,0,-,-]$}" ']                  (S1.70)
        (S3.110)    edge["$t_m$ \\ \tiny {$[0,0,-,-]$}" ']                  (S2A.340)
        (S2A)       edge[in=80,out=30,looseness=4.5, 
                         "$t_{m+1}\dots t_{n-1}$ \\ \tiny {$[0,0,-,-]$}"]   (S2A);
% the next few lines add the legend:
\begin{customlegend}[legend style={at={(current bounding box.north west)}, anchor=north west},
                     legend entries={\textbf{Transitions}, valid, invalid, corrected}]
    \addlegendimage{empty legend}
    \addlegendimage{black}
    \addlegendimage{dotted}
    \addlegendimage{dashed}
\end{customlegend}
    \end{tikzpicture}
\end{document}
Zarko
  • 296,517