The solutions so far work fine if the picture scales linarly with its scale factor. However, if there are text nodes that extend the bounding box or excessive line widths, this assumption doesn't hold.
Instead, I'll assume that the width of a picure follows an affine linear equation w = a * x + b
, where x
is the scaling factor and a
and b
are constants determined by the picture.
My idea is to measure the width of the picture at two different scaling factors and then compute the correct factor. The macro \affinescale
gets four parameters: The desired width, the two scaling factors to sample at and the code for the picture. Inside the code, \affinex
contains the scaling factor. The user can decide how the y axis should be scaled and this construct is not limited to tikzpictures.
The two sample points should be chosen such that they're reasonably close to the correct value because there can be nonlinearities in the scaling behavior of a picture, see my examples 3 and 4 below.
Code
\documentclass{article}
\usepackage{tikz}
\newdimen\affinex
\newcommand{\affinescale}[4]{{%
% #1 = desired width w
% #2 = \affinex for first measurement x1
% #3 = \affinex for second measurement x2
% #4 = code to draw picture
%
% take first measurement
\affinex=#2
\setbox0=\hbox{{\ignorespaces #4\unskip}}%
%\copy0
%\hbox{x1 = \the\affinex, w1 = \the\wd0}%
%
% take second measurement
\affinex=#3
\setbox1=\hbox{{\ignorespaces #4\unskip}}%
%\copy1
%\hbox{x2 = \the\affinex, w2 = \the\wd1}%
%
% calculate x from 2, w1, w2, x1, x2; system of equations to solve:
% w = a * x + b ; desired dimensions
% w1 = a * x1 + b ; measurement 1
% w2 = a * x2 + b ; measurement 2
\pgfmathparse{(\wd1 - \wd0) / ((#3) - (#2))}%
\let\a=\pgfmathresult
\pgfmathparse{(\wd0 - \a * (#2))}%
\let\b=\pgfmathresult
\pgfmathsetlength{\affinex}{((#1) - \b) / \a}%
%\hbox{a = \a, b = \b, x = \the\affinex}%
%
% finally, draw the picture
\ignorespaces #4\unskip%
}}
\setlength{\parindent}{0pt}
\begin{document}
Example 1: scale x and y by the same factor
\vrule width 5cm height 1pt depth 0pt
\affinescale{5cm}{2cm}{3cm}{
\begin{tikzpicture}[x=\affinex,y=\affinex]
\draw (2,0) node[below right] {(2,0)}
-- (1,2) node[above] {(1,2)}
-- (0,0) node[below left] {(0,0)}
-- cycle;
\end{tikzpicture}
}
Example 2: only scale x
\vrule width 10cm height 1pt depth 0pt
\affinescale{10cm}{0.9cm}{1.1cm}{
\begin{tikzpicture}[x=\affinex]
\draw[line width=10pt,black!20!white] (0,0) rectangle (10,1);
\draw (0,0) node[above right] {(0,0)} rectangle (10,1) node[below left] {(10,1)};
\node[right=5pt] at (10,.5) {node text};
\end{tikzpicture}
}
Example 3: node sticks out to the right
\vrule width 5cm height 1pt depth 0pt
\affinescale{5cm}{2cm}{3cm}{
\begin{tikzpicture}[x=\affinex]
\draw[dashed] (0,0) -- (1,0) node[midway,above right] {piecewise linear width};
\end{tikzpicture}
}
Example 4: due to scaling, node doesn't stick out anymore
\vrule width 10cm height 1pt depth 0pt
\affinescale{10cm}{8cm}{9cm}{
\begin{tikzpicture}[x=\affinex]
\draw[dashed] (0,0) -- (1,0) node[midway,above right] {piecewise linear width};
\end{tikzpicture}
}
\end{document}
Result

Maybe this could be extended to some kind of iteration scheme to always figure out the correct scaling factor, but I'm scared of the TeX wizardry that would entail. Also, it would be nice to save the scaling factor in the aux file to avoid typesetting the figure three times for every run of LaTeX.
rounded corners
, but does scaleplot
marker size. I believe in the end, "do not scale graphics" is sound advice.