[Tex/LaTex] Contour partially hidden by a surface in pgfplots

3dpgfplots

Thank's to SE (and particularly these two questions: Draw a bivariate normal distribution in TikZ and How to fix a contour plot at top of a 3D box), I've been able to draw what I was looking for with pgfplots except for one detail.

In this image:

this image

I would like the contour lines drawn on the surface to be hidden by the surface in the foreground but not in the background (like it would be if you were really looking at such a "valley"). Is it possible to do so ?

Here is the code (I downgraded the sampling in order to make it run quicker and don't forget the -shell-escape option to make it run with LaTeX).

\documentclass{standalone}

\usepackage{pgfplots}

\usepackage{amsmath}

\begin{document}

\pgfplotsset{
colormap={whitered}{color(0cm)=(white); color(1cm)=(orange!75!red)}
}

\begin{tikzpicture}
  \begin{axis}[
    colormap name=whitered,
    3d box,
    width=15cm,
    view={25}{25},
    enlargelimits=false,
    grid=major,
    domain=-0.5:4.7,
    y domain=-2:2,
    samples=21,
    xlabel=$x$,
    ylabel=$\dot{x}$,
    zlabel={$\text{E}_{\text{m}}$},
    colorbar,
    colorbar style={
        at={(1,0)},
        anchor=south west,
        height=0.1*\pgfkeysvalueof{/pgfplots/parent axis height},
        title={$\text{E}_{\text{m}}(x,\dot{x})$}
        }
    ]
\addplot3 [surf] {-0.7+4*exp(-0.5*(x+3))*(3*cos(4*x*180/pi)+2.5*cos(2*x*180/pi)) + 0.5*y*y*4};
\addplot3 [contour gnuplot={number=14,labels={false},draw color=black},
    samples=21,
    ] {-0.7+4*exp(-0.5*(x+3))*(3*cos(4*x*180/pi)+2.5*cos(2*x*180/pi)) + 0.5*y*y*4};
\addplot3 [domain=-0.5:4.7,samples=31, samples y=0, thick, smooth]
    (x,-2,{-0.6+4*exp(-0.5*(x+3))*(3*cos(4*x*180/pi)+2.5*cos(2*x*180/pi))});
\addplot3 [contour gnuplot={number=14,labels={false},draw color=black},
    samples=21,
    z filter/.code={\def\pgfmathresult{20}},
    ] {-0.7+4*exp(-0.5*(x+3))*(3*cos(4*x*180/pi)+2.5*cos(2*x*180/pi)) + 0.5*y*y*4};

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

Best Answer

This is very difficult with the current version of pgfplots. The simple reason is that z-buffering is not fully implemented.

I am actually a little bit unsure of this, as I haven't followed that part of pgfplots.

Hence you should do your own z-buffering (which can be quite cumbersome). This means that you have to draw the parts in terms of their appearance on the screen, and thus a lot of double drawings.

Here is a start:

\addplot3 [y domain=0:2,surf]
    {-0.7+4*exp(-0.5*(x+3))*(3*cos(4*x*180/pi)+2.5*cos(2*x*180/pi)) + 0.5*y*y*4};

\addplot3 [y domain=0:2,contour gnuplot={number=14,labels={false},draw color=black},samples=21, ] 
    {-0.7+4*exp(-0.5*(x+3))*(3*cos(4*x*180/pi)+2.5*cos(2*x*180/pi)) + 0.5*y*y*4};

\addplot3 [domain=-0.5:4.7,samples=31, samples y=0, thick, smooth]
    (x,-2,{-0.6+4*exp(-0.5*(x+3))*(3*cos(4*x*180/pi)+2.5*cos(2*x*180/pi))});
\addplot3 [contour gnuplot={number=14,labels={false},draw color=black},
    samples=21,z filter/.code={\def\pgfmathresult{20}}]
    {-0.7+4*exp(-0.5*(x+3))*(3*cos(4*x*180/pi)+2.5*cos(2*x*180/pi)) + 0.5*y*y*4};
\addplot3 [y domain=-2:0,surf] {-0.7+4*exp(-0.5*(x+3))*(3*cos(4*x*180/pi)+2.5*cos(2*x*180/pi)) + 0.5*y*y*4};
\addplot3 [domain=0:.25,contour gnuplot={number=14,labels={false},draw color=black},
    samples=21,
    ] {-0.7+4*exp(-0.5*(x+3))*(3*cos(4*x*180/pi)+2.5*cos(2*x*180/pi)) + 0.5*y*y*4};

which produces:

enter image description here

As you can see, there are some parts that needs to be fine-tuned, but the idea is obvious. Draw the back part, then the contours, then the front part and then fine tune all the small details on placement via the domains until satisfactory results are achieved.

Yes, this is not feasible with multi saddle points of large magnitude in which case it might be better to export from Octave and plot via the graphics option.

Related Question