6

I have been using the extremely nice 3D pie chart code posted by Alain Matthes as an answer to this question for quite some time. I've constantly been tweaking it to get some features function more nicely - for examples, the individual sectors can now be named (instead of just using their index). My latest addition was an attempt to rotate the whole thing, such that the angle would not necessarily start at the mathematical 0 position, but can be shifted arbitrarily. However, I can't seem to get it to work:

\documentclass{standalone}

\usepackage{pgfplots,tikz}
\usetikzlibrary{fadings}

\pgfkeys{%
  /piechartthreed/.cd,
  scale/.code                =  {\def\piechartthreedscale{#1}},
  mix color/.code            =  {\def\piechartthreedmixcolor{#1}},
  mix rate high/.code        =  {\def\piechartthreedmixratehigh{#1}},
  mix rate low/.code         =  {\def\piechartthreedmixratelow{#1}},
  background color/.code     =  {\def\piechartthreedbackcolor{#1}},
  name/.code                 =  {\def\piechartthreedname{#1}}}

\newcommand\piechartthreed[2][]{% 
  \pgfkeys{/piechartthreed/.cd,
    scale            = 1,
    mix color        = gray,
    mix rate high    = 20,
    mix rate low     = 5,
    background color = white,
    zeroangle/.store in = \zeroan,
    zeroangle        = 0,
    name             = pc} 
  \pgfqkeys{/piechartthreed}{#1}
  \begin{scope}[scale=\piechartthreedscale] 
    \begin{scope}[xscale=5,yscale=3] 
      \path[preaction={fill=black,opacity=.8,path fading=circle with fuzzy edge 20 percent,transform canvas={yshift=-15mm*\piechartthreedscale}}] (0,0) circle (1cm);
      \fill[gray](0,0) circle (0.5cm);  
      \path[preaction={fill=\piechartthreedbackcolor,opacity=.8,path fading=circle with fuzzy edge 20 percent,transform canvas={yshift=-10mm*\piechartthreedscale}}] (0,0) circle (0.5cm);
      \pgfmathsetmacro\totan{0}\global\let\totan\totan
      \pgfmathsetmacro\bottoman{180}\global\let\bottoman\bottoman 
      \pgfmathsetmacro\topan{0}\global\let\topan\topan
      \begin{scope}[draw=black,thin]
        \def\piechartthreed@norm{0}
        \foreach \name/\an/\col [count=\xi] in {#2}{%
          \pgfmathparse{\piechartthreed@norm+\an}\xdef\piechartthreed@norm{\pgfmathresult}
        }
        \foreach \name/\val/\col [count=\xi] in {#2}{%
          \pgfmathparse{360/\piechartthreed@norm * \val}\let\an\pgfmathresult
          \pgfmathsetmacro\finan{\totan+\an+\zeroan}
          \pgfmathsetmacro\midan{\totan+\an/2+\zeroan}
          \pgfmathsetmacro\began{\totan+\zeroan}
          \def\space{ } 
          \coordinate (\piechartthreedname\space\name) at (\midan:0.75cm);
          \ifdim 180pt>\began pt 
          % inner border
          \shadedraw[left color=\col!\piechartthreedmixratehigh!\piechartthreedmixcolor,right color=\col!\piechartthreedmixratelow!\piechartthreedmixcolor,draw=black,very thin]
          (\began:.5cm)-- ++(0,-3mm) arc(\began:\finan:.5cm) -- ++(0,3mm)  arc(\finan:\began:.5cm);
          \fi
          \ifdim 360pt<\finan pt 
          % inner border
          \shadedraw[left color=\col!\piechartthreedmixratehigh!\piechartthreedmixcolor,right color=\col!\piechartthreedmixratelow!\piechartthreedmixcolor,draw=black,very thin]
          (\began:.5cm)-- ++(0,-3mm) arc(\began:\finan:.5cm) -- ++(0,3mm)  arc(\finan:\began:.5cm);
          \fi   
          \fill[\col!\piechartthreedmixratehigh!\piechartthreedmixcolor,draw=black] (\began:0.5cm)--(\began:1cm)  arc(\began:\finan:1cm) --(\finan:0.5cm) arc(\finan:\began :0.5cm);     
          \ifdim 180pt<\finan pt
          % outer border
          \pgfmathsetmacro\bbegan{max(\began,180)}
          \pgfmathsetmacro\bfinan{min(\finan,360)}
        \shadedraw[left color=\col!\piechartthreedmixratehigh!\piechartthreedmixcolor,right color=\col!\piechartthreedmixratelow!\piechartthreedmixcolor,draw=black,very thin]
        (\bbegan:1cm)-- ++(0,-3mm) arc(\bbegan:\bfinan:1cm) -- ++(0,3mm)  arc(\bfinan:\bbegan:1cm);
          \fi
          \pgfmathsetmacro\totan{\totan+\an}\global\let\totan\totan 
        } 
      \end{scope}
      \draw[thin,black](0,0) circle (0.5cm);
    \end{scope}  
  \end{scope}
}

\begin{document}
  \begin{tikzpicture}
    \piechartthreed[scale=0.7,
      mix color=gray,
      mix rate high=80,
      mix rate low=60,
      zeroangle = 180,
    ]{%
      yy/0.228/yellow,
      zz/2.67/pink,
      cc/2.9/orange,
      tt/6.3/green,
      gg/8.56/red,
      ww/21.6/blue,
      bb/57.5/brown%
    }
    \draw[black] (pc bb)  -- (-4, 2  ) --++ (-3,0) node[anchor=south west] {$H \to b\bar{b}    $} node[anchor=north west] {$ 57.5 \pm 1.9  $};
    \draw[black] (pc ww)  -- (-4,-2  ) --++ (-3,0) node[anchor=south west] {$H \to W^+W^-      $} node[anchor=north west] {$ 21.6 \pm 0.9  $};
    \draw[black] (pc yy)  -- (-4, 0  ) --++ (-3,0) node[anchor=south west] {$H \to \gamma\gamma$} node[anchor=north west] {$0.228 \pm 0.011$};

    \draw[black] (pc gg)  -- ( 4,-2    ) --++ ( 3,0) node[anchor=south east] {$H \to gg          $} node[anchor=north east] {$ 8.56 \pm 0.86 $};
    \draw[black] (pc tt)  -- ( 4,-0.75 ) --++ ( 3,0) node[anchor=south east] {$H \to \tau^+\tau^-$} node[anchor=north east] {$ 6.30 \pm 0.36 $};
    \draw[black] (pc cc)  -- ( 4, 0.75 ) --++ ( 3,0) node[anchor=south east] {$H \to c\bar{c}   $}  node[anchor=north east] {$ 2.90 \pm 0.35 $};
    \draw[black] (pc zz)  -- ( 4, 2    ) --++ ( 3,0) node[anchor=south east] {$H \to ZZ         $}  node[anchor=north east] {$ 2.67 \pm 0.11 $};
  \end{tikzpicture}
\end{document}

Playing with the zeroangle parameter a bit, you can see that it works nicely for values in the range 0-90, but after that, weird graphic effects pop up, and I don't really understand why this happens.

Any suggestion on how to fix this?

strange overlay effects

  • Hey didn't we talk about this? Stop making 3D pie charts! >:-| – morbusg Nov 20 '16 at 13:36
  • Quite an opinion piece, but nevertheless thanks for the pointer :-) – carsten Nov 20 '16 at 14:18
  • Also see section "7.6 Plots and Charts" in the TikZ/PGF manual. – morbusg Nov 20 '16 at 14:33
8

This is a completely different (and a bit incomplete) version of the pie chart code, which combines the use of layers and a slightly different logic for drawing the inner and outer sides.

It is a bit more verbose than Alain Matthes excellent answer and does not quite match his glossy shading style, but this can be achieved by modifying the pie slice inner, pie slice outer and pie slice top styles.

\documentclass[tikz,border=5]{standalone}
\usepackage{xxcolor}
\usetikzlibrary{calc,arrows,fadings}
\def\pieifthenelse#1#2#3{%
  \def\pienext{#3}%
  \pgfmathparse{#1}%
  \ifdim\pgfmathresult pt=1pt\relax%
    \def\pienext{#2}%
  \fi%
  \pienext}
\tikzset{%
    zero angle/.initial=0,
    start angle/.initial=0,
    end angle/.initial=0,
    pie inner x radius/.initial=2,
    pie inner y radius/.initial=1,
    pie outer x radius/.initial=4,
    pie outer y radius/.initial=2,
    inner radius/.style args={#1 and #2}{%
      pie inner x radius=#1,
      pie inner y radius=#2
    },
    outer radius/.style args={#1 and #2}{%
      pie outer x radius=#1,
      pie outer y radius=#2
    },
    pie thickness/.initial=1,
    pie slice name/.initial=slice,
    pie slice top/.style={fill=gray},
    pie slice inner/.style={fill=gray!50!black},
    pie slice outer/.style={fill=gray!75!black}
}

\pgfdeclarelayer{shadow}
\pgfdeclarelayer{inner}
\pgfdeclarelayer{outer}
\pgfsetlayers{shadow,inner,outer,main}
\newcommand\pieslice[1][]{%
\begingroup%
  \tikzset{#1}%
  \pgfmathparse{Mod(\pgfkeysvalueof{/tikz/start angle}, 360)}%
  \let\pa=\pgfmathresult%
  \pgfmathparse{Mod(\pgfkeysvalueof{/tikz/end angle}, 360)}%
  \let\pb=\pgfmathresult%
  %
  \pgfmathparse{\pb<\pa ? \pb+360 : \pb}%
  \let\pc=\pgfmathresult%
  %
  \edef\rx{\pgfkeysvalueof{/tikz/pie inner x radius}}%
  \edef\ry{\pgfkeysvalueof{/tikz/pie inner y radius}}%
  \edef\Rx{\pgfkeysvalueof{/tikz/pie outer x radius}}%
  \edef\Ry{\pgfkeysvalueof{/tikz/pie outer y radius}}%
  \edef\r{\rx\space and \ry}%
  \edef\R{\Rx\space and \Ry}%
  \edef\t{\pgfkeysvalueof{/tikz/pie thickness}}%
  \edef\name{\pgfkeysvalueof{/tikz/pie slice name}}%
  \begin{pgfonlayer}{shadow}
  \foreach \i [evaluate={\x=\i/5;}] in {0,0.1,...,1}
    \path [fill=black, opacity=0.01, shift=(270:\t), even odd rule]
      (0:\Rx+\x\space and \Ry+\x) arc (0:360:\Rx+\x\space and \Ry+\x) -- cycle
      (0:\rx-\x\space and \ry-\x) arc (0:360:\rx-\x\space and \ry-\x) -- cycle;
  \end{pgfonlayer}
  \pieifthenelse{\pa==\pb}{%
    % Only one slice!
    \begin{pgfonlayer}{inner}%
    \path [every pie slice/.try, pie slice inner/.try]
      (0:\r) arc (0:180:\r) -- ++(0,-\t) arc (180:0:\r) -- cycle;
    \end{pgfonlayer}%
    \begin{pgfonlayer}{outer}%
      \path [every pie slice/.try, pie slice outer/.try]
    (360:\R) arc (360:180:\R) -- ++(0,-\t) arc (180:360:\R) -- cycle;
    \end{pgfonlayer}%
    \path [every pie slice/.try, pie slice top/.try, even odd rule]
      (0:\R) arc (0:360:\R) -- cycle
      (0:\r) arc (0:360:\r) -- cycle;
    }{%
      % Inner
      \begin{pgfonlayer}{inner}%
      \pieifthenelse{\pa>\pb}{%
        \pieifthenelse{\pb<180}{%
          \path [every pie slice/.try, pie slice inner/.try]
            (0:\r) arc (0:\pb:\r) -- ++(0,-\t) arc (\pb:0:\r);
        }{%
          \path [every pie slice/.try, pie slice inner/.try]
            (0:\r) arc (0:180:\r) -- ++(0,-\t) arc (180:0:\r);
        }%
      }{%
        \pieifthenelse{\pa<180}{%
           \pieifthenelse{\pb<180}{%
             \path [every pie slice/.try, pie slice inner/.try]
               (\pa:\r) arc (\pa:\pb:\r) -- ++(0,-\t) arc (\pb:\pa:\r);
           }{%
             \path [every pie slice/.try, pie slice inner/.try]
               (\pa:\r) arc (\pa:180:\r) -- ++(0,-\t) arc (180:\pa:\r);
           }%
        }{%
           \path [every pie slice/.try, pie slice inner/.try]
            (\pa:\r) arc (\pa:\pb:\r) -- ++(0,-\t) arc (\pb:\pa:\r);
         }%
       }%
     \end{pgfonlayer}%
     % Outer
     \begin{pgfonlayer}{outer}%
       \pieifthenelse{\pa>\pb}{%
         \path [every pie slice/.try, pie slice outer/.try]
           (\pa:\R) arc (\pa:360:\R) -- ++(0,-\t) arc (360:\pa:\R) -- cycle;
         \pieifthenelse{\pb>180}{%
           \path [every pie slice/.try, pie slice outer/.try]
             (180:\R) arc (180:\pb:\R) -- ++(0,-\t) arc (\pb:180:\R) -- cycle;
         }{}%
       }{%
      \pieifthenelse{\pa>180}{%
        \path [every pie slice/.try, pie slice outer/.try]
          (\pa:\R) arc (\pa:\pb:\R) -- ++(0,-\t) arc (\pb:\pa:\R) -- cycle;
      }{%
        \pieifthenelse{\pb>180}{%
          \path [every pie slice/.try, pie slice outer/.try]
            (180:\R) arc (180:\pb:\R) -- ++(0,-\t) arc (\pb:180:\R) -- cycle;}{}
        }}%
      \end{pgfonlayer}%
    % Top
    \path [every pie slice/.try, pie slice top/.try] (\pa:\r) -- (\pa:\R)
      arc (\pa:\pc:\R) -- (\pb:\r) arc (\pc:\pa:\r) -- cycle;
  }%
  \path
    (\pa:\r) coordinate (\name-start-inner)
    (\pa:\R) coordinate (\name-start-outer)
    (\pa/2+\pc/2:\r) coordinate (\name-mid-inner)
    (\pa/2+\pc/2:\R) coordinate (\name-mid-outer)
    (\pb:\r) coordinate (\name-end-inner)
    (\pb:\R) coordinate (\name-end-outer);
\endgroup
}

\newcommand\pie[2][]{%
  \begingroup%
  \tikzset{#1}%
  \pgfmathparse{\pgfkeysvalueof{/tikz/zero angle}}%
  \let\zeroangle=\pgfmathresult%
  \foreach \l/\n/\c [remember=\total (initially 0),
    evaluate={\total=\total+\n;}]in {#2}{}%
    \foreach \l/\n/\c [remember=\angleoffset (initially \zeroangle),
    evaluate={\startangle=\angleoffset; \endangle=\startangle+\n/\total*360;
      \angleoffset=\angleoffset+\n/\total*360;}] in {#2}{
      \colorlet{pie slice}{\c}%
      \pieslice[%
        start angle=\startangle,
        end angle=\endangle,
        pie slice name/.expanded=\l]%
    }
  \endgroup%
}
\begin{document}
\foreach \z in {0, 10, ..., 350}{
  \begin{tikzpicture}[>=stealth, line join=round, line cap=round]
  \useasboundingbox (-2,-2) rectangle (2,4);
  \begin{colormixin}{80!white}
\pie[every pie slice/.style={
      pie slice top/.style={fill=pie slice, draw=pie slice},
      pie slice inner/.style={fill=pie slice!50!black, draw=pie slice!50!black},
      pie slice outer/.style={fill=pie slice!50!black, draw=pie slice!50!black}
    },
    inner radius=1 and 1/2,
    outer radius=2 and 1,
    pie thickness=1/2,
    zero angle=\z]
{%
  Red/1/red,
  Yellow/2/yellow,
  Pink/3/pink,
  Green/4/green,
  Orange/5/orange,
  Purple/6/purple,
  Blue/7/blue%
}
\end{colormixin}
\draw [*-, thick, draw=black!80]
  ($(Orange-mid-inner)!0.5!(Orange-mid-outer)$)
  -- ++(0,2) node [above, font=\sffamily] {Orange};
\draw [*-, thick, draw=black!80]
  ($(Blue-mid-inner)!0.5!(Blue-mid-outer)$)
   -- ++(0,2) node [above, font=\sffamily] {Blue};
\end{tikzpicture}
}
\end{document}

enter image description here

  • This is superb! Very impressive! Thank you! – carsten Nov 19 '16 at 14:40
4

Possibly in this case the problem can be solved with layers:

\documentclass{standalone}

\usepackage{pgfplots,tikz}
\usetikzlibrary{fadings}
\pgfdeclarelayer{sides}
\pgfdeclarelayer{shadow}
\pgfsetlayers{shadow,sides,main}
\pgfkeys{%
  /piechartthreed/.cd,
  scale/.code                =  {\def\piechartthreedscale{#1}},
  mix color/.code            =  {\def\piechartthreedmixcolor{#1}},
  mix rate high/.code        =  {\def\piechartthreedmixratehigh{#1}},
  mix rate low/.code         =  {\def\piechartthreedmixratelow{#1}},
  background color/.code     =  {\def\piechartthreedbackcolor{#1}},
  name/.code                 =  {\def\piechartthreedname{#1}}}

\newcommand\piechartthreed[2][]{% 
  \pgfkeys{/piechartthreed/.cd,
    scale            = 1,
    mix color        = gray,
    mix rate high    = 20,
    mix rate low     = 5,
    background color = white,
    zeroangle/.store in = \zeroan,
    zeroangle        = 0,
    name             = pc} 
  \pgfqkeys{/piechartthreed}{#1}
  \begin{scope}[scale=\piechartthreedscale] 
    \begin{scope}[xscale=5,yscale=3]
      \begin{pgfonlayer}{shadow}
      \path[preaction={fill=black,opacity=.8,path fading=circle with fuzzy edge 20 percent,transform canvas={yshift=-15mm*\piechartthreedscale}}] (0,0) circle (1cm);
      \fill[gray](0,0) circle (0.5cm);  
      \path[preaction={fill=\piechartthreedbackcolor,opacity=.8,path fading=circle with fuzzy edge 20 percent,transform canvas={yshift=-10mm*\piechartthreedscale}}] (0,0) circle (0.5cm);
      \end{pgfonlayer}
      \pgfmathsetmacro\totan{0}\global\let\totan\totan
      \pgfmathsetmacro\bottoman{180}\global\let\bottoman\bottoman 
      \pgfmathsetmacro\topan{0}\global\let\topan\topan
      \begin{scope}[draw=black,thin]
        \def\piechartthreed@norm{0}
        \foreach \name/\an/\col [count=\xi] in {#2}{%
          \pgfmathparse{\piechartthreed@norm+\an}\xdef\piechartthreed@norm{\pgfmathresult}
        }
        \foreach \name/\val/\col [count=\xi] in {#2}{%
          \pgfmathparse{360/\piechartthreed@norm * \val}\let\an\pgfmathresult
          \pgfmathsetmacro\finan{\totan+\an+\zeroan}
          \pgfmathsetmacro\midan{\totan+\an/2+\zeroan}
          \pgfmathsetmacro\began{\totan+\zeroan}
          \def\space{ } 
          \coordinate (\piechartthreedname\space\name) at (\midan:0.75cm);
          \ifdim 180pt>\began pt
          \begin{pgfonlayer}{sides}
          % inner border
          \shadedraw[left color=\col!\piechartthreedmixratehigh!\piechartthreedmixcolor,right color=\col!\piechartthreedmixratelow!\piechartthreedmixcolor,draw=black,very thin]
          (\began:.5cm)-- ++(0,-3mm) arc(\began:\finan:.5cm) -- ++(0,3mm)  arc(\finan:\began:.5cm);
          \end{pgfonlayer}
          \fi
          \ifdim 360pt<\finan pt 
          % inner border
           \begin{pgfonlayer}{sides}
          \shadedraw[left color=\col!\piechartthreedmixratehigh!\piechartthreedmixcolor,right color=\col!\piechartthreedmixratelow!\piechartthreedmixcolor,draw=black,very thin]
          (\began:.5cm)-- ++(0,-3mm) arc(\began:\finan:.5cm) -- ++(0,3mm)  arc(\finan:\began:.5cm);
          \end{pgfonlayer}%
          \fi   
          \fill[\col!\piechartthreedmixratehigh!\piechartthreedmixcolor,draw=black] (\began:0.5cm)--(\began:1cm)  arc(\began:\finan:1cm) --(\finan:0.5cm) arc(\finan:\began :0.5cm);     
          \ifdim 180pt<\finan pt
          % outer border
          \begin{pgfonlayer}{sides}
          \pgfmathsetmacro\bbegan{max(\began,180)}
          \pgfmathsetmacro\bfinan{min(\finan,360)}
        \shadedraw[left color=\col!\piechartthreedmixratehigh!\piechartthreedmixcolor,right color=\col!\piechartthreedmixratelow!\piechartthreedmixcolor,draw=black,very thin]
        (\bbegan:1cm)-- ++(0,-3mm) arc(\bbegan:\bfinan:1cm) -- ++(0,3mm)  arc(\bfinan:\bbegan:1cm);
        \end{pgfonlayer}%
          \fi
          \pgfmathsetmacro\totan{\totan+\an}\global\let\totan\totan 
        } 
      \end{scope}
      \draw[thin,black](0,0) circle (0.5cm);
    \end{scope}  
  \end{scope}
}

\begin{document}
  \begin{tikzpicture}
    \piechartthreed[scale=0.7,
      mix color=gray,
      mix rate high=80,
      mix rate low=60,
      zeroangle = 180,
    ]{%
      yy/0.228/yellow,
      zz/2.67/pink,
      cc/2.9/orange,
      tt/6.3/green,
      gg/8.56/red,
      ww/21.6/blue,
      bb/57.5/brown%
    }
  \end{tikzpicture}
\end{document}

enter image description here

  • This is already extremely helpful, but does not produce the desired result for zeroangle=270. – carsten Nov 18 '16 at 14:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.