Skip to content

Commit

Permalink
fix(animation): enable animation on remote view
Browse files Browse the repository at this point in the history
  • Loading branch information
jourdain committed Oct 17, 2024
1 parent a8ec763 commit 660a1d4
Show file tree
Hide file tree
Showing 7 changed files with 281 additions and 0 deletions.
99 changes: 99 additions & 0 deletions examples/validation/Animation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import asyncio

from trame.app import get_server, asynchronous
from trame.ui.vuetify3 import SinglePageLayout
from trame.widgets import vuetify3 as v3, vtk as vtk_widgets

from vtkmodules.vtkFiltersSources import vtkConeSource
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper,
vtkRenderer,
vtkRenderWindow,
vtkRenderWindowInteractor,
)
from vtkmodules.vtkInteractionStyle import vtkInteractorStyleSwitch # noqa
import vtkmodules.vtkRenderingOpenGL2 # noqa

renderer = vtkRenderer()
renderWindow = vtkRenderWindow()
renderWindow.AddRenderer(renderer)

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

cone_source = vtkConeSource()
mapper = vtkPolyDataMapper()
mapper.SetInputConnection(cone_source.GetOutputPort())
actor = vtkActor()
actor.SetMapper(mapper)
renderer.AddActor(actor)
renderer.ResetCamera()

server = get_server(client_type="vue3")
state, ctrl = server.state, server.controller


async def animate():
while True:
await asyncio.sleep(1.0 / state.anime_rate)
if state.mode == "animate":
renderer.GetActiveCamera().Azimuth(1)
if state.mode == "update":
renderer.GetActiveCamera().Azimuth(1)
ctrl.view_update()


@state.change("mode")
def on_animation_change(mode, **_):
if mode == "animate":
ctrl.view_start_animation(state.fps, 60, 1)
else:
ctrl.view_stop_animation()


asynchronous.create_task(animate())

with SinglePageLayout(server) as layout:
layout.title.set_text("Anim({{ anime_rate }}) Push Rate {{ fps }}")

with layout.toolbar:
v3.VSelect(
v_model=("mode", "stop"),
items=("modes", ["stop", "animate", "update"]),
hide_details=True,
)
v3.VSlider(
v_model=("fps", 30),
min=10,
max=120,
step=5,
hide_details=True,
classes="mx-4",
style="max-width: 200px;",
)
v3.VSlider(
v_model=("anime_rate", 30),
min=10,
max=120,
step=5,
hide_details=True,
classes="mx-4",
style="max-width: 200px;",
)

with layout.content:
with v3.VContainer(
fluid=True,
classes="pa-0 fill-height",
):
view = vtk_widgets.VtkRemoteView(
renderWindow, interactive_quality=80, interactive_ratio=1
)
ctrl.view_update = view.update
ctrl.view_start_animation = view.start_animation
ctrl.view_stop_animation = view.stop_animation

if __name__ == "__main__":
server.start()
84 changes: 84 additions & 0 deletions examples/validation/AnimationPV.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import asyncio

from trame.app import get_server, asynchronous
from trame.ui.vuetify3 import SinglePageLayout
from trame.widgets import vuetify3 as v3, paraview as pv_widgets

from paraview import simple

server = get_server(client_type="vue3")
state, ctrl = server.state, server.controller

c = simple.Cone()
r = simple.Show()
v = simple.Render()


async def animate():
while True:
await asyncio.sleep(1.0 / state.anime_rate)
if state.mode == "animate":
camera = v.GetActiveCamera()
camera.Azimuth(1)
pos = camera.GetPosition()
v.CameraPosition = pos
if state.mode == "update":
camera = v.GetActiveCamera()
camera.Azimuth(1)
pos = camera.GetPosition()
v.CameraPosition = pos
ctrl.view_update()


@state.change("mode")
def on_animation_change(mode, **_):
if mode == "animate":
ctrl.view_start_animation(state.fps, 60, 1)
else:
ctrl.view_stop_animation()


asynchronous.create_task(animate())

with SinglePageLayout(server) as layout:
layout.title.set_text("Anim({{ anime_rate }}) Push Rate {{ fps }}")

with layout.toolbar:
v3.VSelect(
v_model=("mode", "stop"),
items=("modes", ["stop", "animate", "update"]),
hide_details=True,
)
v3.VSlider(
v_model=("fps", 30),
min=10,
max=120,
step=5,
hide_details=True,
classes="mx-4",
style="max-width: 200px;",
)
v3.VSlider(
v_model=("anime_rate", 30),
min=10,
max=120,
step=5,
hide_details=True,
classes="mx-4",
style="max-width: 200px;",
)

with layout.content:
with v3.VContainer(
fluid=True,
classes="pa-0 fill-height",
):
view = pv_widgets.VtkRemoteView(
v, interactive_quality=80, interactive_ratio=1
)
ctrl.view_update = view.update
ctrl.view_start_animation = view.start_animation
ctrl.view_stop_animation = view.stop_animation

if __name__ == "__main__":
server.start()
25 changes: 25 additions & 0 deletions trame_vtk/modules/paraview/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,31 @@ def push_image(self, view_proxy, reset_camera=False):
"viewport.image.push", {"view": self.id(view_proxy)}
)

