I want to draw 3D shapes in latex. I have tried out different strategies, but nothing has led me to a good solution yet. First what I want to do, then what I have done so far.
In prioritized order, these are my constraints:
- Output (the figure) must be vector graphics
- I would REALLY like to compile my main document with
pdflatex - Text should preferably be scalable, i.e., the text stays the same, even if I decide to make the figure bigger in my document.
- Objects should have a shading that makes them look like they are lighted from somewhere (otherwise I cannot distinguish between a circle and a sphere).
- I want to use colors defined in my main files preamble - thus dynamic use of main file's preamble content (this is in an individual file).
This is probably not possible at the same time, but a trade off is welcome. What works for you when drawing 3D objects?
My progress so far
I've made friends with Tikz, but when familiarizing myself with its shortcomings (such as 3d solids it seems), I've flirted with PStricks - but this was a messy first date, so unless you guys REALLY advocate her as a part of the solution, I'd rather stay with Tikz (but hey, beggars can't be choosers).
I've made a MWE, where everything is included (in my case it is split in many different files (a PhD-thesis becomes quite large, after all).
This is the code that generates a sphere using the trick of implementing the "ball", which is not inherent 3D:
\documentclass{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.4}
\usepackage{tikz-3dplot}
\tdplotsetmaincoords{60}{-30}
\tdplotsetrotatedcoords{0}{90}{90}%
%\usepackage[rgb]{xcolor}
\definecolor{c1}{rgb}{0.2,0.4,0.6} % Blue-ish
\definecolor{c2}{rgb}{1.0,0.0,0.6} % Pink-is
\definecolor{c3}{rgb}{0.6,0.0,0.0} % Red
\begin{document}
\begin{tikzpicture}
[tdplot_rotated_coords,
scale=3,
cube/.style={color=c1,thick,draw=gray, fill opacity=0.5,line join=round},
mds/.style={ball color=c2, c2, opacity=.8},
helplines/.style={gray,line cap=round},
length/.style={<->,thick,line cap=round},
axis/.style={->,c3,ultra thick,line cap=round},
textlabel/.style={fill opacity=.7,text opacity=1,fill=white,rounded corners}]
\def\d{1}
\def\r{\d*.45}
\def\af{\d*.5}
% Draw backside of the cube
\fill[cube] (0,0,\d) -- (0,\d,\d) -- (\d,\d,\d) -- (\d,0,\d) -- cycle;
\fill[cube] (0,0,0) -- (0,0,\d) -- (\d,0,\d) -- (\d,0,0) -- cycle;
\fill[cube] (\d,0,0) -- (\d,0,\d) -- (\d,\d,\d) -- (\d,\d,0) -- cycle;
% Draw helplines
\foreach \t in {0,12,...,348} % circle
\draw[helplines] ({cos(\t )*\r+\d/2}, \d/2, {sin(\t )*\r+\d/2})
-- ({cos(\t+12)*\r+\d/2}, \d/2, {sin(\t+12)*\r+\d/2});
\draw[helplines] (\d/2,\d/2-\r,\d/2) -- (\d/2,\d/2+\r,\d/2); % vertical line
% Cylinder
\shade[mds] (\d/2,\d/2,\d/2) circle (\r cm); % <= the little " cm" is needed to "trick" (?) everything into working...
% Draw front of cube
\fill[cube,fill=none] (0,0,0) -- (0,\d,0) -- (\d,\d,0) -- (\d,0,0) -- cycle;
\fill[cube,fill=none] (0,\d,0) -- (0,\d,\d) -- (\d,\d,\d) -- (\d,\d,0) -- cycle;
\fill[cube,fill=none] (0,0,0) -- (0,0,\d) -- (0,\d,\d) -- (0,\d,0) -- cycle;
% Draw the axis arrows and annotations
\draw[axis] (0,0,0) -- (\af,0,0) node[textlabel,anchor=east]{$x$};
\draw[axis] (0,0,0) -- (0,\af,0) node[textlabel,anchor=south]{$y$};
\draw[axis] (0,0,0) -- (0,0,\af) node[textlabel,anchor=west]{$z$};
% Draw radius arrow
\draw[mds,length,draw] (\d/2,\d/2,\d/2) -- (\d/2,\d/2,\d/2+\r) node[textlabel,pos=0.5, auto=above]{$r$};
% Draw cube lattice length measures
\draw[cube,length,c1] (0,0,\d*5/6) -- (0,\d,\d*5/6) node[textlabel,pos=0.5, auto=above]{$d$};
\draw[cube,length,c1] (\d*5/6,0,0) -- (\d*5/6,\d,0) node[textlabel,pos=0.5, auto=above]{$d$};
\draw[cube,length,c1] (\d*5/6,\d,0) -- (\d*5/6,\d,\d) node[textlabel,pos=0.5, auto=above]{$d$};
% Material parameters label
\draw[mds] (\d/2,\d/2+\r/2,\d/2) node[textlabel]{$\varepsilon_c,\mu_c$};
\end{tikzpicture}
\end{document}
Which produces:

