plotly / plotly.py

The interactive graphing library for Python :sparkles: This project now includes Plotly Express!
https://plotly.com/python/
MIT License
16.22k stars 2.55k forks source link

Plotly - Animate Scatter3d with fixed Mesh3d with slider #3086

Closed jvrs91 closed 4 months ago

jvrs91 commented 3 years ago

Hi I am struggling on trying to get the animation slider to work. Basically, my goal is to develop an animation of two UAVs (with given x,y,z coordinates over time) over a fixed mesh. The animation runs if I press the play button, but the slider isn't updated. The animation also takes to much time to run. Is there a way to reduce the rendering time? The full mesh data can be found on this link -> https://github.com/jvrs91/plotly.git

import plotly.graph_objects as go

#UAV 1 and UAV 2 coordinates for 6 frames
xx = [[623948.6134,628948.6134],[624948.6134,627948.6134],[625948.6134,626948.6134],[626948.6134,625948.6134],[627948.6134,624948.6134],[628948.6134,623948.6134]]
yy = [[4457407.483,4457407.483],[4457407.483,4457407.483],[4457407.483,4457407.483],[4457407.483,4457407.483],[4457407.483,4457407.483],[4457407.483,4457407.483]]
zz = [[1505,1052.746554],[1496.746554,1056],[1278.690351,1093],[1093,1278.690351],[1056,1496.746554],[1052.746554,1505]]

