如何解决短的、自定义装饰的弧线的“尺寸太大”问题?

如何解决短的、自定义装饰的弧线的“尺寸太大”问题?

我已经看到了避免 tikz 中的“尺寸过大”和“浮点格式错误”错误,可能没有答案,但让我们看看......

我有这个 MWE,带有自定义装饰,可以应用于弧,但不能应用于短弧:

\documentclass[varwidth,tightpage,border=1bp]{standalone}

\usepackage{tikz}
\usetikzlibrary{decorations}
\usepackage{trace}

\pagecolor{yellow!15} % ignored with preview, but not w/ varwidth

\makeatletter
\pgfkeys{/tikz/.cd, %
  /pgf/decoration/deccrosslw/.store in=\deccrosslw, %
  /pgf/decoration/deccrosslw=0.2cm, %
  /pgf/decoration/deccrosslen/.store in=\deccrosslen, %
  /pgf/decoration/deccrosslen=1pt, %
}
\pgfdeclaredecoration{deccross}{initial}
{
  \state{initial}[width=\deccrosslen]
  {
    \pgfpointdecoratedinputsegmentlast
    \pgfgetlastxy{\dcplx}{\dcply} % grab coords in macros
    \typeout{initial \deccrosslen, \pgfdecoratedinputsegmentlength, dcpl \dcplx, \dcply} % print coords to stdout
    \pgfpathmoveto{\pgfpoint{0pt}{0pt}} % already done, but anyway
    \pgfpathlineto{\pgfpoint{\deccrosslen}{0pt}} % trace the actual line out (along "x" axis)
    % cross out
    \pgfpathmoveto{\pgfpoint{0pt}{0pt}}
    \pgfpathlineto{\pgfpoint{0.5*\deccrosslen}{0.5*\deccrosslw}}
    \pgfpathlineto{\pgfpoint{\deccrosslen}{0pt}}
    \pgfpathmoveto{\pgfpoint{0pt}{0pt}}
    \pgfpathlineto{\pgfpoint{0.5*\deccrosslen}{-0.5*\deccrosslw}}
    \pgfpathlineto{\pgfpoint{\deccrosslen}{0pt}}
  }
  \state{final}
  {
    \pgfpathlineto{\pgfpointdecoratedpathlast}
  }
}
\makeatother

\begin{document}

Hello:

\begin{tikzpicture}

\draw[]
  (0,0) -- (1,0);
\draw[decoration={deccross,deccrosslen=5pt,deccrosslw=0.5cm},decorate]
  (0,1) -- (1,1);
\draw[decoration={deccross,deccrosslen=2pt},decorate]
  (0,2) -- (1pt,2); % short - no problem

\draw[] (2,0) -- ++(0,2cm)
  arc (90:45:2cm) ;

\draw[] (4,0) -- ++(0,2cm)
  coordinate(cs)
  decorate[decoration={deccross}]{ arc (90:45:2cm) }
  coordinate(ce);

%\traceon
\draw[decoration={deccross},decorate]
  (cs)
  %arc (90:92:2cm) % ! Dimension too large. <to be read again> \relax
  arc (90:97:2cm) % 90:97 ok, 90:96 dimension too large
  ;

\end{tikzpicture}
\end{document}

当代码运行时,如上所示,生成的内容如下:

测试04a

但是如果取消注释了 short arc (90:92:2cm),则会出现崩溃! Dimension too large.。这正是避免 tikz 中的“尺寸过大”和“浮点格式错误”错误

您能做的不多。...因为 TeX 无法处理您交给它的计算。因为非常老旧但仍然闪亮的齿轮无法处理任何如此精确的东西。我的意思是,由于您的线宽规范,弧长非常小(对于这样的弧来说,它太粗了)。

这可能是同样的问题,但我仍然使用线宽,它实际上很细?另外,请注意,短线没有问题 - 只有短弧才有问题......

我尝试了trace一下,并且我尝试重建“堆栈跟踪”(参见下面选定的复制粘贴);似乎有一个\pgfmathloop要做装饰,在哪里\pgfmathveclen@调用,哪个调用\pgfmathreciprocal@,这最终会导致崩溃。

我能做些什么吗 - 也许通过在我的自定义装饰中编写一个特殊情况 - 让代码不会在短弧上崩溃?

以下是跟踪记录的片段:

