9

I'm trying to plot this function

f(x,y) = -sqrt(x^2+y^2-1)

which should render as something like this (as generated by CalcPlot3D) calcplot3d plot

Problem is, I can't get pgfplots to generate something similar even with a pretty big samples number such as 150. Also, compilation time becomes exceedingly long, which would be a small problem, given I'm externalizing graphs, but still the result is suboptimal. pgfplots plot As you can see in the image, my output is fractured near z=0 (where the function is a circumference), but that is the most important part of the plot for my exposition, since I have to point out that this function has infinite absolute maxima points.

Here is my current code (disclaimer: don't run it unless you're in for 5 minutes of 100% cpu usage)

\documentclass{book}
\usepackage{tikz}
\usepackage{pgfplots}
\pgfplotsset{compat=1.7}
\begin{document}
\begin{tikzpicture}
    \begin{axis}[
        xlabel=$x$, ylabel=$y$,
    ]
    \addplot3[surf, domain =-2:2, domain y=-2:2, unbounded coords=jump, samples=150]
        { x^2 + y^2 >= 1 ? -sqrt(x^2+y^2-1) : NaN };
    \end{axis}
\end{tikzpicture}
\end{document}

Do you guys have a tip on how to plot this function correctly, other than embedding a pre-rendered image?

0

2 Answers 2

13

Normally, in order to get a good finish of radially symmetric functions, one switches to polar coordinates. However, this does not look good at the bottom, at least not without considerable surgery. So one possibility is to superimpose two plots.

\documentclass{book}
\usepackage{pgfplots}
\pgfplotsset{compat=1.17}
\begin{document}
\begin{tikzpicture}
    \begin{axis}[
        xlabel=$x$, ylabel=$y$,
    ]
    \addplot3[surf, domain =-2:2, domain y=-2:2, unbounded coords=jump,
    samples=51]
        { x^2 + y^2 >= 1.1 ? -sqrt(x^2+y^2-1) : NaN };
    \addplot3[surf, domain=1.001:1.2, domain y=0:360,samples=5,samples y=51,
    z buffer=sort]
    ({x*cos(y)},{x*sin(y)},{-sqrt(x^2-1)});
  \end{axis}
 \end{tikzpicture}
\end{document}

enter image description here

Far from perfect but the edges are not jagged.

You can also use just a polar plot or a clipped polar plot. Note that the clip path depends on the view angle, so this one won't work if you drastically change the view.

\documentclass{book}
\usepackage{pgfplots}
\pgfplotsset{compat=1.17}
\begin{document}
\begin{tikzpicture}
    \begin{axis}[xmin=-2,xmax=2,ymin=-2,ymax=2,
        xlabel=$x$, ylabel=$y$]

    \clip plot[domain=0:-2] (-2,{\x},{-sqrt(3+\x*\x)}) -- 
    plot[domain=-2:2] ({\x},-2,{-sqrt(3+\x*\x)})
    -- plot[domain=-2:2] (2,{\x},{-sqrt(3+\x*\x)}) -- (2,2,0) -- (-2,2,0) 
    --cycle;
    \addplot3[surf, domain=1.001:{2*sqrt(2)}, domain y=0:360, 
    samples y=50, z buffer=sort] ({x*cos(y)},{x*sin(y)},{-sqrt(x^2-1)});
  \end{axis}
 \end{tikzpicture}
\end{document}

enter image description here

Or one uses a function that interpolates between the two coordinate systems. The function Rplane is a polar coordinate representation of a square and taken from here and here. Its original purpose was also in the 3d context in order to handle a very similar problem.

\documentclass{book}
\usepackage{pgfplots}
\pgfplotsset{compat=1.17}
\begin{document}
\begin{tikzpicture}
    \begin{axis}[declare function={
    Rplane(\t)=1/max(abs(cos(\t)),abs(sin(\t)));
    Rcheat(\r,\t)=\r*0.5*(tanh(7*(\r-1.5))+1)*Rplane(\t)
        +\r*0.5*(1-tanh(7*(\r-1.5)));}, 
        xlabel=$x$, ylabel=$y$,
    ]
    \addplot3[surf, domain =1:2, domain y=0:360, unbounded coords=jump,
    samples=51,z buffer=sort]
        ({Rcheat(x,y)*cos(y)},{Rcheat(x,y)*sin(y)},{-sqrt(pow(Rcheat(x,y),2)-1) });
  \end{axis}
 \end{tikzpicture}
