如何使用 pgfplots 设置 tikzpicture 的精确宽度和高度?

如何使用 pgfplots 设置 tikzpicture 的精确宽度和高度?

pgfplotsaxis环境有选项widthheight来指定绘制图片的大致宽度和高度,但生成的框并不完全像指定的那么宽或那么高——它可能比要求的少或多,这取决于标题和刻度标签的内容,如下所示。

如何设置 pgfplots 绘制的 tikzpicture 的精确宽度或高度?

我理解,与scale only axiswidthheight选项可以设置轴箱,但我希望设置TikZ 图片主流观点(例如来自指定 tikzpicture 的宽度和高度) 是 TikZ 中没有内置的通用方法来将图片缩放到精确尺寸,因为使用 TikZ,您可以直接将图片精确地绘制到所需的尺寸,然后无需缩放。但是可以用 pgfplots 做到这一点吗?

文档参考:https://tikz.dev/pgfplots/reference-scaling#sec-21.1

/pgfplots/width={⟨dimen⟩}(最初为空)

将最终图片的宽度设置为 {⟨dimen⟩}。

任何非空维度(例如)width=5cm设置所需的目标宽度。任何 TeX 单位都可以接受(例如200pt5in)。

不幸的是,这似乎并不准确,如下面的 MWE 所示,以及下面的警告所解释的那样:

请注意,pgfplots 仅估计轴和刻度标签所需的大小。该估计假设轴框外的任何内容都有固定的空间量。这会导致最终图像可能比规定的尺寸略大或略小。但是,固定量始终相同;它设置为 45pt。这意味着具有相同目标尺寸的多张图片的轴框大小相同——即使描述的大小有所不同。

回答以下问题子图中的 Pgfplots 高度与相邻的包含图形不一致,我尝试使用height=\mylength+\baselineskip, xlabel=\empty, title=\empty,但它改变了绘制的图片(添加了不成比例的更多空白)并且仍然没有达到所要求的高度(如果与 ylabel 一起用于宽度,它会离要求的宽度更远)。

理想情况下,我希望这样做,而不必在将输入提交到文档之前迭代地搜索输入以最小化与预期的实际距离。

\documentclass{article}

\usepackage{amsmath}
\usepackage{pgfplots}
\usepackage{tikz}

\pgfplotsset{compat=1.18}

\newsavebox{\mybox}
\newlength{\mylength} \setlength{\mylength}{100pt}

\begin{document}

\section*{Width}

\begin{lrbox}{\mybox}
  \begin{tikzpicture}
    \begin{axis}[width=\mylength]
      \addplot[domain=0:2] {exp(-x)};
    \end{axis}
  \end{tikzpicture}
\end{lrbox}

\noindent expected=\the\mylength\smallskip\hrule width\mylength\relax
\vspace{\baselineskip}
\noindent actual=\the\wd\mybox\smallskip\hrule width\wd\mybox\relax
\vspace{\baselineskip}
\noindent\usebox{\mybox}

\section*{Height}

\begin{lrbox}{\mybox}
  \begin{tikzpicture}
    \begin{axis}[height=\mylength, xlabel={$\substack{X\\Y}$}, title={XXX}]
      \addplot[domain=0:2] {exp(-x)};
    \end{axis}
  \end{tikzpicture}
\end{lrbox}

expected=\the\mylength\ \vrule height\mylength\relax\quad
actual=\the\ht\mybox\ \vrule height\ht\mybox\relax\quad
\usebox{\mybox}

\end{document}

最小工作示例

答案1

首先,使用 可以获得更一致的结果scale only axis,它完全忽略了标签。这是最重要的部分。

其次,您可以使用\pgfresetboundingbox\path忽略实际尺寸并替换为所需尺寸。

在以下示例中,蓝色矩形是 pgfplots 生成的边界框,红色矩形是将要使用的边界框。此方法是 groupplot 的替代方法。

\documentclass[border=10pt]{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.18}

\newlength{\mylength} \setlength{\mylength}{100pt}

\begin{document}

  \begin{tikzpicture}
    \begin{axis}[scale only axis, width=0.8\mylength, height=0.8\mylength]
      \addplot[domain=0:2] {exp(-x)};
    \end{axis}
    \draw[blue] (current bounding box.south west) rectangle (current bounding box.north east);
    \pgfresetboundingbox
    \path (-0.18\mylength,-0.18\mylength) (0.82\mylength, 0.82\mylength);
    \draw[red] (current bounding box.south west) rectangle (current bounding box.north east);
  \end{tikzpicture}

\end{document}

演示

答案2

可能有更好的方法,但如果您需要精确的尺寸,您可以进行插值(我认为这可以以自动方式完成……)。虽然不完美,但……


