Drawing a proper shadow for overlapping objects

I draw images that are composed of several overlapping nodes. If I add a drop shadow to each node, the shadow of the second node is drawn over the first node, as seen in the picture below (which consist of two cylinder nodes drawn from bottom to top, each with a shadow):

(Thanks to zeroth for providing the image in reply to this related question. )

The documentation to TikZ states it clearly: You can apply a shadow only to a path, but not to a scope. The workaround I'm using is this:

• Create a \foreach loop that loops over {drop shadow,}, thus assigning first drop shadow, then the empty string to a macro, say, \s
• Use \s for the options of every node. (Perhaps I could append \s to every node and every path, didn't try that yet.)

Of course the image is drawn twice in the resulting PDF, which somewhat affects rendering speed and perhaps the size of the resulting PDF. In addition, the resulting code is rather ugly. My questions are:

1. Is there a clean way of achieving the desired result using a TikZ/pgf feature? Perhaps it is possible to turn a sequence of TikZ/pgf commands into a single path?
2. If not:
• Is it possible to add some style parameters to nodes and paths in the first pass so that the details of the image are not rendered when drawing the shadow?
• Would it be technically feasible to implement an environment shadowgroup that would hide the complexity and ugliness behind that?
-
Wasn't this exact question just asked earlier today? Unless this is a totally new question, you should delete this and edit the original question. If this is really a new question, you should add a link in the question to the earlier one as they are clearly related. –  Peter Grill Feb 6 '12 at 19:54
No. The first question was about removing the darker parts in overlapping shadows. This is about drawing the shadow for the whole image at once instead of for each individual object. –  krlmlr Feb 6 '12 at 19:57
Ok. Please edit the question to cross reference them as this is then a follow up to the earlier one, and someone looking at this would be interested in looking at the earlier one as well. –  Peter Grill Feb 6 '12 at 19:58
Why not declaring a new shape? That would save you from a lot of trouble. –  percusse Mar 3 '12 at 12:06

The follow-up question Pass options to the scope that is internally created by preaction was, to my amazement, solvable with my code from "Z-level" in TikZ. I'm going to have to resort to astonishment (and plagarism) now because it turns out that this works with drop shadow with no modification (my solutions tend to be the epitome of hackishness so the fact that one works for something it was not tested with is Definitely Unusual).

Here's the code. I've taken the liberty of removing the nested tikzpictures (see What are most important variables set at the beginning of a tikzpicture? of a scope? and links therein).

\documentclass{article}
%\url{http://tex.stackexchange.com/q/43618/86}
\usepackage{tikz}
\usetikzlibrary{shapes.geometric}

\pgfdeclarelayer{back}
\pgfsetlayers{back,main}

\makeatletter
\pgfkeys{%
/tikz/on layer/.code={
\pgfonlayer{#1}\begingroup
\aftergroup\endpgfonlayer
\aftergroup\endgroup
},
/tikz/node on layer/.code={
\pgfonlayer{#1}\begingroup
\expandafter\def\expandafter\tikz@node@finish\expandafter{\expandafter\endgroup\expandafter\endpgfonlayer\tikz@node@finish}%
},
}
\makeatother

\newcommand{\dbpart}[1]{
\node[drop shadow={opacity=1.0,on layer=back},draw, cylinder, shape aspect=1.5, inner sep=0.3333em, fill=white,
rotate=90, minimum width=1cm, minimum height=0.45cm] (cyl) at (0,#1) {};
}
\newcommand{\dbicon}[1][]{
\begin{scope}[#1]
\dbpart{0cm}%
\dbpart{0.4cm}%
\end{scope}
}

\begin{document}
\begin{tikzpicture}
\dbicon
\end{tikzpicture}
\end{document}


Here's what happens (oh, and I put fill=white as I agree with Frédéric that it looks nicer).

Let's have it without the fill just to see.

-
Marvelous! So, the on layer parameter is used for the preaction of the shadow, which in turn affects the scope around this preaction? Thanks a lot -- I'd never be able to put this together using my limited knowledge about PGF. How about adding on layer to the core implementation of TikZ? –  krlmlr Mar 6 '12 at 20:21
@user946850 I don't have that power! –  Loop Space Mar 6 '12 at 20:26
Well, but it sure would be worth suggesting. -- I have tested your solution with my code, works like a charm. A clear "accept", but I'd like to drop Frédéric a part of the bounty, too, for coming up with layers. Let's see if this works out. –  krlmlr Mar 9 '12 at 8:18
Glad to hear it works. For future reference, I think this is a case where my answer is the one to accept (since it's the one you ended up using) and Frédéric's gets the original bounty (since it's the one that actually involved work - mine was more of a "Oh, look! It works!", no actual effort involved). I don't think you can take back a bounty ... actually, I'll put a bounty on for Frédéric's answer. –  Loop Space Mar 9 '12 at 8:54
@user946850 Done. I have to wait 23hrs to actually award it. –  Loop Space Mar 9 '12 at 8:57

This answer gets the job done, but it might not be as clean and efficient as you would like. It builds on the answer to your previous question and adds layers (background layer).

\documentclass[border=5pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{backgrounds}
\usetikzlibrary{shapes.geometric}

\newcommand{\dbpart}[1]{
\node[draw, cylinder, shape aspect=1.5, inner sep=0.3333em,
rotate=90, minimum width=1cm, minimum height=0.45cm] (cyl) at (0,#1) {};
\begin{scope}[on background layer]
\node[drop shadow={gray!50,opacity=1},cylinder, shape aspect=1.5, inner sep=0.3333em,
rotate=90, minimum width=1cm, minimum height=0.45cm] (cyl) at (0,#1) {};
\end{scope}
}
\newcommand{\dbicon}{
\begin{tikzpicture}
\dbpart{0cm}%
\dbpart{0.4cm}%
\end{tikzpicture}
}

\begin{document}
\begin{tikzpicture}
\node[inner sep=0pt] {
\dbicon
};
\end{tikzpicture}

\end{document}


The result is

On a personal note, if I were to use shadows, I would also fill the cylinders (fill=white). The result would be

-
Thank you. This solution is, compared to my workaround, more efficient in terms of rendering time and also more elegant. Now I wonder if it's possible to somehow draw the shadow on the background layer directly, without invoking the \node command twice. I remember from the docs that drop shadow uses some kind of "path recording and replay" mechanism, which could perhaps be changed to draw the shadow on the background layer? –  krlmlr Mar 3 '12 at 20:23
I didn't fill the cylinders on purpose, to show what's happening here more clearly. In the real picture, they're even shaded :-) –  krlmlr Mar 3 '12 at 20:27
The only way I know how to reuse drawing information is with a preaction or a postaction, but, TMK, these only work within a path, not inside node specifications. In addition the elements included within a layer (you can define your own with \pgfdefinelayer) must be enclosed within the layer (appropriate) layer environment --> you can't use them inside a node specification. It might be possible with deep pgf stuff. –  Frédéric Mar 3 '12 at 21:12
Let's see if anybody knows how to do this: tex.stackexchange.com/q/46957/8057 –  krlmlr Mar 6 '12 at 10:24
I took the liberty to rescale the images. –  krlmlr Mar 7 '12 at 12:42

Here is a shape declaration as a starting point. I didn't quite address the shadow problem, and actually I don't understand why it appears as garbled but I hope finish it later. I would appreciate if somebody can have a look too.

\documentclass{article}
\usepackage{tikz}

\makeatletter
\pgfdeclareshape{dbicon}
{
\anchor{center}{\pgfpointorigin}
\savedanchor{\west}{\pgf@y = 0mm \pgf@x = 1cm} %%
\anchor{east}{\west \pgf@x=-\pgf@x}
\anchor{west}{\west}
\savedanchor{\north}{\pgf@y = 3mm \pgf@x = 0mm} %%
\anchor{north}{\north}

\backgroundpath{
\west \pgf@xa=\pgf@x \pgf@xb=0.75\pgf@xa
\pgfpathmoveto{\pgfpoint{\pgf@xa}{0}}
\pgfpathlineto{\pgfpoint{\pgf@xa}{-\pgf@xb}}
\pgfpatharc{0}{-180}{\pgf@xa and 0.3\pgf@xb}
\pgfpathlineto{\pgfpoint{-\pgf@xa}{0}}
\pgfpathellipse{\pgfpointorigin}{\pgfpoint{\pgf@xa}{0mm}}{\pgfpoint{0mm}{0.3\pgf@xb}}
\pgftransformshift{\pgfpoint{0}{0.9\pgf@xa}} %Shift up and make a full cylinder
\west \pgf@xa=\pgf@x \pgf@xb=0.75\pgf@xa
\pgfpathmoveto{\pgfpoint{\pgf@xa}{0}}
\pgfpathlineto{\pgfpoint{\pgf@xa}{-\pgf@xb}}
\pgfpatharc{0}{-180}{\pgf@xa and 0.3\pgf@xb}
\pgfpathlineto{\pgfpoint{-\pgf@xa}{0}}
\pgfpathellipse{\pgfpointorigin}{\pgfpoint{\pgf@xa}{0mm}}{\pgfpoint{0mm}{0.3\pgf@xb}}
}
}
\makeatother

\begin{document}
\begin{tikzpicture}