Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

Suppose one were to make a simple "dumbbell" shape in TikZ. (My actual example is more complicated, but let's stick to this simple one). That is, we want a shape constructed out of two circles, and a line segment between them.

You'd like to define this via \pgfdeclareshape, and then in your TikZ code, you want to be able to "inherit the anchors" from the subshapes, i.e. you'd like to be able to say something like:

\node[dumbbell] (A) at (0,0) {};
\draw (A.left circle.north east) to (1,3);

As I understand it, this is currently impossible. Or is there a clever way to do it? The point is you don't want to have to manually code in all those anchors on each circle. Even if that's okay in this example, imagine if your shape was complicated and contained dozens of circles, ellipses, trapeziums, etc... you really want to be able to construct a shape made out of "subshapes", and then have a "directory structure" to the anchors.

One way of doing it would be for dumbbellto have a \savedmacrowhich actually constructs two more nodes of type circle, something like this:

\pgfdeclareshape{dumbbell}
...
\savedmacro{}{\pgfnode{circle}{my line.left anchor}{}{[?name of dumbbell node?].left circle}{.code to create circle..}}

The trouble is that we'll need to know the name of the dumbbell node when we create its offspring circles. Because we'll want to actually use it in practice as above, i.e.

\node[dumbbell] (A) at (0,0) {};
\draw (A.left circle.north east) to (1,3);

Is this possible? Can you ask pgf to give you the name of a node?

Update: 17-05-2011. I've tried Martin's second suggestion below of using the key \tikz@fig@name to access the name of the node inside of the \pgfdeclareshape environment, and then creating a new node using \pgfnode based on this name, inside the \backgroundpath. I can't get it to work, I get an error No shape named ... is known. Perhaps the new node I create is somehow only local to the \backgroundpath environment where it is created?

share|improve this question
    
I was also dreaming about a (node.subshape.anchor) notation for some time. However, without hacking PGF this shouldn't be directly possible. But you should be able to simulate this by defining an anchor with the name subshape.anchor in the \pgfdeclareshape. –  Martin Scharrer May 17 '11 at 10:27
    
Fair enough... but then I'd have to repeat all my anchor definitions many times, so I'd need to have \anchor{left circle.north west} and also \anchor{right circle.north west} and so on inside my \pgfdeclareshape{dumbbell}. –  Bruce Bartlett May 17 '11 at 11:01
    
Yes, that's the issue. Have a look on \deferredanchor in the manual. It seems to be new in the 2.10 version and looks promising. –  Martin Scharrer May 17 '11 at 11:06
    
Nodes are always defined global. Otherwise they wouldn't be accessible outside their own path (which is also a TeX group). They are even accessible outside the tikzpicture (but not very useful except with overlay,remember picture). –  Martin Scharrer May 17 '11 at 14:18
    
@Martin: Mmm, I wonder what's going on. I made a stripped down shape called mycircle which basically draws a circle and has a center anchor. Then I declared my dumbbell shape, pertinently, it has a \leftanchor saved anchor for the left part of the dumbbell, where a circle is supposed to go. Inside the \backgroundpath, I have the code \leftanchor \pgfnode{mycircle}{center}{}{\nodename left circle}{}. Then I get the error No shape named D left circle is known if I attempt to use it in TikZ for a dumbbell named D. –  Bruce Bartlett May 17 '11 at 14:28

2 Answers 2

up vote 17 down vote accepted

The answer is "yes, it can" but "no, it shouldn't". A little experimenting shows that the node name is available within the shape definition and so it is possible to define a shape hierarchically. For example, the following does the dumbbell.

\makeatletter
\pgfdeclareshape{dumbbell}{
  \savedanchor{\center}{%
    \pgfpointorigin}
  \anchor{center}{\center}
  \backgroundpath{
    \edef\@temp{%
      \noexpand\node[circle,draw] (\tikz@fig@name-left) at (-1,0) {};
      \noexpand\node[circle,draw] (\tikz@fig@name-right) at (1,0) {};
      \noexpand\draw (\tikz@fig@name-left) -- (\tikz@fig@name-right);
}
    \@temp
}
}

So \node[dumbbell,draw] (a) {}; will draw the dumbbell and name it a. The components can be accessed as a-left and a-right. For example:

\begin{tikzpicture}
\node[dumbbell] (a) at (2,0) {};
\draw (a-left) to[out=60,in=120] (a-right);
\end{tikzpicture}

would produce

dumbbell

But there are lots of problems with this code! The first is that I'm using "high level" (ie pure TikZ) commands inside the \pgfdeclareshape which is Not Good. The second is that the paths constructed in the background path are not all One Path. Even if we converted everything to low-level PGF commands, we would still not have a single path but several: one for each of the node components and one for the bit between them (to see this, note that when a node is constructed on a path then it is possible to colour the node boundary a different colour to the main path. This is only possible with separate paths). This makes styling the node paths an absolute nightmare. You would have to ensure that the styles got inherited correctly, but also you have to protect against infinite recursion.

A more robust method would be to do all the actual drawing in the main shape and then add extra nodes as necessary purely for the purpose of having anchors. Off the top of my head, here's an example of that:

\pgfdeclareshape{dumbbell}{

  \savedanchor{\center}{%
    \pgfpointorigin}
  \anchor{center}{\center}
  \backgroundpath{
    \edef\@temp{%
      \noexpand\node[circle,draw=none,fill=none,minimum size=2cm](\tikz@fig@name-left) at (-2,0) {};
      \noexpand\node[circle,draw=none,fill=none,minimum size=2cm](\tikz@fig@name-right) at (2,0) {};
    }
    \@temp
    \pgfpathcircle{\pgfqpoint{-2cm}{0cm}}{1cm}
    \pgfpathcircle{\pgfqpoint{2cm}{0cm}}{1cm}
    \pgfpathmoveto{\pgfqpoint{-1cm}{0cm}}
    \pgfpathlineto{\pgfqpoint{1cm}{0cm}}
  }
}

(Again, the high-level commands ought to be converted to low-level ones.) Note that I explicitly turn off any rendering commands on the extra shapes. Actually, I would prefer to draw the extra nodes outside the background path just to keep the anchor nodes separate from the main shape. So I think that my actual code would be something a bit like the following:

\documentclass{standalone}
\usepackage{tikz}

\makeatletter
\pgfdeclareshape{dumbbell}{

  \savedanchor{\center}{%
    \pgfpointorigin}
  \anchor{center}{\center}
  \backgroundpath{
    \pgfpathcircle{\pgfqpoint{-2cm}{0cm}}{1cm}
    \pgfpathcircle{\pgfqpoint{2cm}{0cm}}{1cm}
    \pgfpathmoveto{\pgfqpoint{-1cm}{0cm}}
    \pgfpathlineto{\pgfqpoint{1cm}{0cm}}
  }
  \beforebackgroundpath{
    \tikzset{minimum size=2cm}
    {
      \pgftransformxshift{-2cm}
      \pgfnode{circle}{center}{}{\tikz@fig@name-left}{}
    }
    {
      \pgftransformxshift{2cm}
      \pgfnode{circle}{center}{}{\tikz@fig@name-right}{}
    }
  }
}

\makeatother
\begin{document}
\begin{tikzpicture}
\node[dumbbell,draw,line width=2pt,red,fill=orange] (a) at (2,0) {};
\draw (a-left) to[out=60,in=120] (a-right);
\node[dumbbell,red,draw] (b) at (2,-3) {};
\draw (b-left) to[out=60,in=-120] (a-right);
\end{tikzpicture}
\end{document}

dumbell mark 3

Note that I'm now using the correct low-level commands for the nodes. This means, for example, that they are unaffected by any TikZ options. You can see this by removing the draw from the calling command and adding every node/.style={draw} to the \tikzset inside the definition of the shape. The extra nodes still aren't drawn.

In fact, this looks like a really neat solution to a problem I was (honestly!) pondering with the TQFT package inspired by Topological Quantum Field Theory diagrams with pstricks or tikz, namely how to add sensible anchors to the boundary components. I'm glad you asked this question!

share|improve this answer

Update:

Just found this in pgfmoduleshapes.code.tex: The node name seem to be accessible as \pgfreferencednodename It is officially only for generic anchors defined with \pgfdeclaregenericanchor, but seems to be also usable for normal anchors.


The name of a node defined like \node[dumbbell] (A) at (0,0) {}; is apparently stored in \tikz@fig@name and you should be able to saved it aware using the following command inside your \pgfdeclareshape:

\savedmacro{\nodename}{%
    \let\nodename\tikz@fig@name
}

However you probably need to create the subnodes in some of the path commands of the shape, e.g. in \backgroundpath of \foregroundpath. There you can simply use the anchors of the main node to position the subnodes. AFAIK PGF also shifts the coordinate system while processing the node anchors so that the center/origin of the node is at (0,0). I don't think you need to now the name of the main node for this.

share|improve this answer
    
Mmm, interesting. Is the reason that I "probably need to create the subnodes in some of the path commands of the shape" because we can't yet be certain that the anchors of the main node have been set up when we're inside the \savedmacro? Also, is it ok to define an \anchor inside the \backgroundpath{..}, won't pgf barf at that? –  Bruce Bartlett May 17 '11 at 11:37
    
@Bruce: I actually just know got your tricky usage of \savedmacro{}{...}. Yes, I would be sure that the \savedanchors are ready yet. It might depend on the order in the shape declaration. –  Martin Scharrer May 17 '11 at 11:44
    
Ok, thanks. Pondering your suggestions... –  Bruce Bartlett May 17 '11 at 11:51

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.