interactive 3D surface plot – how to control Plotly's update behaviour

Question:

I’m building an interactive plot with the code below: everything works fine, except that Plotly refreshes the figure every time I change a property. So when I move the slider by one tick, Plotly refreshes the figure 4 times, which is annoying to see.

Is there a way to ask Plotly to only refresh the figure at the end of the update function?

import numpy as np
import plotly.graph_objects as go
import panel as pn
pn.extension("plotly")

color_func = lambda x, y, z: x * y
f = lambda r, d: 10 * np.cos(r) * np.exp(-r * d)
x, y = np.mgrid[-7:7:100j, -7:7:100j]
r = np.sqrt(x**2 + y**2)
z = f(r, 0.1)
surfacecolor = color_func(x, y, z)
fig = go.Figure([
    go.Surface(x=x, y=y, z=z, surfacecolor=surfacecolor, cmin=surfacecolor.min(), cmax=surfacecolor.max())
])
fig.update_layout({"scene": {"aspectmode": "cube"}})

slider = pn.widgets.FloatSlider(start=0, end=1, value=0.1)
@pn.depends(slider)
def update(d):
    surfacecolor = color_func(d * x, y, z)
    fig.data[0]["z"] = f(r, d)
    fig.data[0]["surfacecolor"] = surfacecolor
    fig.data[0]["cmin"] = surfacecolor.min()
    fig.data[0]["cmax"] = surfacecolor.max()
    return fig
pn.Column(slider, update)
Asked By: Davide_sd

||

Answers:

Turns out that I need to use panel’s Plotly pane. Note that its constructor requires a dictionary with the keys data, layout. If you give it a Plotly figure it will work, but the update will be extremely slow and unreliable.

So this is the correct way to achieve my goal. By executing this code, the update will be very fast in comparison to the original attempt.

import numpy as np
import plotly.graph_objects as go
import panel as pn
pn.extension("plotly")

color_func = lambda x, y, z: x * y
f = lambda r, d: 10 * np.cos(r) * np.exp(-r * d)
x, y = np.mgrid[-7:7:100j, -7:7:100j]
r = np.sqrt(x**2 + y**2)
z = f(r, 0.1)
surfacecolor = color_func(x, y, z)

fig = dict(
    data=[
        go.Surface(x=x, y=y, z=z,
            surfacecolor=surfacecolor, cmin=surfacecolor.min(), cmax=surfacecolor.max())
    ],
    layout=go.Layout(scene={"aspectmode": "cube"})
)
slider = pn.widgets.FloatSlider(start=0, end=1, value=0.1)
pane = pn.pane.Plotly(fig)

def update(event):
    d = slider.value
    surfacecolor = color_func(d * x, y, z)
    fig["data"][0]["z"] = f(r, d)
    fig["data"][0]["surfacecolor"] = surfacecolor
    fig["data"][0]["cmin"] = surfacecolor.min()
    fig["data"][0]["cmax"] = surfacecolor.max()
    pane.object = fig
slider.param.watch(update, "value")

pn.Column(slider, pane)
Answered By: Davide_sd
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.