\pgfmathloop #1\repeatpgfmathloop ->\def \pgfmathcounter {1}\def \pgfmath@itera
...
#1<-\advance \pgf@xb \c@pgf@counta \pgf@yb \edef \pgf@decorate@temp {\pgf@xa \t
...
 \pgf@y -\pgf@yc \pgfmathveclen@ {\pgfmath@tonumber {\pgf@x }}{\pgfmath@tonumbe
...
...
\pgfmathveclen@ #1#2->\begingroup \pgfmath@x #1pt\relax \pgfmath@y #2pt\relax \
...
fmathreciprocal@ {\pgfmath@tonumber {\pgfmath@y }}\pgfmath@x \pgfmathresult \pg
...
#1<-\pgfmath@tonumber {\pgf@x }
#2<-\pgfmath@tonumber {\pgf@y }
...
...
\pgfmath@reciprocaltemp ->0.00006

\pgfmathreciprocal@@ #1.#2#3#4#5#6#7\pgfmath@ ->\c@pgfmath@counta #2#3#4#5#6\re
...
returnone \pgfmath@x \endgroup
#1<-0
#2<-0
#3<-0
#4<-0
#5<-0
#6<-6
#7<-0000000
...
{\count91}
{changing \count91=6}
{into \count91=166666666}
{\divide}
{changing \count91=166666666}
{into \count91=16666}
{\relax}
{\dimen107}
! Dimension too large.
<to be read again>
                   \relax
\pgfmathreciprocal@@ ...c@pgfmath@counta pt\relax

答案1

好吧,我想要一个解决方法 - 这里有一个解决方法:),尽管我真的很想知道是否有更合适的方法来做到这一点。此解决方法产生以下输出,对应于装饰arc (90:92:2cm)(如预期的那样,短弧可能有点难以看到,但它确实存在):

测试04b.png

基本上,首先我会不断进行暴力破解,增加半径并重新编译,直到通过。结果是:

  • 对于 90:92 的圆弧角,我至少需要 5.4 厘米的半径
  • 对于 90:91 的圆弧角,我至少需要 10.5 厘米的半径

因此,由于我的原始半径是 2 厘米,而我想要显示 90:91(因此我必须使用 10.5 厘米半径)——如果我想要支持 1 度弧,我的放大比例是 10.5/2(\scaleNorm在下面的代码中,其倒数为\scaleInv)。诀窍是:

  • 不要缩小\draw,而是使用明确大的半径,以及相应放大的装饰参数和线宽
  • 然后将包裹\draw在 中{scope},它会缩小,一切(包括线宽),使用transform canvas,恢复到原来的大小

从几何学上讲,弧的曲率应该(并且确实)经得住放大/缩小过程。唯一的问题是正确地锚定范围 - 基本上,我们需要事先指定一个命名坐标,我们将锚定到该坐标(在它下面是cs,它也用于开始弧) - 然后在中transform canvas,我们可以使用shift=($(cs)-\scaleInv*(cs)$)来正确地重新对齐。

请注意,下面可以显示一个装饰性的一度arc (90:91:...)- 但它仍然会与! Dimension too large零度弧崩溃arc (90:90:...);这是不幸的,因为非装饰弧通常不会绘制任何东西,并且默默地通过(并且要实现这一点,需要在解决方法中进行额外的条件检查)。

因此,仅tikzpicture解决方案的一部分(因为 OP 中的 MWE 其他地方没有任何变化)实现了此解决方法,如下所示:

\begin{tikzpicture}

\draw[]
  (0,0) -- (1,0);
\draw[decoration={deccross,deccrosslen=5pt,deccrosslw=0.5cm},decorate]
  (0,1) -- (1,1);
\draw[decoration={deccross,deccrosslen=2pt},decorate]
  (0,2) -- (1pt,2); % short - no problem

\draw[] (2,0) -- ++(0,2cm)
  coordinate(ics)
  arc (90:45:2cm)
  (ics)  { arc (90:97:2cm) }
  ;

\draw[] (4,0) -- ++(0,2cm)
  coordinate(cs)
  decorate[decoration={deccross}]%
  { arc (90:45:2cm) }
  coordinate(ce)
  ;

%\traceon
\pgfmathsetmacro{\scaleInv}{2/10.5}
\pgfmathsetmacro{\scaleNorm}{1/\scaleInv}
\typeout{NOW \scaleInv, \scaleNorm}
%\scalebox{\pgfmathresult}{% not good, way off
% doing draw[scale=0.8 in  again raises Dimension too large ...
% the scope scale (without transform shape) seemingly
%  has no influence on decorations;
% so it basically restores the failure at 90:92 (90:91 passes!)
% transform canvas transforms _everything_ - including stroke
\begin{scope}[
  at=(cs),
  anchor=center,
  % scale=\scaleInv, % may restore bad @ 90:92 - even if below uses 10.5cm radius!
  % transform shape, % no real change to decorations
  transform canvas={
    scale=\scaleInv,                % works with this, but not anchored
    shift=($(cs)-\scaleInv*(cs)$),  % this anchors ok
  },
]
\pgfmathsetmacro{\declB}{\scaleNorm*\deccrosslen}
\pgfmathsetmacro{\decwB}{\scaleNorm*\deccrosslw}
\pgfmathsetmacro{\sclw}{\scaleNorm*\pgflinewidth}
\draw[
  scale=1,
  line width=\sclw,
  decoration={deccross,deccrosslen=\declB,deccrosslw=\decwB},
  decorate
]
  (cs)
  %arc (90:92:2cm) % ! Dimension too large. <to be read again> \relax
  %arc (90:97:2cm) % 90:97 ok, 90:96 dimension too large
  %arc (90:92:5.4cm) % for >= 5.4cm, it passes for 90:92!
  arc (90:92:10.5cm) % for >= 10.5cm, it passes for 90:91!
  ;
\end{scope}

\end{tikzpicture}

相关内容