9

I'm trying to draw the function $f(x,y) = -\log(x)-\log(y)$ as a surface with pgfplots. In order to get smooth level curves, I use a patch plot with patch type=biquadratic.

\documentclass{standalone}

\usepackage{tikz}
\usepackage{pgfplots}
\pgfplotsset{compat=1.7} 
\usepgfplotslibrary{patchplots}

\begin{document}
\begin{tikzpicture}
\begin{axis}[xmin=0, xmax=1, ymin=0, ymax=1.4, zmin=0, zmax=6, 
             axis y line=center, axis x line=center, axis z line=center,
             view/h=70, xtick=\empty, ytick=\empty, ztick=\empty, 
             clip=false, axis on top=false]

\node at (rel axis cs:1,0,0) [above, anchor=north west] {$x_1$};
\node at (rel axis cs:0,1,0) [above, anchor=west] {$x_2$};
\node at (rel axis cs:0,0,1) [above, anchor=south] {$f(x_1,x_2)$};

\addplot3 [patch,patch type=biquadratic,shader=flat,draw=black, draw opacity=0.15,z buffer=sort]
     coordinates { 
         (0.02722593,0.15708631,5.45454545) (0.15708631,0.02722593,5.45454545) (0.14800676,0.01674756,6.00000000) (0.01674756,0.14800676,6.00000000) (0.06539740,0.06539740,5.45454545) (0.15204995,0.02141365,5.72727273) (0.04978707,0.04978707,6.00000000) (0.02141365,0.15204995,5.72727273) (0.05706089,0.05706089,5.72727273)
     };
\end{axis}
\end{tikzpicture}

\end{document}

I've left out zillions of generated data points for clarity and the most obvious one of the "offending" patches. The data consist of 9-tuples as described in section 5.6.1 of the pgfplots manual.

The code I wrote produces the following picture (amazingly beautiful -- Mr. Feuersänger: you rock!):

enter image description here

This is what I want, except: the back of some of the patches is missing in the upper left (red) part. What to do? Is this a bug in pgfplots and/or is there an easy way to fix this?

I don't care much about the particular type of shading, but I want to have clear lattice lines.


For anyone who is interested, here is my python code to generate the data:

from numpy import linspace, pi, sin, cos, log
from scipy.optimize import bisect

# Code to generate patches
# (x(r,theta), y(r,theta), z(r,theta)), where
#    x(r,theta) = 1 - r cos(theta), 
#    y(r,theta) = 1 - r sin(theta), 
#    z(r,theta) = -log(x(r,theta)) - log(y(r,theta)).

PATCH = [(0,0), (2,0), (2,2), (0,2), (1,0), (2,1), (1,2), (0,1), (1,1)]
N     = 21
zmax  = 6
zmin  = -log(1)-log(1)


# Determine the value such that z = -log(x(r,theta)) - log(y(r,theta)).
def zinv(theta, z):
  f = lambda r: -log(1 - r*cos(theta)) - log(1 - r*sin(theta)) - z
  maxr = min(1/cos(theta), 1/sin(theta)) - 1e-6
  return bisect(f, 0, maxr)


P = dict()

# Generate lattice points
for i, theta in enumerate(linspace(1e-6, pi/2-1e-6, N)):
  for j, z in enumerate(linspace(zmin, zmax, N)):
     r = zinv(theta, z)
     x = 1 - r * cos(theta)
     y = 1 - r * sin(theta)
     z = - log(x) - log(y)
     P[i,j] = (x,y,z)

# Output patches
for j in range(0, N-1, 2):
  for i in range(0, N-1, 2):
    for (di, dj) in PATCH:
       print "(%0.8f,%0.8f,%0.8f)" % P[i+di,j+dj],
    print
  • 1
    Can you edit your question to include at least a couple of data points that reproduce the problem? – Jake Jun 24 '13 at 21:19
  • 2
    This happens because you're using shader=flat: If you use shader=interp, the backs of the elements become visible. – Jake Jun 24 '13 at 21:45
  • @Jake: I prefer the flat shader. Is there no other way to do this? (I will add some data points tomorrow morning) – yori Jun 24 '13 at 22:11
  • @Jake: I added the offending patch. I tried with shader=faceted interp and I this indeed solves the problem. However, (1) it makes my pdf file 4 times as large, and it takes a long time to render in a pdf viewer and (2) I'm worried about printing this correctly. – yori Jun 25 '13 at 7:22
6

Jake is right in his comment: the problems are caused by the fact that shape-based filling is only implemented for interpolated shadings (and hard to achieve otherwise).

And you are right in your observation that faceted interp is quite inefficient with respect to both pdf size and rendering time. Unfortunately, I am unaware of systematic solutions how to improve faceted interp.

I fear you have to choose between higher sampling density combined with faceted (meaning that your lattice is not as nice as in your screenshot) or the higher demands on computational power/pdf size of faceted interp.

Concerning interpolated shadings as such: I have made quite good experience with printed shadings (at least for shader=interp).

PS Thanks for the praise.


Note that recent releases of pgfplots come with the new feature patch type sampling which allows to let pgfplots sample your patch.

Provided you knew the ranges of r and theta without solving nonlinear equations, you could have used something like

\addplot3 [patch,
    patch type sampling,
    variable=\r,
    variable y=\t,
%   domain=?
    domain y=1e-6:pi/2-1e-6,
    patch type=biquadratic,shader=flat,draw=black, draw opacity=0.15,z buffer=sort]
    ({1- r*cos(deg(t))},
     {1- r*sin(deg(t))},
     {-ln(x)-ln(y)});

or something like that. In this context, x is the *resultingx coordinate, same fory`.

But perhaps you do not know r.

  • Thank you! r depends on theta. It is the distance from (1,1) (bottom right corner) to the top of the surface looking in the direction theta (the distance is measured in the x_1x_2 plane). It might be possible to rescale r though, because this distance is min(1/sin(theta), 1/cos(theta)) or something like that. (and BTW I didn't praise you enough) – yori Jun 25 '13 at 20:49
  • But more importantly: I noticed in evince that shader=interp basically divides each patch into very small subpatches (correct me if I'm wrong), and I assume that each subpatch has a uniform color. Is it perhaps possible to decrease the number of subpatches that are generated? It seems to me that maybe 4x4 or 8x8 or so should suffice for my drawing, especially because I actually shading from light blue to darker blue, so the shading is not as dramatic as in the figure in my question. – yori Jun 25 '13 at 20:52
  • I am aware of some limitations in free viewers regarding advanced shadings - and evince has the worst issues regarding these shadings. Dividing a single patch into very small subpatches is an inefficient yet simple way to render these patches. You may want to post feature requests for evince to improve the renderer. – Christian Feuersänger Jun 26 '13 at 16:51
  • You're right, I just printed the graph yesterday and it looks very good. I will indeed try to file a bug/feature request at the evince team. Thank you Christian and Jake for your help. – yori Jun 27 '13 at 6:51
  • @Yori : if you post requests for evince, you may want to provide tex.stackexchange.com/questions/99133/… as example document. – Christian Feuersänger Jun 30 '13 at 11:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.