25

I'm confused about tikz pics. I can freely define them without issue, and even add coordinates inside that will be available by name inheritance like (-A) as for example in \pic (S) at (0,0) {myPic}; \draw (S-A) -- ++(0,0);. This is all documented around p. 255 of the pgf manual.

However I don' understand why it is not possible to give a name to a pic like the one above if no coordinate is defined inside it. By default it would be great if it could behave like a normal node.

MWE:

\documentclass{minimal}
\usepackage{tikz}

\tikzset{
    myBlock/.pic = {
        \node[draw,minimum width=1cm,minimum height=0.5cm] at (0,0) {};
    },
    myBlockNamed/.pic = {
        \node[draw,minimum height=1cm,minimum width=0.5cm] (-m) at (0,0) {};
    },
}

\begin{document}
\begin{tikzpicture}
    \pic () at (0,0) {myBock}; % can't give it a name
    \pic (S) at (3,0) {myBlockNamed}; % can give it a name
    \draw (S-A) -- ++(2,0);
\end{tikzpicture}
\end{document}

The underlying point is I'm working on a control blocs library (so that we can stop the chat below with @Alenanno). For example here is the definition of a math operator

\tikzset{
    mySum background/.style = {
        circle, minimum size=1.5em, fill=white,
    },
    mySum edge/.style = {
        draw=black, circle, minimum size=1.5em, line width=.5pt,
    },
    pics/bOp/.style = {
    code = {
      \node [mySum background] {};
      \foreach \t [count=\i] in {#1}{
        \pgfmathsetmacro{\angle}{\i*90}
        \node[anchor=center, font=\tiny] at (\angle:0.45em) {$\t$};
      }
      \node [mySum edge] {};
    },
  },
}
2
  • pic is the mechanism to include a predefined drawing. It is not a referable object. Unless there is something defined in it, that is. Think of it as a container. And your example needs some corrections to be compiled e.g., \pic at (0,0) {myBlock};\pic (S) at (3,0) {myBlockNamed};\draw (S-m) -- ++(2,0); works. The first one you need to remove the at (0,0) from the definition
    – percusse
    Commented May 2, 2015 at 9:01
  • As far as I know you cannot refer to a pic directly. Is there a reason why you're using that instead of a standard node?
    – Alenanno
    Commented May 2, 2015 at 9:02

4 Answers 4

24

There is a trick that can be used in some circumstances which exploits the fact that a pic is put inside a scope which can be named with the local bounding box key. Note that this will not work in trees or graphs.

\documentclass[tikz,border=5]{standalone}
\tikzset{pics/.cd,
  pic a/.style={code={
      \node [fill=red!20, shape=circle] {A};
  }}
}

\begin{document}
\begin{tikzpicture}
\pic [local bounding box=A1] at (0,0) {pic a};
\pic [local bounding box=A2] at (3,0) {pic a};
\foreach \i in {0,15,...,345}{
  \draw [red, ->] (\i:1) -- (A1);
  \draw [blue, ->] (3,0)++(\i:1) -- (A2);
}
\end{tikzpicture}
\end{document}

enter image description here

It is also possible to use the name given to the pic by defining a "wrapper" key which explicitly adds a scope to the code:

\documentclass[tikz,border=5]{standalone}
\usetikzlibrary{fit}%
\makeatletter
\tikzset{pics/named scope code/.style={code={\tikz@fig@mustbenamed%
  \begin{scope}[local bounding box/.expanded=\tikz@fig@name]#1\end{scope}%
}}}
\makeatother

\tikzset{pics/.cd,
  pic a/.style={named scope code={
      \node [fill=red!20, shape=circle] {A};
  }}
}

\begin{document}
\begin{tikzpicture}
\pic (A1) at (0,0) {pic a};
\pic (A2) at (3,0) {pic a};
\foreach \i in {0,15,...,345}{
  \draw [red, ->] (\i:1) -- (A1);
  \draw [blue, ->] (3,0)++(\i:1) -- (A2);
}
\end{tikzpicture}
\end{document}

The result is the same as before.

2
  • Got a quick question. Does the second method (explicitly adding a scope to the picture) also won't work with trees and graphs?
    – user101590
    Commented Sep 5, 2016 at 0:43
  • Don't worry. I tried it, but the tree can't be built as there's a lot of problems with the node shape names (the lines that connect the nodes and build the tree).
    – user101590
    Commented Sep 5, 2016 at 1:12
6

From the Pgf Manual (version 3.0.0), page 252:

  1. Unlike nodes, pics cannot be referenced later on. You can reference nodes that are inside a pic, but not “the pic itself.” In particular, you cannot draw lines between pics the way you can draw them between nodes. In general, whenever it makes sense that some drawing could conceivably be connected to other node-like-things, then a node is better than a pic.

  2. If pics are used to emulate the full power of a node (which is possible, in principle), they will be slower to construct and take up more memory than a node achieving the same effect.

So, if you're doing something a node does well, and there is no particular reason for using pic, you're better off using a node. There is a workaround in the answer to the question "How to give a name to \pic", but it's exactly that, a workaround.

However, if you want to be able to position nodes around another node, you can check this question I asked some time ago (How to position nodes around another node in TiKZ?) or you use a Tikz foreach with a regular node.

Updated answer

Now that I know what you need, here's a solution with Tikz. The newcommand \mySumn (you can rename it) takes two arguments like \mySumn{#1}{#2}, where #1 is the node's name, and #2 is the position.

