Search code examples
pythonnumpymatplotlib3dmplot3d

Wireframe joins the wrong way in numpy matplotlib mplot3d


I'm trying to create a 3D wireframe in Python using matplotlib.

When I get to the actual graph plotting, however, the wireframe joins the wrong way, as shown in the images below.

How can I force matplotlib to join the wireframe along a certain axis?

My code is below:

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d

def rossler(x_n, y_n, z_n, h, a, b, c):
#defining the rossler function
x_n1=x_n+h*(-y_n-z_n)
y_n1=y_n+h*(x_n+a*y_n)
z_n1=z_n+h*(b+z_n*(x_n-c))   
return x_n1,y_n1,z_n1

#defining a, b, and c
a = 1.0/5.0
b = 1.0/5.0
c = 5

#defining time limits and steps
t_0 = 0
t_f = 32*np.pi
h = 0.01
steps = int((t_f-t_0)/h)

#3dify
c_list = np.linspace(5,10,6)
c_size = len(c_list)
c_array = np.zeros((c_size,steps))

for i in range (0, c_size):
    for j in range (0, steps):
        c_array[i][j] = c_list[i]

#create plotting values
t = np.zeros((c_size,steps))
for i in range (0, c_size):
    t[i] = np.linspace(t_0,t_f,steps)
x = np.zeros((c_size,steps))
y = np.zeros((c_size,steps))
z = np.zeros((c_size,steps))
binvar, array_size = x.shape

#initial conditions
x[0] = 0
y[0] = 0
z[0] = 0

for j in range(0, c_size-1):
    for i in range(array_size-1):
        c = c_list[j]
        #re-evaluate the values of the x-arrays depending on the initial conditions
        [x[j][i+1],y[j][i+1],z[j][i+1]]=rossler(x[j][i],y[j][i],z[j][i],t[j][i+1]-t[j][i],a,b,c)

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_wireframe(t,x,c_array, rstride=10, cstride=10)
plt.show()

I am getting this as an output:

enter image description here

The same output from another angle:

enter image description here

Whereas I'd like the wireframe to join along the wave-peaks. Sorry, I can't give you an image I'd like to see, that's my problem, but I guess it'd be more like the tutorial image.


Solution

  • If I understood, you want to link the 6 traces with polygons. You can do that by triangulating the traces 2 by 2, then plotting the surface with no edges or antialising. Maybe choosing a good colormap will also help.

    Just keep in mind that this will be a very heavy plot. The exported SVG weight 10mb :)

    import matplotlib.tri as mtri
    
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    
    for LineIndex in range(c_size-1):
        # If plotting all at once, you get a MemoryError. I'll plot each 6 points
        for Sample in range(0, array_size-1, 3):
            # I switched x and c_array, because the surface  and the triangles 
            # will look better by default
            X = np.concatenate([t[LineIndex,Sample:Sample+3], t[LineIndex+1,Sample:Sample+3]])
            Y = np.concatenate([c_array[LineIndex,Sample:Sample+3], c_array[LineIndex+1,Sample:Sample+3]])
            Z = np.concatenate([x[LineIndex,Sample:Sample+3], x[LineIndex+1,Sample:Sample+3]])
            T = mtri.Triangulation(X, Y)
    
            ax.plot_trisurf(X, Y, Z, triangles=T.triangles, edgecolor='none', antialiased=False)
    
    ax.set_xlabel('t')
    ax.set_zlabel('x')
    plt.savefig('Test.png', format='png', dpi=600)
    plt.show()
    

    Here is the resulting image: enter image description here