[Tex/LaTex] Drawing a proper shadow for overlapping objects

groupingnodesrenderingshadowstikz-pgf

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):

example

(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?

Best Answer

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{https://tex.stackexchange.com/q/43618/86}
\usepackage{tikz}
\usetikzlibrary{shapes.geometric}
\usetikzlibrary{shadows}

\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).

Drop shadow on background layer

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

Drop shadow on background layer, main shape not filled