3d cube with a surface

I want to draw a unit shell showing the arrangement of the atoms as shown here. (Zumdahl, Chemical Principles, 5ed., Houghton Mifflin, p. 773.)

I've come up with the following code that draws the atom faces (the cubes above). It uses the idea you only have to define a cubicle face once: then it can be slanted to draw the other faces. However, I am clueless how to (mathematically) draw the shadings (the clipped spheres). Suggestions?

If possible, use the idea of defining only one cubicle side. It is very convenient since I will have to draw all of the examples above and possibly more (I added two faces to the example). However, if this is not possible, other solutions are appreciated. One does not be able to rotate the solution.

\documentclass{minimal}
\usepackage{tikz}
\begin{document}

\begin{tikzpicture}
\pgfmathsetmacro{\D}{4}
\pgfmathsetmacro{\halfD}{\D/2}

\newcommand{\mycubicleface}{
\pgfmathsetmacro{\R}{\D/2}
\draw [fill=blue!30] (0,\R) arc [start angle=90, end angle=0, radius=\R] -- +(-\R,0);
\draw [fill=blue!30] (0,\R) arc [start angle=-90, end angle=0, radius=\R] -- +(-\R,0);
\draw [fill=blue!30] (\D,\R) arc [start angle=-90, end angle=-180, radius=\R] -- +(\R,0);
\draw [fill=blue!30] (\D,\R) arc [start angle=90, end angle=180, radius=\R] -- +(\R,0);
\draw (0,0) rectangle +(\D,\D);
}

%\newcommand{\mycubicleface}{
%   \pgfmathsetmacro{\R}{\D/sqrt(8)}
%   \draw [fill=blue!30] (\D/2,\D/2) circle [radius=\R];
%   \draw [fill=blue!30] (0,\R) arc [start angle=90, end angle=0, radius=\R] -- +(-\R,0);
%   \draw [fill=blue!30] (0,\D - \R) arc [start angle=-90, end angle=0, radius=\R] -- +(-\R,0);
%   \draw [fill=blue!30] (\D,\D - \R) arc [start angle=-90, end angle=-180, radius=\R] -- +(\R,0);
%   \draw [fill=blue!30] (\D,\R) arc [start angle=90, end angle=180, radius=\R] -- +(\R,0);
%   \draw (0,0) rectangle +(\D,\D);
%}

\begin{scope}[yslant=-.5]
\mycubicleface
\end{scope}
\begin{scope}[xshift=\D cm, yshift=-\halfD cm, yslant=.5]
\mycubicleface
\end{scope}
\begin{scope}[xshift=\D cm, yshift=\halfD cm, yslant=.5, xslant=-1]
\mycubicleface
\end{scope}

\end{tikzpicture}
\end{document}

• Instead of yslant and xslant, use the 3d coordinate system to specify coordinates like (1,2,3). A solid 3d sphere at (x,y,z) projected onto 2d is basically a filled circle at the same coordinate with the same radius, with some shading like (here)[texample.net/tikz/examples/spherical-and-cartesian-grids/]. And then you want to clip that circle in a clever way... – Turion Dec 15 '14 at 15:03
• Another thought... do you think it is worth doing this with TikZ? Have you considered a ray tracer like POV-Ray? – Turion Dec 15 '14 at 15:06
• I would like to do it with tikz :) It is an interesting mathematical problem. Perhaps the solution will be useful to others, too! – Mappi Dec 17 '14 at 5:44

This is the counter part of your result.

The idea comes from this texample that draws some meridians/circle of latitude on a sphere. Instead of drawing those arcs, I use them as clipping path. Some works are needed to connect the arcs together.

\documentclass[tikz]{standalone}
\usepackage{tikz,spath}

