Vtk Warp#

Animate a warped PyVista surface with a Player widget. The temporal function is inspired by the HoloViews Live Data guide.

import numpy as np
import panel as pn
import pyvista as pv

pn.extension('vtk', sizing_mode="stretch_width")

Surface#

A flat plane carries the values of the temporal function at time=0.

alpha = 2
xvals = np.linspace(-4, 4, 101)
yvals = np.linspace(-4, 4, 101)
xs, ys = np.meshgrid(xvals, yvals)

def time_function(time):
    return np.sin(((ys/alpha)**alpha+time)*xs)

mesh_ref = pv.ImageData(
    dimensions=(xvals.size, yvals.size, 1),
    spacing=(xvals[1]-xvals[0], yvals[1]-yvals[0], 1),
    origin=(xvals.min(), yvals.min(), 0)
)
mesh_ref.point_data['values'] = time_function(0).flatten(order='F')
pl_ref = pv.Plotter()
pl_ref.add_mesh(mesh_ref, cmap='rainbow')

pn.ui.VTK(pl_ref.ren_win, min_height=600)

Animation#

We warp the surface by its values and update it for each player frame. VTK meshes are mutated in place, so synchronize() pushes the new state to the browser.

mesh_warped = mesh_ref.warp_by_scalar()
pl = pv.Plotter()
pl.add_mesh(mesh_warped, cmap='rainbow')

camera = {
    'position': [13.443258285522461, 12.239550590515137, 12.731934547424316],
    'focalPoint': [0, 0, 0],
    'viewUp': [-0.41067028045654297, -0.40083757042884827, 0.8189500570297241]
}
vtkpan = pn.ui.VTK(pl.ren_win, orientation_widget=True, sizing_mode='stretch_both', camera=camera)
frame = pn.ui.Player(value=0, start=0, end=50, interval=100, loop_policy="reflect")

def update_3d_warp(value):
    # The player runs from 0 to 50 but the function is animated over 0 to 10.
    data = time_function(value / 5).flatten(order='F')
    mesh_ref.point_data['values'] = data
    mesh_warped.point_data['values'] = data
    mesh_warped.points = mesh_ref.warp_by_scalar(factor=0.5).points
    vtkpan.synchronize()

pn.bind(update_3d_warp, frame, watch=True)

main = pn.ui.Column(
    pn.ui.Markdown("This app demonstrates the use of Panel to animate a `VTK` rendering."),
    frame,
    vtkpan,
    min_height=800
)

page = pn.ui.Page(main=[main], title='VTK Warp')

page

Served App#

if pn.state.served:
    page.servable()