10

I'd like to have text that appears as though it's written "on" one of the walls of a 3D tikz plot. MWE:

\documentclass{standalone}

\usepackage{tikz,pgfplots}
\usetikzlibrary{positioning,3d}
\pgfplotsset{compat=1.10}

\begin{document}

\begin{tikzpicture}
\begin{axis}[
    view={60}{30},
    %axis equal,
    clip=false,
    xmin=0,xmax=1,
    ymin=0,ymax=1,
    zmin=0,zmax=1,
    ]

    %\addplot3 (1,1,1);

    \path (axis cs:0,0,0) -- (axis cs:0,1,0) node[midway,above, sloped,xslant=0.2] {Text};

\end{axis}
\end{tikzpicture}

\end{document}

MWE1

The thing that I'm not happy with in this case is that I needed to set xslant manually. I could calculate it based on geometry, but pgfplots angles seem to not always be as you would expect. Example:

\documentclass{standalone}

\usepackage{tikz,pgfplots}
\usetikzlibrary{positioning,3d}
\pgfplotsset{compat=1.10}

\begin{document}

\begin{tikzpicture}
\begin{axis}[
    view={45}{90},
    %axis equal,
    clip=false,
    xmin=0,xmax=1,
    ymin=0,ymax=1,
    zmin=0,zmax=1,
    ]

    %\addplot3 (1,1,1);

    \path (axis cs:0,0,0) -- (axis cs:0,1,0) node[midway,above, sloped,xslant=0.2] {Text};

\end{axis}
\end{tikzpicture}

\end{document}

MWE2

[this is exactly he same as the previous example except that I've changed the view]. This gives a view of the plot from directly above, showing that the x and y axes are not perpendicular. I think that this can be changed with axis equal or axis equal image, but I'd rather not use those as they have side-effects.

I assume that pgfplots has internally calculated how the x, y, and z direction vectors are mapped to the 2-D page... is there a way that I can extract this information? If I had that available, then it would be easy to find the appropriate slope using trig. This would work without axis equal or axis equal image

user1476176
  • 1,225

3 Answers3

6

I wasn't sure whether you wanted flat or vertical letters, so I did both. Note the use of [scale mode=scale uniformly]. Also note that yscale is the denominator of xslant, and xscale is the denominator of yslant.

text in 3d

\documentclass{standalone}

\usepackage{tikz,pgfplots}
\usetikzlibrary{positioning,3d}
\pgfplotsset{compat=1.10}

\begin{document}

\begin{tikzpicture}
\def\h{80}% horizontaql viewing angle
\def\v{10}% vertical viewing angle
\begin{axis}[
    scale mode=scale uniformly,
    view={\h}{\v},
    clip=false,
    xmin=0,xmax=1,
    ymin=0,ymax=1,
    zmin=0,zmax=1,
   ]
  \pgfmathparse{atan(tan(\h)*sin(\v))}
  \let\a=\pgfmathresult
  \pgfmathparse{atan(tan(90-\h)*sin(\v))}
  \let\b=\pgfmathresult  

    \path (axis cs:0,0,0) -- (axis cs:0,1,0) node[color=blue,midway,above,sloped,
    xslant=tan(\a+\b+90),yscale=sin(\a+\b)] {Text};

    \path (axis cs:0,0,0) -- (axis cs:0,1,0) node[color=red,midway,above,
    yslant=tan(\b),xscale=cos(\b)] {Text};

\end{axis}
\end{tikzpicture}
\end{document}

do the math

John Kormylo
  • 79,712
  • 3
  • 50
  • 120
  • I like this! Is there a reason that you used option "sloped" for the blue text and just explicitly set xslant for the red text? – user1476176 Nov 19 '14 at 00:30
  • Flat text is best done using rotate (sloped) and xslant. Vertical text is best done using yslant. – John Kormylo Nov 19 '14 at 01:58
  • Cool. I'm accepting this because I think you've got all the ingredients I need, but I think that there is a mistake in the blue text alignment. It looks right with the combination of angles you have, but it doesn't seem to adjust properly for other values. – user1476176 Nov 19 '14 at 06:25
  • I went back and did the math. It might be possible to simplify the calculations, but right now my brain hurts. – John Kormylo Nov 19 '14 at 17:17
  • Haha, I did the math too. I think the way that seems most intuitive to me is to use the transformation matrix instead of setting slants and scales individually. This was also the only way that made sense to me when I tried to project text on the the x-y plane (which is a more complicated transformation)... I'll post this as another answer – user1476176 Nov 19 '14 at 18:46
3

You should switch to PST-solides3d, which can produce much satisfying projection of texts and also figures. See the following examples from its documentation: enter image description here

enter image description here

Werner
  • 603,163
Troy Woo
  • 535
3

Building on John's solution, here is a MWE which uses option cm to explicitly set the transformation matrix for each node:

\documentclass{standalone}

\usepackage{tikz,pgfplots}
\pgfplotsset{compat=1.10}

\begin{document}

\begin{tikzpicture}

\newcommand{\viewa}{30}
\newcommand{\viewb}{30}

\begin{axis}[scale mode=scale uniformly,% needed for text
    view={\viewa}{\viewb},
    clip=false,
    xmin=0,xmax=1,
    ymin=0,ymax=1,
    zmin=0,zmax=1,
    ]           

    \node[color=red,
        cm={sin(\viewa),cos(\viewa)*sin(\viewb),
        0,cos(\viewb),
        (0,0)}
            ]   
     at (axis cs:0,0.5,0.5) {$x$ plane};                                

    \node[color=blue,
        cm={cos(\viewa),-sin(\viewa)*sin(\viewb),
        sin(\viewa),cos(\viewa)*sin(\viewb),
        (0,0)}
            ]   
     at (axis cs:0.5,0.5,0) {$z$ plane};     

    \node[color=black,
        cm={cos(\viewa),-sin(\viewa)*sin(\viewb),
        0,cos(\viewb),
        (0,0)}
            ]   
     at (axis cs:0.5,1,0.5) {$y$ plane};     

\end{axis}
\end{tikzpicture}

\end{document}

enter image description here

In general, to make a node appear as though it is suspended on a plane with direction vectors u = (u_x,u_y,u_z) and v = (v_z,v_y,v_z)

  • Define a = u_x * cos(a) + u_y * sin(a), where a and b are the view angles defined in the LaTeX code above
  • Define b = - u_x * sin(a)sin(b) + u_y * cos(a)sin(b) + u_z * cos(b)
  • Define c = v_x * cos(a) + v_y * sin(a)
  • Define d = - v_x * sin(a)sin(b) + v_y * cos(a)sin(b) + v_z * cos(b)
  • Place the node with option cm={a,b,c,d,(0,0)}, where a-d represent the expressions above.

In the MWE, I used

  • blue text: u = (1,0,0), v = (0,1,0)
  • red text: u = (0,1,0), v = (0,0,1)
  • black text: u = (1,0,0), v = (0,0,1)
user1476176
  • 1,225