svg.path tikz ライブラリの相対座標を修正する方法

svg.path tikz ライブラリの相対座標を修正する方法

相対コマンドを使用するとバグやエラーが発生するようです。私の場合、滑らかな曲線を描画すると曲線が間違って描画される例を見つけました。

次のケースを確認してください (最初のケースは完全な相対、最後のケースは完全な絶対)。コマンドの相対的な使用では誤って描画されますが、絶対的な使用では期待どおりに動作しています。

\documentclass{article}

\usepackage{tikz}
\usetikzlibrary{svg.path}

\begin{document}
  % Original
  \begin{tikzpicture}
    \draw svg "M 256 8 C 119 8 8 119 8 256 s 111 248 248 248 s 248 -111 248 -248 S 393 8 256 8 z";
  \end{tikzpicture}
  % Mix of relative
  \begin{tikzpicture}
  \draw svg "M 256 8 C 119 8 8 119 8 256 s 111 248 248 248 S 504 393 504 256 S 393 8 256 8 z";
  \end{tikzpicture}
  % Mix of relative
  \begin{tikzpicture}
  \draw svg "M 256 8 C 119 8 8 119 8 256 S 119 504 256 504 s 248 -111 248 -248 S 393 8 256 8 z";
  \end{tikzpicture}
  % Full absolute
  \begin{tikzpicture}
  \draw svg "M256 8 C 119 8 8 119 8 256 S 119 504 256 504 S 504 393 504 256 S 393 8 256 8 z";
  \end{tikzpicture}
\end{document}

答え1

s演算子の定義において

\pgfparserdef{svgpath}{all}{the letter s}
{
  \pgf@lib@svg@finish@prev
  \pgf@lib@svg@read@nums{4}{\pgf@lib@svg@curveto@rel@smooth}
}

\def\pgf@lib@svg@curveto@rel@smooth{%
  \ifnum\pgf@lib@svg@count=0\relax% nothing read
  \else%
    % Draw curve
    % Compute first control point
    \ifx\pgf@lib@svg@bezier@last\pgfutil@empty%
      \def\pgf@lib@svg@first@cp{\pgfqpoint{\pgf@lib@svg@last@x}{\pgf@lib@svg@last@y}}
    \else
      \def\pgf@lib@svg@first@cp{
        \pgfpointadd
        {\pgfqpoint{\pgf@lib@svg@last@x}{\pgf@lib@svg@last@y}}
        {\pgfpointdiff
          {\pgf@lib@svg@bezier@last}
          {\pgfqpoint{\pgf@lib@svg@last@x}{\pgf@lib@svg@last@y}}
        }
      }
    \fi
    \pgfpathcurveto
    {\pgf@lib@svg@first@cp}
    {\pgfpointadd{\pgfqpoint{\pgf@lib@svg@last@x}{\pgf@lib@svg@last@y}}{\pgfqpoint{\pgf@lib@svg@get@num{0}pt}{\pgf@lib@svg@get@num{1}pt}}}%
    {\pgfpointadd{\pgfqpoint{\pgf@lib@svg@last@x}{\pgf@lib@svg@last@y}}{\pgfqpoint{\pgf@lib@svg@get@num{2}pt}{\pgf@lib@svg@get@num{3}pt}}}%
    % Clear quadratic last point and save new last control point:
    \let\pgf@lib@svg@quad@last=\pgfutil@empty%
    \pgf@process{\pgfpointadd{\pgfqpoint{\pgf@lib@svg@last@x}{\pgf@lib@svg@last@y}}{\pgfqpoint{\pgf@lib@svg@get@num{2}pt}{\pgf@lib@svg@get@num{3}pt}}}
    \edef\pgf@lib@svg@bezier@last{\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}}%
    % update
    \advance\pgf@lib@svg@last@x by\pgf@lib@svg@get@num{2}pt%
    \advance\pgf@lib@svg@last@y by\pgf@lib@svg@get@num{3}pt%
    % Go on
    \pgf@lib@svg@read@nums{4}{\pgf@lib@svg@curveto@rel@smooth}
  \fi
}

次の行(現在は386行目)pgflibrarysvg.path.code.tex

