tikzkeys 在 foreach 语句中不起作用

tikzkeys 在 foreach 语句中不起作用

为了解决我的使用预先存在的点定义 2D 画布,我偶然发现了@andrew swann 的回答通过 pgfkeys 传递 3d 坐标并尝试去适应它。

这就是 3d 坐标除了转换为 2d 坐标之外的存储方式。

    % save 3d coordinates of point
    \def\tdcoord #1 at (#2,#3,#4);{\pgfkeys{/tdcoords/#1/.is
    family,/tdcoords/#1,x/.initial=#2,y/.initial=#3,z/.initial=#4}}

尽管一切正常,并且我能够调整画布,只需给出用附加定义的点\tdcoords,但只有当该调用不在 foreach 循环中时它才有效。

错误消息是

  • 未定义的控制序列
  • 缺失数字,视为零
  • \pgfmath@dimen@@ 的参数有一个额外的 }
  • 段落在 \pgfmath@dimen@@ 完成之前结束
  • 额外的 },或者被遗忘的 \endgroup

有人知道为什么或如何解决这个问题吗?

改编的MWE:

    \documentclass{standalone}
    \usepackage{tikz}
    \usetikzlibrary{calc}
    \usepackage{tikz-3dplot}

    % my adaption attempt
    \makeatletter
    \tikzoption{canvasP}[]{\@setPOxy#1}
    \def\@setPOxy #1,#2,#3%
      {\def\tikz@plane@origin{\pgfpointxyz{\GetX(#1)}{\GetY(#1)}{\GetZ(#1)}}%
       \def\tikz@plane@x{\pgfpointxyz{\GetX(#2)}{\GetY(#2)}{\GetZ(#2)}}%
       \def\tikz@plane@y{\pgfpointxyz{\GetX(#3)}{\GetY(#3)}{\GetZ(#3)}}%
       \tikz@canvas@is@plane}
    \makeatother

    % save 3d coordinates of point
    \def\tdcoord #1 at (#2,#3,#4);{\pgfkeys{/tdcoords/#1/.is
    family,/tdcoords/#1,x/.initial=#2,y/.initial=#3,z/.initial=#4}}

    % Get the 3d coordinate components of a point
    \def\GetX(#1){\pgfkeysvalueof{/tdcoords/(#1)/x}}
    \def\GetY(#1){\pgfkeysvalueof{/tdcoords/(#1)/y}}
    \def\GetZ(#1){\pgfkeysvalueof{/tdcoords/(#1)/z}}

    % Define multiple 3d points following tkz-euclide notation
    \newcommand{\tkzDefdPoints}[2][]{
      \foreach \ptx/\pty/\ptz/\name in {#2}{
        \path [#1] (\ptx,\pty,\ptz) coordinate (\name);
        \tdcoord (\name) at (\ptx,\pty,\ptz);
      }
    }

    \tdplotsetmaincoords{70}{110}

    \begin{document}
    \begin{tikzpicture}[tdplot_main_coords]
      \draw[->] (0,0,0) -- (5,0,0) node[right]{$x$};
      \draw[->] (0,0,0) -- (0,5,0) node[above]{$y$};
      \draw[->] (0,0,0) -- (0,0,5) node[below left]{$z$};

      \tkzDefdPoints{2/2/2/A,2/3/2/B,2/2/3/C,
                     2/2/2/D,2/3/2/E,2/2/3/F}

      \tdcoord (A) at (2,2,2);
      \tdcoord (B) at (2,3,2);
      \tdcoord (C) at (2,2,3);

      \draw (A) -- (B) -- (C) -- cycle;

      \begin{scope}[canvasP={A,B,C}]
        \draw[red] (1,0) -- (1,1) -- (0,1);
      \end{scope}

      % \begin{scope}[canvasP={D,E,F}]
      %   \draw[blue] (0,1) -- (1,2) -- (2,1) -- (1,0);
      % \end{scope}
    \end{tikzpicture}
    \end{document}

更新:在重命名点时,我刚刚意识到这可能与 foreach 语句无关。将坐标名称更改为非单个字母的名称会\tdcoord (AX) at (2,2,2);产生完全相同的错误,即使在 foreach 循环之外调用它也是如此。

答案1

  1. \@setPOxy没有分隔符,意味着canvasP={AA,BB,CC}将导致#3存在CC丢失到 TeX。
  2. 您正在将\ptx“等”存储在值键中,而不是值中(这需要/.initial/.expanded)。
  3. 循环主体\foreach是本地的,循环主体之后所有键分配都会丢失。

我们可以使用以下方法解决 1.

\tikzset{canvasP/.code=\@setPOxy#1\@stop}
\def\@setPOxy #1,#2,#3\@stop
  {\def\tikz@plane@origin{\pgfpointxyz{\GetX(#1)}{\GetY(#1)}{\GetZ(#1)}}%
   \def\tikz@plane@x{\pgfpointxyz{\GetX(#2)}{\GetY(#2)}{\GetZ(#2)}}%
   \def\tikz@plane@y{\pgfpointxyz{\GetX(#3)}{\GetY(#3)}{\GetZ(#3)}}%
   \tikz@canvas@is@plane}

但由于 PGFkeys 已经使用了分隔值/参数,因此我们可以在这里直接使用键:

canvasP/.code args={#1,#2,#3}{%
  \def\tikz@plane@origin{\pgfpointxyz{\GetX(#1)}{\GetY(#1)}{\GetZ(#1)}}%
  \def\tikz@plane@x{\pgfpointxyz{\GetX(#2)}{\GetY(#2)}{\GetZ(#2)}}%
  \def\tikz@plane@y{\pgfpointxyz{\GetX(#3)}{\GetY(#3)}{\GetZ(#3)}}%
  \tikz@canvas@is@plane}

当我们解决 3 时,2.问题就不再是问题了。

我们需要循环遍历您的列表,但不能将其放在组内。(我们也可以进行类似于以下的全局定义另一个答案我的原因与此类似,但那没有必要。

我们也可以使用 PGfplots,\pgfplotsinvokeforeach但这也不是必需的,我们可以使用.listPGFkeys 的处理程序:

def dPoints/.style 2 args={%
  @def dPoints/.code args={##1/##2/##3/##4}{%
    \path [#1] (##1,##2,##3) coordinate (##4);
    \tdcoord (##4) at (##1,##2,##3);},
  @def dPoints/.list={#2}
}

这将设置一个键@def dPoints,该键采用四个参数,与循环/非常相似\foreach,但它不会将其分配给宏,也不会在将其与 一起使用时在组内执行.list。 (在内部,.list使用范围 (即分组)\foreach循环,但它会在循环后执行所有内容。)

#1的参数def dPoints不再是可选的(但它可以为空)但因为我们有它,所以我们@def dPoints每次想要使用它时都需要重新定义。

代码

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\usepackage{tikz-3dplot}

% my adaption attempt
\makeatletter
\tikzset{
  canvasP/.code args={#1,#2,#3}{%
    \def\tikz@plane@origin{\pgfpointxyz{\GetX(#1)}{\GetY(#1)}{\GetZ(#1)}}%
    \def\tikz@plane@x{\pgfpointxyz{\GetX(#2)}{\GetY(#2)}{\GetZ(#2)}}%
    \def\tikz@plane@y{\pgfpointxyz{\GetX(#3)}{\GetY(#3)}{\GetZ(#3)}}%
    \tikz@canvas@is@plane},
  def dPoints/.style 2 args={%
    @def dPoints/.code args={##1/##2/##3/##4}{%
      \path [#1] (##1,##2,##3) coordinate (##4);
      \tdcoord (##4) at (##1,##2,##3);},
    @def dPoints/.list={#2}
  }
}
\makeatother

% save 3d coordinates of point
\def\tdcoord #1 at (#2,#3,#4);{\pgfkeys{/tdcoords/#1/.is
family,/tdcoords/#1,x/.initial=#2,y/.initial=#3,z/.initial=#4}}

% Get the 3d coordinate components of a point
\def\GetX(#1){\pgfkeysvalueof{/tdcoords/(#1)/x}}
\def\GetY(#1){\pgfkeysvalueof{/tdcoords/(#1)/y}}
\def\GetZ(#1){\pgfkeysvalueof{/tdcoords/(#1)/z}}

% Define multiple 3d points following tkz-euclide notation
\newcommand{\tkzDefdPoints}[2][]{\tikzset{def dPoints={#1}{#2}}}

\tdplotsetmaincoords{70}{110}

\begin{document}
\begin{tikzpicture}[tdplot_main_coords]
\draw[->] (0,0,0) -- (5,0,0) node[right]{$x$};
\draw[->] (0,0,0) -- (0,5,0) node[above]{$y$};
\draw[->] (0,0,0) -- (0,0,5) node[below left]{$z$};

\tkzDefdPoints{2/2/2/A,2/3/2/B,2/2/3/C,
               2/2/2/D,2/3/2/E,2/2/3/F}

\tdcoord (A) at (2,2,2);
\tdcoord (B) at (2,3,2);
\tdcoord (C) at (2,2,3);

\draw (A) -- (B) -- (C) -- cycle;

\begin{scope}[canvasP={A,B,C}]
  \draw[red] (1,0) -- (1,1) -- (0,1);
\end{scope}

 \begin{scope}[canvasP={D,E,F}]
   \draw[blue] (0,1) -- (1,2) -- (2,1) -- (1,0);
 \end{scope}
\end{tikzpicture}
\end{document}

相关内容