20

I want to create a series of images that visualise a recursion tree as walked on by an algorithm. That means that different styles are applied to nodes depending on the step the algorithm is in, such as hiding, highlighting, etc. Consider this example:

example
[source]

As you can see, some styles are (not) applied "from image i on"; for instance, hidden nodes become unvisited (grayed out) and then normal, but never change back. Other styles are applied more flexibly, for example the yellowish highlighting active (which shows what node the visualised algorithm currently investigates). Above image was created by explicitly writing code for six images. Naturally, I want to avoid duplicating code as much as possible.

I thought that the following should be a straightforward way to achieve this:

\tikzset{<TikZ style definitions>}
\newcommand{\image}[1]{<TikZ image code>}
\begin{document}
  \foreach \n in {1,...,<N>}{%
    \image{\n}
  }
\end{document}

Now, \image has to choose styles depending on parameter #1 (i.e. \n); I am fine with hardcoding which style is applied on which image numbers.

But neither

\node \ifthenelse{<condition>}{[style=<name>]}{} {<label>};

nor

\node[\ifthenelse{<condition>}{style=<name>}{}] {<label>};

works. So I tried to use ifthenelse inside the (now parametric) style definition, which also failed, and then put

\tikzset{%
  test/.code={\ifthenelse{<condition>}{style=<name>}{}}
}

in the preamble, together with

\node[test=<param>] {<label>};

in the image; this does not work, either.

How can I choose TikZ styles conditionally?

A feasible workaround might be to create the nodes conditionally (i.e. duplicating node definitions for each case), but this is nasty for trees. Therefore, I would prefer a more surgical solution.

I use xifthen for conditions, in particular \isin. For example, I try assigning style active by checking

