Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I am trying to replicate the following figure here

enter image description here

and this is what I have so far

\documentclass[12pt,a4paper]{standalone}

\usepackage{tikz,tkz-euclide,pgfplots}
\usetkzobj{all}
\newcommand{\ball}[2]{%
        \begin{scope}[shift = {(#1,#2)}
            \fill[red] (0,0) circle (0.5);
            \clip (0,0) circle (0.5);
            \shade[shading=ball,ball color=white,1] (0.15,0.25) circle (0.7);
        \end{scope}}

\begin{document}%

    \begin{tikzpicture}     
        \def\height{0.75}
        \def\length{3}

        \tkzDefPoint(0,0){A}         \tkzDefPoint(0.9*\length,-\height){B}
        \tkzDefPoint(2*\length,0){C} \tkzDefPoint(1.1*\length, \height){D}
        \tkzDrawPolygon(A,B,C,D)

        \begin{scope}[shift={(0,2)}]
                \tkzDefPoint(0,0){A1}         \tkzDefPoint(0.9*\length,-\height){B1}
                \tkzDefPoint(2*\length,0){C1} \tkzDefPoint(1.1*\length, \height){D1}
                \tkzDrawPolygon(A1,B1,C1,D1)
        \end{scope}

        \tkzDrawSegments(A,A1 B1,B C1,C D1,D);  
%               \ball{2}{2}
    \end{tikzpicture}
\end{document}

By using the tips I got from this question. I think I almost got it, but the code will not compile when uncommenting the last line. What is the reason for this?

Other means create this image would also appreciated, it is always refreshing to see different approaches to a problem.

share|improve this question
    
You forgot to close the scope options: \begin{scope}[shift = {(#1,#2)}]. You also have an undefined option for \shade: instead of \shade[shading=ball,ball color=white,1], write \shade[ball color=white]. I would also suggest \shade[ball color=white] (0.15,0.25) circle (1); –  hpesoj626 Jun 23 '13 at 12:22

1 Answer 1

A possible solution:

\documentclass[png,border=10pt,tikz]{standalone}

\usepackage{tikz,tkz-euclide,pdftexcmds}
\usetikzlibrary{backgrounds}
\pgfdeclarelayer{foreground}
\pgfsetlayers{background,main,foreground}
\usetkzobj{all}

\makeatletter

\pgfdeclareradialshading[tikz@ball]{my ball}{\pgfqpoint{5bp}{10bp}}{%
 color(0bp)=(tikz@ball!10!white);
 color(8bp)=(tikz@ball!65!white);
 color(12bp)=(tikz@ball);
 color(25bp)=(tikz@ball!70!black);
 color(50bp)=(black)}

\pgfdeclareradialshading[tikz@ball]{new ball}{\pgfqpoint{12bp}{12bp}}{%
 color(0cm)=(tikz@ball!10!white);
 color(0.5cm)=(tikz@ball!50!white);
 color(0.75cm)=(tikz@ball);
 color(0.9cm)=(tikz@ball!80!black);
 color(1cm)=(tikz@ball!60!black)} 

% code by percusse:
% http://tex.stackexchange.com/a/74237/13304
\pgfdeclareradialshading{ball shadow}{\pgfpointorigin}%
{color(0cm)=(black);
color(2mm)=(gray!70);
color(3mm)=(gray!30);
color(7mm)=(white)
} 

 % to make possible use "myball color=..." 
\tikzoption{my ball color}{\pgfutil@colorlet{tikz@ball}{#1}\def\tikz@shading{my ball}\tikz@addmode{\tikz@mode@shadetrue}}

\tikzoption{new ball color}{\pgfutil@colorlet{tikz@ball}{#1}\def\tikz@shading{new ball}\tikz@addmode{\tikz@mode@shadetrue}}

\tikzoption{ball shadow color}{\pgfutil@colorlet{tikz@ball}{#1}\def\tikz@shading{ball shadow}\tikz@addmode{\tikz@mode@shadetrue}}


\newcommand{\ball}[3][white]{%
        \begin{scope}[shift = {(#2,#3)}]
          \node[shading=ball shadow,xscale=2,yscale=0.3,circle,minimum size=7mm] 
            at (0.15,-0.225){};
          % test to decide which shading use: special one for white balls
          % not 100% efficient: maybe it's better to use directly a custom
          % white ball shading, but this way one could always use 
          % "my ball color=green" say
          \ifnum\pdf@strcmp{#1}{white}=\z@%  
            \shade[my ball color=#1] (0.15,0.25) circle (0.5);
          \else
            \shade[new ball color=#1] (0.15,0.25) circle (0.5);
          \fi
        \end{scope}
}
\makeatother

\begin{document}%

\begin{tikzpicture}     
    \def\height{0.75}
    \def\length{3}

    \tkzDefPoint(0,0){A}         \tkzDefPoint(0.9*\length,-1.1\height){B}
    \tkzDefPoint(1.9*\length,0){C} \tkzDefPoint(1.1*\length, \height){D}
    %\tkzDrawPolygon(A,B,C,D) % Beware to put the right segments in foreground
    % now this can not be a polygon: need to be split
    % Same would apply to the shifted one if the balls touch the upper layer
    \tkzDrawSegments(C,D D,A);

    \begin{scope}[shift={(0,2)}]
            \tkzDefPoint(0,0){A1}\tkzDefPoint(0.9*\length,-\height){B1}
            \tkzDefPoint(1.9*\length,0){C1} \tkzDefPoint(1.1*\length, \height){D1}
            \tkzDrawPolygon(A1,B1,C1,D1)
    \end{scope}

    \tkzDrawSegments(A,A1 C1,C D1,D);
    \begin{pgfonlayer}{foreground}
      \tkzDrawSegments(B,B1);
      % other segments in foreground
      \tkzDrawSegments(A,B B,C);
    \end{pgfonlayer}

    % balls: beware the order matters
    \ball{1}{0.15}
    \ball[orange]{2.6}{0.35}
    \ball{1.85}{-0.2}
    \ball[orange]{4.3}{0.25}
    \ball[orange]{3.425}{-0.1}
\end{tikzpicture}
\end{document}

The result:

enter image description here

The tricks are:

  • define the shading in a similar way as How to shade mindmap concepts? so that the \ball command, besides the usual capability of being shifted, can also set the color;
  • draw specific segments, like the segment B,B1 in the foreground layer: in such a way balls seem to be in background;
  • draw the balls with a precise order: to not use too much the pgfonlayer enviroment, the simplest thing to do is to first draw the balls behind, proceeding then with the other ones.
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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