Semitransparent outside “clipped” region?

To highlight part of a graph for a presentation that I'm writing, I would like to "circle" an area and make semitransparent everything outside that area. Anything inside that area should remain opaque. Is there a way to accomplish this inside TikZ?

As an example, in the following picture, I would like to make nodes a and c semitransparent, as well as the fraction of the edge from a to b that lies outside the circle.

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{positioning}

\begin{document}
\begin{tikzpicture}[vertex/.style={draw, fill={#1}}]
\node (a) [vertex=blue] {};
\node (b) [vertex=blue, right=of a] {};
\node (c) [vertex=green, right=of b] {};

\draw (a) edge (b);

\draw [red] (b) circle [radius=0.75];
\end{tikzpicture}
\end{document}


Of course, I could always accomplish this by manually making the desired pieces transparent, but for a complex picture it would be better to use some kind of clipping. I've read through the manual and vaguely understand clipping and transparency, but I don't see how to put those keys together to get what I want.

4 Answers

Two attempts here, the first is not ideal, but "fakes" transparency by overlaying a semi-transparent white rectangle and uses the spy library:

\documentclass[tikz,border=5]{standalone}
\usetikzlibrary{positioning,spy}
\tikzset{%
white out/.style={
preaction={%
even odd rule,
fill=white,
fill opacity=0.75,
insert path={
% This covers the current picture.
(current bounding box.south west)
rectangle
(current bounding box.north east)
}
}
}
}

\begin{document}
\begin{tikzpicture}[vertex/.style={draw, fill={#1}}, spy using outlines={circle, magnification=1}]

\node (a) [vertex=blue] {};
\node (b) [vertex=blue, right=of a] {};
\node (c) [vertex=green, right=of b] {};

\draw (a) edge (b);

\spy [size=0.75cm] on (b) in node [thin, red, white out] at (b);

\end{tikzpicture}
\end{document}


The second manages the semi-transparency but must capture the original picture inside a scope:

\documentclass[tikz,border=5]{standalone}
\usetikzlibrary{positioning}

\newbox\tikzcapturebox
\tikzset{capture scope/.style={
execute at begin scope={\global\setbox\tikzcapturebox=\hbox\bgroup},
execute at end scope={\egroup\copy\tikzcapturebox}
},
captured scope as path picture/.style={
preaction={path picture={\pgfextra{\copy\tikzcapturebox}}}
}}

\begin{document}
\begin{tikzpicture}[vertex/.style={draw, fill={#1}}]

\node [text=gray!50, font=\footnotesize] at (1.25,0) {some text behind the figure};

\begin{scope}[transparency group, opacity=0.5, capture scope]
\node (a) [vertex=blue] {};
\node (b) [vertex=blue, right=of a] {};
\node (c) [vertex=green, right=of b] {};
\draw (a) edge (b);
\end{scope}

\draw [red, captured scope as path picture] (a) circle [radius=0.5];
\draw [blue, captured scope as path picture] ([xshift=-0.25cm]c) circle [radius=0.25];

\end{tikzpicture}
\end{document}


• I would've never thought of this idea! I agree that it's not quite ideal, but it seems to work great for my purpose. I'll wait to accept this answer, in case someone comes up with something else, but otherwise I'll try to remember to accept your answer in a day or so. Thanks so much for your help! – Henry DeYoung May 29 '14 at 21:24

Here is a very simple solution (trick: a white background).

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{positioning}
\begin{document}
\begin{tikzpicture}[vertex/.style={draw, fill={#1}}]
\node (a) [vertex=blue] {};
\node (b) [vertex=blue, right=of a] {};
\node (c) [vertex=green, right=of b] {};
\draw (a) edge (b);
\draw [red] (b) circle [radius=0.75];

\fill[white,fill opacity=.75]
(current bounding box.south west) rectangle (current bounding box.north east)
(b) circle[radius=0.75cm+.5\pgflinewidth];
\end{tikzpicture}
\end{document}

• I like this answer for its simplicity without manually repeating the nodes a, b, and c, etc. One question: if I comment out the red circle (and use a different fill color for the rectangle-circle path, say, gray, just to see what is going on), I get a strange result in which only some of the area surrounding the circle is filled transparent gray. Using even odd rule doesn't seem to help. Do you know why? – Henry DeYoung May 30 '14 at 20:19
• @HenryDeYoung If you comment out the red circle, the current bouding box changes... Instead of comment out the red circle, try to change its \draw to \path and remove +.5\pgflinewidth. – Paul Gaborit May 31 '14 at 8:42
• @PaulGaborit: this might be a rather stupid question, but what if I want to use a rectangle for the "spyglass" instead of a circle? I cannot seem to get it to work. Thanks in advance for any input! :) – Kjetil Sonerud Jun 16 '15 at 20:07
• @KjetilSonerud You may use the even odd rule option (p.173, pgfmanual).. – Paul Gaborit Jun 16 '15 at 21:37
• @PaulGaborit: Perfect - worked like a charm! Thanks a lot - just what I was looking for! :) – Kjetil Sonerud Jun 17 '15 at 19:21

A layman's solution (whose doesn't like spies ;)):

\documentclass[tikz,border=5pt]{standalone}
\usetikzlibrary{positioning}

\begin{document}
\begin{tikzpicture}[vertex/.style={draw, fill={#1}}]
\node (a) [vertex=blue] {};
\node (b) [vertex=blue, right=of a] {};
\node (c) [vertex=green, right=of b] {};

\draw (a) edge (b);

\draw [red] (b) circle [radius=0.75];
\fill[white,fill opacity=0.75](current bounding box.south west) rectangle
(current bounding box.north east);
\begin{scope}
\clip (b) circle [radius=0.75];
\node [vertex=blue, right=of a] {};
\draw (a) edge (b);
\draw [red] (b) circle [radius=0.75];
\end{scope}
\end{tikzpicture}
\end{document}


• circle[radius=0.75cm+.5\pgflinewidth] gives a better clip region. – Paul Gaborit May 30 '14 at 13:57
• This is a good idea too, but I'd rather not have to explicitly recreate the parts inside the red circle. I am greedy that way. :) – Henry DeYoung May 30 '14 at 20:30

This looks like the same problem with inverse clipping and plus filling with some color with opacity less than 1. I stole Paul's nice solution from How can I invert a 'clip' selection within TikZ?. And to make it do more than clipping you need to reuse the path and hence the preaction.

EDIT: I confused myself. No clipping is necessary obviously (thanks Henry). Just filling is enough. Renamed it to invselect. Result stays the same.

\documentclass{beamer}
\usepackage{tikz}
\usetikzlibrary{positioning}

\tikzset{
vertex/.style={draw, fill={#1}},
invselect/.style={insert path={{[reset cm]
(-16383.99999pt,-16383.99999pt) rectangle (16383.99999pt,16383.99999pt)
}}
},
hilite/.style={fill=#1,opacity=0.9,overlay,invselect}
}

\begin{document}
\begin{frame}

\begin{tikzpicture}
\fill[yellow] (-3,-2) rectangle (3,2);
\node (a) [vertex=blue] {};
\node (b) [vertex=blue, right=of a] {};
\node (c) [vertex=green, right=of b] {};

\draw (a) edge (b);

\draw [red] (b) circle [radius=0.75];

\path<2>[hilite=white] (b) circle (0.75) ;
\path<3>[hilite=brown] (a) circle (1);
\end{tikzpicture}

\end{frame}
\end{document}


• Thanks for your help! One quick question: When I remove the preaction=clip, there is no change to the results (as far as I can tell). Why is that option there? Also (ok, two questions, I guess), based on the answer that you linked to, I would have expected preaction={clip, insert path=...}, but that doesn't have the correct result. So, could you explain why moving the insert path=... outside the preaction is the right thing to do? Sorry to ask so many questions; I just want to understand so I can better solve future problems that I run into. – Henry DeYoung May 30 '14 at 21:22
• @HenryDeYoung Too few coffees. Of course you are right. There is no need for clipping. I'm probably getting tired from work. I'll fix it. – percusse May 30 '14 at 21:33