Skip to content

Commit

Permalink
docs(example): add more examples
Browse files Browse the repository at this point in the history
  • Loading branch information
jourdain committed Sep 13, 2024
1 parent 8438611 commit c3bfb11
Show file tree
Hide file tree
Showing 2 changed files with 378 additions and 0 deletions.
170 changes: 170 additions & 0 deletions examples/demo/widget_clip.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import vtk

from trame.app import get_server
from trame.ui.html import DivLayout
from trame.widgets import html, client, vtklocal
from trame.decorators import TrameApp, change
from trame.assets.remote import HttpFile

FULL_SCREEN = "position:absolute; left:0; top:0; width:100vw; height:100vh;"
K_RANGE = [0.0, 15.6]

BIKE = HttpFile(
"bike.vtp", "https://github.com/Kitware/trame-app-bike/raw/master/data/bike.vtp"
)
TUNNEL = HttpFile(
"tunnel.vtu", "https://github.com/Kitware/trame-app-bike/raw/master/data/tunnel.vtu"
)

if not BIKE.local:
BIKE.fetch()

if not TUNNEL.local:
TUNNEL.fetch()


def create_vtk_pipeline():
renderer = vtk.vtkRenderer()
renderWindow = vtk.vtkRenderWindow()
renderWindow.AddRenderer(renderer)
renderWindow.OffScreenRenderingOn()

renderWindowInteractor = vtk.vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
renderWindowInteractor.GetInteractorStyle().SetCurrentStyleToTrackballCamera()

bikeReader = vtk.vtkXMLPolyDataReader()
bikeReader.SetFileName(BIKE.path)

tunnelReader = vtk.vtkXMLUnstructuredGridReader()
tunnelReader.SetFileName(TUNNEL.path)
tunnelReader.Update()
input_bounds = tunnelReader.output.bounds

plane = vtk.vtkPlane(
origin=(
0.5 * (input_bounds[0] + input_bounds[1]),
0.5 * (input_bounds[2] + input_bounds[3]),
0.5 * (input_bounds[4] + input_bounds[5]),
)
)

clipper = vtk.vtkCutter(
cut_function=plane,
input_connection=tunnelReader.output_port,
)

bike_mapper = vtk.vtkPolyDataMapper()
bike_actor = vtk.vtkActor()
bike_mapper.SetInputConnection(bikeReader.GetOutputPort())
bike_actor.SetMapper(bike_mapper)
renderer.AddActor(bike_actor)

clip_mapper = vtk.vtkPolyDataMapper()
clip_actor = vtk.vtkActor()
clip_mapper.SetInputConnection(clipper.GetOutputPort())
clip_actor.SetMapper(clip_mapper)
renderer.AddActor(clip_actor)

lut = vtk.vtkLookupTable()
lut.SetHueRange(0.7, 0)
lut.SetSaturationRange(1.0, 0)
lut.SetValueRange(0.5, 1.0)

clip_mapper.SetLookupTable(lut)
clip_mapper.SetColorModeToMapScalars()
clip_mapper.SetScalarModeToUsePointData()
clip_mapper.SetArrayName("k")
clip_mapper.SetScalarRange(K_RANGE)

renderWindow.Render()
renderer.ResetCamera()
renderer.SetBackground(0.4, 0.4, 0.4)

rep = vtk.vtkImplicitPlaneRepresentation(
place_factor=1.25,
outline_translation=False,
)
rep.DrawPlaneOff()
rep.PlaceWidget(input_bounds)
rep.normal = plane.normal
rep.origin = plane.origin

plane_widget = vtk.vtkImplicitPlaneWidget2(
interactor=renderWindowInteractor, representation=rep
)
plane_widget.On()

return renderWindow, plane, plane_widget


@TrameApp()
class App:
def __init__(self, server=None):
self.server = get_server(server)
self.rw, self.plane, self.widget = create_vtk_pipeline()
self._build_ui()

# reserve state variable for widget update
self.state.plane_widget = None

@property
def state(self):
return self.server.state

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

@change("plane_widget")
def _on_widget_update(self, plane_widget, **_):
if plane_widget is None:
return

origin = plane_widget.get("origin")
normal = plane_widget.get("normal")

self.plane.SetOrigin(origin)
self.plane.SetNormal(normal)

# prevent requesting geometry too often
self.ctrl.view_update()

def _build_ui(self):
with DivLayout(self.server):
client.Style("body { margin: 0; }")
with html.Div(style=FULL_SCREEN):
with vtklocal.LocalView(self.rw) as view:
view.update_throttle.rate = 20
self.ctrl.view_update = view.update_throttle
self.widget_id = view.register_widget(self.widget)
view.listeners = (
"listeners",
{
self.widget_id: {
"InteractionEvent": {
"plane_widget": {
"origin": (
self.widget_id,
"WidgetRepresentation",
"Origin",
),
"normal": (
self.widget_id,
"WidgetRepresentation",
"Normal",
),
},
},
},
},
)


def main():
app = App()
app.server.start()


if __name__ == "__main__":
main()
208 changes: 208 additions & 0 deletions examples/vtk/widgets_plane_slice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
# import os
from pathlib import Path