def get_current_image_quality(self, render_window):
return self._trame_server.protocol_call(
"viewport.image.push.quality.get", self.id(render_window)
)

def set_image_quality(self, render_window, quality, ratio):
self._trame_server.protocol_call(
"viewport.image.push.quality",
self.id(render_window),
quality,
ratio,
)

def start_animation(self, render_window, fps=30, quality=100, ratio=1):
self._trame_server.protocol_call("viewport.image.animation.fps.max", fps)
self.set_image_quality(render_window, quality, ratio)
self._trame_server.protocol_call(
"viewport.image.animation.start", self.id(render_window)
)

def stop_animation(self, render_window):
self._trame_server.protocol_call(
"viewport.image.animation.stop", self.id(render_window)
)

def camera(self, view_proxy):
view_proxy.UpdatePropertyInformation()
return {
Expand Down
14 changes: 14 additions & 0 deletions trame_vtk/modules/paraview/protocols/publish_image_delivery.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,20 @@ def remove_render_observer(self, view_id):

return {"result": "success"}

@export_rpc("viewport.image.push.quality.get")
def get_view_quality(self, view_id):
response = dict(quality=1, ratio=1)
s_view = self.get_view(view_id)

if s_view:
real_view_id = s_view.GetGlobalIDAsString()
if real_view_id in self.tracking_views:
observer_info = self.tracking_views[real_view_id]
response["quality"] = observer_info.get("quality", 100)
response["ratio"] = observer_info.get("ratio", 1)

return response

@export_rpc("viewport.image.push.quality")
def set_view_quality(self, view_id, quality, ratio=1, update_linked_view=True):
s_view = self.get_view(view_id)
Expand Down
25 changes: 25 additions & 0 deletions trame_vtk/modules/vtk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,31 @@ def push_image(self, render_window, reset_camera=False):
"viewport.image.push", {"view": self.id(render_window)}
)

def get_current_image_quality(self, render_window):
return self._trame_server.protocol_call(
"viewport.image.push.quality.get", self.id(render_window)
)

def set_image_quality(self, render_window, quality, ratio):
self._trame_server.protocol_call(
"viewport.image.push.quality",
self.id(render_window),
quality,
ratio,
)

def start_animation(self, render_window, fps=30, quality=100, ratio=1):
self._trame_server.protocol_call("viewport.image.animation.fps.max", fps)
self.set_image_quality(render_window, quality, ratio)
self._trame_server.protocol_call(
"viewport.image.animation.start", self.id(render_window)
)

def stop_animation(self, render_window):
self._trame_server.protocol_call(
"viewport.image.animation.stop", self.id(render_window)
)

def camera(self, render_window):
camera = render_window.GetRenderers().GetFirstRenderer().GetActiveCamera()
return {
Expand Down
15 changes: 15 additions & 0 deletions trame_vtk/modules/vtk/protocols/publish_image_delivery.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ def animate(self):

next_animate_time = time.time() + 1.0 / self.target_frame_rate
for v_id in self.views_in_animations:
self.tracking_views[v_id]["mtime"] = 0
self.push_render(v_id, True)

next_animate_time -= time.time()
Expand Down Expand Up @@ -273,6 +274,20 @@ def remove_render_observer(self, view_id):

return {"result": "success"}

@export_rpc("viewport.image.push.quality.get")
def get_view_quality(self, view_id):
response = dict(quality=1, ratio=1)
s_view = self.get_view(view_id)

if s_view:
real_view_id = str(self.get_global_id(s_view))
if real_view_id in self.tracking_views:
observer_info = self.tracking_views[real_view_id]
response["quality"] = observer_info.get("quality", 100)
response["ratio"] = observer_info.get("ratio", 1)

return response

@export_rpc("viewport.image.push.quality")
def set_view_quality(self, view_id, quality, ratio=1):
s_view = self.get_view(view_id)
Expand Down
19 changes: 19 additions & 0 deletions trame_vtk/widgets/vtk/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,8 @@ class VtkRemoteView(HtmlElement):

def __init__(self, view, ref=None, **kwargs):
super().__init__("vtk-remote-view", **kwargs)
self._is_animating = False
self._img_quality = None
self._helper = activate_module_for(None, self.server, view)
self._helper.has_capabilities("web", "rendering")

Expand Down Expand Up @@ -752,6 +754,23 @@ def update(self, **kwargs):
"""
self._helper.push_image(self.__view)

def start_animation(self, fps=30, quality=100, ratio=1):
if self._is_animating:
return

self._is_animating = True
self._img_quality = self._helper.get_current_image_quality(self.__view)
self._helper.start_animation(self.__view, fps, quality, ratio)

def stop_animation(self):
if not self._is_animating:
return

self._is_animating = False
self._helper.set_image_quality(self.__view, **self._img_quality)
self._helper.stop_animation(self.__view)
self.update()

def reset_camera(self, **kwargs):
self.server.js_call(ref=self.__ref, method="resetCamera")

Expand Down

0 comments on commit 660a1d4

Please sign in to comment.