#3d Map fixed over animation
mesh3d_map = go.Mesh3d(
dict(x = [623948.625, .... ],
y = [4462839.5, .....],
z = [1400, 1393 ...],
intensity = [1400, ....],
name = 'Terrain',
showlegend = True,
colorscale = 'Viridis',
showscale = False,
opacity = 1,
flatshading = False,
visible = True,
hoverinfo = 'skip')

initUAVData = go.Scatter3d(
                x=xx[0],
                y=yy[0],
                z=zz[0],
                name=f'str(0)',
                mode='markers',
                marker=dict(
                    color='Green',
                    size=12  
                )
)

fig = go.Figure(
            data=[mesh3d_map,initUAVData]
        )

#Frames generation

frames = []
for i in range(6):

            scatter = go.Scatter3d(
                x=xx[i],
                y=yy[i],
                z=zz[i],
                name=f'str(i)',
                mode='markers',
                marker=dict(
                    color='Green',
                    size=12  
                ),
            )
            frame = go.Frame(data=[scatter], traces=[1])
            frames.append(frame)

#Assign frames to fig
fig.frames = frames
#Fig layout config
fig.layout = go.Layout(
font = dict(size = 13, family = 'Open Sans'), 
title = dict(x = 0.45, text = '3D Dubins Path Optimisation'), 
width = 1300, 
height = 650, 
margin = dict(b = 50, l = 50, r = 50, t = 50, pad = 0), 
modebar = dict(orientation = 'h'), 
autosize = True, 
showlegend = True, 
legend = dict( 
x = 1.16, 
y = 0.5, 
valign = 'middle', 
xanchor = 'center', 
yanchor = 'middle', 
borderwidth = 1, 
orientation = 'v'), 
scene = dict( 
aspectmode='data',
#X Axis 
xaxis = dict( 
type = 'linear', 
dtick = 500, 
range = [623405, 629923], 
tick0 = 0, 
title = dict(text = 'x_[m]'), 
showgrid = True, 
showline = True, 
tickmode = 'linear', 
zeroline = True, 
autorange = False, 
gridwidth = 5, 
tickangle = 45, 
tickprefix = '', 
ticksuffix = '', 
showticklabels = True, 
backgroundcolor = '#eefbff'), 
#Y Axis 
yaxis = dict( 
type = 'linear', 
dtick = 500, 
range = [4456864, 4463382], 
tick0 = 0, 
title = dict(text = 'y_[m]'), 
showgrid = True, 
showline = True, 
tickmode = 'linear', 
zeroline = True, 
autorange = False, 
gridwidth = 5, 
tickangle = 45, 
tickprefix = '', 
ticksuffix = '', 
showticklabels = True, 
backgroundcolor = '#eefbff'), 
#Z Axis 
zaxis = dict( 
type = 'linear', 
dtick = 100, 
range = [332, 2134], 
tick0 = 0, 
title = dict(text = 'z_[m]'), 
showgrid = True, 
showline = True, 
tickmode = 'linear', 
zeroline = True, 
autorange = False, 
gridwidth = 5, 
tickangle = 0, 
tickprefix = '', 
ticksuffix = '', 
showticklabels = True, 
backgroundcolor = '#eefbff'), 
#Camera 
camera = dict( 
up = dict(x = 0, y = 0, z = 1), 
eye = dict(x = 1.25, y = 1.25, z = 1.25), 
center = dict(x = 0, y = 0, z = 0), 
projection = dict(type = 'perspective')), 
aspectratio = dict(x = 3, y = 3, z = 3)),
#Add play button
updatemenus=[
                dict(
                    type="buttons",
                    buttons=[
                        dict(
                            label="Play",
                            method="animate",
                            args=[None]

                        )
                    ]
                )
            ]

)

#Slider generation
sliders_dict = {
    'active': 0,
    'yanchor': 'top',
    'xanchor': 'left',
    'currentvalue': {
        'font': {'size': 20},
        'prefix': 'Frame:',
        'visible': True,
        'xanchor': 'right'
    },
    'transition': {'duration': 300, 'easing': 'cubic-in-out'},
    'pad': {'b': 10, 't': 50},
    'len': 0.9,
    'x': 0.1,
    'y': 0,
    'steps': []
}

for j in range(6):
    slider_step = {'args': [
        [f'str(j)'],
        {'frame': {'duration': 300, 'redraw': True},
         'mode': 'immediate',
       'transition': {'duration': 300}}
     ],
     'label': str(j),
     'method': 'animate'}
    sliders_dict['steps'].append(slider_step)
#Assign slider to fig  
fig['layout']['sliders'] = [sliders_dict]

fig.show()
empet commented 3 years ago

I couldn't reproduce your bug., because the link you posted is empty. The slider definition used in your code has often been reported on the plotly forum as not working. An explicit slider definition is as follows:

sliders = [dict(steps= [dict(method= 'animate',
                             args= [[f'{k}'], #frame name
                                     dict(mode= 'immediate',
                                          frame= dict(duration=100, redraw= True ),
                                          transition_duration= 0)],
                             label=f'frame{k+1}'  #OPTIONAL
                         ) for k in range(0, len(frames))], 
            transition_duration= 0,
            x=0,
            y=0,
            currentvalue=dict(font=dict(size=12), visible=True, xanchor= 'center'),
            len=1.0)
       ]     

To increase the frame rate set frame['duration']=10 (0r 20, 30, by trial and error), and transition_duration=0. The relationship between the two durations is as follows: The transition duration defines the amount of time spent interpolating a trace from one state to another(this is valid for scatter animation), while the frame duration defines the total time spent in that state, including time spent transitioning. Only scatter traces may be smoothly transitioned from one state to the next. Other trace types are updated instantaneously.

jvrs91 commented 3 years ago

I couldn't reproduce your bug., because the link you posted is empty. The slider definition used in your code has often been reported on the plotly forum as not working. An explicit slider definition is as follows:

sliders = [dict(steps= [dict(method= 'animate',
                             args= [[f'{k}'], #frame name
                                     dict(mode= 'immediate',
                                          frame= dict(duration=100, redraw= True ),
                                          transition_duration= 0)],
                             label=f'frame{k+1}'  #OPTIONAL
                         ) for k in range(0, len(frames))], 
            transition_duration= 0,
            x=0,
            y=0,
            currentvalue=dict(font=dict(size=12), visible=True, xanchor= 'center'),
            len=1.0)
       ]     

To increase the frame rate set frame['duration']=10 (0r 20, 30, by trial and error), and transition_duration=0. The relationship between the two durations is as follows: The transition duration defines the amount of time spent interpolating a trace from one state to another(this is valid for scatter animation), while the frame duration defines the total time spent in that state, including time spent transitioning. Only scatter traces may be smoothly transitioned from one state to the next. Other trace types are updated instantaneously.

Hi, thank you very much for your suggestion. I tried it but still nothing happens to the slider. It isn't updated with the play button and if I manually move the slider the frames aren't updated. Here is the link for the code (I already implemented your suggestion and my previous slider definition is commented). The mesh is quite big. Can the mesh size be related to the slow rendering when the play button is pressed? https://1drv.ms/u/s!AnfrICxwHeIiiehQ9i5wZ4LbSeKpsQ?e=oVsqFs

empet commented 3 years ago

The slider definition I posted in the initial answer is a right one. I used it in tens animations. args definition in your updatemenus is incomplete. It lacks the frame duration and transition duration, and plotly.js uses the default value of 500 for frame['duration']. This makes your animation very slow,

updatemenus=[dict(type='buttons',
                  showactive=False,
                  buttons=[dict(
                            label='Play',
                            method='animate',
                            args=[None, dict(frame=dict(duration=150, redraw=True),
                                             transition=dict(duration=0),
                                             fromcurrent=True,
                                             mode='immediate')])])] 

frame['duration'] and transition['duration'] must have the same values as those in the sliders definition.

Your code contains many unnecessary settings, that are default settings in fig.layout definition,

In zaxis definition I commented out unnecessary attributes:

zaxis = dict( 
             #type = 'linear',
             dtick = 100, 
             range = [332, 2134], 
             tick0 = 0, 
             title = dict(text = 'z_[m]'), 
             #showgrid = True, 
             #showline = True, 
             #tickmode = 'linear', 
             #zeroline = True, 
             autorange = False, 
             gridwidth = 5, 
             #tickangle = 0, 
             #tickprefix = '', 
             #ticksuffix = '', 
             #showticklabels = True, 
             backgroundcolor = '#eefbff')

Then camera properties, up, eye, center, projection can be ommited, because you set the default dict for each one. On Plotly forum you can find many answered questions and examples of animations, as models: https://community.plotly.com/search?q=animation%20category%3A10.

jvrs91 commented 3 years ago

Hi @empet . Even with the full definition of updatemenus the slider doesn't work and the animation is still slow no matter what frame and transition duration I choose. In order to make the animation faster I had to change the plot from Mesh3d to Surface.

gvwilson commented 4 months ago

Hi - we are trying to tidy up the stale issues and PRs in Plotly's public repositories so that we can focus on things that are still important to our community. Since this one has been sitting for several years, I'm going to close it; if it is still a concern, please add a comment letting us know what recent version of our software you've checked it with so that I can reopen it and add it to our backlog. Thanks for your help - @gvwilson