I have worked on some code for manually coding a cylinder, but I encounter two main problems:
- I cannot dynamically change the fill, to create a shading effect.
- I cannot draw the top/bottom circles (ellipses in the viewpoint perspective)
Here's my code:
\documentclass{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.4}
\usepackage{tikz-3dplot}
\tdplotsetmaincoords{60}{-30}
\tdplotsetrotatedcoords{0}{90}{90}%
%\usepackage[rgb]{xcolor}
\definecolor{c1}{rgb}{0.2,0.4,0.6} % Blue-ish
\definecolor{c2}{rgb}{1.0,0.0,0.6} % Pink-is
\definecolor{c3}{rgb}{0.6,0.0,0.0} % Red
\begin{document}
\begin{tikzpicture}
[tdplot_rotated_coords,
scale=3,
cube/.style={color=c1,thick,draw=gray, fill opacity=0.5,line join=round},
mdc/.style={fill=c2, color=c2,draw=none, opacity=.4,line join=round},
helplines/.style={gray,line cap=round},
length/.style={<->,thick,line cap=round},
axis/.style={->,c3,ultra thick,line cap=round},
textlabel/.style={fill opacity=.7,text opacity=1,fill=white,rounded corners}]
\def\d{1}
\def\r{\d*.45}
\def\af{\d*.5}
% Draw backside of the cube
\fill[cube] (0,0,\d) -- (0,\d,\d) -- (\d,\d,\d) -- (\d,0,\d) -- cycle;
\fill[cube] (0,0,0) -- (0,0,\d) -- (\d,0,\d) -- (\d,0,0) -- cycle;
\fill[cube] (\d,0,0) -- (\d,0,\d) -- (\d,\d,\d) -- (\d,\d,0) -- cycle;
% Draw helplines
\foreach \t in {0,12,...,348} % circle
\draw[helplines] ({cos(\t )*\r+\d/2}, \d/2, {sin(\t )*\r+\d/2})
-- ({cos(\t+12)*\r+\d/2}, \d/2, {sin(\t+12)*\r+\d/2});
\draw[helplines] (\d/2,\d/2-\r,\d/2) -- (\d/2,\d/2+\r,\d/2); % vertical line
% Cylinder
\foreach \t in {0,12,...,348}
\draw[mdc] ({cos(\t )*\r+\d/2}, 0, {sin(\t )*\r+\d/2}) % side vertice of cylinder
-- ({cos(\t+12)*\r+\d/2}, 0, {sin(\t+12)*\r+\d/2})
-- ({cos(\t+12)*\r+\d/2}, \d, {sin(\t+12)*\r+\d/2})
-- ({cos(\t )*\r+\d/2}, \d, {sin(\t )*\r+\d/2})
-- cycle;
% Draw front of cube
\fill[cube,fill=none] (0,0,0) -- (0,\d,0) -- (\d,\d,0) -- (\d,0,0) -- cycle;
\fill[cube,fill=none] (0,\d,0) -- (0,\d,\d) -- (\d,\d,\d) -- (\d,\d,0) -- cycle;
\fill[cube,fill=none] (0,0,0) -- (0,0,\d) -- (0,\d,\d) -- (0,\d,0) -- cycle;
% Draw the axis arrows and annotations
\draw[axis] (0,0,0) -- (\af,0,0) node[textlabel,anchor=east]{$x$};
\draw[axis] (0,0,0) -- (0,\af,0) node[textlabel,anchor=south]{$y$};
\draw[axis] (0,0,0) -- (0,0,\af) node[textlabel,anchor=west]{$z$};
% Draw radius arrow
\draw[mdc,length,draw] (\d/2,\d/2,\d/2) -- (\d/2,\d/2,\d/2+\r) node[textlabel,pos=0.5, auto=above]{$r$};
% Draw cube lattice length measures
\draw[cube,length,c1] (0,0,\d*5/6) -- (0,\d,\d*5/6) node[textlabel,pos=0.5, auto=above]{$d$};
\draw[cube,length,c1] (\d*5/6,0,0) -- (\d*5/6,\d,0) node[textlabel,pos=0.5, auto=above]{$d$};
\draw[cube,length,c1] (\d*5/6,\d,0) -- (\d*5/6,\d,\d) node[textlabel,pos=0.5, auto=above]{$d$};
% Material parameters label
\draw[mdc] (\d/2,\d/2+\r/2,\d/2) node[textlabel]{$\varepsilon_c,\mu_c$};
\end{tikzpicture}
\end{document}
Which produces:

I am definitely open to a whole new approach to my work routine, but it must be advantageous, otherwise I will just drop the shading requirement (which is half the fun though, one looses depth perception without it).
Thank you so much for taking the time to read this LOOOONG question.


latexmkscript and/or compiling withshell-escapeenabled? – Charles Staats May 02 '14 at 22:57pdflatexand no additional options, but you can automate the workflow (reduce it to a single command) either by usinglatexmk, or (if you use theasypictureBpackage) by usingpdflatex --shell-escape filename.tex. With either of these options, the graphics files will not be recompiled unless they have changed from the last tex run. Alternatively, you can write and compile each.asyfile individually and then include them using\includegraphics; but then you would have to edit the.asyfile in order to rescale. – Charles Staats May 02 '14 at 23:28pgfplotswhich has knowledge of depth and which knows how to shade items according to some color data. See also http://tex.stackexchange.com/questions/164209/pgfplots-3d-surface-plot-from-data-with-free-format, http://tex.stackexchange.com/questions/173602/pgfplots-3d-creating-a-filled-solid-of-revolution?rq=1 , http://tex.stackexchange.com/questions/60584/tikz-3d-library-and-circle-fading, http://tex.stackexchange.com/questions/124916/draw-sphere-pgfplots-with-axis-at-center – Christian Feuersänger May 03 '14 at 11:05pgfplotsand keep much of what you have regardingtikzstyles/nodes/arrows etc. Thus: what, precisely, does your requirement (4) mean? – Christian Feuersänger May 03 '14 at 21:21pgfplots/tikzare available to disambiguate 3d shapes rendered in 2d? changing the color of patches (like a parametric function) seems a bit overkill and time consuming (considering I need to hand code the lightening of each patch, right?). Lines, like in Herbert's example, does the trick too, it does also, however, make the picture a bit more messy. Not a lot for the sphere, but with more complicated structures, I cannot afford this approach. That is why my #4 has the "light" requirement, I see it as an easy(!) way to visualize the spatial nature of the object. – Clausen May 03 '14 at 21:30