\end{document}

enter image description here

7
  • Thanks a lot, this is very nice. I don't know why it didn't occur me to switch to polar. Probably I'll go for the radial plot alone like so \addplot3[surf, domain=1.001:2, domain y=0:360, samples y=50, z buffer=sort] ({cos(y)},{x*sin(y)},{-sqrt(x^2-1)});
    – ceres-c
    Commented May 6, 2020 at 20:20
  • @ceres-c Sure, that works, and is simpler. I was assuming that you want the Cartesian finish at the boundary of the axes, which is why I added this more complicated proposal.
    – user194703
    Commented May 6, 2020 at 20:22
  • @schrödingers-cat Yes, that helps in understanding how the function continues. On the other hand, having 2 different wireframe patterns is a bit distracting; as if the function was a piecewise or something like that. I'll probably end up with the function you have suggested as the cartesian finish wins over the different meshes
    – ceres-c
    Commented May 6, 2020 at 20:33
  • @ceres-c I certainly agree with all you are saying. (In principle one can use clip to clip the polar plot but this more efforts.)
    – user194703
    Commented May 6, 2020 at 20:40
  • 1
    @ceres-c I added an example with a clip path. It requires a bit of engineering.
    – user194703
    Commented May 6, 2020 at 21:21
5

I have two more truncations

\documentclass{book}
\usepackage{tikz}
\usepackage{pgfplots}
\pgfplotsset{compat=1.7}
\begin{document}

\pgfmathdeclarefunction{volcano_z}{2}{%
    \pgfmathsetmacro\radsq{#1^2 + #2^2}% \radsq is radius^2 in FPU notation
    \pgfmathfloattofixed{\radsq}\let\radsqsafe=\pgfmathresult % in safe notation
    \ifdim\radsqsafe pt > 1pt\relax
        \pgfmathparse{-sqrt(\radsq-1)}%
    \else\ifdim\radsqsafe pt > 0.25pt\relax
        \pgfmathparse{+0}%
    \else % \radsq pt <= 0.25
        \pgfmathparse{NaN}%
    \fi\fi
}

\begin{tikzpicture}
    \begin{axis}[xlabel=$x$, ylabel=$y$,]
    \addplot3[surf,domain =-2:2,unbounded coords=jump,samples=32]
        {volcano_z(x,y)};
    \end{axis}
\end{tikzpicture}

\pgfmathdeclarefunction{volcano_x}{2}{%
    \pgfmathsetmacro\radsq{#1^2 + #2^2}% \radsq is radius^2 in FPU notation
    \pgfmathfloattofixed{\radsq}\let\radsqsafe=\pgfmathresult % in safe notation
    \ifdim\radsqsafe pt > 1pt\relax
        \pgfmathparse{#1}%
    \else\ifdim\radsqsafe pt > 0.25pt\relax
        \pgfmathparse{#1/sqrt(\radsq)}%
    \else % \radsq pt <= 0.25
        \pgfmathparse{NaN}%
    \fi\fi
}

\begin{tikzpicture}
    \begin{axis}[xlabel=$x$, ylabel=$y$,]
    \addplot3[surf,domain =-2:2,unbounded coords=jump,samples=32]
        (   {volcano_x(x,y)},
            {volcano_x(y,x)},
            {volcano_z(x,y)}
        );
    \end{axis}
\end{tikzpicture}
\end{document}

1
  • 2
    I like the second graph a lot as it retains both precision in the upper part of the image and the cartesian wireframe in the lowest part. This approach also has the advantage of being flexible: the view angle can be modified without issues. Thanks for posting your take at it, much appreciated!
    – ceres-c
    Commented May 13, 2020 at 0:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .