diff --git a/examples/01_vtk/vtk_cone_simple_touch.py b/examples/01_vtk/vtk_cone_simple_touch.py new file mode 100644 index 0000000..324c0f6 --- /dev/null +++ b/examples/01_vtk/vtk_cone_simple_touch.py @@ -0,0 +1,109 @@ +import vtkmodules.vtkRenderingOpenGL2 # noqa +from vtkmodules.vtkFiltersSources import vtkConeSource + +# Required for interactor initialization +from vtkmodules.vtkInteractionStyle import vtkInteractorStyleSwitch # noqa +from vtkmodules.vtkRenderingCore import ( + vtkActor, + vtkPolyDataMapper, + vtkRenderer, + vtkRenderWindow, + vtkRenderWindowInteractor, +) + +from trame.app import TrameApp +from trame.app.testing import enable_testing +from trame.decorators import change +from trame.ui.vuetify3 import SinglePageLayout +from trame.widgets import vuetify3 as v3 + +# use this import path to allow -e install for dev +from trame_rca.widgets import rca + +v3.enable_lab() + +DEFAULT_RESOLUTION = 6 + + +class ConeApp(TrameApp): + def __init__(self, server=None): + super().__init__(server) + + self.server.cli.add_argument("--encoder", default="turbo-jpeg") # jpeg + args, _ = self.server.cli.parse_known_args() + self.state.encoder = args.encoder + self.render_window, self.renderer, self.cone_source = self.setup_vtk() + self.build_ui() + + def setup_vtk(self): + renderer = vtkRenderer() + renderWindow = vtkRenderWindow() + renderWindow.AddRenderer(renderer) + + renderWindowInteractor = vtkRenderWindowInteractor() + renderWindowInteractor.SetRenderWindow(renderWindow) + renderWindowInteractor.GetInteractorStyle().SetCurrentStyleToMultiTouchCamera() + + cone_source = vtkConeSource() + mapper = vtkPolyDataMapper() + mapper.SetInputConnection(cone_source.GetOutputPort()) + actor = vtkActor() + actor.SetMapper(mapper) + + renderer.AddActor(actor) + renderer.ResetCamera() + + return renderWindow, renderer, cone_source + + def build_ui(self): + with SinglePageLayout(self.server, full_height=True) as self.ui: + self.ui.title.set_text("RCA rendering") + + with self.ui.toolbar: + v3.VSpacer() + v3.VSlider( + v_model=("resolution", DEFAULT_RESOLUTION), + min=3, + max=60, + step=1, + hide_details=True, + density="compact", + style="max-width: 300px", + ) + + v3.VBtn(icon="mdi-undo-variant", click=self.update_reset_resolution) + + with self.ui.content: + with v3.VContainer( + fluid=True, + classes="pa-0 fill-height position-relative", + ): + view = rca.RemoteControlledArea(display="image") + self.view_handler = view.create_view_handler( + self.render_window, + encoder=self.state.encoder, + ) + + self.ui.icon.click = self.reset_camera + + @change("resolution") + def update_cone(self, resolution, **kwargs): + self.cone_source.SetResolution(resolution) + self.view_handler.update() + + def update_reset_resolution(self): + self.state.resolution = DEFAULT_RESOLUTION + + def reset_camera(self): + # FIXME may need to reset center of roll/zoom/... + self.renderer.ResetCamera() + self.view_handler.update() + + +# ----------------------------------------------------------------------------- +# Main +# ----------------------------------------------------------------------------- +if __name__ == "__main__": + app = ConeApp() + enable_testing(app.server) + app.server.start() diff --git a/vue-components/src/utils/interactorStyle.js b/vue-components/src/utils/interactorStyle.js index c76bace..99c9b72 100644 --- a/vue-components/src/utils/interactorStyle.js +++ b/vue-components/src/utils/interactorStyle.js @@ -199,23 +199,29 @@ function vtkInteractorStyleRemoteMouse(publicAPI, model) { publicAPI.handleStartPinch = (callData) => { publicAPI.startDolly(); - const { scale } = callData; + const { scale: factor, touches: positions } = callData; // model._interactor.requestAnimation(publicAPI.handleStartPinch); publicAPI.invokeStartInteractionEvent(START_INTERACTION_EVENT); publicAPI.invokeRemoteGestureEvent({ type: 'StartPinch', - scale, + factor, + positions, ...model.remoteEventAddOn, }); }; //---------------------------------------------------------------------------- - + let lastPinchEvent = {}; publicAPI.handlePinch = (callData) => { - const { scale } = callData; + const { scale: factor, touches: positions } = callData; + lastPinchEvent = { + factor, + positions, + }; publicAPI.invokeRemoteGestureEvent({ type: 'Pinch', - scale, + factor, + positions, ...model.remoteEventAddOn, }); }; @@ -226,6 +232,7 @@ function vtkInteractorStyleRemoteMouse(publicAPI, model) { publicAPI.endDolly(); publicAPI.invokeRemoteGestureEvent({ type: 'EndPinch', + ...lastPinchEvent, ...model.remoteEventAddOn, }); // model._interactor.cancelAnimation(publicAPI.handleStartPinch); @@ -236,12 +243,13 @@ function vtkInteractorStyleRemoteMouse(publicAPI, model) { publicAPI.handleStartRotate = (callData) => { publicAPI.startRotate(); - const { rotation } = callData; + const { rotation, touches: positions } = callData; // model._interactor.requestAnimation(publicAPI.handleStartRotate); publicAPI.invokeStartInteractionEvent(START_INTERACTION_EVENT); publicAPI.invokeRemoteGestureEvent({ type: 'StartRotate', rotation, + positions, ...model.remoteEventAddOn, }); }; @@ -249,10 +257,15 @@ function vtkInteractorStyleRemoteMouse(publicAPI, model) { //---------------------------------------------------------------------------- publicAPI.handleRotate = (callData) => { - const { rotation } = callData; + const { rotation, touches: positions } = callData; + model.lastRotateEvent = { + rotation, + positions, + }; publicAPI.invokeRemoteGestureEvent({ type: 'Rotate', rotation, + positions, ...model.remoteEventAddOn, }); }; @@ -263,6 +276,7 @@ function vtkInteractorStyleRemoteMouse(publicAPI, model) { publicAPI.endRotate(); publicAPI.invokeRemoteGestureEvent({ type: 'EndRotate', + ...model.lastRotateEvent, ...model.remoteEventAddOn, }); // model._interactor.cancelAnimation(publicAPI.handleStartRotate); @@ -273,12 +287,13 @@ function vtkInteractorStyleRemoteMouse(publicAPI, model) { publicAPI.handleStartPan = (callData) => { publicAPI.startPan(); - const { translation } = callData; + const { translation, touches: positions } = callData; // model._interactor.requestAnimation(publicAPI.handleStartPan); publicAPI.invokeStartInteractionEvent(START_INTERACTION_EVENT); publicAPI.invokeRemoteGestureEvent({ type: 'StartPan', translation, + positions, ...model.remoteEventAddOn, }); }; @@ -286,10 +301,15 @@ function vtkInteractorStyleRemoteMouse(publicAPI, model) { //---------------------------------------------------------------------------- publicAPI.handlePan = (callData) => { - const { translation } = callData; + const { translation, touches: positions } = callData; + model.lastPanEvent = { + translation, + positions, + }; publicAPI.invokeRemoteGestureEvent({ type: 'Pan', translation, + positions, ...model.remoteEventAddOn, }); }; @@ -300,6 +320,7 @@ function vtkInteractorStyleRemoteMouse(publicAPI, model) { publicAPI.endPan(); publicAPI.invokeRemoteGestureEvent({ type: 'EndPan', + ...model.lastPanEvent, ...model.remoteEventAddOn, }); // model._interactor.cancelAnimation(publicAPI.handleStartPan);