I tried to add an optional argument using above of but didn't succeed. However you can do the same with this code but in a different way. Consider the following:

\mySumn{p1}{0,0};
\mySumn{p2}{$(p1.north east)+(1em,1em)$};

It will place a node p1 at the coordinates (0,0) and then it will place another node p2 at p1's north east +1em on the x and the y axis (diagonally above on the right).

Of course you can still draw edges from one node to the other.

Output

figure 1

Code

\documentclass[margin=10pt]{standalone}
\usepackage{tikz}
\usepackage{xparse}

\usetikzlibrary{calc,arrows.meta,positioning}

\tikzset{
    mySumbk/.style = {
        draw, circle, minimum size=1.5em, fill=white,
    },
}

\newcommand{\mySumn}[2]{
    \node[mySumbk,fill=white] (#1) at (#2) {};
        \foreach \angle/\maop in {
        90/+,
        180/-
        }{
   \node[anchor=center, font=\tiny] at ($(#1)+(\angle:.45em)$) {$\maop$};
}}

\begin{document}
\begin{tikzpicture}[>=latex]

\mySumn{p1}{0,0};
\mySumn{p2}{$(p1.north east)+(1em,1em)$};

\draw (p1) -- (3,0);
\draw (p1) edge[out=90,in=180] (p2);
\end{tikzpicture}
\end{document}
10
  • The thing is the syntax becomes ugly if one has to use a node: \node (S) at (0,0) {\tikz{\draw pic {myBlock};}};which is all but convenient.
    – s__C
    Commented May 2, 2015 at 9:56
  • @s__C Why not simply \node[myBlock] (S) at (0,0) {};? Of course, you'd need to fix the \tikzset accordingly.
    – Alenanno
    Commented May 2, 2015 at 9:57
  • Then I cannot define anymore a complex myBlock. I used a rectangle in the MWE just to keep it simple... I have pics with arguments etc.
    – s__C
    Commented May 2, 2015 at 10:01
  • Well, then you should mention exactly what you're using. :P What is the shape like? You can probably still do it without using a pic.
    – Alenanno
    Commented May 2, 2015 at 10:04
  • Its a rectangular shape with \draw commands inside, so I really doubt I can bypass defining pics...
    – s__C
    Commented May 2, 2015 at 10:05
1

Old question, but I was not happy with the local bounding box solution since I couldn't find a way to have a better control over it (distance to the inner content, for instance).

My solution was to use a nested picture in the pic, together with the remember picture option. The names are passed as arguments in a cascading fashion. With this, you can not only reference the pic as a node (because in fact it is a node) but also reference its contents.

Another point is that with this solution the content of a pic is always centralized, which was also that I was looking for when working on this code.

Be aware:

  • you have to compile the document twice because of remember picture.
  • nested nodes can be tricky because of inheritance.

enter image description here

Here is the code:

\documentclass[tikz]{standalone}

\usetikzlibrary{positioning}

\begin{document}
\begin{tikzpicture}[remember picture]
  \tikzset{
    mine/.pic={
      \node[draw](#1){
        \begin{tikzpicture}[remember picture]
          \node[draw](#1-1){#1-1};          
          \node[draw, right=of #1-1](#1-2){#1-2};
        \end{tikzpicture}
      };
    },
  }

  \pic{mine=a};
  \pic[right=of a]{mine=b};

  \draw[thick, blue, <->](a-1)to[out=90, in=90](b-1);
  \draw[thick, blue, <->](a-2)to[out=-90, in=-90](b-2);
  \draw[thick, red](a)to(b);
\end{tikzpicture}
\end{document}
3
  • 3
    Don't nest TikZ pictures. There is always a better way.
    – percusse
    Commented Jan 20, 2018 at 17:02
  • 1
    @percusse Well, I also don't like to nest pictures. In my answer I pointed the reasons why I chose that path. I will be happy to learn about other ways to do that with those points in mind.
    – tcpaiva
    Commented Jan 20, 2018 at 17:36
  • You could use a fit node around the one defined by the local bounding box to achieve the same effect without nesting pictures. For positioning a pic, take a look at the latest version of tikzmark. Commented Jun 30, 2022 at 16:56
0

It appears that if I define a command like for my operator:

\tikzset{
    mySum background/.style = {
        circle, minimum size=1.5em, fill=white,
    },
    mySum edge/.style = {
        draw=black, circle, minimum size=1.5em, line width=.5pt,
    },
}
\newcommand{\bOp}[1]{
  \tikz[outer sep=0pt]{
    \node [mySum background] {};
    \foreach \t [count=\i] in {#1}{
      \pgfmathsetmacro{\angle}{\i*90}
      \node[anchor=center, font=\tiny] at (\angle:0.45em) {$\t$};
    }
    \node [mySum edge] {};
  }
}

It is then possible to create a node that contains a fake pic, as what is above is exactly what one would use to define a pic.

\node (1) at (0,0) {\bOp{+,-,,}};
\draw (4,0) -- (1);
5
  • Your code does not compile (and I'm still not sure what you want to do). :P
    – Alenanno
    Commented May 2, 2015 at 16:01
  • It should compile now...
    – s__C
    Commented May 2, 2015 at 16:32
  • Nesting TikZ pictures is a Bad Idea.
    – cfr
    Commented May 2, 2015 at 16:39
  • Do you have a better one @cfr?
    – s__C
    Commented May 2, 2015 at 18:38
  • 1
    No. But Mark Wibrow already explained one.
    – cfr
    Commented May 2, 2015 at 19:22

You must log in to answer this question.

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