有关的:TikZ 图片前的水平空间
考虑以下 MWE
\documentclass{article}
\usepackage{tikz}
\usepackage[showframe]{geometry}
\begin{document}
\noindent
\begin{tikzpicture}
\draw [red] (0pt,0pt) -- ++(\linewidth,0pt);
\node [anchor=south,font=\sffamily\footnotesize] at (current bounding box.north) {\textcolor{red}{\the\pgflinewidth} too wide};
\end{tikzpicture}
\noindent
\begin{tikzpicture}
\draw [red,line width=1pt] (0pt,0pt) -- ++(\linewidth,0pt);
\node [anchor=south,font=\sffamily\footnotesize] at (current bounding box.north) {\textcolor{red}{1pt} too wide};
\end{tikzpicture}
\noindent
\begin{tikzpicture}
\draw [red,line width=10pt] (0pt,0pt) -- ++(\linewidth,0pt);
\node [anchor=south,font=\sffamily\footnotesize] at (current bounding box.north) {\textcolor{red}{10pt} too wide};
\end{tikzpicture}
\noindent
\begin{tikzpicture}
\draw [red,line width=100pt] (0pt,0pt) -- ++(\linewidth,0pt);
\node [anchor=south,font=\sffamily\footnotesize] at (current bounding box.north) {\textcolor{red}{100pt} too wide};
\end{tikzpicture}
\end{document}
我天真地认为,这不会产生满满的盒子,但事实确实如此
Overfull \hbox (0.4pt too wide) in paragraph at lines 246--251
[]
Overfull \hbox (1.0pt too wide) in paragraph at lines 252--257
[]
Overfull \hbox (10.0pt too wide) in paragraph at lines 258--263
[]
Overfull \hbox (100.0pt too wide) in paragraph at lines 264--269
[]
输出结果非常清晰地显示了线的位移
是什么原因造成的?我该如何预防?
- 显然,防止框过满的一个选项是简单地将线的长度减少
\pgflinewidth
。即使在较小的宽度下,这一点也很明显,但随着线条变粗,这显然不可行。 - 另一种选择是在不考虑边界框的情况下绘制线,然后重复路径(不绘制它)来设置边界框。
- 贡萨洛·梅迪纳建议使用
overlay
,但这意味着边界框是错误的。
[已编辑,删除误导性示例。]
有没有推荐的方法来解决此问题?
答案1
是什么原因造成的?我该如何预防?
当描边/绘制路径时,PGF 会将.5\pgflinewidth
四边添加到边界框(如果此时建立的边界框较小)。
这样做不仅是为了覆盖线帽rect
,round
也是为了覆盖线宽本身。
即使在您的示例中,一条宽度为 100pt 的简单水平线也应该产生一张高度为 100pt 的图片,否则它会覆盖文本:
\documentclass{article}
\usepackage{tikz}
\usepackage[showframe, pass]{geometry}
\setlength\parindent{0pt}
\begin{document}
I'm covered by a red bar.
\tikz[line width=100pt, opacity=.5]
\useasboundingbox[postaction={draw=red, overlay}]
(0pt,0pt) -- +(\linewidth,0pt);
I cover a red bar.
\vspace{50pt}
I'm covered by a red bar.
\tikz[line width=100pt, opacity=.5]
\useasboundingbox[postaction={draw=red, overlay}]
(0pt,0pt) -- +(0pt,2cm);
I cover a red bar.
\end{document}
并非全部miter
线连接将被涵盖。
显然,防止框过满的一个选项是简单地将线的长度减少
\pgflinewidth
。即使在较小的宽度下,这一点也很明显,但随着线条变粗,这显然不可行。
与line cap=rect
可能相结合:
\tikz[line width=100pt]
\draw[red, line cap=rect]
(0pt, 0pt) -- +(\linewidth-\pgflinewidth,0pt);
另一种选择是在不考虑边界框的情况下绘制线,然后重复路径(不绘制它)来设置边界框。
不完全是,这仍然没有涵盖垂直维度上的实际线宽。(不过,对于 0.4pt 的线宽,这实际上并不明显。)我们至少可以postaction
不必再次重复路径(也不必解析路径并构建软路径)。
Still covered.
\tikz[line width=100pt, opacity=.5]
\path[postaction={draw=red, overlay}]
(0pt, 0pt) -- +(\linewidth,0pt);
Still covering.
0pt
如果你想要的只是从到画水平线,\linewidth
不要使用 TikZ,而是使用\rule
trim left=0pt, trim right=\linewidth
:
\tikz[line width=100pt, trim left=+0pt, trim right=+\linewidth]
\draw[red] (0pt, 0pt) -- +(\linewidth,0pt);
如果您知道路径的宽度而不知道线宽(或者可以通过两个坐标确定,因为它还允许)trim left=(left coordinate of the picture), trim right=(right coordinate of the picture)
,这些键很有用。
我以前用过这些选项,尤其是当涉及到跨越整个的图片时\linewidth
。
现在,让我们忽略圆线帽(无论如何,边界框总是完美的)和对接线帽(我相信只有水平和垂直线才适合它)。
对于圆线连接边界框也将是完美的,
什么决定了“真正的”边界框?
对于每个路径段,我们只需要计算与该段平行的路径.5\pgflinewidth
。对于两个路径段之间的每个连接,我们需要计算
这需要进行大量的数学解析,我不想这样做,但对于对接线帽和斜接线连接,nfold
库提供了算法。我们使用它来将原始路径向两侧偏移线宽的一半,并将其用作路径的边界框。
代码
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{nfold}
\makeatletter
\tikzset{
real bb/.style={% this is not perfect because it messes with the original
% postaction and softpath system
overlay, % ignore the original's path bb
postaction={overlay=false, path only, qoffset=+1},
postaction={overlay=false, path only, qoffset=-1}},
qoffset/.code=\tikz@addoption{\pgfgetpath\tikz@temp\pgfsetpath\pgfutil@empty
\pgfoffsetpathqfraction\tikz@temp{.5\pgflinewidth}{#1}}}
\makeatother
\usepackage[showframe, pass]{geometry}
\setlength\parindent{0pt}
\tikzset{every picture/.append style={execute at end picture={
\draw[help lines] ([xshift=+.5\pgflinewidth,yshift=+.5\pgflinewidth]
current bounding box.south west) rectangle
([xshift=+-.5\pgflinewidth,yshift=+-.5\pgflinewidth]
current bounding box.north east);}}}
\begin{document}
\section{Using only the path as bounding box}
Using only the path for the bounding box does not work
because a horizontal line still has a height and a
vertical line still has a width.
\vspace{35pt}
I'm covered by a red bar.
\tikz[line width=100pt, opacity=.5]
\useasboundingbox[postaction={draw=red, overlay}]
(0pt,0pt) -- +(\linewidth,0pt);
I cover a red bar.
\vspace{50pt}
I'm covered by a red bar.
\tikz[line width=100pt, opacity=.5]
\useasboundingbox[postaction={draw=red, overlay}]
(0pt,0pt) -- +(0pt,2cm);
I cover a red bar.
\bigskip
\section{Trimming left and right and \texttt{real bb}}
For a simple horizontal line, I'd use \verb|trim left| and \verb|trim right|:
\tikz[line width=100pt, trim left=+0pt, trim right=+\linewidth]
\draw (0pt,0pt) --+(\linewidth,0pt);
Otherwise, we'll try \verb|real bb|:
\tikz[line width=100pt]
\draw[real bb] (0pt,0pt) --+(\linewidth,0pt);\bigskip
\pagebreak
Another example where the default approach fails:\medskip
\tikz[line width=25pt, baseline=(current bounding box)]
\draw [ rotate=45] (0,-1) rectangle (2,1);\qquad\qquad
\tikz[line width=25pt, baseline=(current bounding box)]
\draw [yscale=.5, rotate=45] (0,-1) rectangle (2,1);\bigskip
And with \verb|real bb|:
\tikz[line width=25pt, baseline=(current bounding box)]
\draw [ rotate=45, real bb] (0,-1) rectangle (2,1);
\tikz[line width=25pt, baseline=(current bounding box)]
\draw [yscale=.5, rotate=45, real bb] (0,-1) rectangle (2,1);
\begingroup
\tikzset{every picture/.append style={line width=20pt, text=white, sloped}}
\section{Line Caps}
Only a round line cap will always fit perfectly.
\foreach \linecap in {butt, rect, round}
\tikz[line cap=\linecap]
\draw (0,0) -- node{\linecap} +(30:2);\medskip
The \verb|real bb| key can be used to “fix” the default butt line cap.
The round cap does not need fixing.
Good luck with the rect line cap.\medskip
\foreach \linecap in {butt, rect, round}
\tikz[line cap=\linecap]
\draw[real bb] (0,0) -- node{\linecap} +(30:2);
\pagebreak
\section{Line Joins}
Once again, only a round line join will fit perfectly.
\foreach \linejoin in {miter, bevel, round}
\tikz[line join=\linejoin]
\draw (0,0) -- node{\linejoin} ++(30:1.5) -- +(-30:1.5);
With \verb|real bb| again:
Miter line join looks good.
The round line join didn't need it.
And the bevel does its own thing.
(And there's also the \verb|miter limit| changing things.)
\foreach \linejoin in {miter, bevel, round}
\tikz[line join=\linejoin]
\draw[real bb] (0,0) -- node{\linejoin} ++(30:1.5) -- +(-30:1.5);
\endgroup
\section{Hmm…}
\tikz[line width=1cm, trim left=+-.5\linewidth, trim right=+.5\linewidth]
\draw (0,0) node {What about this?} circle[radius=.5\linewidth];
\end{document}