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.

I'm struggling to get my head around the pgfkeys package. I currently use the keycommand package, which I find very easy to understand: it's just ordinary LaTeX commands with named rather than numbered arguments. However, I would like to use pgfkeys because my project is heavily based on TikZ, and I believe it is a more powerful solution. I think understanding how to translate the following little example from keycommand to pgfkeys would help me a lot.

Example

I make a command to draw a doublecircle gadget. It has two arguments: diameter and color, both optional, with default values 1 and red respectively.

\documentclass[border=5mm]{standalone}

\usepackage{tikz}
\usepackage{keycommand}

\newcounter{currentheight}
\setcounter{currentheight}{0}

\newkeycommand{\doublecircle}[diameter=1,color=red]{%
  \fill[\commandkey{color}] (0,\thecurrentheight) circle (\commandkey{diameter}/2);
  \fill[\commandkey{color}] (\commandkey{diameter}/2,\thecurrentheight) circle (\commandkey{diameter}/2);
}

\begin{document}

\begin{tikzpicture}[y=-1cm]
\doublecircle[color=blue]
\addtocounter{currentheight}{5}
\doublecircle[diameter=4]
\addtocounter{currentheight}{6}
\doublecircle[diameter=3,color=green]
\end{tikzpicture}

\end{document}

Result

three doublecircle gadgets

share|improve this question
1  
If you work it out, you become a PGF Master. –  Nicholas Hamilton May 13 '13 at 10:46
    
Is there a specific reason you used a counter (only integers) instead of a simple macro or a "value"? Does the pgfkeys implementation needs an equivalent add to height key? –  Qrrbrbirlbel May 13 '13 at 12:40
    
No good reason. It's just that I know how to add values to counters, and I don't know how to do that off the top of my head if I had just used a simple macro like \newcommand{\currentheight}{0}. –  John Wickerson May 13 '13 at 12:46
1  
Have you seen my answer to a similar question? It's very popular and seems like exactly what you're asking. –  Ryan Reich May 13 '13 at 14:20

2 Answers 2

up vote 12 down vote accepted

A possible solution:

\documentclass[border=5mm]{standalone}

\usepackage{tikz}

\pgfkeys{/tikz/.cd,% to set the path
  circle height/.initial=0, % initial value
  circle height/.get=\circleheight, % to get the value from a macro
  circle height/.store in=\circleheight, % to store the value into a macro
  diameter/.initial=1,
  diameter/.get=\diameter,
  diameter/.store in=\diameter,
  circle color/.initial=red,
  circle color/.get=\circlecolor,
  circle color/.store in=\circlecolor,
}

\newcommand{\pgfdoublecircle}[1][]{%
  \tikzset{circle color=red,diameter=1,#1}% the default options will be overwritten by the one introduced
  \fill[\circlecolor] (0,\circleheight) circle (\diameter/2);
  \fill[\circlecolor] (\diameter/2,\circleheight) circle (\diameter/2);
}

\begin{document}
\begin{tikzpicture}[y=-1cm]
\pgfdoublecircle[circle color=blue]
\pgfdoublecircle[circle height=5,diameter=4]
\pgfdoublecircle[circle height=11,diameter=3,circle color=green]
\end{tikzpicture}

\end{document}

According to Qrrbrbirlbel comment and other Q/A like What do the pgfkeys key handlers .get and .store in do? and Arguments for TikZ style I was convinced to abandon the inefficient method to set up keys. Hence, it should be preferred:

\pgfkeys{/tikz/.cd,% to set the path
  circle height/.store in=\circleheight, % to store the value into a macro
  circle height=0, % initial value
  diameter/.store in=\diameter,
  diameter=1,
  circle color/.store in=\circlecolor,
  circle color=red,
}

The final result remains obviously consistent with the latter approach.

share|improve this answer
    
I preferred to put clearly the height coordinate rather than with an increasing counter, but that approach is also doable. –  Claudio Fiandrino May 13 '13 at 10:51
    
This is splendid, thank you. So much more helpful than poring over the manual! –  John Wickerson May 13 '13 at 10:56
    
I notice that you set red as the default color twice in the code: you have both circle color/.initial=red and \tikzset{circle color=red,...}. Is this necessary? –  John Wickerson May 13 '13 at 12:42
    
@JohnWickerson Yes, because the \pgfdoublecircle macro isn’t scoped or grouped. The circle color=blue will survive the macro expansion and the value will be still blue for the next \pgfdoublecircles. –  Qrrbrbirlbel May 13 '13 at 12:59
    
Thanks @Qrrbrbirlbel. In which case, can one manage without setting the .initial values? –  John Wickerson May 13 '13 at 13:01

There are a few ways to get, set and store values, see also the first part of another answer of mine.
Depending on the key and how its value is needed one must use the one or the other way.

Oftentimes we can also see

/my namespace/key/.code=\pgfmathsetmacro\namespace@key{#1}

which can be used here too and makes the value available in an already calculated form, though here it isn’t really needed as TikZ throws almost anything in PGF Math itself. See (yet) another answer of mine on how to use this more effectively (the part about the +).

Code

\documentclass[tikz,border=5mm]{standalone}
\newcommand*{\doublecircleset}{\pgfqkeys{/double circle}}
\makeatletter
\doublecircleset{
  /double circle/.code={\pgfqkeys{/double circle}{#1}},
  color/.initial=red, % initialize key 'color' with value 'red'
  height/.initial=0, % see color
  diameter/.store in=\doublecircle@diameter, % the key 'diameter' will be used more than once and also in the 'scope', let's store it right away
  diameter=1,                                % the default value is '1'
  add to height/.style={height/.expanded=\pgfkeysvalueof{/double circle/height}+#1} % let's take the value of the 'height' key and add '#1'.
                                                     % expansion is needed otherwise we have a selv-referencing macro (and an infinite loop)
}
\newcommand{\doublecircle}[1][]{%
  % let's group the whole macro so that the changes to the keys and whatnot are local, this is how TikZ works.
  % we could have used '\begingroup \doublecircleset{#1} … … … \endgroup' here, but the scope allows us to set the radius too.
  \scope[/double circle={#1}, radius=(\doublecircle@diameter)/2] % alright, execute the key=values and set the radius
    \pgfkeysgetvalue{/double circle/height}\doublecircle@height  % we need the height more than once, a single macro would be nice to use
    \fill[color=\pgfkeysvalueof{/double circle/color}]           %
      (0,{\doublecircle@height}) circle []                       % if we use () or , in the key (which is allowed: PGF math) we need to enclose it in braces
      (xyz cs: x={(\doublecircle@diameter)/2},                   % either way
               y={\doublecircle@height}) circle []
      ;
  \endscope
}
\makeatother
\begin{document}
\begin{tikzpicture}[y=-1cm]
\doublecircle[color=blue]
\doublecircleset{add to height=5}
\doublecircle[diameter=4]
\doublecircleset{add to height=6}
\doublecircle[diameter=3, height=2*(2+3), color=green]
\end{tikzpicture}
\end{document}
share|improve this answer
    
Thanks so much @Qrrbrbirlbel, I appreciate all the explanations. Is \scope[X] ... \endscope the same as \begin{scope}[X]...\end{scope}? –  John Wickerson May 13 '13 at 13:34
1  
@JohnWickerson Yes, it is (more or less). TikZ includes \begingroup and \endgroup in its environments on its own; using LaTeX’s \begin and \end would just add another group (and some testing). –  Qrrbrbirlbel May 13 '13 at 13:38
    
Ok, thanks. Also, what does xyz cs mean? –  John Wickerson May 13 '13 at 13:40
1  
@JohnWickerson Check the PGF manual for the xyz coordinate system. It is simply the explicit version of (x, y, z) (where all are initially 0). –  Qrrbrbirlbel May 13 '13 at 13:48

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.