1

The pre-problem is tikz tree use pic as tree node, which has a perfect answer. What's more, I want to get pic worked with forest package, with nodes and cooredinates inside pic able to be referenced after.

Example pic:

\documentclass[tikz, border=1cm]{standalone}
\makeatletter
\tikzset{
  test n/.store in=\test@n,
  test n=3,
  test/.pic={
    \foreach \x [evaluate=\x as \ang using (\x-1)*360/\test@n] in {1, ..., \test@n} {
      \draw (0, 0) -- (\ang:5mm) coordinate (-\x);
\end{tikzpicture}
\end{document}

enter image description here

Here is @Schrödinger's try, which make pics work but coordinates inside pic fail to be referenced after.

\documentclass[varwidth, border=1cm]{standalone}
\usepackage{forest}
\makeatletter
\tikzset{
  test n/.store in=\test@n,
  test n=3,
  pics/test/.style={code={
    \tikzset{test/.cd,#1}%
    \foreach \x [evaluate=\x as \ang using (\x-1)*360/\test@n] in {1, ..., \test@n} {
      {\draw (0, 0) -- (\ang:5mm) coordinate (-\x);}
    \pgfkeys{/tikz/test/extra}  
    }
  }},test/.cd,extra/.code={}
}
\makeatother

\begin{document}
\begin{forest}
[pft
 [{\pic (a1) [test n=6] {test};\\},matrix]
 [{\pic (a2) [test n=6] {test};\\},matrix]
]
% \draw (a1-1) -- (a2-2); not worked
\end{forest}
\end{document}

enter image description here

3
  • 3
    The forest package "hacks" the TikZ coordinate system, see section 3.16 The forest coordinate system of the forest manual. Even with tikzcshack=false I was not able to come up with a nice, working solution. In the past, some users have used the \subnode command from the tikzmark library, which does not get "hacked", but this probably won't lead to a very elegant solution of the "access pic coordinates from outside" problem in this context. That is, I do not know how many users besides Sašo Zivanović and cfr will be able to give a nice answer here.
    – user194703
    Jun 9, 2020 at 17:06
  • Thanks for your help and try anyway, they have helped a lot!
    – ZhiyuanLck
    Jun 9, 2020 at 17:09
  • forest cs and the hack which makes it possible to use it implicitly (e.g. simply !u for the parent node, instead of (forest cs:!u)) are actually unrelated to the issue. Jun 10, 2020 at 18:55

1 Answer 1

2

It can be done, but requires some (perhaps unadvisable) hacking.

Forest typesets all nodes using PGF's deferred node positioning mechanism (\pgfpositionnodelater -- pgfpositionnodenow, see section 107.2.3 of the PGF manual). Using this mechanism, it is possible to typeset the node first (in a box) and position it (anywhere) later. Forest clearly needs (some) such procedure, because it must compute the outlines of the nodes before it can pack them into a tree.

It is two features of the deferred node positioning which conspire to make the normal reference to the PGF/TikZ nodes within these nodes impossible. First, when a deferred node is created, its name is prefixed by not yet positionedPGFINTERNAL. Second, while the content of a deferred node is typeset, deferred positioning (\ifpgflatenodepositioning) is in effect and applies to any nodes within the content. In effect, the name of any node (and this includes coordinates) within a deferred node (and thus within any Forest node) is prefixed by not yet positionedPGFINTERNAL.

So in principle, it is possible to refer to nodes within Forest nodes, by manually prefixing them with not yet positionedPGFINTERNAL, but with a caveat: the coordinates of these inner nodes are relative to the origin of the Forest node. The following code demostrates both. (Look at the final two lines and the definition of \nyp, the rest is as in the OPs code.)

\documentclass{article}
\usepackage{forest}
\usetikzlibrary{matrix}
\makeatletter
\tikzset{
  test n/.store in=\test@n,
  test n=3,
  pics/test/.style={code={
    \tikzset{test/.cd,#1}%
    \foreach \x [evaluate=\x as \ang using (\x-1)*360/\test@n] in {1, ..., \test@n} {
      {\draw (0, 0) -- (\ang:5mm) coordinate (-\x);}
    \pgfkeys{/tikz/test/extra}  
    }
  }},test/.cd,extra/.code={}
}
\makeatother

\begin{document}

\def\nyp{not yet positionedPGFINTERNAL}
\begin{forest}
[pft
 [{\pic (a6) [test n=6] {test};\\},matrix,name=matrix6]
 [{\pic (a5) [test n=5] {test};\\},matrix,name=matrix5]
]
\fill[red] (matrix6) + (\nyp a6-2) circle [radius=2pt];
\fill[blue] (matrix5) + (\nyp a5-2) circle [radius=2pt];
\end{forest}

\end{document}

Now I don't know nearly enough about PGF/TikZ internals to be able to judge whether \ifpgflatenodepositioning remaining true when the content of a deferred node is typeset is a feature or a bug. I played a bit and switched it off (\pgflatenodepositioningfalse\pic ... or even \pgfpositionnodelater{\relax}\pic ...), and I could then refer to the inner nodes as (a6-2) and (a5-2) — but their positions were not the same as above, they seem to be shifted by an offset I can't figure out.

(Only after I already wrote the above I noticed that the blue dot comes a bit off even in the above code. So apparently, some weird shift happens even without the attempt to switch off deferred positioning inside the node.)

If you wanted the normal access to the inner nodes, it is of course possible to precompute them, e.g. like this:

\documentclass{article}
\usepackage{forest}
\usetikzlibrary{matrix}
\makeatletter
\tikzset{
  test n/.store in=\test@n,
  test n=3,
  pics/test/.style={code={
    \tikzset{test/.cd,#1}%
    \foreach \x [evaluate=\x as \ang using (\x-1)*360/\test@n] in {1, ..., \test@n} {
      {\draw (0, 0) -- (\ang:5mm) coordinate (-\x);}
    \pgfkeys{/tikz/test/extra}  
    }
  }},test/.cd,extra/.code={}
}
\makeatother

\begin{document}

\def\nyp{not yet positionedPGFINTERNAL}
\begin{forest}
[pft
 [{\pic (a6) [test n=6] {test};\\},matrix,name=matrix6]
 [{\pic (a5) [test n=5] {test};\\},matrix,name=matrix5]
]
\path
  foreach \mn in {6,5} {
    (matrix\mn)
    foreach \tn in {1,...,5} {
       + (\nyp a\mn-\tn)
       coordinate(a\mn-\tn)
    }
  }
;
\fill[red] (a6-2) circle [radius=2pt];
\fill[blue] (a5-2) circle [radius=2pt];
\end{forest}

\end{document}

It might be also possible automatically collect the names of the inner nodes (by hacking into \pgfmultipartnode, \pgfcoordinate etc) and perform the above "\nyp prefix stripping" automatically. But I'd say that's too hacky even for my taste, plus there's still the issue of the mysterious shift, which possibly makes this entire answer completely useless. ;-)

1
  • An excellent answer! Thank you for your effort. That makes me learn a lot about tikz.
    – ZhiyuanLck
    Jun 11, 2020 at 3:18

You must log in to answer this question.

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