Skip to content

From remote rendering to local?

In this hands on session, we will expand the previous solution and run it using different modality.

  • remote rendering (what we've done)
  • local rendering using vtk.js
  • local rendering using VTK.wasm

WARNING

For WASM, you will need to install trame-vtklocal

bash
uv pip install trame-vtklocal

Option 1 (repo)

bash
python ./code/04-visualization-3d/04-vtk-trame-remote.py
python ./code/04-visualization-3d/04-vtk-trame-local-js.py
python ./code/04-visualization-3d/04-vtk-trame-local-wasm.py

Option 2 (copy/paste)

Create file with content

Create the files and paste their content below into it.

diff
127,129c127
<                     with vtkw.VtkRemoteView(
<                         self.render_window, interactive_ratio=1
<                     ) as view:
---
>                     with vtkw.VtkLocalView(self.render_window) as view:
diff
6c6
< from trame.widgets import vuetify3 as v3, vtk as vtkw
---
> from trame.widgets import vuetify3 as v3, vtklocal
127,130c127,128
<                     with vtkw.VtkRemoteView(
<                         self.render_window, interactive_ratio=1
<                     ) as view:
<                         self.ctrl.view_update = view.update
---
>                     with vtklocal.LocalView(self.render_window, throttle_rate=20) as view:
>                         self.ctrl.view_update = view.update_throttle
py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from trame.app import get_server
from trame.ui.vuetify3 import SinglePageLayout
from trame.widgets import vuetify3 as v3, vtk as vtkw
from trame.decorators import TrameApp, change

from vtkmodules.vtkInteractionStyle import vtkInteractorStyleSwitch  # noqa
import vtkmodules.vtkRenderingOpenGL2  # noqa

from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonComputationalGeometry import vtkParametricKuen
from vtkmodules.vtkCommonCore import vtkMath
from vtkmodules.vtkFiltersSources import vtkParametricFunctionSource
from vtkmodules.vtkRenderingCore import (
    vtkActor,
    vtkPolyDataMapper,
    vtkProperty,
    vtkRenderWindow,
    vtkRenderWindowInteractor,
    vtkRenderer,
)


def setup_vtk():
    colors = vtkNamedColors()

    colors.SetColor("BkgColor", [26, 51, 102, 255])

    surface = vtkParametricKuen()
    source = vtkParametricFunctionSource()

    renderer = vtkRenderer()
    mapper = vtkPolyDataMapper()
    actor = vtkActor()

    backProperty = vtkProperty()
    backProperty.SetColor(colors.GetColor3d("Tomato"))

    # Create a parametric function source, renderer, mapper, and actor
    source.SetParametricFunction(surface)

    mapper.SetInputConnection(source.GetOutputPort())

    actor.SetMapper(mapper)
    actor.SetBackfaceProperty(backProperty)
    actor.GetProperty().SetDiffuseColor(colors.GetColor3d("Banana"))
    actor.GetProperty().SetSpecular(0.5)
    actor.GetProperty().SetSpecularPower(20)

    renderWindow = vtkRenderWindow()
    renderWindow.AddRenderer(renderer)

    renderer.AddActor(actor)
    renderer.SetBackground(colors.GetColor3d("BkgColor"))
    renderer.ResetCamera()
    renderer.GetActiveCamera().Azimuth(30)
    renderer.GetActiveCamera().Elevation(-30)
    renderer.GetActiveCamera().Zoom(0.9)
    renderer.ResetCameraClippingRange()

    surface.SetMinimumU(-4.5)
    surface.SetMaximumU(4.5)
    surface.SetMinimumV(0.05)
    surface.SetMaximumV(vtkMath.Pi() - 0.05)

    renderer.ResetCamera()
    renderer.GetActiveCamera().Azimuth(30)
    renderer.GetActiveCamera().Elevation(-30)
    renderer.GetActiveCamera().Zoom(0.9)
    renderer.ResetCameraClippingRange()
    renderWindow.Render()

    interactor = vtkRenderWindowInteractor()
    interactor.SetRenderWindow(renderWindow)
    interactor.GetInteractorStyle().SetCurrentStyleToTrackballCamera()

    return renderWindow, surface


@TrameApp()
class VtkApp:
    def __init__(self, server=None):
        self.server = get_server(server)
        self.render_window, self.source = setup_vtk()
        self._build_ui()

    @property
    def ctrl(self):
        return self.server.controller

    @change("u_range", "v_range")
    def on_change(self, u_range, v_range, **_):
        self.source.SetMinimumU(u_range[0])
        self.source.SetMaximumU(u_range[1])
        self.source.SetMinimumV(v_range[0])
        self.source.SetMaximumV(v_range[1])
        self.ctrl.view_update()

    def _build_ui(self):
        with SinglePageLayout(self.server, full_height=True) as layout:
            with layout.toolbar.clear() as tb:
                tb.density = "compact"
                v3.VRangeSlider(
                    label="U",
                    v_model=("u_range", [-4.5, 4.5]),
                    min=-4.5,
                    max=4.5,
                    step=0.1,
                    density="compact",
                    hide_details=True,
                    classes="mx-4",
                )
                v3.VRangeSlider(
                    label="V",
                    v_model=("v_range", [0.05, vtkMath.Pi() - 0.05]),
                    min=0.05,
                    max=vtkMath.Pi() - 0.05,
                    step=0.05,
                    density="compact",
                    hide_details=True,
                    classes="mx-4",
                )
            with layout.content:
                with v3.VContainer(fluid=True, classes="ma-0 pa-0 h-100"):
                    with vtkw.VtkRemoteView(
                        self.render_window, interactive_ratio=1
                    ) as view:
                        self.ctrl.view_update = view.update
                        self.ctrl.view_reset_camera = view.reset_camera


if __name__ == "__main__":
    app = VtkApp()
    app.server.start()
py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from trame.app import get_server
from trame.ui.vuetify3 import SinglePageLayout
from trame.widgets import vuetify3 as v3, vtk as vtkw
from trame.decorators import TrameApp, change

from vtkmodules.vtkInteractionStyle import vtkInteractorStyleSwitch  # noqa
import vtkmodules.vtkRenderingOpenGL2  # noqa

from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonComputationalGeometry import vtkParametricKuen
from vtkmodules.vtkCommonCore import vtkMath
from vtkmodules.vtkFiltersSources import vtkParametricFunctionSource
from vtkmodules.vtkRenderingCore import (
    vtkActor,
    vtkPolyDataMapper,
    vtkProperty,
    vtkRenderWindow,
    vtkRenderWindowInteractor,
    vtkRenderer,
)


def setup_vtk():
    colors = vtkNamedColors()

    colors.SetColor("BkgColor", [26, 51, 102, 255])

    surface = vtkParametricKuen()
    source = vtkParametricFunctionSource()

    renderer = vtkRenderer()
    mapper = vtkPolyDataMapper()
    actor = vtkActor()

    backProperty = vtkProperty()
    backProperty.SetColor(colors.GetColor3d("Tomato"))

    # Create a parametric function source, renderer, mapper, and actor
    source.SetParametricFunction(surface)

    mapper.SetInputConnection(source.GetOutputPort())

    actor.SetMapper(mapper)
    actor.SetBackfaceProperty(backProperty)
    actor.GetProperty().SetDiffuseColor(colors.GetColor3d("Banana"))
    actor.GetProperty().SetSpecular(0.5)
    actor.GetProperty().SetSpecularPower(20)

    renderWindow = vtkRenderWindow()
    renderWindow.AddRenderer(renderer)

    renderer.AddActor(actor)
    renderer.SetBackground(colors.GetColor3d("BkgColor"))
    renderer.ResetCamera()
    renderer.GetActiveCamera().Azimuth(30)
    renderer.GetActiveCamera().Elevation(-30)
    renderer.GetActiveCamera().Zoom(0.9)
    renderer.ResetCameraClippingRange()

    surface.SetMinimumU(-4.5)
    surface.SetMaximumU(4.5)
    surface.SetMinimumV(0.05)
    surface.SetMaximumV(vtkMath.Pi() - 0.05)

    renderer.ResetCamera()
    renderer.GetActiveCamera().Azimuth(30)
    renderer.GetActiveCamera().Elevation(-30)
    renderer.GetActiveCamera().Zoom(0.9)
    renderer.ResetCameraClippingRange()
    renderWindow.Render()

    interactor = vtkRenderWindowInteractor()
    interactor.SetRenderWindow(renderWindow)
    interactor.GetInteractorStyle().SetCurrentStyleToTrackballCamera()

    return renderWindow, surface


@TrameApp()
class VtkApp:
    def __init__(self, server=None):
        self.server = get_server(server)
        self.render_window, self.source = setup_vtk()
        self._build_ui()

    @property
    def ctrl(self):
        return self.server.controller

    @change("u_range", "v_range")
    def on_change(self, u_range, v_range, **_):
        self.source.SetMinimumU(u_range[0])
        self.source.SetMaximumU(u_range[1])
        self.source.SetMinimumV(v_range[0])
        self.source.SetMaximumV(v_range[1])
        self.ctrl.view_update()

    def _build_ui(self):
        with SinglePageLayout(self.server, full_height=True) as layout:
            with layout.toolbar.clear() as tb:
                tb.density = "compact"
                v3.VRangeSlider(
                    label="U",
                    v_model=("u_range", [-4.5, 4.5]),
                    min=-4.5,
                    max=4.5,
                    step=0.1,
                    density="compact",
                    hide_details=True,
                    classes="mx-4",
                )
                v3.VRangeSlider(
                    label="V",
                    v_model=("v_range", [0.05, vtkMath.Pi() - 0.05]),
                    min=0.05,
                    max=vtkMath.Pi() - 0.05,
                    step=0.05,
                    density="compact",
                    hide_details=True,
                    classes="mx-4",
                )
            with layout.content:
                with v3.VContainer(fluid=True, classes="ma-0 pa-0 h-100"):
                    with vtkw.VtkLocalView(self.render_window) as view:
                        self.ctrl.view_update = view.update
                        self.ctrl.view_reset_camera = view.reset_camera


if __name__ == "__main__":
    app = VtkApp()
    app.server.start()
py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from trame.app import get_server
from trame.ui.vuetify3 import SinglePageLayout
from trame.widgets import vuetify3 as v3, vtklocal
from trame.decorators import TrameApp, change

from vtkmodules.vtkInteractionStyle import vtkInteractorStyleSwitch  # noqa
import vtkmodules.vtkRenderingOpenGL2  # noqa

from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonComputationalGeometry import vtkParametricKuen
from vtkmodules.vtkCommonCore import vtkMath
from vtkmodules.vtkFiltersSources import vtkParametricFunctionSource
from vtkmodules.vtkRenderingCore import (
    vtkActor,
    vtkPolyDataMapper,
    vtkProperty,
    vtkRenderWindow,
    vtkRenderWindowInteractor,
    vtkRenderer,
)


def setup_vtk():
    colors = vtkNamedColors()

    colors.SetColor("BkgColor", [26, 51, 102, 255])

    surface = vtkParametricKuen()
    source = vtkParametricFunctionSource()

    renderer = vtkRenderer()
    mapper = vtkPolyDataMapper()
    actor = vtkActor()

    backProperty = vtkProperty()
    backProperty.SetColor(colors.GetColor3d("Tomato"))

    # Create a parametric function source, renderer, mapper, and actor
    source.SetParametricFunction(surface)

    mapper.SetInputConnection(source.GetOutputPort())

    actor.SetMapper(mapper)
    actor.SetBackfaceProperty(backProperty)
    actor.GetProperty().SetDiffuseColor(colors.GetColor3d("Banana"))
    actor.GetProperty().SetSpecular(0.5)
    actor.GetProperty().SetSpecularPower(20)

    renderWindow = vtkRenderWindow()
    renderWindow.AddRenderer(renderer)

    renderer.AddActor(actor)
    renderer.SetBackground(colors.GetColor3d("BkgColor"))
    renderer.ResetCamera()
    renderer.GetActiveCamera().Azimuth(30)
    renderer.GetActiveCamera().Elevation(-30)
    renderer.GetActiveCamera().Zoom(0.9)
    renderer.ResetCameraClippingRange()

    surface.SetMinimumU(-4.5)
    surface.SetMaximumU(4.5)
    surface.SetMinimumV(0.05)
    surface.SetMaximumV(vtkMath.Pi() - 0.05)

    renderer.ResetCamera()
    renderer.GetActiveCamera().Azimuth(30)
    renderer.GetActiveCamera().Elevation(-30)
    renderer.GetActiveCamera().Zoom(0.9)
    renderer.ResetCameraClippingRange()
    renderWindow.Render()

    interactor = vtkRenderWindowInteractor()
    interactor.SetRenderWindow(renderWindow)
    interactor.GetInteractorStyle().SetCurrentStyleToTrackballCamera()

    return renderWindow, surface


@TrameApp()
class VtkApp:
    def __init__(self, server=None):
        self.server = get_server(server)
        self.render_window, self.source = setup_vtk()
        self._build_ui()

    @property
    def ctrl(self):
        return self.server.controller

    @change("u_range", "v_range")
    def on_change(self, u_range, v_range, **_):
        self.source.SetMinimumU(u_range[0])
        self.source.SetMaximumU(u_range[1])
        self.source.SetMinimumV(v_range[0])
        self.source.SetMaximumV(v_range[1])
        self.ctrl.view_update()

    def _build_ui(self):
        with SinglePageLayout(self.server, full_height=True) as layout:
            with layout.toolbar.clear() as tb:
                tb.density = "compact"
                v3.VRangeSlider(
                    label="U",
                    v_model=("u_range", [-4.5, 4.5]),
                    min=-4.5,
                    max=4.5,
                    step=0.1,
                    density="compact",
                    hide_details=True,
                    classes="mx-4",
                )
                v3.VRangeSlider(
                    label="V",
                    v_model=("v_range", [0.05, vtkMath.Pi() - 0.05]),
                    min=0.05,
                    max=vtkMath.Pi() - 0.05,
                    step=0.05,
                    density="compact",
                    hide_details=True,
                    classes="mx-4",
                )
            with layout.content:
                with v3.VContainer(fluid=True, classes="ma-0 pa-0 h-100"):
                    with vtklocal.LocalView(self.render_window, throttle_rate=20) as view:
                        self.ctrl.view_update = view.update_throttle
                        self.ctrl.view_reset_camera = view.reset_camera


if __name__ == "__main__":
    app = VtkApp()
    app.server.start()

Run example

bash
python 04-vtk-trame-remote.py
python 04-vtk-trame-local-js.py
python 04-vtk-trame-local-wasm.py