Can someone help me figure out how to control the animation speed of this? I have seen ātransition/durationā property, but not really sure how to use this in the above setting.
y=np.arange(100)
fig=go.Figure(go.Scatter(y=y[:1], mode='lines', name='Testing Points'))
fig.update_layout(title='Animation Test',
title_x=0.5,
width=600, height=600,
xaxis_title='Time',
yaxis_title='Test Points',
yaxis_range=(0,99),
xaxis_range=(0,99), #you generate y-values for i =0, ...99,
#that are assigned, by default, to x-values 0, 1, ..., 99
updatemenus=[dict(buttons = [dict(
args = [None, {"frame": {"duration": 50,
"redraw": False},
"fromcurrent": True,
"transition": {"duration": 0}}],
label = "Play",
method = "animate")],
type='buttons',
showactive=False,
y=1,
x=1.12,
xanchor='right',
yanchor='top')])
frames= [go.Frame(data=[go.Scatter(y=y[:i])]) for i in range(1, 100)]
fig.update(frames=frames)
fig.show()
The transition duration defines the amount of time spent interpolating a trace from one state to another (currently limited to scatter traces), while the frame duration defines the total time spent in that state, including time spent transitioning.
My goal is a smoother and faster transition between different frames. You can see the final output in html format here.
Using the code you provided in this thread, I have noticed that the parameters ādurationā for both frame and transition are not affecting the output
The animation of a Mesh3d rotation is sufficiently smooth when are rotated its vertices,
not the camera eye: https://chart-studio.plotly.com/~empet/15684
For surfaces the smoothnes depends on the shape of x,y,z and the number of frames:
import numpy as np
from numpy import sin, cos, pi
import plotly.graph_objects as go
def rot(alpha): #planar rotation of alpha radians
return np.array([[cos(alpha), -sin(alpha)],
[sin(alpha), cos(alpha)]])
### HERE you define the x, y, z arrays for go.Surface
fig = go.Figure(go.Surface(x=x,
y=y,
z=z,
colorscale='balance',
colorbar=dict(thickness=20, len=0.6)))
fig.update_layout(width=800,
height=800,
scene=dict(camera=dict(eye=dict(x=1.65, y=1.65, z=0.8)),
aspectratio=dict(x=1,
y=1,
z=0.35))
);
frames = []
T = np.arange(0, 2, 0.125)
xy = np.stack((x, y))
for t in T:
xr, yr = np.einsum('ik, kjm -> ijm', rot(t), xy)# batch rotation of all points (x,y) on the surface
frames.append(go.Frame(data=[go.Surface(x=xr, y=yr)])) #z is the same in a 3d rotation about zaxis
fig.update(frames=frames);
fig.update_layout(updatemenus=[dict(type='buttons',
showactive=False,
y=0.7,
x=-0.15,
xanchor='left',
yanchor='bottom',
buttons=[dict(label='Play',
method='animate',
args=[None, dict(frame=dict(duration=5, redraw=True),
transition=dict(duration=4),
fromcurrent=True,
mode='immediate'
)]
)
]
)
])
Thank you for your kind reply. I am wondering if the code is gonna work since xy = np.stack((x, y)) is dim = 2 but np.einsum('ik, kjm -> ijm', requires dim=3 for the operand number 1 (i.e. rot(t) ). In the first example here the rotation is a 3x3 matrix composed of three rotations around the three cartesian axes, but here is a plane rotation around z-axis.
The code you posted is working with some modifications:
import numpy as np
from numpy import sin, cos, pi
import plotly.graph_objects as go
def rot(alpha): #planar rotation of alpha radians
return np.array([[cos(alpha), -sin(alpha)],
[sin(alpha), cos(alpha)]])
### HERE you define the x, y, z arrays for go.Surface
fig = go.Figure(go.Surface(x=x,
y=y,
z=z,
colorscale='balance',
colorbar=dict(thickness=20, len=0.6)))
fig.update_layout(width=800,
height=800,
scene=dict(camera=dict(eye=dict(x=1.65, y=1.65, z=0.8)),
aspectratio=dict(x=1,
y=1,
z=0.35))
);
frames = []
T = np.arange(0, 2*np.pi, 0.125) # full rotation around z axis
xy = np.stack((x, y))
for t in T:
xr, yr = np.einsum('ik, kj -> ij', rot(t), xy)# batch rotation of all points (x,y) on the surface
frames.append(go.Frame(data=[go.Surface(x=xr, y=yr, z=z)])) #z is the same in a 3d rotation about zaxis
fig.update(frames=frames);
fig.update_layout(updatemenus=[dict(type='buttons',
showactive=False,
y=0.7,
x=-0.15,
xanchor='left',
yanchor='bottom',
buttons=[dict(label='Play',
method='animate',
args=[None, dict(frame=dict(duration=5, redraw=True),
transition=dict(duration=4),
fromcurrent=True,
mode='immediate'
)]
)
]
)
])
What about implementing a full 3d rotation? Iāll need to compose the R matrix of the three Ralpha x Rbeta x R gamma matrixes, but how to do that in a neat way?
your x=X[0:200,0:100] has the shape (200, 100)
y, as well , (200, 100)
xy = np.stack((x,y)) has the shape (2, 200, 100)
The rotation matrix has the shape (2,2). Hence the definition is correct!!! np.einsum performs āa multiplicationā of the rotation matrix with each column vector of elements x[k, m], y[k, m], but a batch multiplication.
If you are familiar with tensors, np.einsum, above, defines the tensor product of the involved tensors.
LE: If you define a 3d rotation about z, rot3d(alpha), the code is similar, but unnecessary:
frames = []
T = np.arange(0, 2*np.pi, 0.125) # full rotation around z axis
xyz = np.stack((x, y, z)) # shape (3, 200, 100)
for t in T:
xr, yr , zr = np.einsum('ik, kjm -> ijm', rot3d(t), xyz)# batch rotation of all points (x,y,z) on the surface
frames.append(go.Frame(data=[go.Surface(x=xr, y=yr, z=zr)]))
I was reshaping the X and Y to (20000,) thinking that your code was working for dim=1 vectors. Now I get it!
What about implementing an R as a composition of the Ra x Rb x Rc ? How would you design the rot() function to take all of them in account? Would you proceed as in your example keeping them separated?
I didnāt omit z=z . Plotly frames record ONLY the data or attributes that change from frame to frame. Adding z=z overloads the frames and makes a less smooth animation
Without the complete code it is difficult to express an opinion. What shape has xyz? Why did you flatten the x, y, z arrays to get ones of shape (20000,)!!!
The full project is a two layers comparison of theoretical x,y,z (20000,) and experimental x,y,z (836,) data. They should rotate the same amount synchronized. It is easier to work with the data processed in the same way and then reshape them just for the surface data (i.e. the theoretical data)
the shape is (3,20000)
The full code is the following. It is not working because I donāt know how to update layouts from different supblots (there is not so much literature).
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)]])
framesexp=[]
framesth=[]
fig9 = go.Figure()
fig9 = make_subplots(rows=1, cols=2,
subplot_titles=('Experiment', 'Theory'),
specs=[[{"type": "mesh3d"}, {"type": "surface"}]],)
fig9.add_trace(go.Mesh3d(x=xgo, y=ygo, z=zgo,i=tigo, j=tjgo, k=tkgo, intensity=dgo,
colorscale="Viridis",
colorbar_len=0.75,
flatshading=True,
lighting=dict(ambient=0.5,diffuse=1,fresnel=4,specular=0.5,roughness=0.05,facenormalsepsilon=0,vertexnormalsepsilon=0),lightposition=dict(x=100,y=100,z=1000)),1,1)
fig9.add_trace(go.Surface(x=X, y=Y, z=Z, surfacecolor=d_matrix, connectgaps=True),1,2)
T = np.arange(0, 2*np.pi, 0.125)
xyzm = np.stack((xgo, ygo, zgo))
xyzs = np.stack((X.reshape(-1), Y.reshape(-1), Z.reshape(-1)))
for alpha in T:
xrm, yrm, zrm = np.einsum('ik, kj -> ij', rot3d(alpha,0.25*np.pi,0.), xyzm)# batch rotation of all points (x,y,z) on the Mesh
xrs, yrs, zrs = np.einsum('ik, kj -> ij', rot3d(alpha,0.25*np.pi,0.), xyzs)# batch rotation of all points (x,y,z) on the Mesh
framesexp.append(go.Frame(data=[go.Mesh3d(x=xrm, y=yrm, z=zrm)])) #z is the same in a 3d rotation about zaxis
framesth.append(go.Frame(data=[go.Surface(x=xrs.reshape(100,200), y=yrs.reshape(100,200), z=zrs.reshape(100,200))])) #z is the same in a 3d rotation about zaxis
fig9.update((frames=framesexp),1,1);
fig9.update_layout(updatemenus=[dict(type='buttons',
showactive=False,
y=0.7,
x=-0.15,
xanchor='left',
yanchor='bottom',
buttons=[dict(label='Play',
method='animate',
args=[None, dict(frame=dict(duration=5, redraw=True),
transition=dict(duration=4),
fromcurrent=True,
mode='immediate'
)]
)
]
)
])
# fig9.show()
plotly.offline.plot(fig9, filename=r'\OUTPUTS\test_double.html')
in particulat this is poorly written and just a wild (wrong) guess: fig9.update((frames=framesexp),1,1);
Valid properties:
annotations
A tuple of
:class:`plotly.graph_objects.layout.scene.Annotation`
instances or dicts with compatible properties
annotationdefaults
When used in a template (as
layout.template.layout.scene.annotationdefaults), sets
the default property values to use for elements of
layout.scene.annotations
aspectmode
If "cube", this scene's axes are drawn as a cube,
regardless of the axes' ranges. If "data", this scene's
axes are drawn in proportion with the axes' ranges. If
"manual", this scene's axes are drawn in proportion
with the input of "aspectratio" (the default behavior
if "aspectratio" is provided). If "auto", this scene's
axes are drawn using the results of "data" except when
one axis is more than four times the size of the two
others, where in that case the results of "cube" are
used.
aspectratio
Sets this scene's axis aspectratio.
bgcolor
camera
:class:`plotly.graph_objects.layout.scene.Camera`
instance or dict with compatible properties
domain
:class:`plotly.graph_objects.layout.scene.Domain`
instance or dict with compatible properties
dragmode
Determines the mode of drag interactions for this
scene.
hovermode
Determines the mode of hover interactions for this
scene.
uirevision
Controls persistence of user-driven changes in camera
attributes. Defaults to `layout.uirevision`.
xaxis
:class:`plotly.graph_objects.layout.scene.XAxis`
instance or dict with compatible properties
yaxis
:class:`plotly.graph_objects.layout.scene.YAxis`
instance or dict with compatible properties
zaxis
:class:`plotly.graph_objects.layout.scene.ZAxis`
instance or dict with compatible properties
Since in your fig.data[0] is defined the Mesh3d, while fig.data[1] contains data for the Surface, when you are defining each go.Frame.data, data[0] contains updates from the Mesh3d, and data[1] for the Surface.
Moreover to be sure that these updates are performed like that, set traces =[0,1], to inform plotly.js that fig.data[0], and fig.data[1] are updated by the frame data[0], respectively by the frame data[1].
There is the first frame before rotation where it is displayed correctly. Then suddenly it changes. Should d_matrix be recalculated every time a rotation is performed? For Mesh3d is defined in the same way, but it is displayed correctly and even when in the former attempt the rotation of the second trace was displayed in the first one, the surface was correctly displayed.
Without d_matrix the texture is working fine. I tried to change the type of plot from Surface to Mesh3D (therefore surfacecolor to intensity) and the problem in not there anymore, but I need a Surface!
As you rotate the surface, the distance to origin is changing, but it seems that you kept dmatrix constant in each frame. To remove this bug consider the x, y coords on your surface, and z= dmatrix, and rotate the associated points (x,y,z) the same way you rotated the surface. After each rotation, set in each frame the surfacecolor=z_after_rotation.
The rotation of the surface should be around the origin and the distance of each point shouldnāt change! Why in Mesh3D is instead working they way it should (i.e. each point maintains the same distance during the rotation hence the same colour) ? When I recalculate the d_matrix with the new rotated coordinates the value of the scale is also changing at each iteration (just for the Surface) and this is simply wrong. Is it shifting?