Controlling animation speed using graph_objects in python

Yes, you are right, the distance is constant. Only when the surfacecolor is a general function of (x,y,z), it changes during the rotation.

I experimented a similar approach as your, and it works. Compare the code below with your code and eventually you could detect what is wrong.

import numpy as np
from numpy import sqrt
import plotly.graph_objects as go


def rot3d(alpha,beta,gamma):  #planar rotation of alpha radians
    return np.array([[np.cos(alpha)*np.cos(beta), np.cos(alpha)*np.sin(beta)*np.sin(gamma)-np.sin(alpha)*np.cos(gamma), np.cos(alpha)*np.sin(beta)*np.cos(gamma)+np.sin(alpha)*np.sin(gamma)], 
                      [np.sin(alpha)*np.cos(beta), np.sin(alpha)*np.sin(beta)*np.sin(gamma)+np.cos(alpha)*np.cos(gamma), np.sin(alpha)*np.sin(beta)*np.cos(gamma)-np.cos(alpha)*np.sin(gamma)],
                      [-np.sin(beta),              np.cos(beta)*np.sin(gamma),                                           np.cos(beta)*np.cos(gamma)]])


u = np.linspace(0, 2*np.pi, 50)
v = np.linspace(-1, 1, 8)
u,v = np.meshgrid(u,v)
tp = 1 + 0.5*v*np.cos(u/2.)
x = tp*np.cos(u)
y = tp*np.sin(u)
z = 0.5*v*np.sin(u/2.)
dist = sqrt(x**2+y**2+z**2)
fig= go.Figure(go.Surface(x=x, y=y, z=z, 
                          surfacecolor=dist, 
                          colorscale='balance', 
                          colorbar_thickness=25))

m, n = x.shape

X = x.flatten()
Y= y.flatten()
Z= z.flatten()

xyz = np.vstack((X,Y,Z))


T = np.linspace(0,2*np.pi, 36)
frames =[]

for t in T:
    xf, yf, zf = rot3d(t,  np.pi/4, 0) @ xyz
    xs, ys, zs  = xf.reshape((m,n)), yf.reshape((m,n)), zf.reshape((m,n))  
    #ds = sqrt(xs**2+ys**2+zs**2)
    frames.append(go.Frame(data=[go.Surface(x=xs, y=ys, z=zs, surfacecolor=dist )]))
fig.update(frames=frames)    
fig.update_scenes(xaxis_visible=False, yaxis_visible=False, zaxis_visible=False)
                  #camera_eye=dict(x=1.5, y=1.5, z=1))
fig.update_layout(
         title='Animation Test',
         width=600,
         height=600,
         
         updatemenus=[dict(type='buttons',
                  showactive=False,
                  y=1,
                  x=0.8,
                  xanchor='left',
                  yanchor='bottom',
                  pad=dict(t=45, r=10),
                  buttons=[dict(label='Play',
                                 method='animate',
                                 args=[None, dict(frame=dict(duration=5, redraw=True), 
                                                             transition=dict(duration=0),
                                                             fromcurrent=True,
                                                             mode='immediate'
                                                            )]
                                            )
                                      ]
                              )
                        ]
)

Maybe either your data definition or its handling led to the weird surface texture.

1 Like

I apologise to revive (again) this thread. I experience difficulties in setting the range and/or the aspect ratio of the two scenes separately. The range of the second trace is 1000 times smaller compared to the first one.

I tried something like:

fig.update_scenes(aspectratio=dict(x=1,y=1,z=1),
                  xaxis=dict(autorange=False, range=[-15,15], showgrid=False, zeroline=False, visible=False),
                  yaxis=dict(autorange=False, range=[-15,15], showgrid=False, zeroline=False, visible=False),
                  zaxis=dict(autorange=False, range=[-15,15], showgrid=False, zeroline=False, visible=False))

that works but obviously affects both traces. I tried to add trace[0,1], but I get the output

ValueError</b> : Invalid property specified for object of type plotly.graph_objs.layout.Scene: 'traces'

An issue opened on github there the use of scene2_xaxis_range that I couldn’t find anywhere else.

very naively, I tried something like from the reference of Figure
fig.select_traces(col=1,).update_scenes(.....)
with negative results
'generator' object has no attribute 'update_scenes'

What would be the right way of proceeding?

@giammi56

As a rule, after defining fig =make_subplot(...) print:
print(fig.layout)
to see the name of scenes: scene1 and scene2 or scene and scene2. To update axes range don’t use fig.update_scenes, because it updates both scene and scene2 with the same settings.
Instead, update layout:

fig.update_layout(scene=dict(xaxis_range=...., xaxis_visible=False, xaxis_autorange=False,
                                           ysxis_....
                                            zaxis_....),
                  scene2=dict(xaxis2= dict(range=[a, b], visible=False, autorange=False),
                                             yaxis2=dict(),
                                             zaxis2=dict())

xaxis2_range is not recognized. That’s why you must use the dict instead.
)

1 Like

Is that possible to use an array or list for the duration and transition for button in the figure? I am trying to create some animation with dot moving smoothly on a map. As discussed in this question, it seems that scatter_geo does not support smooth animation like that in scatter, so I have to interpolate frames between them. However, I only want to insert frames for several years (otherwise the whole figure will be too large), so to make the final animation looks like constant speed, it will be better if I can control the speed for each frame separately. I tried to redefine the duration and transition for each frame by myself, but it seems that those info is not used and only the duration and transition for the button are used. I also tried to use a list with the same length of the frames for the button duration and transition, but it also does not seem to work (although it does not report error). Thanks.