完整代码

完整代码

svg库中的命令可以在 TikZ 中svg.path显示 svg <path>。但在一种特殊情况下,坐标计算失败。

当路径的一部分由z(或Z) 关闭并且以下路径指令是相对的 (小字符,如l, c, ...) 时,会发生这种情况。然后 tikz 应该计算相对于移动点的下一个坐标z(因此是路径上一部分的开头),但它的行为就像z不存在一样。

举个例子。前两条路径应该与第二条路径等价。蓝色路径(没有z)是这样的,但红色路径(z有 )不是这样的。

\documentclass[tikz,border=7mm]{standalone}
\usetikzlibrary{svg.path}

\begin{document}
  \begin{tikzpicture}[yscale=-1]
    \begin{scope} % absolute paths
      \draw[blue] svg{M0,0 L 10,10   L 10,20};
      \draw[red]  svg{M0,0 L 10,10 Z L  0,10};
    \end{scope}

    \begin{scope}[xshift=20pt] % "equivalent" relative paths
      \draw[blue] svg{M0,0 l 10,10   l 0,10};
      \draw[red]  svg{M0,0 l 10,10 z l 0,10}; % tikz put the last point at (10,20)
    \end{scope}
  \end{tikzpicture}
\end{document}

在此处输入图片描述

我们可以看出等价SVG是没有问题的。

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="-10 -10 50 40" width="500" height="400">
  <g fill="none" stroke-width=".4">
    <path stroke="blue" d="M0,0 L 10,10   L 10,20"/>
    <path stroke="red"  d="M0,0 L 10,10 Z L  0,10"/>
  </g>
  <g fill="none" stroke-width=".4" transform="translate(20)">
    <path stroke="blue" d="M0,0 l 10,10   l 0,10"/>
    <path stroke="red"  d="M0,0 l 10,10 z l 0,10"/>
  </g>
</svg>

在此处输入图片描述

问题:我们怎样才能修复svg.path图书馆?

笔记:我还很惊讶像(20,0) -- cycle -- ++(0,10)和这样的路径在 TikZ 中并不等价(例如,(20,0) -- ++(0,10)它们在内部不会产生相同的结果)......\draw[xshift=10] ...;为什么

答案1

SVG 中的字母z/ 、 Ti 中的Z--cycleZ、\closepathPGF 中的 以及 PDF 中的字母h关闭当前子路径。对于 TiZ 和 PGF,它们不需要记住当前子路径的起点,因为 PDF 渲染器会完成这项工作。§¶对于SVG,由 Web 浏览器完成这项工作。

于是尴尬的事情出现了:对于 PFG 上的 SVG 引擎,相对命令(小写)的参考点由\pgf@lib@svg@last@x和保存\pgf@lib@svg@last@y。它们由所有命令更新,但z/Z因为 PGF 不知道它们应该是什么——它们应该代表当前子路径的初始点,但 PGF 就是不记得了。

因此我们必须分配两个寄存器来帮助 PGF 记住它。

\newdimen\pgf@lib@svg@lastmoveto@x
\newdimen\pgf@lib@svg@lastmoveto@y

然后每当我们遇到m/时更新它们M
(代码都是从 复制的,pgflibrarysvg.path.code.tex除了带有% NEW LINE. 的代码)

\def\pgf@lib@svg@moveto{%
  \pgf@lib@svg@clear@bezier@quad%
  \pgf@lib@svg@last@x=\pgf@lib@svg@get@num{0}pt%
  \pgf@lib@svg@last@y=\pgf@lib@svg@get@num{1}pt%
  \pgf@lib@svg@lastmoveto@x=\pgf@lib@svg@last@x% NEW LINE
  \pgf@lib@svg@lastmoveto@y=\pgf@lib@svg@last@y% NEW LINE
  \pgfpathmoveto{\pgfqpoint{\pgf@lib@svg@last@x}{\pgf@lib@svg@last@y}}%
  \pgf@lib@svg@read@nums{2}{\pgf@lib@svg@lineto}
}
\def\pgf@lib@svg@moveto@rel{%
  \pgf@lib@svg@clear@bezier@quad%
  \advance\pgf@lib@svg@last@x by\pgf@lib@svg@get@num{0}pt%
  \advance\pgf@lib@svg@last@y by\pgf@lib@svg@get@num{1}pt%
  \pgf@lib@svg@lastmoveto@x=\pgf@lib@svg@last@x% NEW LINE
  \pgf@lib@svg@lastmoveto@y=\pgf@lib@svg@last@y% NEW LINE
  \pgfpathmoveto{\pgfqpoint{\pgf@lib@svg@last@x}{\pgf@lib@svg@last@y}}%
  \pgf@lib@svg@read@nums{2}{\pgf@lib@svg@lineto@rel}
}

现在我们知道如何更新\pgf@lib@svg@last@x\pgf@lib@svg@last@y

