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.
latexmk
script and/or compiling withshell-escape
enabled?pdflatex
and no additional options, but you can automate the workflow (reduce it to a single command) either by usinglatexmk
, or (if you use theasypictureB
package) 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.asy
file individually and then include them using\includegraphics
; but then you would have to edit the.asy
file in order to rescale.pgfplots
which has knowledge of depth and which knows how to shade items according to some color data. See also tex.stackexchange.com/questions/164209/…, tex.stackexchange.com/questions/173602/… , tex.stackexchange.com/questions/60584/…, tex.stackexchange.com/questions/124916/…pgfplots
and keep much of what you have regardingtikz
styles/nodes/arrows etc. Thus: what, precisely, does your requirement (4) mean?