\newcommand\pgfmathsinandcos[3]{
\pgfmathsetmacro#1{sin(#3)}\pgfmathsetmacro#2{cos(#3)}}

\newcommand\LongitudePlane[3][current plane]{
\pgfmathsinandcos\sinEl\cosEl{#2}\pgfmathsinandcos\sint\cost{#3}
\tikzset{#1/.style={cm={\cost,\sint*\sinEl,0,\cosEl,(0,0)}}}}

\newcommand\LatitudePlane[3][current plane]{%
\pgfmathsinandcos\sinEl\cosEl{#2}\pgfmathsinandcos\sint\cost{#3}
\pgfmathsetmacro\yshift{\cosEl*\sint}
\tikzset{#1/.style={cm={\cost,0,0,\cost*\sinEl,(0,\yshift)}}}}

\newcommand\ClipLongitudeCircle[2]{
\LongitudePlane\angEl{#1}
\pgfmathsetmacro\angVis{atan(sin(#1)*cos(\angEl)/sin(\angEl))}
\path[save path=\tmppath,current plane](\angVis:\R)arc(\angVis:\angVis+180:\R);
\pgfoonew\patha=new spath(\tmppath)
\pgfmathsetmacro\angVis{-atan(sin(\angEl)*cos(#1)/sin(#1))}
\path[save path=\tmppath](-90+\angVis:\R)arc(-90+\angVis:#2180-90+\angVis:\R);
\pgfoonew\pathb=new spath(\tmppath)
\patha.concatenate with lineto(,\pathb)\patha.close()\patha.use path with tikz(clip)}

\newcommand\ClipLatitudeCircle[2]{
\LatitudePlane{\angEl}{#1}
\path[save path=\tmppath,current plane](-180:\R)arc(-180:0:\R);
\pgfoonew\patha=new spath(\tmppath)
\path[save path=\tmppath](0:\R)arc(0:#2180:\R);
\pgfoonew\pathb=new spath(\tmppath)
\patha.concatenate with lineto(,\pathb)\patha.close()\patha.use path with tikz(clip)}

\newcommand\EighthSphere[3]{
\ClipLongitudeCircle{45-\angPh}{#1}
\ClipLongitudeCircle{135-\angPh}{#2}
\ClipLatitudeCircle{0}{#3}
\fill[ball color=white](0,0)circle(\R);}

\begin{document}
\def\angEl{20} % elevation angle in interval [1,89]
\def\angPh{10} % phase angle in interval [-44,44]
\pgfmathsetmacro\uofx{cos(-135-\angPh)}
\pgfmathsetmacro\vofx{sin(-135-\angPh)*sin(\angEl)}
\pgfmathsetmacro\uofy{cos(-45-\angPh)}
\pgfmathsetmacro\vofy{sin(-45-\angPh)*sin(\angEl)}
\pgfmathsetmacro\uofz{0}
\pgfmathsetmacro\vofz{cos(\angEl)}
\begin{tikzpicture}
\begin{scope}[x={(\uofx cm,\vofx cm)},y={(\uofy cm,\vofy cm)},z={(\uofz cm,\vofz cm)}]
\path(-6,-6,-6)coordinate(A){}(6,6,6)coordinate(B){};
\path(6,-6,-6)coordinate(P){}(6,6,-6)coordinate(Q){}(-6,6,-6)coordinate(R){}
(-6,6,6)coordinate(S){}(-6,-6,6)coordinate(T){}(6,-6,6)coordinate(U){};
\end{scope}
\path(-12,-12)(12,12);
\draw(P)--(Q)--(R)--(S)--(T)--(U)--cycle;
\clip(P)--(Q)--(R)--(S)--(T)--(U)--cycle;
\begin{scope}[transform canvas={shift=(A)}]
\EighthSphere{+}{-}{+}
\end{scope}
\begin{scope}[transform canvas={shift=(P)}]
\EighthSphere{+}{+}{+}
\end{scope}
\begin{scope}[transform canvas={shift=(R)}]
\EighthSphere{-}{-}{+}
\end{scope}
\begin{scope}[transform canvas={shift=(T)}]
\EighthSphere{+}{-}{-}
\end{scope}
\begin{scope}[transform canvas={shift=(Q)}]
\EighthSphere{-}{+}{+}
\end{scope}
\begin{scope}[transform canvas={shift=(S)}]
\EighthSphere{-}{-}{-}
\end{scope}
\begin{scope}[transform canvas={shift=(U)}]
\EighthSphere{+}{+}{-}
\end{scope}
\end{tikzpicture}
\end{document}

• Ideas how to draw the original part aswell? The slanting and this solution do not match that well and this is way better. – Mappi Mar 17 '15 at 19:16
• @Mappi That is where I stuck on... slant is mathematically enough to work but the factor is hard to calculated for arbitrary \angEl and \angPh. Also, using only 2D trick I cannot simulate the z-index. I shall work on this latter. (Not next hours, but days, or.. wee...) – Symbol 1 Mar 17 '15 at 22:57
• I have now done it. Should I post my answer as a new answer and give the credit for you there, or should I suggest editions to your post? – Mappi Mar 30 '15 at 16:43
• @Mappi Post it and accept it. So it will be the topmost answer for people's conveniency. And do not forget to explain if possible because I was too lazy to do so :P – Symbol 1 Mar 30 '15 at 17:09
• There it is! Thank you, most of the credits go to you :) Also, feel free to edit if you want to explain; I am feeling lazy too! :) – Mappi May 2 '15 at 15:18

Here it finally is! You can produce all three cubes by commenting and uncommenting the following code.

The answer is basically the answer of Symbol 1, only modified. Note that you need to install spath package manually as informed here.

The commented sections in the code are for debugging (drawing the to-be-clipped paths).

The final result is satisfying. However, please note the incorrect stacking and shading in the middlemost image with the two half spheres.

\documentclass[tikz]{standalone}
\usepackage{spath}
\usepackage{tikz}
\usetikzlibrary{calc}

\begin{document}

% Source: LaTeX-Community.org
%         <http://www.latex-community.org/viewtopic.php?f=4&t=2111>
\begin{tikzpicture}
\newcommand\pgfmathsinandcos[3]{%
\pgfmathsetmacro#1{sin(#3)}%
\pgfmathsetmacro#2{cos(#3)}%
}
\newcommand\LongitudePlane[3][current plane]{%
\pgfmathsinandcos\sinEl\cosEl{#2} % elevation
\pgfmathsinandcos\sint\cost{#3} % azimuth
\tikzset{#1/.style={cm={\cost,\sint*\sinEl,0,\cosEl,(0,0)}}}
}
\newcommand\LatitudePlane[3][current plane]{%
\pgfmathsinandcos\sinEl\cosEl{#2} % elevation
\pgfmathsinandcos\sint\cost{#3} % latitude
\pgfmathsetmacro\yshift{\cosEl*\sint}
\tikzset{#1/.style={cm={\cost,0,0,\cost*\sinEl,(0,\yshift)}}}
}

%\newcommand\DrawLongitudeCircle[2][4]{
%  \LongitudePlane{\angEl}{#2}
%%  \tikzset{current plane/.prefix style={scale=#1}}
%   % angle of "visibility"
%  \pgfmathsetmacro\angVis{atan(sin(#2)*cos(\angEl)/sin(\angEl))}
%  \draw[current plane, blue] (\angVis:\R) arc (\angVis:\angVis+180:\R);
%  \draw[current plane, blue] (\angVis-180:\R) arc (\angVis-180:\angVis:\R);
%}

%\newcommand\DrawLatitudeCircle[2][5]{
%  \LatitudePlane{\angEl}{#2}
%%  \tikzset{current plane/.prefix style={scale=#1}}
%%  \pgfmathsetmacro\sinVis{sin(#2)/cos(#2)*sin(\angEl)/cos(\angEl)}
%%  % angle of "visibility"
%%  \pgfmathsetmacro\angVis{asin(min(1,max(\sinVis,-1)))}
%  \draw[current plane, red] (\angVis:\R) arc (\angVis:-\angVis-180:\R);
%  \draw[current plane, red] (180-\angVis:\R) arc (180-\angVis:\angVis:\R);
%}

\newcommand\ClipLongitudeCircle[2]{
\LongitudePlane{\angEl}{#1}
\pgfmathsetmacro\angVis{atan(sin(#1)*cos(\angEl)/sin(\angEl))}
\path[save path=\tmppath, current plane] (\angVis:\R) arc (\angVis:\angVis+180:\R); % current plane transformation
\pgfoonew\patha=new spath(\tmppath)
\pgfmathsetmacro\angVis{-atan(sin(\angEl)*cos(#1)/sin(#1))}
\path[save path=\tmppath] (-90+\angVis:\R) arc (-90+\angVis:#2 180-90+\angVis:\R); % no coordinate transform (no current plane)
\pgfoonew\pathb=new spath(\tmppath)
\patha.concatenate with lineto(,\pathb)
\patha.close()
\patha.use path with tikz(clip)
%   \patha.use path with tikz(fill=magenta, opacity=.2)
%   \patha.use path with tikz(draw=magenta, very thick)
}

\newcommand\ClipLatitudeCircle[2]{
\LatitudePlane{\angEl}{#1}
\path[save path=\tmppath,current plane] (-180:\R) arc (-180:0:\R);
\pgfoonew\patha=new spath(\tmppath)
\path[save path=\tmppath] (0:\R) arc (0:#2 180:\R);
\pgfoonew\pathb=new spath(\tmppath)
\patha.concatenate with lineto(,\pathb)
\patha.close()
\patha.use path with tikz(clip)
%   \patha.use path with tikz(fill=cyan, opacity=.2)
%   \patha.use path with tikz(draw=cyan, very thick)
}

\newcommand\ClippedEightSphere[4]{
\begin{scope}[transform canvas={shift=(#4)}]
\ClipLongitudeCircle{45-\angPh}{#1}
\ClipLongitudeCircle{135-\angPh}{#2}
\ClipLatitudeCircle{0}{#3}
\fill[ball color=white] (0,0) circle (\R);
\end{scope}}

\newcommand\ClippedLatitudeSphere[3]{
\begin{scope}[transform canvas={shift=(#1)}]
\LatitudePlane{\angEl}{#2}
\ClipLatitudeCircle{0}{#3}
\end{scope}}

\newcommand\ClippedLongitudeSphere[3]{
\begin{scope}[transform canvas={shift=(#1)}]
\LongitudePlane{\angEl}{#2}
\ClipLongitudeCircle{#2}{#3}
\end{scope}}

\newcommand\DrawLongitudeArc[4]{
\LongitudePlane{\angEl}{#2}
\begin{scope}[current plane, transform canvas={shift=(#1)}]
\fill [cyan] (0,0) -- ++(#3:\R) arc [start angle=#3, delta angle=#4, radius=\R] -- cycle;
\draw ++(#3:\R) arc [start angle=#3, delta angle=#4, radius=\R];
\end{scope}}

\newcommand\DrawLatitudeArc[4]{
\LatitudePlane{\angEl}{#2}
\begin{scope}[current plane, transform canvas={shift=(#1)}]
\fill [cyan] (0,0) -- ++(#3:\R) arc [start angle=#3, delta angle=#4, radius=\R] -- cycle;
\draw ++(#3:\R) arc [start angle=#3, delta angle=#4, radius=\R];
\end{scope}}

\def\D{8} % cubic side length
\def\angEl{20} % elevation angle in interval [1,89]
\def\angPh{10} % phase angle in interval [-44,44]
\pgfmathsetmacro\uofx{cos(-135-\angPh)}
\pgfmathsetmacro\vofx{sin(-135-\angPh)*sin(\angEl)}
\pgfmathsetmacro\uofy{cos(-45-\angPh)}
\pgfmathsetmacro\vofy{sin(-45-\angPh)*sin(\angEl)}
\pgfmathsetmacro\uofz{0}
\pgfmathsetmacro\vofz{cos(\angEl)}

% The coordinates of the cube
\begin{scope}[x={(\uofx cm,\vofx cm)}, y={(\uofy cm,\vofy cm)}, z={(\uofz cm,\vofz cm)}]
\coordinate (C1) at (\D,0,0);
\coordinate (C2) at (\D,0,\D);
\coordinate (C3) at (0,0,\D);
\coordinate (C4) at (0,\D,\D);
\coordinate (C5) at (0,\D,0);
\coordinate (C6) at (\D,\D,0);
\coordinate (C7) at (0,0,0);
\coordinate (C8) at (\D,\D,\D);
%    \foreach \n in {1,2,...,8} \node at (C\n) {C\n};

\coordinate (C0) at ($(C2)!.5!(C5)$);
\coordinate (S1) at ($(C2)!.5!(C6)$);
\coordinate (S2) at ($(C2)!.5!(C4)$);
\coordinate (S3) at ($(C8)!.5!(C5)$);
\coordinate (S4) at ($(C6)!.5!(C7)$);
\coordinate (S5) at ($(C1)!.5!(C3)$);
\coordinate (S6) at ($(C5)!.5!(C3)$);
\end{scope}

% Draw the clipped spheres
\ClippedEightSphere{+}{-}{+}{C7}
\ClippedLongitudeSphere{S5}{45-\angPh}{+}
\ClippedLongitudeSphere{S6}{135-\angPh}{-}
\ClippedLatitudeSphere{S4}{0}{+}

\ClippedEightSphere{-}{+}{-}{C8}
\ClippedEightSphere{+}{-}{-}{C3}
\ClippedEightSphere{+}{+}{-}{C2}
%   \fill[ball color=white] (C0) circle [radius=\R];
\ClippedEightSphere{-}{-}{-}{C4}
\ClippedEightSphere{+}{+}{+}{C1}
\ClippedEightSphere{-}{-}{+}{C5}
\ClippedEightSphere{-}{+}{+}{C6}

% Draw the half spheres
\ClippedLatitudeSphere{S2}{0}{-}
\ClippedLongitudeSphere{S3}{45-\angPh}{-}
\ClippedLongitudeSphere{S1}{135-\angPh}{+}
\DrawLatitudeArc{S2}{0}{0}{360}
\DrawLongitudeArc{S1}{135-\angPh}{0}{360}
\DrawLongitudeArc{S3}{45-\angPh}{0}{360}

% Draw the Arcs
\DrawLongitudeArc{C1}{135-\angPh}{90}{90}
\DrawLongitudeArc{C2}{135-\angPh}{-90}{-90}
\DrawLongitudeArc{C4}{45-\angPh}{-90}{-90}
\DrawLongitudeArc{C5}{45-\angPh}{90}{90}
\DrawLongitudeArc{C6}{135-\angPh}{90}{-90}
\DrawLongitudeArc{C6}{45-\angPh}{90}{-90}
\DrawLongitudeArc{C8}{135-\angPh}{-90}{90}
\DrawLongitudeArc{C8}{45-\angPh}{-90}{90}
\DrawLatitudeArc{C2}{0}{45-\angPh}{-90}
\DrawLatitudeArc{C3}{0}{-45-\angPh}{-90}
\DrawLatitudeArc{C4}{0}{135-\angPh}{90}
\DrawLatitudeArc{C8}{0}{135-\angPh}{-90}

% Draw the cube
\draw (C1)--(C2)--(C3)--(C4)--(C5)--(C6)--cycle;
\draw (C2)--(C8)--(C6);
\draw (C8)--(C4);

\coordinate (r) at ($(C2) - (\R/10,0)$);
\draw [<->, current plane] (r) -- node [left] {$r$} +(-90:\R);