N. Jonas Figge
N. Jonas Figge

Reputation: 142

Matplotlib plot_surface: How to convert 1D arrays to required 2D input?

Maybe this question is a duplicate because I can imagine that many people face this problem. Forgive me if so.

I want to plot a sphere in Matplotlib 3D. For that, I have a bunch of xyz coordinates. When I plot it with plot_trisurf, I get this:

enter image description here

So I wanted to try plot_surface, but then I get the error ValueError: Argument Z must be 2-dimensional.

This post explains why the input for plot_surface is 2D.

My question ist: How can I convert my regular xyz coordinates into the format plot_surface needs?

Edit: Okay, I understood that 3-tuples can be differently interpreted. Is there a way then to use plot_trisurf with some kind of polar coordinates, so that it doesn't interpolate "through the xy plane" but from the coordinate origin, spherically?

Upvotes: 0

Views: 572

Answers (1)

JohanC
JohanC

Reputation: 80279

If your points are created in a mesh-like way, it is best to create mesh at the same time, such as in this post.

It seems plot_trisurf creates a mesh for an open surface (like a rectangular table cloth) but not for a closed surface.

If the points aren't nicely organized, but you know all points lie on a convex 3D surface (e.g. a sphere), you can calculate the 3D convex hull and draw that.

The code below does just that. Note that some triangles look darker and some lighter. This is because the triangles returned by ConvexHull aren't nicely oriented (so that e.g. a clockwise orientation would indicate the outside face of the polygon). For that you'd need to calculate the surface normal for each triangle and reverse the triangle in case the dot product of that normal with the center of the triangle would be negative (supposing 0,0,0 lies inside the sphere).

If you need more 3D plotting power, the Mayawi library would be more appropriate.

from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from scipy.spatial import ConvexHull
import numpy as np

xyz = np.random.randn(3, 50) # random 3D points
xyz /= np.linalg.norm(xyz, axis=0) # project each point on a unit sphere

fig = plt.figure()
ax = fig.gca(projection='3d')

hull = ConvexHull(xyz.T)
ax.plot_trisurf(*xyz, triangles=hull.simplices, linewidth=0.2, antialiased=True)
plt.show()

example 3D sphere from random points

Upvotes: 2

Related Questions