# How to coherently combine 3D and contour plots with pgfplots

Both contour and 3d plots can be done easily in pgfplots, but I have a hard time combining them nicely.

Here's an example of what I'd like to achieve (from the matplotlib examples):

-
“Both contour and 3d plots can be done easily in pgfplots”: Let us see that! What did you try to combine them? –  Qrrbrbirlbel Dec 19 '12 at 20:24

## 3 Answers

Pgfplots can compute the z contours by means of gnuplot and its contour gnuplot interface.

The projection onto the x axis (i.e. with fixed y) can be done by means of a matrix line plot in which you replace the y coordinate of the input matrix by some fixed constant.

The projection onto the y axis (i.e. with fixed x) is more involved (at least if mesh/ordering=x varies as in my example below) because one needs to transpose the input matrix. In my example below, I simply replaced the meaning of x and y to achieve the transposal. This, of course, would be more involved for a data matrix (and I think that pgfplots has no builtin to do it).

Here is what I got so far:

\documentclass{standalone}
\usepackage{pgfplots}

\begin{document}
\begin{tikzpicture}
\begin{axis}[
domain=-2:2,
domain y=0:2*pi,
]

\newcommand\expr[2]{exp(-#1^2) * sin(deg(#2))}

\addplot3[
contour gnuplot={
% cdata should not be affected by z filter:
output point meta=rawz,
number=10,
labels=false,
},
samples=41,
z filter/.code=\def\pgfmathresult{-1.6},
]
{\expr{x}{y}};

\addplot3[
samples=41,
samples y=10,
domain=0:2*pi,
domain y=-2:2,
% we want 1d (!) individually colored mesh segments:
mesh, patch type=line,
x filter/.code=\def\pgfmathresult{-2.5},
]
(y,x,{\expr{y}{x}});

\addplot3[
samples=41,
samples y=10,
% we want 1d (!) individually colored mesh segments:
mesh, patch type=line,
y filter/.code=\def\pgfmathresult{8},
]
{\expr{x}{y}};

\addplot3[surf,samples=25]
{\expr{x}{y}};

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


As you see, the first contour is the z contour. It is computed using gnuplot (and requires the -shell-escape mechanism to this end!).

The x and y projections are computed using the same matrix of function values. I chose a different sampling density to control how many "contour lines" shall be drawn. Note that these lines are conceptionally different from the z contours: they are already part of the sampling procedure and do not need to be computed externally. Note that I used mesh, patch type=line to tell pgfplots that (a) it should use individually colored segments and (b) it should not color the 2d structure, just the lines in scanline order (which is mesh/ordering=x varies in my case).

-
I found it suprisingly difficult to come from "contour plot" to "projection onto a line". I will think about a simpler solution which does not need these special distinctions. –  Christian Feuersänger Dec 21 '12 at 21:47
Very clever to use the plot3 to also make the side projections. (I can't compete with the master.) @ChristianFeuersanger: Do you plan to bring native contour plots to pgfplots or is it beyond the scope? If so, what algorithm would you use to implement it? –  alfC Dec 22 '12 at 2:12
there is an experimental branch containing a native contour plot algorithm. However, it is unfinished so far and Nick Papior Andersen has not worked on it for some time. I will ask him. Concerning the contour gnuplot stuff: one can simply exchange the coordinates! That means there is a much simpler approach with splot ... using 1:3:2 or something like that. I will think about a suitable pgfplots option. –  Christian Feuersänger Dec 22 '12 at 7:29
I have implemented coherent support for this feature in pgfplots (by means of a key contour dir=x|y|z). It is currently part of the unstable (cf pgfplots.sourceforge.net). –  Christian Feuersänger Dec 29 '12 at 9:53

here is a solution with pst-solides3d for the function z=sin(x)*sin(y), which can be adopted to your function.

\documentclass[12pt]{article}
\usepackage{pst-solides3d}
\pagestyle{empty}

\begin{document}
\psset{arrowlength=3,arrowinset=0,viewpoint=50 30 20 rtp2xyz,Decran=50,
lightsrc=viewpoint}

\begin{pspicture}(-7,-8)(7,8)
\axesIIID[linecolor=gray](0,0,0)(7,7,7)
\psSolid[ngrid=.3 .3,object=grille,base=1 8 1 8,
linewidth=0.4pt,linecolor=gray!50,action=draw]%

{\psset{object=courbe,r=0,linecolor=blue,resolution=360,function=Fxy}
\multido{\rA=0.0+1.0}{8}{%
\defFunction[algebraic]{Fxy}(x){x}{0}{sin(x)*sin(\rA)+3}
\psSolid[range=1 8]}
\multido{\rA=0.0+1.0}{8}{%
\defFunction[algebraic]{Fxy}(y){0}{y}{sin(\rA)*sin(y)+3}
\psSolid[range=1 8]}}
\psSurface[ngrid=.3 .3,fillcolor=green!30,incolor=gray!30,
linewidth=0.4pt,algebraic](1,1)(8,8){ sin(x)*sin(y) +3 }

\end{pspicture}
\end{document}


and the same for a more complicated funktion:

-

pgfplot supports contour plots by using the external-gnuplot. The side curves are obtained by two parametric curves. You can combine the 4 plots by putting all of them inside the axis environment.

The code is redundant, specially because the function in gnuplot (which does the contour) can not be passed from the tex code, as far as I know.

The result is the following:

The code follows:

\documentclass{scrartcl}
\usepackage{pgfplots}
\begin{document}
\begin{tikzpicture}
\begin{axis}[domain=-5:5]
\addplot3[domain=-5:5,samples=80,samples y=0,mark=none,black, opacity=0.5,thick]({x},{6.},{exp(-x*x - 0*0 + x*0. + 0.)});
\addplot3[domain=-5:5,samples=80,samples y=0,mark=none,black, opacity=0.5,thick]({-6.},{x},{exp(-0*0 - x*x + 0.*x + x)});
\addplot3 +[no markers,
raw gnuplot,
mesh=false,
z filter/.code={\def\pgfmathresult{-2}}
] gnuplot {
set contour base;
set cntrparam levels 20;
unset surface;
set view map;
set isosamples 500;
set samples 100;
splot [-5:5][-5:5][0:1] exp(-x*x-y*y + x*y + y);
};
\end{axis}
\addplot3[surf,opacity=0.5,samples=40] {exp(-x*x-y*y + x*y + y)};
\end{tikzpicture}
\end{document}

-
Could you elaborate on point 2? Can’t I just move the first \addplot3 at the end of all \addplot3s? The surf plot is always on top? Nonetheless opacity should be included anyway to show hidden contour lines. –  Qrrbrbirlbel Dec 20 '12 at 4:46
@Qrrbrbirlbel, I was thinking in case one wants to change the view point, but you are right, this doesn't make sense for this application. I modified the code, but still left the opacity simply because that is what is shown in the example of the question. Admittedly the OP probably wants a fully automatic solution, but I don't know how to do that, I mention that in the new answer. –  alfC Dec 20 '12 at 5:54