\documentclass{article}
\usepackage{a4wide}
\usepackage{amsmath}
\usepackage{pgfplots}
\pgfplotsset{compat=1.18}

\newsavebox{\mybox}
\newlength{\mylength} \setlength{\mylength}{100pt}
\newlength{\resone}
\newlength{\restwo}
\newlength{\offset}
\newlength{\desired}

\newcommand{\mydo}{%

\begin{lrbox}{\mybox}
  \begin{tikzpicture}
    \begin{axis}[width=\mylength, ylabel={$\substack{X\\Y}$}, title={XXX}]
      \addplot[domain=0:2] {exp(-x)};
    \end{axis}
  \end{tikzpicture}
\end{lrbox}

\noindent expected=\the\mylength\smallskip\hrule width\mylength\relax
\vspace{\baselineskip}
\noindent actual=\the\wd\mybox\smallskip\hrule width\wd\mybox\relax
\vspace{\baselineskip}
\noindent\usebox{\mybox}
}

\begin{document}

\section*{Width}

\mydo

\setlength{\resone}{\wd\mybox}

\setlength{\mylength}{200pt}

\mydo

\setlength{\restwo}{\wd\mybox}

\begin{align*}
    w_1 &= \the\resone = a + b\cdot\mathrm{100pt} \\
    w_2 &= \the\resone = a + b\cdot\mathrm{200pt}
\end{align*}

\pgfmathsetlength{\offset}{-\restwo+2*\resone}
\pgfmathsetmacro{\coeff}{(\restwo-\resone)/100pt}
Coefficient $a$ is \pgfmathprintnumber[precision=4]{\coeff}, $a$  is \the\offset

\pgfmathsetlength{\desired}{(100pt-\offset)/\coeff}
To have 100pt, you need \the\desired

\bigskip

\setlength{\mylength}{\desired}
\mydo


\end{document}

在此处输入图片描述

答案3

我们ticklabel style={draw=red},可以看到标签是具有inner sep和的节点outer sep

在此处输入图片描述

  • 在 pgfmanual 的文档中

/pgf/inner sep=(dimension)(无默认值,最初为 .3333em)

/pgf/outer sep=(dimension) 或 “auto” (无默认值)

  • 获取数字的高度

    \newlength{\hdigit} \settoheight{\hdigit}{9}

  • 并将其line width除以 2 (观察结果为line width=10pt

如果我们希望带有标签的图表的高度为 100pt,则必须从图表中删除这些尺寸。

\documentclass{article}
%https://tex.stackexchange.com/questions/699336/how-to-set-the-exact-width-and-height-of-tikzpicture-with-pgfplots
\usepackage{pgfplots}
\pgfplotsset{compat=1.18}
\usepackage{showframe}
\newsavebox{\mybox}
\newlength{\mylength} \setlength{\mylength}{100pt}

\newlength{\hdigit}
\settoheight{\hdigit}{9}
\newlength{\wnumber}
\settowidth{\wnumber}{0.8}
%
\newlength{\decaly}
\setlength{\decaly}{0.6666em}% inner sep x 2
\addtolength{\decaly}{0pt}%  outer sep x 1
\addtolength{\decaly}{\hdigit}% text height
\addtolength{\decaly}{0.2pt}% line width/2
%
\newlength{\decalx}
\setlength{\decalx}{0.6666em}% inner sep x 2
\addtolength{\decalx}{0pt}%  outer sep x 1
\addtolength{\decalx}{\wnumber}% width number 0.8
\addtolength{\decalx}{0.2pt}% line width
\begin{document}

\section{height}
\begin{lrbox}{\mybox}
  \begin{tikzpicture}
    \begin{axis}[
      line width=0.4pt,
      scale only axis,
      height=\mylength-\decaly,
      ticklabel style={
        %draw=red,
        outer sep=0pt,
        },
      ]    
      \addplot[domain=0:2] {exp(-x)};
    \end{axis}
\end{tikzpicture}
\end{lrbox}
actual=\the\ht\mybox

\usebox{\mybox}
\hspace{1pt}\rule{1pt}{100pt}

\section{width}
\begin{lrbox}{\mybox}
  \begin{tikzpicture}
    \begin{axis}[
      line width=0.4pt,
      scale only axis,
      width=\mylength-\decalx,
      ticklabel style={
        %draw=red,
        outer sep=0pt,
        },
      ]    
      \addplot[domain=0:2] {exp(-x)};
    \end{axis}
  \end{tikzpicture}
\end{lrbox}
actual=\the\wd\mybox

\noindent
\usebox{\mybox}
\end{document}

在此处输入图片描述

相关内容