\ifthenelse{\isin{!#1!}{!1!2!4!6!}}{<set style active>}{<no style>}

in the case of root node a, where #1 is the parameter of \image. Ideally, I would be able to put such a statement at every affected node, that is the information which style is applied on which image is defined locally at the styled elements (for all images at once).

4
  • 1
    Using \node \ifnum#1=1 [style 1]\fi {<label>}; works fine. I think that you might get better answers if you made your question a little more concrete: What are the styles going to change, are they always going to be used in a loop, how many different options will you need? This can probably be solved more elegantly if the problem is described more concretely.
    – Jake
    Commented May 15, 2012 at 17:06
  • @Jake I already said that I needed more complex conditions than number equality. I will include a small example tomorrow, though, if there is no answer till then.
    – Raphael
    Commented May 15, 2012 at 18:09
  • I have been using the solution from Is there something like \providetikzstyle similar to \providecommand?, although it really should be \providetikzset based on Should \tikzset or \tikzstyle be used to define TikZ styles?. Commented May 15, 2012 at 18:42
  • For those interested, see here how it turned out.
    – Raphael
    Commented Jul 9, 2012 at 20:55

3 Answers 3

14

A really simple approach would probably be to use the image number directly to index the style:

\documentclass{article}

\usepackage{tikz}

\tikzset{
    image/.cd,
    1/.style={fill=orange},
    2/.style={fill=yellow},
    3/.style={fill=cyan}
}

\newcommand{\image}[1]{
    \tikz{ \draw [image/#1] circle [radius=1cm];}
}

\begin{document}
  \foreach \n in {1,...,3}{%
    \expandafter\image\expandafter{\n}
  }
\end{document}

However, as Jake pointed out, a more complex selection scheme is probably wanted by the OP, who in particular mentioned a "parametric style". The following uses Jake's example, but moves the testing from the macro to the style definition:

\documentclass{article}

\usepackage{tikz}
\usepackage{xifthen}

\tikzset{
    image/.cd,
    1/.style={fill=orange},
    2/.style={fill=yellow},
    3/.style={fill=cyan},
    test/.code={%
      \ifthenelse{\isin{#1}{ABC}}{\tikzset{image/1}}{
      \ifthenelse{\isin{#1}{DEF}}{\tikzset{image/2}}{
      \ifthenelse{\isin{#1}{GHI}}{\tikzset{image/3}}{
      }}}
    },
}

\newcommand{\image}[1]{
    \tikz{ \draw [image/test=#1] circle [radius=1cm];}
}

\begin{document}
  \foreach \n in {A,E,G}{%
    \expandafter\image\expandafter{\n}
  }
\end{document}

enter image description here

6
  • I don't think the OP is actually using numbers in their application, but strings and substrings. At least that's what I gather from their last sentence "I use xifthen for conditions, in particular \isin." and the somewhat testy "I already said that I needed more complex conditions than number equality.".
    – Jake
    Commented May 15, 2012 at 19:59
  • @Jake: Hm... I now found the \isin part, but I must still be overlooking where they already said that more complex conditions are needed. I will wait for a more complete MWE that details these constraints.
    – Daniel
    Commented May 15, 2012 at 20:11
  • Sorry, that last bit was in reply to my comment on their question.
    – Jake
    Commented May 15, 2012 at 20:14
  • @Jake: No, that was fine. I had found the comment, but still do not know to what exactly the already in "as I already said" refers. Said where? Anyway, I have improved my solution that now resembles yours, but with the tests in the style definition.
    – Daniel
    Commented May 15, 2012 at 20:35
  • Ah yes, now I understand. Nice answer!
    – Jake
    Commented May 15, 2012 at 20:46
14

A style may define other styles... So, no test ! ;-)

\documentclass{standalone}

\usepackage{tikz}

\tikzset{
   set styles 1/.style={
     circle style/.style={fill=red},
     square style/.style={fill=orange},
   },
   set styles 2/.style={
     circle style/.style={fill=cyan},
     square style/.style={fill=violet,fill opacity=.5},
   },
   set styles A/.style={
     circle style/.style={draw},
     square style/.style={fill=violet,fill opacity=.5},
   },
}
\newcommand\image[1]{
  \begin{scope}[set styles #1]
    \path[circle style] circle [radius=.6cm];
    \path[square style] (-.5,-.5) rectangle (.5,.5);
  \end{scope}
}

\begin{document}

\foreach \n in {1,2,A}{%
  \begin{tikzpicture}
    \image{\n}
  \end{tikzpicture}
}

\end{document}

enter image description here

Here, your example with this approach:

\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage{tikz}
\usepackage[paperwidth=4cm,paperheight=3.25cm,margin=.10cm]{geometry}
\usetikzlibrary{positioning}

\begin{document}

\tikzset{%
  active/.style={fill=yellow!30},
  unvisited/.style={opacity=.2},
  hidden/.style={opacity=0},
  lb/.style={color=green!70!black},
  ub/.style={color=red!70!black},
  ind exp/.style={scale=.7},
  step 1/.style={
    a/.style={active},
    b/.style={},
    c/.style={},
    a b/.style={hidden},
    a c/.style={hidden},
    a ind/.style={lb},
    a exp/.style={hidden},
    b ind/.style={hidden},
    b exp/.style={hidden},
    c ind/.style={hidden},
    c exp/.style={hidden},
  },
  step 2/.style={
    step 1,
    a b/.style={unvisited},
    a c/.style={unvisited},
    b ind/.style={lb},
    c ind/.style={lb},
  },
  step 3/.style={
    step 2,
    a/.style={},
    b/.style={active},
    a b/.style={},
    b exp/.style={ub},
  },
  step 4/.style={
    step 3,
    a/.style={active},
    b/.style={},
    a exp/.style={ub},
  },
  step 5/.style={
    step 4,
    a/.style={},
    c/.style={active},
    a c/.style={},
    c exp/.style={ub},
  },
  step 6/.style={
    step 5,
    a/.style={active},
    c/.style={},
  },
}

\newcommand\mypict[2]{
  \begin{tikzpicture}[level 1/.style={sibling distance=10mm}]
    \begin{scope}[step #1]    
      \node [a] (a) {$[a]$}
      child [a b] { node[b] (b) {$[b]$} }
      child [a c] { node[c] (c) {$[c]$} };

      \node[a ind,ind exp,below right=-3.5mm and -2mm of a] {$5$};
      \node[a exp,ind exp,above right=-3.5mm and -2mm of a] {$#2$};

      \node[b ind,ind exp,below right=-3.5mm and -2mm of b] {$6$};
      \node[b exp,ind exp,above right=-3.5mm and -2mm of b] {$7$};

      \node[c ind,ind exp,below right=-3.5mm and -2mm of c] {$5$};
      \node[c exp,ind exp,above right=-3.5mm and -2mm of c] {$5$};

      \node[right=1cm of a,scale=.7] {$p#1$};
    \end{scope}
  \end{tikzpicture}
}

\foreach \n/\aexp in {1/5,2/5,3/5,4/7,5/7,6/5}{
  \mypict{\n}{\aexp}
  \clearpage
}

\end{document}

enter image description here

6
  • This is very nice, but I don't think it addresses the OPs requirement of checking for the presence of a substring in a larger string (as indicated by the hint that "I use xifthen for conditions, in particular \isin"). Though they never disclosed why they need those kinds of conditionals, so maybe this solution is exactly what they're looking for...
    – Jake
    Commented May 15, 2012 at 21:22
  • You're probably right (I missed his first comment). Perhaps he will say more tomorrow. Commented May 15, 2012 at 22:15
  • @Jake: Maybe you skipped my first paragraph. I agree it might not be clear what precisely my needs are, but "the never disclosed why" is clearly not true. Anyway, I'll edit later.
    – Raphael
    Commented May 16, 2012 at 8:52
  • I added an example. As it is, your solution would require me to define the same styles multiple times; is this the most efficient way to do things?
    – Raphael
    Commented May 17, 2012 at 13:09
  • @Raphael I added your example to my solution. I'm not sure this is the most efficient way but it works and the factorization is good. Commented May 18, 2012 at 8:34
10

You could use a "variable" style in your drawing that is set to the desired value according to your condition before the drawing command:

\documentclass{article}

\usepackage{xifthen}
\usepackage{tikz}

\tikzset{
    current style/.style={},
    first/.style={fill=orange},
    second/.style={fill=yellow},
    third/.style={fill=cyan}
}

\newcommand{\image}[1]{
    \ifthenelse{\isin{#1}{ABC}}{\tikzset{current style/.style=first}}{
        \ifthenelse{\isin{#1}{DEF}}{\tikzset{current style/.style=second}}{
            \ifthenelse{\isin{#1}{GHI}}{\tikzset{current style/.style=third}}{}
        }
    }
    \tikz{ \draw [current style] circle [radius=1cm];}
}

\begin{document}
  \foreach \n in {A,E,G}{%
    \expandafter\image\expandafter{\n}
  }
\end{document}
3
  • Isn't /.is choice more comfortable? Commented May 15, 2012 at 18:28
  • Well, that depends on what kind of "complex conditions" the OP needs. I don't think you can use the \isin string comparison method for .is choice, right?
    – Jake
    Commented May 15, 2012 at 18:33
  • I don't know. I didn't test it. You are the expert of tikz ;-) Commented May 15, 2012 at 18:54

You must log in to answer this question.

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