Animate n points along y axis given velocity

How can I animation n points along y axis given their velocity ? ( or appear running )

y axis is fixed between -2 and 2 & velocity changes between 0 & 100 mph
all points on x axis have different velocity .

something close to this in dash python but not matplotlib

Hi @yogi_dhiman, here is what I achieved. I did not figure out yet, how to create an animation so that the points travel the same amount in y- direction. I guess I have to tweak the y values. The thing is, that all arrays must be of the same length.

import plotly.express as px
import numpy as np
import pandas as pd

# frames for animation
step = 21
frame = np.linspace(0 , 0.1, step)

# data
x = [1 for _ in range(step)]
y = np.asarray([0.1*i for i in range(step)])

# different velocities
faster = y * 2
slower = y / 2

# create DataFrame
df = pd.DataFrame({'x':x,'y':y, 'slower':slower, 'faster':faster, 'frame':frame})

# create figure
fig = px.strip(
df,
x=x,
y=['y', 'slower', 'faster'],
animation_frame='frame',
range_x=[0.5, 1.5],
range_y=[0, 4],
height=600,
width=600
)
fig.show()

creates:

OK, I figured out how to limit the y coordinate:

import plotly.express as px
import numpy as np
import pandas as pd

# frames for animation
step = 21
frame = np.linspace(0 , 0.1, step)

# data
x = [1 for _ in range(step)]
y = np.asarray([0.1*i for i in range(step)])

# different velocities
faster = y * 2
slower = y / 2

# the slowest point defines the maximum y coordinate
max_y = slower.max()

# limit faster points to y_max
y = [i if i<=max_y else max_y for i in y]
faster = [i if i<=max_y else max_y for i in faster]

# create DataFrame
df = pd.DataFrame({'x':x,'y':y, 'slower':slower, 'faster':faster, 'frame':frame})

# create figure
fig = px.strip(
df,
x=x,
y=['y','slower', 'faster'],
animation_frame='frame',
range_x=[0.5, 1.5],
range_y=[0, max_y],
height=600,
width=600
)
fig.show()

creates:

1 Like

Aimped that is so cool !
thanks a lot ; This really gave me a direction .

I am curious to know , just to mark it as solution ( what if I donβt want to animate it on play click rather leave my animation - update on a interval call back routine ) - assume your suggested code snipped in my call back routine ;
Just your thought on that can work

Hi @yogi_dhiman , I thought you wanted it as a βstaticβ plotly figure without using dash. With dash there might be different approaches.

Do you want the callback to return an animatied figure such as the above or are you planing to animate the figure by triggering the callback with an intervall?

I want to animate the figure by triggering the callback given an interval

@yogi_dhiman here is an solution using dash. I would try to change to a clientside callback to assure a smooth animation if your interval is quite small

from dash import Dash, Input, Output, dcc, html
from dash.exceptions import PreventUpdate
import plotly.graph_objects as go

app = Dash()

app.layout = html.Div(
[
dcc.Graph(
id='graph',
figure=go.Figure(
data=go.Scatter(
x=[1, 2, 3],
y=[0, 0, 0],
mode='markers',
marker={'color': ['red', 'green', 'blue']}
),
layout={'yaxis_range': [0, 5], 'width': 500, 'height': 500}
)
),
html.Button(
'start animation',
id='btn'
),
dcc.Interval(
id='interval',
interval=100,
n_intervals=0,
max_intervals=0
),
html.Div(id='dump')
]
)

@app.callback(
Output('interval', 'max_intervals'),
Input('btn', 'n_clicks'),
prevent_initiall_call=True
)
def start_interval(click):
if not click:
raise PreventUpdate
else:
return 25

@app.callback(
Output('graph', 'figure'),
Output('dump', 'children'),
Input('interval', 'n_intervals'),
prevent_initial_call=True
)
def update(n):
x = [1, 2, 3]
# different velocities by multiplication of constant with n
y = [0.1 * n, 0.1 * 2*n, 0.1 * 0.5*n]

fig = go.Figure(
data=go.Scatter(
x=x,
y=y,
mode='markers',
marker={'color': ['red', 'green', 'blue']}
),
layout={'yaxis_range': [0, 5], 'width': 500, 'height': 500}
)
return fig, f'frame number: {n}'

if __name__ == "__main__":
app.run_server(debug=True, port=8080)

creates:

mred animation

Here is the clientside callback:

app.clientside_callback(
"""
function(frame, fig) {
newFig = JSON.parse(JSON.stringify(fig))
newFig['data'][0]['y'] = [0.1 * frame, 0.1 * 2*frame, 0.1 * 0.5*frame]
return [newFig, frame]
}
""",
Output('graph', 'figure'),
Output('dump', 'children'),
Input('interval', 'n_intervals'),
State('graph', 'figure'),
prevent_initial_call=True)

@jinnyzor this should look familiar to you

1 Like