11

I often find myself in the following situation: A tikzpicture of mine consists of a main area and some outer material like text labels which are hardly symmetric around the main area. If such a picture is centered the main area is not in the center which often looks not very nice.

Not nicely centered

I would like to state which (rectangle) area of the picture should be placed in the center. This should be best possible with either a macro or an option (TikZ setting) where I can state the lower-left and upper-right coordinates of the main area.

Nicely centered

I know I could use use as bounding box when drawing the main area so that everything is not taken as part of the official picture, but this is often not useful. If the picture is converted to a PDF image using preview or standalone the outer areas would be clipped. Also the main area might be quite complicated and not easily put into a single scope.

So my idea is to have some code which measure the existing bounding box around the given main area and expands the bounding box by adding some white space so that this area is in the center. Horizontal centering is the main target, but vertical centering might also be possible.

An example would be:

\documentclass{article}

\usepackage{tikz}

\begin{document}

\hrule

\begin{figure}
    \centering
\begin{tikzpicture}[<some key>={1,0}{6,5}]
 \draw (1,0) rectangle (6,5);
 \draw (1,0) -- (6,5);
 \node [left] at (0,2.5) {Text};
 \orsomemacro{1,0}{6,5}
\end{tikzpicture}
\caption{Caption}
\end{figure}

\end{document}

I personally would prefer a TikZ key/style, but an alternative macro provided in addition would be a bonus. Special care must be taken because the coordinates are not yet defined at the beginning of the picture. The coordinate argument should allow any TikZ coordinate including calc expressions with internal ( ). The coordinates could be given using { } instead of ( ) to simply the parsing. However, normal TikZ syntax is of course preferred.

I (of course) already coded something like this and will post it as an answer after a while. I'm happy to see other solutions and will be happy to give away a bounty for the best one.

4
  • 1
    This reminds me of Fermat's correspondence with Pascal. He would think of problems, solve them, then send the problems to Pascal as challenges, then send him the solutions. I'm not sure I could be better than you at this! May 5, 2011 at 11:51
  • I think to match TikZ style it should be a path operation like \useasboundingbox and \clip May 5, 2011 at 12:01
  • @Matthew: Yes, a path operation might also be possible. Basically just the bounding box of that path is required in the end. May 5, 2011 at 12:58
  • Great question, I was just wondering this!
    – Otis
    May 17, 2012 at 16:37

2 Answers 2

5

Here my first attempt. It defines a xcenter around={lower left}{upper right} key which should be used in the optional argument of the tikzpicture, e.g. \begin{tikzpicture}[xcenter around={1,0}{6,5}].

\usetikzlibrary{calc}

\tikzset{xcenter around/.style 2 args={execute at end picture={%
  \useasboundingbox let \p0 = (current bounding box.south west), \p1 = (current bounding box.north east),
                        \p2 = (#1), \p3 = (#2)
                    in
        ({min(\x2 + \x3 - \x1,\x0)},\y0) rectangle ({max(\x3 + \x2 - \x0,\x1)},\y1);
}}}

I'm working on an extended version which adds more functionality.

6
  • The problem with this solution is that the coordinate has to be specified at the start and so can't be defined using a node. Incidentally, why do you want to specify a rectangle to centre around rather than just a point? May 6, 2011 at 18:35
  • @Andrew: No, you are mistaken. Because of the execute at end picture you can use later defined nodes. Normally I have rectangle windows like a plot window with sounding labels and I know the min and max values which make up the rectangle, but I would have to calculate the center point from them first. A center point can be easily supported by setting both \p2 and \p3 to it. It is included in my extended version which I currently finalize. May 6, 2011 at 18:42
  • Ah, yes, my mistake. I got confused between when you have to specify it (which is at the start) and when the coordinate is processed. I guess the most flexible would be to accept one or two arguments and adjust accordingly. May 6, 2011 at 19:08
  • @Andrew: Matthews idea to allow to specify a full path is also interesting. May 6, 2011 at 19:22
  • I feel that you're making it overly complicated! Thinking about how I might actually use this, I'd expect to centre at a point. I could easily feed that point in as something more complicated (such as the centre of a rectangle or of the bounding box of a path) using the calc library, but the centring routine itself should just take a point. May 6, 2011 at 20:51
4

Okay, here's my first effort

\documentclass{article}
\thispagestyle{empty}
\usepackage{tikz}

\pgfkeys{/tikz/.cd,
  centre picture/.style={
    execute at begin picture={\resetbb},
    execute at end picture={\enlargebb}
  },
  centre picture at/.code={\global\def\centrecoords{#1}}
}

\newcommand{\resetbb}{%
  \global\let\centrecoords\relax
  \global\let\pictcx\relax
  \global\let\pictcy\relax
}
\resetbb


\newcommand{\enlargebb}{%
  \path (current bounding box.north west);
  \pgfgetlastxy{\bbw}{\bbn}
  \path (current bounding box.south east);
  \pgfgetlastxy{\bbe}{\bbs}
  \ifx\centrecoords\relax
  \else
  \path \centrecoords;
  \pgfgetlastxy{\pictcx}{\pictcy}
  \fi
  \pgfmathsetmacro{\bbww}{\pictcx + max(\bbe - \pictcx,\pictcx - \bbw)}
  \pgfmathsetmacro{\bbee}{\pictcx - max(\bbe - \pictcx,\pictcx - \bbw)}
  \pgfmathsetmacro{\bbnn}{\pictcy + max(\bbn - \pictcy,\pictcy - \bbs)}
  \pgfmathsetmacro{\bbss}{\pictcy - max(\bbn - \pictcy,\pictcy - \bbs)}
\fill (\pictcx, \pictcy) circle[radius=2pt];
\draw (\bbww pt,\bbnn pt) rectangle (\bbee pt,\bbss pt);
  \path (\bbww pt,\bbnn pt) rectangle (\bbee pt,\bbss pt);
}

\def\centrepictureat#1;{%
\path #1;
\pgfgetlastxy{\pictcx}{\pictcy}
\xdef\pictcx{\pictcx}
\xdef\pictcy{\pictcy}
}

\begin{document}
\hrule

\begin{figure}
    \centering
\begin{tikzpicture}[centre picture]
 \draw (1,0) rectangle (6,5);
 \draw (1,0) -- (6,5);
 \node [left] at (0,2.5) {Text};
%\node[centre picture at={(1,1)}] {};
\centrepictureat (2,0);
\end{tikzpicture}
\caption{Caption}
\end{figure}

\end{document}

Result (with bounding box and centre point drawn for effect; obvious modification to remove them)

centred image

It works by saving the coordinate as given and then at the end of the picture it computes the rectangle centred at the given coordinate which is just big enough to contain the bounding box. There are two interfaces: a key which can be specified on a path (centre picture at=coord) and a command which is "path-like" \centrepictureat coord;).

It could do with some polishing, I'm sure (particularly with the two interfaces, I added the second as a whim; and I gave no thought to the macro names). And for some reason the current version of the standalone package doesn't like it!

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .