[Tex/LaTex] optional arguments to tikzpicture to set style globally

pgfkeystikz-pgf

I can't get my head around how to get tikz and pgfkeys to do what I want. I would like to define a command which draws a little tikz picture. I'd like this command that would take optional keyval arguments that get passed to the optional argument of \tikz and that get dealt with appropriately. This last stage is causing me trouble. Here's what I have so far

\documentclass{article}
\usepackage{tikz}
\usepackage{xparse}
\tikzset{  
  foo/.style={color=#1},
  foo/.default=black,
  bar/.style={color=#1},
  bar/.default=black,
}

\NewDocumentCommand{\drawthing}{o}{
  \tikz[#1]{\draw[thick,foo] (0,1)--(1,0);
    \draw[thick,foo] (0,0)--(1,1);
    \draw[thick,bar] (0,0) -- (0,1);
  }
}

\begin{document}
Here is text\drawthing[foo=red,bar=blue]
\end{document}

So what this does is \drawthing draws three lines. Two are "foo" lines and one is a "bar" line. I'd like to be able to control the colours of these two types of thing individually. The default style should be that both are black.

The problem is I'm not understanding something about how the keys work. I'm sure it's a simple thing but I am really struggling with it. So it looks like foo=red is having no effect. When I write \draw[thick,foo=green] that works fine. But I want to have this set globally for this picture, so I want to have an option that works as an argument to the tikzpicture as a whole.

What obvious and simple trick have I missed?

Best Answer

The reason is that you have specified the foo style with a default key which kicks in if none given.

What you need to realize is that a style only applies to the current segment of code. \draw[foo] ... and \draw[color=black] is equivalent in your case.

Usage of keys

When you use keys you have to explicitly use them or redefine them.

Consider this:

\tikzset{
  foo/.style={#1},
  bar/.style={foo={#1}}
}

This will let foo have an empty style! It will simply do nothing unless you add an argument.
\draw[foo={color=green,thick}] is thus equivalent to \draw[color=green,thick]. Also \draw[bar={color=green,thick}] is equivalent to \draw[foo={color=green,thick}]. It is just another alias for the same key.

If you however do the styles like this:

\tikzset{
  foo/.style={#1},
  bar/.style={foo/.append style={#1}}
}

You will collect styles in to the key foo by using style bar. This is very good in cases where scoped environments are used:

\begin{tikzpicture} % Now all styles who uses foo will do nothing
  \draw[foo] (0,0) -- ++(1,0);
  \begin{scope}[foo=very thick,bar={color=red},yshift=-1ex]
    \draw[foo] (0,0) -- ++(1,0); % Will be red and thick
    \draw[foo=dotted] (0,-1ex) -- ++(1,0); % Will be red and thick and dashed
  \end{scope}
\end{tikzpicture}

First picture

However, as you can see using bar has the effect of adding to the foo style until the end of the environment.

In your case

What goes wrong?

If we use the following definitions and the following code it is seen that other behaviour is taking place

\tikzset{
  foo/.style={color=#1},
  foo/.default=black, % If no argument is given then this kicks in as argument.
  bar/.style={foo/.append style={#1}}
}

\begin{tikzpicture}[foo=red] % Now everything will have the style red attached
  \draw (0,0) -- ++(1,0); % red
  \draw[foo] (0,-1ex) -- ++(1,0); % will have black color as foo is called without an argument, so /.default kicks in!
  \begin{scope}[bar=very thick,yshift=-2ex]
    \draw[foo] (0,0) -- ++(1,0); % Will be thick, but not red
    \draw[foo=red] (0,-1ex) -- ++(1,0); % Will be red and thick
  \end{scope}
\end{tikzpicture}

Second picture

Remember everytime you define default this will be the argument passed if none is given. Thus it could potentially overwrite what you have done.

However as you will see here everything that is not related to the argument #1 is handled properly

\tikzset{
  foo/.style={dashed,color=#1},
  foo/.default=black, % If no argument is given then this kicks in as argument.
  bar/.style={foo/.append style={#1}}
}

\begin{tikzpicture}[foo=red] % Now everything will have the style red and dashed attached
  \draw (0,0) -- ++(1,0); % red, dashed
  \draw[foo] (0,-1ex) -- ++(1,0); % will have black color and dashed as foo is called with out an argument, so /.default kicks in!
  \begin{scope}[bar=very thick,yshift=-2ex]
    \draw[foo] (0,0) -- ++(1,0); % Will be dashed, thick, but not red
    \draw[foo=red] (0,-1ex) -- ++(1,0); % Will be red and thick and dashed
  \end{scope}
\end{tikzpicture}

Third picture

Keys are read consecutively

If you write dashed,very thick,dotted,thick it will only be dotted and thick, not very thick, nor combined dashed and dotted.

Solution to your problem

The solution is to use another key as the style, but which you never should need to use otherwise.

\tikzset{
  foo hidden/.style={}, % Provide a hidden style
  foo/.style={foo hidden/.append style={color=#1}}, % foo changes `foo hidden`
  foo/.default=orange,
  bar hidden/.style={},
  bar/.style={bar hidden/.append style={color=#1}}, % bar changes `bar hidden`
  bar/.default=green,
}

\NewDocumentCommand{\drawthing}{o}{
  \tikz[#1]{\draw[thick,foo hidden] (0,1)--(1,0);
    \draw[thick,foo hidden] (0,0)--(1,1);
    \draw[thick,bar hidden] (0,0) -- (0,1);
  }
}

Blue and red: \drawthing[foo=red,bar=blue]

Green and blue: \drawthing[foo=blue,bar]

Black and orange: \drawthing[foo]

Fourth picture

If you want it to always have the default you are then allowed to do:

\NewDocumentCommand{\drawthing}{o}{
  \tikz[foo,bar,#1]{\draw[thick,foo hidden] (0,1)--(1,0);
     \draw[thick,foo hidden] (0,0)--(1,1);
     \draw[thick,bar hidden] (0,0) -- (0,1);
  }
}