enthought / mayavi

3D visualization of scientific data in Python
http://docs.enthought.com/mayavi/mayavi/
Other
1.3k stars 284 forks source link

Animation tools not working with plot3D #1145

Closed Thatoneguy333 closed 1 year ago

Thatoneguy333 commented 2 years ago

I have some historic state data (nothing more than a three-column float64 type numpy array), and I am attempting to plot it's trajectory over time. I am unable, however, to achieve any animation at all when using the plot3D function; running the exact same code, but with a points3D plot instead, works fine. `x, y, z = state_hist[:, 0], state_hist[:, 1], state_hist[:, 2]

fig = mlab.gcf()

marker = mlab.points3d(x[0:10], y[0:10], z[0:10], scale_factor=200)

marker = mlab.plot3d(x[0:10], y[0:10], z[0:10], colormap='hot', tube_radius=None)

@mlab.animate(delay=200) def anim(): for i in range(100): print('Updating scene...') marker.mlab_source.set(x=x[i:10+i], y=y[i:10+i], z=z[i:10+i]) yield

anim() mlab.show()` ^This code does not work (I get a continuous "updating scenes" text output, but the image does not change at all)

`x, y, z = state_hist[:, 0], state_hist[:, 1], state_hist[:, 2]

fig = mlab.gcf() marker = mlab.points3d(x[0:10], y[0:10], z[0:10], scale_factor=200)

marker = mlab.plot3d(x[0:10], y[0:10], z[0:10], colormap='hot', tube_radius=None)

@mlab.animate(delay=200) def anim(): for i in range(100): print('Updating scene...') marker.mlab_source.set(x=x[i:10+i], y=y[i:10+i], z=z[i:10+i]) yield

anim() mlab.show() ` ^This code does work as intended (yielding both the text output and an animation that seems to make sense); in fact, I am able to make slightly modified versions of this work for all other functions other than plot3D. Is plot3D intended to be used differently than other objects, or is this unintended functionality?

JAGulin commented 1 year ago

@Thatoneguy333 This is an old issue now, but I found a workaround if you are still around.

Is plot3D intended to be used differently than other objects, or is this unintended functionality?

I don't have much insight on Mayavi, but I think this is a bug. The documentation have working examples with animation of plot3d, so it's neither unintended nor very different. My investigation showed that the problem appear with tube_radius=None. I found no example/test of tube_radius=None with animation. It's hard to motivate that the radius should affect animation, so I consider it a bug even with the workaround found.

In Plot3d (mayavi/tools/helper_functions.py) the TubeFactory is dropped in the tube_radius=None case. self.pipeline.remove(TubeFactory) Looks like a problem to me, but not sure if there should be a LineFactory instead, to keep drawing the line. Workaround: The line is drawn initially, but not after "set", so a workaround is to instead call "reset" just before yield.

Since the code above was not self-sustained, I've included a test program below, mixing example code and what I thought was the relevant part of the above code. As in the code above I have some commented out code to show different behavior. You'll figure out what to do.

# Example adapted from 
# http://docs.enthought.com/mayavi/mayavi/mlab.html?highlight=mlab_source#animating-the-data

import numpy as np
from mayavi import mlab

# Produce some nice data.
n_mer, n_long = 6, 11
pi = np.pi
dphi = pi/1000.0
phi = np.arange(0.0, 2*pi + 0.5*dphi, dphi, 'd')
mu = phi*n_mer
x = np.cos(mu)*(1+np.cos(n_long*mu/n_mer)*0.5)
y = np.sin(mu)*(1+np.cos(n_long*mu/n_mer)*0.5)
z = np.sin(n_long*mu/n_mer)*0.5

# View it.
i = 0
vx,vy,vz=x[i:10+i], y[i:10+i], z[i:10+i]
extent=[min(x), max(x), min(y), max(y), min(z), max(z)]
print(extent)
# This works! Sadly reset_zoom is not helping when animation goes off screen...
#l = mlab.plot3d(vx, vy, vz, tube_radius=0.025, colormap='Spectral', reset_zoom=True)
# Apparently the problem is tube_radius=None. Animations aren't updated then.
l = mlab.plot3d(vx, vy, vz, tube_radius=None, colormap='Spectral')
# I thought tube_sides = 1 could be a workaround, but didn't allow < 3. And then None didn't work anyway.
#l = mlab.plot3d(vx, vy, vz, tube_radius=0.01, tube_sides=3, extent=[min(x), max(x), min(y), max(y), min(z), max(z)] )
# Keeping points for comparison. Apparently zoom is fine here.
# l = mlab.points3d(vx, vy, vz, colormap='Spectral')

# Now animate the data.
ms = l.mlab_source
print(l) # This is a mayavi.modules.surface.Surface for plot and mayavi.modules.glyph.Glyph for points
print(ms) # This is a mayavi.tools.sources.MLineSource for plot and mayavi.tools.sources.MGlyphSource for points

@mlab.animate(delay=200)
def anim():
  for i in range(100):
    vx,vy,vz=x[i:10+i], y[i:10+i], z[i:10+i]
    print('Updating scene...',i)
    # Example uses trait_set instead of set. Neither seems documented, but they are equivalent.
    # Changing non-data "traits" like tube_radius is silently ignored
    ms.trait_set(x=vx, y=vy, z=vz)
    #ms.reset(x=vx, y=vy, z=vz) # Calling "reset" instead of "set" draws the line even with tube_radius=None
    yield

anim()
mlab.show()
prabhuramachandran commented 1 year ago

When you change the shape of the data, you must always use reset. This is not a bug but how this is supposed to work and is actually documented here: https://mayavi.readthedocs.io/en/latest/mlab_animating.html Use ms.reset(...) and add a l.scene.reset_zoom() to update the scene. If you are looking for more information please see https://github.com/prabhuramachandran/mayavi-tutorial which has a link to a 4 hour tutorial I conducted. It may help.