\pgf@process{\pgfpointadd{\pgfqpoint{\pgf@lib@svg@last@x}{\pgf@lib@svg@last@y}}{\pgfqpoint{\pgf@lib@svg@get@num{2}pt}{\pgf@lib@svg@get@num{3}pt}}}
\edef\pgf@lib@svg@bezier@last{\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}}%

は間違っています。これは、\pgf@lib@svg@get@num{2}と が{3}前の曲線の終点であるためです。しかし、標準では、前の曲線の 2 番目の制御点を記憶する必要があるとされています。数値を と に変更すると{0}{1}期待どおりに動作します。

(その行は 300 行目からコピーされたものと思われますが、C演算子では{2}および は{3}2 番目の制御点を意味します。)

MWEはこちら

\documentclass{article}

\usepackage{tikz}
\usetikzlibrary{svg.path}
\makeatletter

\def\pgf@lib@svg@curveto@rel@smooth{%
  \ifnum\pgf@lib@svg@count=0\relax% nothing read
  \else%
    % Draw curve
    % Compute first control point
    \ifx\pgf@lib@svg@bezier@last\pgfutil@empty%
      \def\pgf@lib@svg@first@cp{\pgfqpoint{\pgf@lib@svg@last@x}{\pgf@lib@svg@last@y}}
    \else
      \def\pgf@lib@svg@first@cp{
        \pgfpointadd
        {\pgfqpoint{\pgf@lib@svg@last@x}{\pgf@lib@svg@last@y}}
        {\pgfpointdiff
          {\pgf@lib@svg@bezier@last}
          {\pgfqpoint{\pgf@lib@svg@last@x}{\pgf@lib@svg@last@y}}
        }
      }
    \fi
    \pgfpathcurveto
    {\pgf@lib@svg@first@cp}
    {\pgfpointadd{\pgfqpoint{\pgf@lib@svg@last@x}{\pgf@lib@svg@last@y}}{\pgfqpoint{\pgf@lib@svg@get@num{0}pt}{\pgf@lib@svg@get@num{1}pt}}}%
    {\pgfpointadd{\pgfqpoint{\pgf@lib@svg@last@x}{\pgf@lib@svg@last@y}}{\pgfqpoint{\pgf@lib@svg@get@num{2}pt}{\pgf@lib@svg@get@num{3}pt}}}%
    % Clear quadratic last point and save new last control point:
    \let\pgf@lib@svg@quad@last=\pgfutil@empty%
    \pgf@process{\pgfpointadd{\pgfqpoint{\pgf@lib@svg@last@x}{\pgf@lib@svg@last@y}}{\pgfqpoint{\pgf@lib@svg@get@num{0}pt}{\pgf@lib@svg@get@num{1}pt}}} %%%%%% fixing this line
    \edef\pgf@lib@svg@bezier@last{\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}}%
    % update
    \advance\pgf@lib@svg@last@x by\pgf@lib@svg@get@num{2}pt%
    \advance\pgf@lib@svg@last@y by\pgf@lib@svg@get@num{3}pt%
    % Go on
    \pgf@lib@svg@read@nums{4}{\pgf@lib@svg@curveto@rel@smooth}
  \fi
}


\begin{document}
  % Original
  \begin{tikzpicture}
    \draw svg "M 256 8 C 119 8 8 119 8 256 s 111 248 248 248 s 248 -111 248 -248 S 393 8 256 8 z";
  \end{tikzpicture}
  % Mix of relative
  \begin{tikzpicture}
  \draw svg "M 256 8 C 119 8 8 119 8 256 s 111 248 248 248 S 504 393 504 256 S 393 8 256 8 z";
  \end{tikzpicture}
  % Mix of relative
  \begin{tikzpicture}
  \draw svg "M 256 8 C 119 8 8 119 8 256 S 119 504 256 504 s 248 -111 248 -248 S 393 8 256 8 z";
  \end{tikzpicture}
  % Full absolute
  \begin{tikzpicture}
  \draw svg "M256 8 C 119 8 8 119 8 256 S 119 504 256 504 S 504 393 504 256 S 393 8 256 8 z";
  \end{tikzpicture}
\end{document}

関連情報