\def\pgf@lib@svg@closepath{
  \pgf@lib@svg@finish@prev
  \pgf@lib@svg@last@x=\pgf@lib@svg@lastmoveto@x% NEW LINE
  \pgf@lib@svg@last@y=\pgf@lib@svg@lastmoveto@y% NEW LINE
  \pgfpathclose
  \let\pgf@lib@svg@finish@prev=\relax
  \pgf@lib@svg@clear@bezier@quad%
  \pgfparserswitch{initial}%
}

完整代码

\documentclass[tikz,border=7mm]{standalone}
\usetikzlibrary{svg.path}

\makeatletter

\newdimen\pgf@lib@svg@lastmoveto@x
\newdimen\pgf@lib@svg@lastmoveto@y

\def\pgf@lib@svg@moveto{%
  \pgf@lib@svg@clear@bezier@quad%
  \pgf@lib@svg@last@x=\pgf@lib@svg@get@num{0}pt%
  \pgf@lib@svg@last@y=\pgf@lib@svg@get@num{1}pt%
  \pgf@lib@svg@lastmoveto@x=\pgf@lib@svg@last@x% NEW LINE
  \pgf@lib@svg@lastmoveto@y=\pgf@lib@svg@last@y% NEW LINE
  \pgfpathmoveto{\pgfqpoint{\pgf@lib@svg@last@x}{\pgf@lib@svg@last@y}}%
  \pgf@lib@svg@read@nums{2}{\pgf@lib@svg@lineto}
}
\def\pgf@lib@svg@moveto@rel{%
  \pgf@lib@svg@clear@bezier@quad%
  \advance\pgf@lib@svg@last@x by\pgf@lib@svg@get@num{0}pt%
  \advance\pgf@lib@svg@last@y by\pgf@lib@svg@get@num{1}pt%
  \pgf@lib@svg@lastmoveto@x=\pgf@lib@svg@last@x% NEW LINE
  \pgf@lib@svg@lastmoveto@y=\pgf@lib@svg@last@y% NEW LINE
  \pgfpathmoveto{\pgfqpoint{\pgf@lib@svg@last@x}{\pgf@lib@svg@last@y}}%
  \pgf@lib@svg@read@nums{2}{\pgf@lib@svg@lineto@rel}
}

\def\pgf@lib@svg@closepath{
  \pgf@lib@svg@finish@prev
  \pgf@lib@svg@last@x=\pgf@lib@svg@lastmoveto@x% NEW LINE
  \pgf@lib@svg@last@y=\pgf@lib@svg@lastmoveto@y% NEW LINE
  \pgfpathclose
  \let\pgf@lib@svg@finish@prev=\relax
  \pgf@lib@svg@clear@bezier@quad%
  \pgfparserswitch{initial}%
}

\begin{document}
  \begin{tikzpicture}[yscale=-1]
    \begin{scope} % absolute paths
      \draw svg{M0,0 L 10,10 Z L 0,10};
    \end{scope}
    \begin{scope}[xshift=20pt] % "equivalent" relative paths
      \draw svg{M0,0 l 10,10 z l 0,10}; % tikz put the last point at (10,20)
    \end{scope}
  \end{tikzpicture}
\end{document}


§ PGF 实际上记得一些事情:pgfsyssoftpath.code.tex我们看到

\def\pgfsyssoftpath@closepath{%
  \expandafter\pgfsyssoftpath@addtocurrentpath\expandafter{\expandafter\pgfsyssoftpath@closepathtoken\pgfsyssoftpath@lastmoveto}%
}

\pgfsyssoftpath@lastmoveto类似的东西在哪里{10pt}{10pt}代表初始点。这个宏对于装饰和形状来说是必不可少的。但这是相当低级的。

¶ 钛Z 确实记得一些事情:tikz.code.tex我们看到

\def\tikz@@close cycle{%
  \tikz@flush@moveto%
  \edef\tikz@timer@start{\noexpand\pgfqpoint{\the\tikz@lastx}{\the\tikz@lasty}}
  \tikz@make@last@position{\expandafter\pgfpoint\pgfsyssoftpath@lastmoveto}%
  \tikz@path@close{\expandafter\pgfpoint\pgfsyssoftpath@lastmoveto}%
  \def\pgfstrokehook{}%
  \edef\tikz@timer@end{\noexpand\pgfqpoint{\the\tikz@lastx}{\the\tikz@lasty}}%
  \let\tikz@timer=\tikz@timer@line%
  \let\tikz@tangent\tikz@timer@start%
  \tikz@scan@next@command%
}

其中\tikz@make@last@position检索中的坐标\pgfsyssoftpath@lastmoveto并将其存储在\tikz@lastxsaved和中\tikz@lastysaved。因此 TiZ 变体

\tikz\draw(0,0)--(1,1)--cycle--++(1,0);

表现良好。

相关内容