pythonmatplotlibanimationmatplotlib-animationmatplotlib-3d

Updating x/y/z limits in matplotlib Animation 3D


I came to this problem when I tried to adjust the limits of the axis in the 3D animation. It seemed that the range of the limits on the screen is not what I want, but the output of the limits on the console was Ok. The plot line could not be displayed well in the axes. I'm very confused, because I don't even know where the problem is.

Here's my code.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Button, TextBox
from matplotlib import animation
x = np.array([0.0])
y = np.array([1.0])
z = np.array([0.0])
v = np.array([1.0, 0.0, 1.0])
tmax = 1
tmin = 0
fig2 = plt.figure()
# fig2.set_visible(False)
ax = fig2.add_axes([0.075, 0.1, 0.6, 0.8],projection='3d')


def set_axes_equal(ax: plt.Axes):

    limits = np.array([ax.get_xlim3d(),ax.get_ylim3d(),ax.get_zlim3d(),])
    origin = np.mean(limits, axis=1)
    radius = 0.5 * np.max(np.abs(limits[:, 1] - limits[:, 0]))
    _set_axes_radius(ax, origin, radius)

def _set_axes_radius(ax, origin, radius):
    x, y, z = origin
    ax.set_xlim3d([x - radius, x + radius])
    ax.set_ylim3d([y - radius, y + radius])
    ax.set_zlim3d([z - radius, z + radius])

ax.set_box_aspect((1, 1, 1))

def set_lim():
    global tmax, tmin

    tmax = max(np.max(x), np.max(y), np.max(z))

    tmin = min(np.min(x), np.min(y), np.min(z))
    print(ax.get_xlim3d())

    if tmax != tmin:
        ax.set_xlim(tmin, tmax)
        ax.set_ylim(tmin, tmax)
        ax.set_zlim(tmin, tmax)
    else: 
        ax.set_xlim(-10, 10)
        ax.set_ylim(-10, 10)
        ax.set_zlim(-10, 10)

k = 1.0
dt = 0.1

p, = ax.plot(x, y, z)

def fv(v:np.ndarray):
    return np.cross(v, np.array([0., 0., 1.]))

def runge_kutta(y:np.ndarray, t:float, f):
    k1 = t * f(y)
    k2 = t * f(y + 0.5 * k1)
    k3 = t * f(y + 0.5 * k2)
    k4 = t * f(y + k3)
    return y + (k1 + 2 * k2 + 2 * k3 + k4) / 6.

def update(i):
    global k, dt, p, x, y, z, v
    v = runge_kutta(v, dt, fv)
    dx = x[-1] + v[0] * dt
    dy = y[-1] + v[1] * dt
    dz = z[-1] + v[2] * dt
    x = np.append(x, [dx])
    y = np.append(y, [dy])
    z = np.append(z, [dz])
    set_lim()
    set_axes_equal(ax)
    if i >= 50:x, y, z = x[1:], y[1:], z[1:]
    p.set_data_3d(x, y, z)
    return (p,)
   

ani = animation.FuncAnimation(fig2, update, interval=100, frames=None, blit=True)

ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')

plt.show()

when the program runs on its own:

look, the plot line is out of the range

when i move the camera:

now the limits are correct

Please forgive my poor English. I have been tried to express my problem as clearly as possible. If there's any question about the above, I will try my best to explain it.

Adjusting the camera position manually can make the limits updated successfully, but It cannot update on its own. I just wish the plot line can be at the center of the Axes3D without manually adjusting the camera position.


Solution

  • When you tell matplotlib.animation.FuncAnimation to blit it stops updating the axes it seems. Replace your line with this one:

    ani = animation.FuncAnimation(fig2, update, interval=100, frames=None)
    

    and it should work as you describe.

    Let me know if you have any questions.