from trame.app import get_server
from trame.ui.html import DivLayout
from trame.widgets import html, client
from trame_vtklocal.widgets import vtklocal
from trame.decorators import TrameApp, change

# Required for vtk factory
import vtkmodules.vtkRenderingOpenGL2 # noqa
from vtkmodules.vtkInteractionStyle import vtkInteractorStyleSwitch # noqa

from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonDataModel import vtkPlane
from vtkmodules.vtkFiltersCore import vtkCutter
from vtkmodules.vtkFiltersSources import vtkSphereSource
from vtkmodules.vtkIOXML import vtkXMLPolyDataReader
from vtkmodules.vtkInteractionWidgets import (
vtkImplicitPlaneRepresentation,
vtkImplicitPlaneWidget2,
)
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer,
)


def create_vtk_pipeline(file_to_load):
colors = vtkNamedColors()

fp = None
if file_to_load:
fp = Path(file_to_load)
if not (fp.is_file() and fp.suffix == ".vtp"):
print("Expected an existing file name with extension .vtp:")
print("Got", fp)
return

# Setup a visualization pipeline.
source = (
vtkXMLPolyDataReader(file_name=fp)
if file_to_load
else vtkSphereSource(radius=10.0)
)
source.Update()

input_bounds = source.output.bounds
plane = vtkPlane(
origin=(
0.5 * (input_bounds[0] + input_bounds[1]),
0.5 * (input_bounds[2] + input_bounds[3]),
0.5 * (input_bounds[4] + input_bounds[5]),
)
)

clipper = vtkCutter(
cut_function=plane,
input_connection=source.output_port,
)

# Create a mapper and actor.
mapper = vtkPolyDataMapper(input_connection=clipper.output_port)
actor = vtkActor(mapper=mapper)
actor.property.line_width = 3
actor.property.color = (0, 0, 0)
mapper2 = vtkPolyDataMapper(input_connection=source.output_port)
actor2 = vtkActor(mapper=mapper2)
actor2.property.opacity = 0.2

# A renderer and render window
renderer = vtkRenderer(background=colors.GetColor3d("SlateGray"))
renderer.AddActor(actor)
# renderer.AddActor(actor2)
ren_win = vtkRenderWindow()
ren_win.AddRenderer(renderer)

# An interactor
iren = vtkRenderWindowInteractor(render_window=ren_win)
iren.GetInteractorStyle().SetCurrentStyleToTrackballCamera()

rep = vtkImplicitPlaneRepresentation(
place_factor=1.25,
outline_translation=False,
)
rep.PlaceWidget(input_bounds)
rep.normal = plane.normal
rep.origin = plane.origin

plane_widget = vtkImplicitPlaneWidget2(interactor=iren, representation=rep)

renderer.ResetCamera(input_bounds)
ren_win.Render()

plane_widget.On()

return ren_win, plane_widget, plane


# -----------------------------------------------------------------------------
# GUI
# -----------------------------------------------------------------------------


@TrameApp()
class App:
def __init__(self, server=None):
self.server = get_server(server, client_type="vue3")

# enable shared array buffer
self.server.http_headers.shared_array_buffer = True

self.server.cli.add_argument("--data")
args, _ = self.server.cli.parse_known_args()
self.render_window, self.widget, self.plane = create_vtk_pipeline(args.data)

# Allocation state variable for widget state
self.state.plane_widget = None

# Build UI
self.html_view = None
self.ui = self._ui()

@property
def state(self):
return self.server.state

@change("plane_widget")
def _on_widget_update(self, plane_widget, **_):
if plane_widget is None:
return

# update cutting plane
self.plane.normal = plane_widget.get("normal")
self.plane.origin = plane_widget.get("origin")

# prevent requesting geometry too often
self.html_view.update_throttle()

def toggle_listeners(self):
if self.state.wasm_listeners is not None and len(self.state.wasm_listeners):
self.state.wasm_listeners = {}
else:
self.state.wasm_listeners = {
self.widget_id: {
"InteractionEvent": {
"plane_widget": {
"normal": (
self.widget_id,
"WidgetRepresentation",
"Normal",
),
"origin": (
self.widget_id,
"WidgetRepresentation",
"Origin",
),
}
}
}
}

def one_time_update(self):
self.html_view.eval(
{
"plane_widget": {
"origin": (self.widget_id, "WidgetRepresentation", "Origin"),
"normal": (self.widget_id, "WidgetRepresentation", "Normal"),
}
}
)

def _ui(self):
with DivLayout(self.server) as layout:
client.Style("body { margin: 0; }")
html.Button(
"Toggle listeners",
click=self.toggle_listeners,
style="position: absolute; left: 1rem; top: 1rem; z-index: 10;",
)
html.Button(
"Update cut",
click=self.one_time_update,
style="position: absolute; right: 1rem; top: 1rem; z-index: 10;",
)
with html.Div(
style="position: absolute; left: 0; top: 0; width: 100vw; height: 100vh;"
):
self.html_view = vtklocal.LocalView(
self.render_window,
throttle_rate=20,
listeners=("wasm_listeners", {}),
)
self.widget_id = self.html_view.register_widget(self.widget)

return layout


# -----------------------------------------------------------------------------
# Main
# -----------------------------------------------------------------------------

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

0 comments on commit c3bfb11

Please sign in to comment.