Time series#
You have at your disposal two ways of changing data in a plot:
Send new data from the backend
Send time series in the form of a dictionary
Sending data at each timestep#
You can update a plot object attribute using the Python backend.
import k3d
import numpy as np
import time
np.random.seed(2022)
x = np.random.randn(100,3).astype(np.float32)
plt_points = k3d.points(x,
color=0x528881,
point_size=0.2)
plot = k3d.plot()
plot += plt_points
plot.display()
for t in range(20):
plt_points.positions = x - t/10*x/np.linalg.norm(x,axis=-1)[:,np.newaxis]
time.sleep(0.5)
Sending a dictionary of all timesteps#
You can create an animation using only the frontend.
Time is represented as a str denoting wall time.
import k3d
import numpy as np
np.random.seed(2022)
x = np.random.randn(100,3).astype(np.float32)
plt_points = k3d.points(x,
color=0x528881,
point_size=0.2)
plot = k3d.plot()
plot += plt_points
plot.display()
plt_points.positions = {str(t):x - t/5*x/np.linalg.norm(x,axis=-1)[:,np.newaxis] for t in range(10)}
plot.start_auto_play()
You can control the animation from the K3D panel or through several attributes:
plot.start_auto_play() # Start the animation
plot.stop_auto_play() # Stop the animation
plot.fps # Number of frame
plot.time = O.5 # Read animation at a specific time
Discrete timesteps#
Between keyframes the frontend interpolates, which assumes the data has a fixed size and that a point keeps its identity from one frame to the next. That is false for a lidar scan, per frame detections or particles that appear and disappear, so interpolation can be turned off:
plot.time_interpolation = False # hold each keyframe until the next one
Playback then steps rather than glides. Keyframes of unequal size are never blended in either mode: the nearer one is shown whole.
Stepping through frames#
Stepping starts from the keyframe nearest the current time, so it behaves the same whether time sits exactly on a frame or between two, and it clamps at both ends rather than wrapping. Each call returns the new time.
plot.next_frame() # -> 0.3
plot.previous_frame() # -> 0.2
plot.step_frame(3) # three frames forward
With time_interpolation off, the time slider in the K3D panel snaps to the
nearest keyframe, since a continuous slider would otherwise stop between two of them.
See examples/time_series_frame_stepping.ipynb for the whole flow, including wiring the steps
to your own ipywidgets buttons.
Inspecting the keyframes#
Both of these are answered by the plot itself, so they work headless and before display():
plot.get_time_series_times() # [0.0, 0.1, 0.2, ...]
plot.get_time_series_range() # [min, max]
Objects need not share keys - the times are their union - and camera_animation counts too.
The range always contains 0.0, because that is what time is clamped to.
From JavaScript#
An external control needs the same numbers, plus notice when something moves. On the frontend instance:
K3DInstance.getTimeSeriesInfo(); // {min, max, times}
K3DInstance.stepFrame(1); // returns the new time
K3DInstance.on(K3DInstance.events.TIME_CHANGE, (time) => { ... });
K3DInstance.on(K3DInstance.events.AUTO_PLAY_CHANGE, (playing) => { ... });
TIME_CHANGE fires on every frame while playback runs, so its listeners have to be cheap. It
is a notification only and is never written back to the kernel.
The standalone bundle also exports timeSeries.interpolateTimeSeries,
timeSeries.getObjectsWithTimeSeriesAndMinMax and timeSeries.getTimeSeriesTimes, so a time
series can be read or driven from outside without reimplementing the interpolation.