TikZ 3.0 中的嵌套图片

TikZ 3.0 中的嵌套图片

TiKz 3.0 推出了pics,我很喜欢它们,但它们似乎没有我希望的那么强大。例如,我无法使用这样的嵌套图片:

\tikzset{
  one/.pic = {\draw[pic actions] (0,0) -- (1,1);},
  two/.pic = {\path pic[pic actions]{one};}
}
\begin{tikzpicture}
  \path pic[ultra thick, red]{one};
  \path pic[thin]{two};
\end{tikzpicture}

这是一个错误吗,或者这只是图片的工作方式?

没有错误消息。简单地说 (Xe/PDF)LaTeX 没有完成工作。

编辑 :按照 Mark Wibrow 的评论,如果我们删除pic actionspic{two},则会起作用:

\tikzset{
  one/.pic = {\draw[pic actions] (0,0) -- (1,1);},
  two/.pic = {\path pic{one};}
}
\begin{tikzpicture}
  \path pic[ultra thick, red]{one};
  \path pic[thin, blue]{two};
\end{tikzpicture}

在此处输入图片描述

因此问题似乎出在内部pic actions

答案1

我刚刚遇到这个问题,它似乎仍然是 2023 年 TikZ(PGF 版本 3.1.10)的一个问题。

深入挖掘代码,我们发现:

  • pic actions扩展为\tikz@addmode{\tikz@picmode}附加\tikz@picmode到宏的\tikz@mode
  • 但是当 TikZ 遇到时,它pic会做的是\let\tikz@picmode=\tikz@mode\tikz@picmode内容外部图片的\tikz@mode

因此,嵌套 pic,我们有以下旅程:

  • 在最外层,\tikz@mode包含对路径执行什么操作的指令(例如,绘制还是填充)。

  • 当 TikZ 遇到第一张图片时,\tikz@picmode会被设置为这个,所以现在包含这些指令。 在 内pic\tikz@mode会被清除。

  • 现在我们调用pic actions附加\tikz@picmode到的\tikz@mode,所以\tikz@mode现在由(至少)组成\tikz@picmode(可能还有其他内容添加到其中)。

  • 我们想要一个嵌套的图片,所以让 TikZ 遇到另一个pic。它的别名\tikz@mode\tikz@picmode,这意味着\tikz@picmode现在由(至少)组成\tikz@picmode

    这就是我们的问题。这是老\def\a{\a}\a问题了。 \tikz@picmode包含对自身的引用,因此当它(最终)被调用时,它会进入无限循环。

一个可能的解决方案(可能会引入其他问题,但至少解决了这个问题)是添加一个扩展步骤,这样当\tikz@picmode附加到\tikz@mode时,它实际上是内容\tikz@picmode附加到\tikz@mode。这避免了无限递归。

所以pic actions应该是:

\makeatletter
\tikzset{
  pic actions/.code=\expandafter\tikz@addmode\expandafter{\tikz@picmode}
}
\makeatother

在我自己的代码中,我目前正在调用这个,sub pic actions因为我不喜欢破坏 TikZ 版本的东西,即使我认为我有改进。

\documentclass{article}
%\url{https://tex.stackexchange.com/q/215580/86}
\usepackage{tikz}

\makeatletter
\tikzset{
  sub pic actions/.code=\expandafter\tikz@addmode\expandafter{\tikz@picmode}
}
\makeatother

\tikzset{
  working demo/.pic={
    \pic[sub pic actions] {demo sub};
  },
  not working demo/.pic={
    \pic[pic actions] {demo sub};
  },
  demo sub/.pic={
    \path[pic actions] (0,0) to[out=30,in=180] ++(1.5,.5) -- ++(0,.5) -- ++(.3,0) -- ++(0,-.5) to[out=0,in=150] ++(2.5,-.5) to[out=210,in=-30] (0,0);
  }
}

\begin{document}
\begin{tikzpicture}
\pic[draw] {
  %not
  working demo};
\end{tikzpicture}
\end{document}

答案2

我在这里给出我对这个问题的部分了解。

免责声明

我不是专家,我在这里写的大部分内容都是猜测。

先决条件

有些样式从范围继承到路径,有些则不继承。有些样式从节点和图片的路径继承到路径,有些则不继承。

我不知道确切的规则是什么,但我认为唯一不能被继承的风格是“动作”(正如我们在有关图片动作的文档中所读到的:“动作”是绘图、填充、着色和剪切或它们的任意组合)。

如何为图片添加造型

我不知道图片样式究竟是如何工作的,但我的调查(不是对代码的调查,因为正如我所说,我不是专家)让我找到了以下算法:

\tikzset{picname/.pic={the pic code}}
\pic[some style]{picname};

变成了类似

\begin{scope}[coordinate transform, every pic, some style]
  the pic code
\end{scope}

这是过于简化的版本,因为有foreground code等等background code......

pic actions当我们想要继承某些动作时,它很有用,比如说fill“某种风格”到内部路径“图片代码”. 并且这在文档中有详细记录。

“图片操作”的问题

问题是pic actions,当我们用类似的东西来调查它时

\pgfkeysgetvalue{/tikz/pic actions/.@cmd}{\temp};
\wlog{\meaning\temp}

结果是

\long macro:#1\pgfeov ->\tikz@addmode {\tikz@picmode }

这不是我们观察到的某些“普通”风格,例如:

\long macro:#1\pgfeov ->\pgfkeysalso {fill}

pic actions因此,这可能就是为什么使用嵌套图片失败的关键。

我认为pic actions只能用于简单继承。但是仍然存在错误pic actions,因为当您在嵌套图片中使用它时,latex 会冻结而不会引发错误。

当我们需要更复杂的继承时,我们需要使用一些样式,如下所示。

如何克服“图片操作”

下面是一个示例,说明如何创建具有复杂风格和真正复杂嵌套的复杂图片(其工作方式类似于@cfr 答案)。

\tikzset {
  every cherry fruit/.style = {fill,red},
  cherry fruit/.style={every cherry fruit/.append style={#1}},
  every cherry stem/.style = {brown, ultra thick},
  cherry stem/.style={every cherry stem/.append style={#1}},
  every cherry leaf/.style = {fill, green},
  cherry leaf/.style={every cherry leaf/.append style={#1}},
  cherry/.pic = {
    \draw[every cherry fruit] (0,0) {[rounded corners=1cm] -- (1,1) -- (2,-1) -- (0,-3) -- (-2,-1) -- (-1,1)} -- cycle;
    \draw[bend left, every cherry stem] (0,0) to coordinate[pos=.7] (A) (1,2);
    \draw[bend left, every cherry leaf] (A) to +(-1.5,1) to cycle;
  },
  every cherry one/.style={},
  cherry one/.style={every cherry one/.append style={#1}},
  every cherry two/.style={},
  cherry two/.style={every cherry two/.append style={#1}},
  two cherries/.pic = {
    \pic[every cherry one]{cherry};
    \pic[xshift=5cm, every cherry two]{cherry};
  }
}
\begin{tikzpicture}
  \pic[cherry fruit=yellow]{cherry};
  \pic[xshift=5cm]{cherry};
  \pic[yshift=-5cm, cherry fruit={fill=none, draw, ultra thick}, cherry one={cherry fruit=yellow}]{two cherries};
\end{tikzpicture}

在此处输入图片描述

答案3

您可以将所需的选项作为参数传递给two

\documentclass[tikz, border=5pt]{standalone}
\tikzset{
  one/.pic = {\path[pic actions] (0,0) -- (1,1);},
  two/.pic = {\path pic[#1]{one};}
}

\begin{document}

  \begin{tikzpicture}
    \path pic[ultra thick, red, draw]{one};
    \path pic{two={thin, blue, draw}};
  \end{tikzpicture}

\end{document}

由于我不确定您如何使用它,所以我不确定这是您已拒绝的选项、尚未拒绝但现在必须拒绝的选项,还是可能有用的选项。

相关内容