Description
Bug summary
Summary
Problem with 3d animation using FuncAnimation
and the mplot3d
toolkit. Maybe this problem comes from not enough clarifications in the documentation. Following this it is presented a simplified version of the program that led me to these conclusions.
Explanation
Looking through the documentation regarding animation and blitting, it is possible to see two different approaches:
- The first one (see here) is presented at the first explanation page about creating animations with the
animation
module. Here it is explained that in the function passed toFuncAnimation
there has to be some method that updates the data in each artist in order to generate the new frame. It is written that "the update function uses theset_*
function for different artists to modify the data". - However, the second approach (presented here, where blitting is actually explained) tells the user to use the
draw_artist()
method in order to update/redraw efficiently the artists in the function of the render loop. I haven't found many low-level details (except some look directly at the source code) about the functioning ofFuncAnimation
orplt.show()
, so I couldn't make real assumptions about their involvement in this.
Mixing the informations, I have encountered many problems that I will try to replicate and explain in the following code.
Code for reproduction
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Line3D
from matplotlib.animation import FuncAnimation
fig = plt.figure()
ax = fig.add_subplot(projection='3d', xlim=(0,100), ylim=(0,100), zlim=(0,100))
line = Line3D( [0], [0], [0], marker="o", markersize=15, color='r', animated=True)
def update(t):
line.set_data_3d([t],[t],[t])
ax.draw_artist(line)
return line,
anim = FuncAnimation(fig=fig, func=update, frames=None, blit=True, cache_frame_data=False)
plt.show()
Actual outcome
File "C:\Users\user\Desktop...\axes3d_issue.py", line 13, in update
ax.draw_artist(line)
File "C:\Users\user\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\LocalCache\local-packages\Python312\site-packages\matplotlib\axes_base.py", line 3080, in draw_artist
a.draw(self.figure.canvas.get_renderer())
File "C:\Users\user\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\LocalCache\local-packages\Python312\site-packages\matplotlib\artist.py", line 72, in draw_wrapper
return draw(artist, renderer)
^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\user\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\LocalCache\local-packages\Python312\site-packages\mpl_toolkits\mplot3d\art3d.py", line 270, in draw
xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, self.axes.M)
^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'M'
Expected outcome
The rendering of the image.
Additional information
Some considerations:
- Maybe the
draw_artist()
method should be used in combination withplt.pause()
or by API developers, but neither is specified in the documentation. Removing theax.draw_artist(line)
(following thus the first approach described at the beginning) is indeed a solution to the problem. - Using Visual Studio Code Data inspection debug tool along with
debugpy
, I've noticed that in theupdate()
function theLine3D
artist seems to lose all the references to the parent classesAxes3D
andFigure
, hence some error messages. However, trying to add something likeline.axes = ax
leads directly to an exception raised due to the next problem. - It seems that without calling the 'Axes3D.draw()' method first, the projection matrix variable
M
of the same class is never updated fromNone
to some value. Actually, calling theAxes3D.get_proj()
method forM
and computing the inverse matrix also for theinvM
attribute seems to work. This appears to be a flaw in every case, but maybe it corroborates the hypothesis of some kind of "reservedness" of the method for some special cases.
If this is the case, it is not specified neither in the already cited documentation, neither in the (quite short, for some reason)mplot3d
documentation (see here).
I think the documentation about this topic should be a bit clearer. Some more explanation about the inner working of some ubiquitous functions like plt.show()
or the clear
methods, another set of functions that is presentend unclearly, could help users understand and resolve their problems, along with using them more consciously.
Operating system
Microsoft Windows 11 Home
Matplotlib Version
3.8.2
Matplotlib Backend
TkAgg
Python version
3.12.2
Jupyter version
No response
Installation
pip