39

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:

  1. Output (the figure) must be vector graphics
  2. I would REALLY like to compile my main document with pdflatex
  3. Text should preferably be scalable, i.e., the text stays the same, even if I decide to make the figure bigger in my document.
  4. 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).
  5. 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: Sphere with tikz

I have worked on some code for manually coding a cylinder, but I encounter two main problems:

  1. I cannot dynamically change the fill, to create a shading effect.
  2. 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: Cylinder with Tikz

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.

Clausen
  • 555
  • Asymptote is without peer when it comes to shading and lighting, but has trouble with some of your other requirements. How open are you to using the latexmk script and/or compiling with shell-escape enabled? – Charles Staats May 02 '14 at 22:57
  • @Charles: In my search I did encounter asymptote - and yes, it seems like it is the most powerful of them all. I have not played around with it, what, exactly, would be the work flow? I am in general open to "right" solutions, even if they take a different approach. My requirements are not set in stone, but preferred. It might even be an advantage having external files, to ease the compilation!? – Clausen May 02 '14 at 23:18
  • 1
    Clausen: There are several possible workflows. None of them work with pdflatex and no additional options, but you can automate the workflow (reduce it to a single command) either by using latexmk, or (if you use the asypictureB package) by using pdflatex --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. – Charles Staats May 02 '14 at 23:28
  • 4
    You could combine your tikz approach (i.e. nodes, arrows, pointers) with the 3d capabilities of pgfplots which 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:05
  • @Christian: The shading depending on color is too convoluted an option, as the shading (in my context) is too inherent a feature. I need to draw even more complicated unit cells (which the sphere and the cylinder are) later on. – Clausen May 03 '14 at 20:27
  • @Charles: Could you add the information from yuor comment (#3 in this thread) to your answer, that would clear up things for future visitors I think :) – Clausen May 03 '14 at 20:29
  • 1
    @Clausen: done, more or less. – Charles Staats May 03 '14 at 21:06
  • 2
    @Clausen thanks for the response. In principle, pgfplots can support highly complicated shadings which would satisfy the requirement to "distinghuish between" various unit cells (including circle and sphere). If you say that you are bound to shadings generated by some light source, you should stick with asymptote or other external rendering tools. If you are open to "shadings which disambiguate", you could benefit of pgfplots and keep much of what you have regarding tikz styles/nodes/arrows etc. Thus: what, precisely, does your requirement (4) mean? – Christian Feuersänger May 03 '14 at 21:21
  • @Christian: What tools in pgfplots/tikz are 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
  • Ok, thanks for the clarification. For your sub-question: I was thinking on a color map which maps, say, your y coordinate to a colormap as in http://tex.stackexchange.com/questions/124916/draw-sphere-pgfplots-with-axis-at-center (without the mesh lines and without the opacity). – Christian Feuersänger May 04 '14 at 14:33

2 Answers2

26

Here's an alternative using Asymptote. It fulfills some version of most of your requests; for instance, the colors are defined in the preamble (but in Asymptote code rather than TeX code, so you might need to define TeX versions separately).

% To run: pdflatex --shell-escape filename.tex

\documentclass[margin=10pt,convert]{standalone}
\usepackage{asypictureB}

\begin{asyheader}
pen c1 = rgb(0.2,0.4,0.6); // Blue-ish
pen c2 = rgb(1.0,0.0,0.6); // Pink-ish
pen c3 = rgb(0.6,0.0,0.0); // Red

texpreamble("\newbox\BoxForRules
\newcommand{\boxToRule}[1]{%
    \setbox\BoxForRules=\hbox{\hspace{2pt}#1\hspace{2pt}}%
    $\rlap{\rule[\dimexpr-\dp\BoxForRules-2pt]{\wd\BoxForRules}{\dimexpr\ht\BoxForRules+\dp\BoxForRules+4pt}}\mbox{\hspace{2pt}#1\hspace{2pt}}$%
}");

import three;

void framed_label(string s, triple position, pen p = currentpen) {
    label("\boxToRule{" + s + "}", position=position, p=white + opacity(0.6));
    label(s, position=position, p=p);
}
\end{asyheader}

\begin{document}
\begin{asypicture}{name=cylinder}
    settings.outformat = "pdf";
    settings.render = 0;

    real unit = 4cm;
    unitsize(unit);

    currentprojection = orthographic((-4,2.8,-2), up=Y);

    real d = 1;
    real r = 0.45 d;
    real af = 0.5 d;

    pen cubedraw = linewidth(0.8pt) + gray;
    pen cubefill = opacity(0.5) + c1;
    pen axis = linewidth(1.0 pt) + c3;

    //Draw backside of cube
    draw( surface((0,0,d) -- (0,d,d) -- (d,d,d) -- (d,0,d) -- cycle), 
        meshpen = cubedraw, 
        surfacepen = emissive(cubefill));
    draw( surface((0,0,0) -- (0,0,d) -- (d,0,d) -- (d,0,0) -- cycle), meshpen = cubedraw, surfacepen = emissive(cubefill));
    draw( surface((d,0,0) -- (d,0,d) -- (d,d,d) -- (d,d,0) -- cycle), meshpen = cubedraw, surfacepen = emissive(cubefill));

    //Draw cylinder as surface of revolution
    path3 cyl_center = shift(d/2,0,d/2) * (O -- d*Y);
    path3 cyl_edge = shift(r*X) * cyl_center;
    int n = 10;
    guide3 to_revolve = point(cyl_edge, 0);
    for (int i = 1; i <= n; ++i)
        to_revolve = to_revolve -- point(cyl_edge, i/n);
    surface cylinder = surface(to_revolve, c=(d/2,0,d/2), axis=Y);
    draw(cylinder, meshpen = 0.3 c2 + 0.5 gray, 
            surfacepen = material(c2 + opacity(0.7), emissivepen = 0.2 c2));


    //Draw the radius arrow and center line
    draw(cyl_center, 0.8 c2);
    //draw(circle(c=(d/2,d/2,d/2), r=r, normal=Y), 0.8 c2);
    draw( (d/2,d,d/2) -- (d/2,d,d/2+r), c2 + linewidth(0.8), arrow=Arrows3(TeXHead2));
    framed_label("$r$", position=(d/2, d, d/2 + r/2), p=c2);

    //Draw front of cube
    draw( (0,0,0) -- (0,d,0) ^^ (d,d,0) -- (0,d,0) ^^ (0,d,d) -- (0,d,0), cubedraw);

    //Draw the axis arrows and annotations
    draw(O -- af*X, axis, arrow=Arrow3(TeXHead2));
    framed_label("$x$", position=(1 + 18pt/unit)*af*X, p=axis);
    draw(O -- af*Y, axis, arrow=Arrow3(TeXHead2));
    framed_label("$y$", position=(1 + 12pt/unit)*af*Y, p=axis);
    draw(O -- af*Z, axis, arrow=Arrow3(TeXHead2));
    framed_label("$z$", position=(1 + 10pt/unit)*af*Z, p=axis);

    //Draw cube lattice length measures
    draw((0,0,d*5/6) -- (0,d,d*5/6), p=linewidth(0.8pt) + c1, arrow=Arrows3(TeXHead2));
    framed_label("$d$", (0,d/2,d*5/6), c1);
    draw((d*5/6,0,0) -- (d*5/6,d,0), p=linewidth(0.8pt) + c1, arrow=Arrows3(TeXHead2));
    framed_label("$d$", (d*5/6, d/2, 0), c1);
    draw((d*5/6, d, 0) -- (d*5/6, d, d), p=linewidth(0.8pt) + c1, arrow=Arrows3(TeXHead2));
    framed_label("$d$", (d*5/6, d, d/2), c1);

    //Material parameters label
    framed_label("$\varepsilon_c, \mu_c$", position=(d/2, d/2+r/2, d/2), p=c1);
\end{asypicture}

\begin{asypicture}{name=sphere}
    settings.outformat = "pdf";
    settings.render = 0;
    import graph3;

    real unit = 4cm;
    unitsize(unit);

    currentprojection = orthographic((-4,2.8,-2), up=Y);

    real d = 1;
    real r = 0.45 d;
    real af = 0.5 d;

    pen cubedraw = linewidth(0.8pt) + gray;
    pen cubefill = opacity(0.5) + c1;
    pen axis = linewidth(1.0 pt) + c3;

    //Draw backside of cube
    draw( surface((0,0,d) -- (0,d,d) -- (d,d,d) -- (d,0,d) -- cycle), 
        meshpen = cubedraw, 
        surfacepen = emissive(cubefill));
    draw( surface((0,0,0) -- (0,0,d) -- (d,0,d) -- (d,0,0) -- cycle), meshpen = cubedraw, surfacepen = emissive(cubefill));
    draw( surface((d,0,0) -- (d,0,d) -- (d,d,d) -- (d,d,0) -- cycle), meshpen = cubedraw, surfacepen = emissive(cubefill));

    //Draw sphere as surface of revolution
    triple centerpoint = (d/2, d/2, d/2);
    path3 centerline = (centerpoint - r*Y) -- (centerpoint + r*Y);
    path3 to_revolve = Arc(centerpoint - r*Y, centerpoint + r*Y, c=centerpoint, normal=X, n=16);
    surface sphere = surface(to_revolve, c=centerpoint, axis=Y);
    draw(sphere, meshpen = 0.3 c2 + 0.5 gray, 
            surfacepen = material(c2 + opacity(0.7), emissivepen = 0.2 c2));


    //Draw the radius arrow and helper lines
    draw(centerline, 0.8 c2);
    draw(circle(c=(d/2,d/2,d/2), r=r, normal=Y), 0.8 c2);
    draw( (d/2,d/2,d/2) -- (d/2,d/2,d/2+r), c2 + linewidth(0.8), arrow=Arrows3(TeXHead2));
    framed_label("$r$", position=(d/2, d/2, d/2 + r/2), p=c2);

    //Draw front of cube
    draw( (0,0,0) -- (0,d,0) ^^ (d,d,0) -- (0,d,0) ^^ (0,d,d) -- (0,d,0), cubedraw);

    //Draw the axis arrows and annotations
    draw(O -- af*X, axis, arrow=Arrow3(TeXHead2));
    framed_label("$x$", position=(1 + 18pt/unit)*af*X, p=axis);
    draw(O -- af*Y, axis, arrow=Arrow3(TeXHead2));
    framed_label("$y$", position=(1 + 12pt/unit)*af*Y, p=axis);
    draw(O -- af*Z, axis, arrow=Arrow3(TeXHead2));
    framed_label("$z$", position=(1 + 10pt/unit)*af*Z, p=axis);

    //Draw cube lattice length measures
    draw((0,0,d*5/6) -- (0,d,d*5/6), p=linewidth(0.8pt) + c1, arrow=Arrows3(TeXHead2));
    framed_label("$d$", (0,d/2,d*5/6), c1);
    draw((d*5/6,0,0) -- (d*5/6,d,0), p=linewidth(0.8pt) + c1, arrow=Arrows3(TeXHead2));
    framed_label("$d$", (d*5/6, d/2, 0), c1);
    draw((d*5/6, d, 0) -- (d*5/6, d, d), p=linewidth(0.8pt) + c1, arrow=Arrows3(TeXHead2));
    framed_label("$d$", (d*5/6, d, d/2), c1);

    //Material parameters label
    framed_label("$\varepsilon_c, \mu_c$", position=(d/2, d/2+r/2, d/2), p=c1);
\end{asypicture}

\end{document}

Here's the result:

enter image description here

From a technical standpoint, the most difficult aspect was the white boxes framing the text; as you can see, I gave up on the rounded corners.

I'd also note that a lot of this would be easier if you were willing to use high-resolution rasterized graphics: it would be determined automatically what objects go in front of what others (independent of the drawing order), and the gridlines would not be necessary.


A note on workflows using Asymptote: here are some of the available options. (All of them require a working Asymptote installation, although that is automatic with TeXLive.)

  1. Use a standard latex command (e.g., pdflatex) with the -shell-escape option enabled. This requires the asypictureB package. On any given run, only those pictures that have changed (or been deleted) since the last run will be recompiled. You can create a "common preamble" that is shared among all your Asymptote files including e.g. color definitions, but definitions in your TeX preamble do not carry over automatically.
    This is the option I have suggested using above.

  2. Use the latexmk script, with the configuration file augmented as described in this documentation. For this, you should probably use the asymptote package rather than asypictureB, which is not designed to be used this way. You still have the option of a common Asymptote preamble. With the inline option of the asymptote package, you also have access to all the packages and macros of your main document in the labels.

  3. Use either asymptote or asypictureB with multiple runs to compile the pictures separately. The command sequence
    pdflatex filename
    asy filename-*.asy
    pdflatex filename
    will usually work with either package (or both), although every image will be recompiled--including those that have not changed since the last run. A more efficient alternative to the middle line is offered by the asypictureB package; see the package documentation, section 2.1, page 4.

Personally, I prefer the first option, because it offers the best debugging support by a fair margin. At the same time, I recommend that LaTeX users in general be familiar with using latexmk, since it can be used to automate an entire workflow, including making the index (if necessary) and multiple runs to define labels and the table of contents (again, only if necessary; latexmk is good at detecting this).

  • 1
    I swear I'll convert to Asymptote one of these days. – nnunes May 03 '14 at 13:43
  • "[...] this would be easier if you were willing to use high-resolution rasterized graphics". How? Still with asymptote? – Clausen May 03 '14 at 20:29
  • It outputs the error: imgconvert is not recognized as an an internal or external command, operable program or batch file. when run from cmd on windows7.. Somwhere in the log file I get this: runsystem(imgconvert -density 300 asy_SO1.pdf -quality 90 asy_SO1.png)...executed. Is that from asymptote? – Clausen May 03 '14 at 20:50
  • If you're trying to compile these to png, Asymptote does this by calling an external program that your computer apparently either does not have installed or that is not being successfully located, for whatever reason. If you did not change the settings to png, then I have no idea why this error is showing up. – Charles Staats May 03 '14 at 21:09
  • If you want to see what Asymptote can do in rasterized format, don't use these images--they were specifically designed for vector graphics. Check out the image in this answer, for instance. – Charles Staats May 03 '14 at 21:11
  • @CharlesStaats Very nice picture. But I guess there is no general way to simultaneously allow for vector output and being able to change the view angle without getting the depth order of the picture elements wrong, right? I noticed that when I compiled your asymptote code as standalone pictures with prc, i.e. using the vector-picture as preview image, then the arrows tips are rendered incorrectly (they change their color). This doesn't happen when I compile the code without prc. Do you happen to know why this could be? Shouldn't the preview image be the same as the one without prc? – Philipp May 15 '14 at 15:51
  • @Philipp: I am not able to duplicate your error, but I know that arrowheads can behave strangely sometimes in 3d--especially if your preview image somehow got rasterized without your realizing it. One thing you can try is to specify a material as a second parameter for the Arrow3 function; for instance, I've found that Arrow3(TeXHead2, emissive(c2)) works in a rasterized image to produce an arrow that is exactly the color c2, without any shading. For a vector graphic, something like Arrow3(TeXHead2, c2) works better. – Charles Staats May 15 '14 at 20:27
  • @CharlesStaats Interesting that you don't see this behavior. Maybe it is a deficiency of the Windows version of Asymptote. Thanks for the tips about arrow heads. – Philipp May 15 '14 at 20:45
13

Sphere and cube have its center in (0,0,0). Uncomment \axesIIID to see the internal coordinate system. Run the example with xelatex or latex->dvisps->ps2pdf or use pdflatex which needs \usepackage[crop=off]{auto-pst-pdf}.

\documentclass[pstricks]{standalone}
\usepackage{pst-solides3d}
\begin{document}

\psset{viewpoint=30 40 20 rtp2xyz,Decran=30}
  \begin{pspicture}(-3.5,-3.5)(3.5,3.5)
%  \axesIIID(2,2,2)(4,4,4)
   \psSolid[object=cube,a=4,fillcolor=blue,opacity=0.2,action=draw*]%
   \psSolid[object=sphere,r=1.5,linewidth=0.1pt,ngrid=20 20,fillcolor=red,opacity=0.2,action=draw*]%
   \psSolid[object=vecteur,args=0 -2 0](2,2,-2)
   \psSolid[object=vecteur,args=-2 0 0](2,2,-2)
   \psSolid[object=vecteur,args=0 0 2](2,2,-2)
   \psPoint(2,2,0.2){Z}\rput(Z){z}\psPoint(2,-0.2,-2){X}\rput(X){x}\psPoint(-0.2,2,-2){Y}\rput(Y){y}
   \psPoint(2,-1.6,-2){a1}\psPoint(2,-1.6,2){a2}\pcline{<->}(a1)(a2)\ncput*{d}
   \psPoint(2,-1.6,2){a1}\psPoint(-2,-1.6,2){a2}\pcline{<->}(a1)(a2)\ncput*{d}
   \psPoint(-1.6,2,-2){a1}\psPoint(-1.6,2,2){a2}\pcline{<->}(a1)(a2)\ncput*{d}
   \psPoint(0,0,0){a1}\psPoint(1,-1,0){a2}\pcline{->}(a1)(a2)\ncput*{r}
  \end{pspicture}

\end{document}

enter image description here

  • This looks nice as well, but how is it integrated within my latex document? I haven't figured (no pun intended) out how to use ps-tricks (which seems pretty awesome) with my current document. – Clausen May 03 '14 at 17:59
  • 1
    As I wrote use xelatex or load package \usepackage[crop=off]{auto-pst-pdf} for running pdflatex. Id you have Perl installed, you can use [crop=on]. –  May 03 '14 at 18:01