# Outline of 3D plot and shading

I'm trying to draw the outline of a 3D graph (a simple function à la -x^2+y^2) but I don't know how to get the envelope'' of that graph.

\documentclass[tikz, border=2mm]{standalone}
\begin{document}

\def\nlines{30}
\def\npoints{14}
\def\xslope{-15}
\def\yslope{25}
\def\coeff{50}
\def\offset{20}
\def\colFunc{magenta!60}
\def\colDom{blue!40}
\begin{tikzpicture}[x={(\xslope:1)}, y={(\yslope:1)}, z={(0cm,1cm)}, scale=.3]
\draw[thick, ->] (-1,-1,-1) -- ++ (1, 0,   0) node [right] {x};
\draw[thick, ->] (-1,-1,-1) -- ++ (0,  1, 0) node [right] {y};
\draw[thick, ->] (-1,-1,-1) -- ++ (0,  0,   1) node [above] {z};

\foreach \x in {0,...,\npoints} {
\foreach \y in {0,...,\nlines} {
\path
(\x,\y,0)
node[circle,
inner sep=0,
minimum size=1pt,
fill=\colDom]
(pt_\x_\y) {};
\path
(\x,\y,-\coeff*\x/\npoints*\x/\npoints+\coeff*\x/\npoints
+\coeff*\y/\nlines*\y/\nlines-\coeff*\y/\nlines
+\offset)
node[circle,
inner sep=0,
minimum size=1.2pt,
fill=\colFunc]
(f_\x_\y) {};
}
}

\draw[fill=blue, opacity=.1]
plot[thick, draw = blue, smooth, variable=\x,samples at={0,0.1,...,\npoints}]
(\x,0,-\coeff*\x/\npoints*\x/\npoints+\coeff*\x/\npoints
+\offset)
--
plot[thick, draw = blue, smooth, variable=\y,samples at={0,0.1,...,\nlines}]
(\npoints,\y,\coeff*\y/\nlines*\y/\nlines-\coeff*\y/\nlines
+\offset)
--
plot[thick, draw = blue, smooth, variable=\x,samples at={\npoints,...,0.1,0}]
(\x,\nlines,-\coeff*\x/\npoints*\x/\npoints+\coeff*\x/\npoints
+\offset)
--
plot[thick, draw = blue, smooth, variable=\y,samples at={\nlines,...,0.1,0}]
(0,\y,+\coeff*\y/\nlines*\y/\nlines-\coeff*\y/\nlines
+\offset)
--
cycle;
\end{tikzpicture}
\end{document}


The result is a little underwhelming (but not surprising)

All help appreciated!

Also, I'd like to shade the surface correctly.

• Would pgfplots be a possibility? – user121799 Jun 20 at 21:31
• @marmot I'm ok with any solution, frankly : ) – Olivier Bégassat Jun 20 at 21:32

To a very first approximation. It is far from perfect because it does not draw all the marks on top of the surface that should be visible. They can be added when it is clear which view angles you choose. A solution that works with arbitrary view angles and/or surfaces is much harder to obtain (and AFAIK not done as of now).

\documentclass[tikz,border=3.14mm]{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.16}
\begin{document}
\begin{tikzpicture}
\begin{axis}[view/h=-70,hide axis,colormap/blackwhite,domain=-5:5,domain y=-5:5,mark size=1pt,clip mode=individual,mark layer=like plot]
\end{axis}
\end{tikzpicture}
\end{document}


Let's assume want to draw only the balls on top without doing the analytic computation. This can be done by employing restrict expr to domain. The following is obtained by guessing.

\documentclass[tikz,border=3.14mm]{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.16}
\begin{document}
\begin{tikzpicture}
\begin{axis}[view={60}{30},hide axis,colormap/blackwhite,domain=-5:5,domain y=-5:5,mark size=1pt,clip mode=individual,mark layer=like plot]
restrict expr to domain={x+0.33*y}{-1.5:7}] {y^2-x^2};
\end{axis}
\end{tikzpicture}
\end{document}


(The features on the top left of the surface originate from a fellow marmot just walking in front of the light source when I took the screen shot. ;-)

A cleaner way is to derive an analytic expression that tells us where the normal of the plane, which can be taken to be just the value of the plane in this case, points in the z direction of the screen coordinates. To this end, we need the normal of the screen, which can be expressed in terms of \pgfkeysvalueof{/pgfplots/view/az} and \pgfkeysvalueof{/pgfplots/view/el}, and is, according to what I find, given by

({cos(\pgfkeysvalueof{/pgfplots/view/el})*sin(-1*\pgfkeysvalueof{/pgfplots/view/az})},


{cos(\pgfkeysvalueof{/pgfplots/view/el})cos(-1\pgfkeysvalueof{/pgfplots/view/az})}, {-sin(\pgfkeysvalueof{/pgfplots/view/el})})

Then we demand that the projection of the normal of the surface on the normal of the screen is positive. (I am not 100% convinced that I did all computations right but my first guess appears to work rather well.)

\documentclass[tikz,border=3.14mm]{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.16}
\begin{document}
\foreach \X in {50,55,...,130,125,120,...,55}
{\begin{tikzpicture}[declare function={f(\x,\y)=0.1*(\y*\y-\x*\x);}]
\begin{axis}[view={\X}{30},unit vector ratio=1 1 1,
hide axis,colormap/blackwhite,domain=-5:5,domain y=-5:5,mark size=1pt,clip mode=individual,mark layer=like plot]
restrict expr to domain={2*x*cos(\pgfkeysvalueof{/pgfplots/view/el})*sin(-1*\pgfkeysvalueof{/pgfplots/view/az})
-2*y*cos(\pgfkeysvalueof{/pgfplots/view/el})*cos(-1*\pgfkeysvalueof{/pgfplots/view/az})
-sin(\pgfkeysvalueof{/pgfplots/view/el})}{-100:0}] {f(x,y)};
\end{axis}
\end{tikzpicture}}
\end{document}


• Thank you :) That's pretty good! – Olivier Bégassat Jun 21 at 0:05
• Nice work! Is it possible that in the last image some points are missing when the plot is rotating? – manooooh Jun 21 at 5:25
• @manooooh Thanks! Yes, very possible. – user121799 Jun 21 at 5:27