1

I want to create some plots of the farfield of electromagnetic scattering processes.

To do this, I calculated values θ, φ and r. The coordinates θ and φ create a regular grid on the unitsphere so I can use plot_Surface (found here) with conversion to cartesian coordinates.

My problem is now, that I need a way to color the surface with respect to the radius r and not height z, which seems to be the default.

Is there a way, to change this dependency?

Stefan
  • 2,460
  • 1
  • 17
  • 33
  • Maybe this helps: http://stackoverflow.com/questions/6539944/color-matplotlib-plot-surface-command-with-surface-gradient/6543777#6543777 – Paul Mar 25 '13 at 14:02
  • I didn't notice this possibility. I try to implement this. Thanks! – Stefan Mar 25 '13 at 14:19
  • also look into `mayavi` from Enthought. It is a different framework, but it is built around openGL so does much better at 3D renderings. – tacaswell Mar 25 '13 at 15:14

1 Answers1

2

I don't know how you're getting on, so maybe you've solved it. But, based on the link from Paul's comment, you could do something like this. We pass the color values we want using the facecolor argument of plot_surface.

(I've modified the surface3d demo from the matplotlib docs)

EDIT: As Stefan noted in his comment, my answer can be simplified to:

from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np

fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(-5, 5, 0.25)
xlen = len(X)
Y = np.arange(-5, 5, 0.25)
ylen = len(Y)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
maxR = np.amax(R)
Z = np.sin(R)

# Note that the R values must still be normalized.
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, facecolors=cm.jet(R/maxR),
        linewidth=0)

plt.show()

And (the end of) my needlessly complicated original version, using the same code as above though omitting the matplotlib.cm import,

# We will store (R, G, B, alpha)
colorshape = R.shape + (4,)
colors = np.empty( colorshape )
for y in range(ylen):
    for x in range(xlen):
        # Normalize the radial value.
        # 'jet' could be any of the built-in colormaps (or your own).
        colors[x, y] = plt.cm.jet(R[x, y] / maxR )

surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, facecolors=colors,
        linewidth=0)

plt.show()
Sam
  • 557
  • 6
  • 20
  • 1
    Thanks! That's basically what I've done. But I used the radius directly via `facecolors = cm.jet(rdata)` in `plot_surface`. – Stefan Mar 27 '13 at 12:43
  • Ah yes, of course! Much cleaner. I will make a note of this in my answer for anyone else who stumbles across it. – Sam Mar 27 '13 at 14:56
  • @Sam I found your solution very helpful, thank you -- would you know how to extend the colormap to an (X,Y,Z) dependence? I tried to extend your solution, but ran into problems. – AaronJPung Jun 07 '18 at 16:39
  • You should be able to extent the above with e.g. `Z = np.arange(-5, 5, 0.25)`, `zlen = len(Z)`, `X, Y, Z = np.meshgrid(X, Y, Z)` and `R = np.sqrt(X**2 + Y**2 + Z**2)`. Then add a third nested for loop `for z in range(zlen): colors[x, y, z] = plt.cm.jet(R[x, y, z] / maxR)`. But if that's not working, or not what you want, probably best to ask a new question. – Sam Jun 08 '18 at 20:28