4

TiKz 3.0 introduce pics and I like them, but it seems that they are not as powerfull as I wish. For example I'm not able to use nested pics like this :

\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}

Is this a bug, or is this simply how pics works?

There is no error message. Simply (Xe/PDF)LaTeX don't finish the job.

EDIT : Following Mark Wibrow's comment if we delete pic actions from pic{two} it works:

\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}

enter image description here

So the problem seems to be inside pic actions.

9
  • 1
    tikzpictures can't be nested, (TikZ) matrix can't be nested, (TikZ) pics can't be nested... Commented Dec 5, 2014 at 16:40
  • You can put tikzpictures inside a node, and so make nastings. So in some sens you can ;) And why there is no error if it is by design ?
    – Kpym
    Commented Dec 5, 2014 at 17:05
  • You could try two/.pic={\pic {one};} Commented Dec 5, 2014 at 17:23
  • 2
    @Kpym TikZ (in its current version) is not designed to allow tikzpictures into a node. Sometimes it works, but this is not guaranteed. It's the same for matrices (explicitly forbidden). For pics, I could be wrong. Commented Dec 5, 2014 at 23:13
  • 1
    @Kpym In fact, nested pics are allowed but there is always some bugs. Commented Dec 5, 2014 at 23:26

3 Answers 3

5

I just came across this and it seems to still be an issue with TikZ in 2023 (PGF version 3.1.10).

Digging a bit deeper in the code, we get that:

  • pic actions expands to \tikz@addmode{\tikz@picmode} which appends \tikz@picmode to the macro \tikz@mode.
  • But when TikZ encounters a pic then what it does is \let\tikz@picmode=\tikz@mode. So \tikz@picmode is the contents of the \tikz@mode of the outer picture.

Therefore, with a nested pic, we have the following journey:

  • At the outermost level, \tikz@mode contains the instructions on what to do to the path (whether to draw or fill it, for example).

  • When TikZ encounters the first pic, \tikz@picmode is let to this, so now contains these instructions. Within the pic, \tikz@mode is cleared.

  • Now we invoke pic actions which appends \tikz@picmode to \tikz@mode, so that \tikz@mode now consists of (at least) \tikz@picmode (there might have been other stuff also added to it).

  • We want to have a nested pic, so let's let TikZ encounter another pic. It aliases \tikz@mode as \tikz@picmode which means that \tikz@picmode now consists of (at least) \tikz@picmode.

    And here's our problem. It's the old \def\a{\a}\a problem. \tikz@picmode contains a reference to itself, so when it is (finally) invoked then it enters an infinite loop.

A possible solution (which might introduce additional problems, but at the very least solves this) is to add an expansion step so that when \tikz@picmode is appended to \tikz@mode then it is actually the contents of \tikz@picmode that is appended to \tikz@mode. This avoids the infinite recursion.

So pic actions ought to be:

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

In my own code, I'm currently calling this sub pic actions as I don't like to clobber TikZ's version of things even if I think I have an improvement.

\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}
3

I give here my partial knowledge of the problem.

Disclaimer

I'm not a texpert, and most of the thing that I write here are guesses.

Prerequisite

Some styles are inherited from the scope to the path and other are not. Some styles are inherited from the path to nodes and pics and other are not.

I don't know what is the precise rule, but I suppose that the only styles that are not inherited are the "actions" (as we can reed in the documentation about pic actions : "actions" are drawing, filling, shading, and clipping or any combination thereof).

How styling pics works

I don't know how exactly styling pics works, but my investigation (not of the code, because as I said I'm not an texpert) leads me to the following algorithm :

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

is transformed to something like

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

This is oversimplified version because there is foreground code and background code and so on ...

So pic actions is useful when we want to inherit some action, let say fill from 'some style' to a path that is inside 'the pic code'. And this is well documented in the doc.

The problem with 'pic actions'

The problem with pic actions is that when we investigate it with something like

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

the result is

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

which is not what we observe for some "ordinary" style, like :

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

So probably here is the key why using pic actions in nested pics fails.

I think that pic actions must be used only for simple inheritance. But still there is a bug in pic actions, because when you use it in nested pics latex freeze without throwing an error.

When we need more complexe inheritance, we need to use some styling as shown below.

How to overcome 'pic actions'

Here is an example of how we can create complexe picture with complexe style and realy complex nesting (it works in a fashion similar to @cfr answer).

\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}

enter image description here

2

You could pass the options you want as an argument to 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}

Since I'm not sure just how you're using this, I'm not sure whether this is an option you've rejected, an option which you've not rejected yet but will have to reject now, or an option which might be of use.

3
  • thanks for your answer, but it doesn't respond to my question 'Is this a bug, or is this simply how pics works?'. I know that I can do what you propose, but doing this pic{two} is not used like standard pic. I'm more and more convinced that this is a bug. I can't see a reason to not be able to pass pic actions style to the subpic.
    – Kpym
    Commented Dec 6, 2014 at 7:11
  • @Kpym I was really responding to your comment about the problem of one being drawn but not the other.
    – cfr
    Commented Dec 6, 2014 at 15:30
  • ok. And I was responding to Mark Wibrow when he said 'In fact it seems to make not much sense to say \path pic [pic actions] {...}.' ;)
    – Kpym
    Commented Dec 6, 2014 at 15:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .