RenderWindowInteractor

Introduction

vtkRenderWindowInteractor provides an interaction mechanism for
mouse/key/time events. It handles routing of mouse/key/timer messages to
vtkInteractorObserver and its subclasses. vtkRenderWindowInteractor also
provides controls for picking, rendering frame rate.

vtkRenderWindowInteractor serves to hold user preferences and route messages
to vtkInteractorStyle. Callbacks are available for many events. Platform
specific subclasses should provide methods for manipulating timers,
TerminateApp, and an event loop if required via

Initialize/Start/Enable/Disable.

Caveats

vtkRenderWindowInteractor routes events through VTK’s command/observer design
pattern. That is, when vtkRenderWindowInteractor (actually, one of its
subclasses) sees an event, it translates it into a VTK event using the
InvokeEvent() method. Afterward, any vtkInteractorObservers registered for
that event are expected to respond appropriately.

Methods

animationEvent

Argument Type Required Description
args Yes

button3DEvent

Argument Type Required Description
args Yes

cancelAnimation

Argument Type Required Description
requestor Yes
skipWarning Boolean No

delete

Stop animating if the renderWindowInteractor is deleted.

disable

enable

Enable/Disable interactions.
By default interactors are enabled when initialized.
Initialize() must be called prior to enabling/disabling interaction.
These methods are used when a window/widget is being shared by multiple renderers and interactors.
This allows a “modal” display where one interactor is active when its data is to be displayed and all other interactors associated with the widget are disabled when their data is not displayed.

endAnimationEvent

Argument Type Required Description
args Yes

endInteractionEvent

Argument Type Required Description
args Yes

endMouseMoveEvent

Argument Type Required Description
args Yes

endMouseWheelEvent

Argument Type Required Description
args Yes

endPanEvent

Argument Type Required Description
args Yes

endPinchEvent

Argument Type Required Description
args Yes

endPointerLockEvent

Argument Type Required Description
args Yes

endRotateEvent

Argument Type Required Description
args Yes

exitPointerLock

extend

Method use to decorate a given object (publicAPI+model) with vtkRenderWindowInteractor characteristics.

Argument Type Required Description
publicAPI Yes object on which methods will be bounds (public)
model Yes object on which data structure will be bounds (protected)
initialValues IRenderWindowInteractorInitialValues No (default: {})

findPokedRenderer

Argument Type Required Description
x Number Yes
y Number Yes

getContainer

getCurrentRenderer

getDesiredUpdateRate

getEnableRender

getEnabled

getFirstRenderer

getInitialized

getInteractorStyle

getLastFrameTime

getLightFollowCamera

getMouseScrollDebounceByPass

getPicker

getPreventDefaultOnPointerDown

getPreventDefaultOnPointerUp

getRecognizeGestures

getStillUpdateRate

getView

handleAnimation

handleKeyDown

Argument Type Required Description
event KeyboardEvent Yes

handleKeyPress

Argument Type Required Description
event KeyboardEvent Yes

handleKeyUp

Argument Type Required Description
event KeyboardEvent Yes

handleMouseDown

Argument Type Required Description
event PointerEvent Yes

handleMouseMove

Argument Type Required Description
event PointerEvent Yes

handleMouseUp

Argument Type Required Description
event PointerEvent Yes

handlePointerCancel

Argument Type Required Description
event PointerEvent Yes

handlePointerDown

Argument Type Required Description
event PointerEvent Yes

handlePointerEnter

Argument Type Required Description
event PointerEvent Yes

handlePointerLeave

Argument Type Required Description
event PointerEvent Yes

handlePointerLockChange

handlePointerMove

Argument Type Required Description
event PointerEvent Yes

handlePointerUp

Argument Type Required Description
event PointerEvent Yes

handleTouchEnd

Argument Type Required Description
event PointerEvent Yes

handleTouchMove

Argument Type Required Description
event PointerEvent Yes

handleTouchStart

Argument Type Required Description
event PointerEvent Yes

handleVisibilityChange

handleWheel

Argument Type Required Description
event MouseEvent Yes

initialize


interactionEvent

Argument Type Required Description
args Yes

invokeAnimation

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeButton3D

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeEndAnimation

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeEndInteractionEvent

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeEndMouseMove

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeEndMouseWheel

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeEndPan

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeEndPinch

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeEndPointerLock

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeEndRotate

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeInteractionEvent

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeKeyDown

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeKeyPress

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeKeyUp

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeLeftButtonPress

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeLeftButtonRelease

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeMiddleButtonPress

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeMiddleButtonRelease

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeMouseEnter

invokeMouseLeave

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeMouseMove

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeMouseWheel

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeMove3D

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokePan

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokePinch

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokePointerEnter

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokePointerLeave

invokeRenderEvent

invokeRightButtonPress

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeRightButtonRelease

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeRotate

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeStartAnimation

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeStartInteractionEvent

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeStartMouseMove

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeStartMouseWheel

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeStartPan

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeStartPinch

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeStartPointerLock

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

invokeStartRotate

Argument Type Required Description
callData IRenderWindowInteractorEvent Yes

isAnimating

isPointerLocked

keyDownEvent

Argument Type Required Description
args Yes

keyPressEvent

Argument Type Required Description
args Yes

keyUpEvent

Argument Type Required Description
args Yes

leftButtonPressEvent

Argument Type Required Description
args Yes

leftButtonReleaseEvent

Argument Type Required Description
args Yes

middleButtonPressEvent

Argument Type Required Description
args Yes

middleButtonReleaseEvent

Argument Type Required Description
args Yes

mouseEnterEvent

Argument Type Required Description
args Yes

mouseLeaveEvent

Argument Type Required Description
args Yes

mouseMoveEvent

Argument Type Required Description
args Yes

mouseWheelEvent

Argument Type Required Description
args Yes

move3DEvent

Argument Type Required Description
args Yes

newInstance

Method use to create a new instance of vtkRenderWindowInteractor

onAnimation

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onButton3D

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onEndAnimation

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onEndInteractionEvent

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onEndMouseMove

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onEndMouseWheel

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onEndPan

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onEndPinch

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onEndPointerLock

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onEndRotate

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onInteractionEvent

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onKeyDown

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onKeyPress

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onKeyUp

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onLeftButtonPress

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onLeftButtonRelease

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onMiddleButtonPress

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onMiddleButtonRelease

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onMouseEnter

Argument Type Required Description
cb Yes The callback to be called

onMouseLeave

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onMouseMove

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onMouseWheel

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onMove3D

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onPan

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onPinch

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onPointerEnter

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onPointerLeave

Argument Type Required Description
cb Yes The callback to be called

onRenderEvent

Argument Type Required Description
cb Function Yes The callback to be called.
priority Number No The priority of the event.

onRightButtonPress

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onRightButtonRelease

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onRotate

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onStartAnimation

Argument Type Required Description
cb Yes The callback to be called

onStartInteractionEvent

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onStartMouseMove

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onStartMouseWheel

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onStartPan

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onStartPinch

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onStartPointerLock

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

onStartRotate

Argument Type Required Description
cb InteractorEventCallback Yes The callback to be called.
priority Number No The priority of the event.

panEvent

Argument Type Required Description
args Yes

pinchEvent

Argument Type Required Description
args Yes

recognizeGesture

we know we are in multitouch now, so start recognizing

Argument Type Required Description
event Yes
positions Yes

render

only render if we are not animating. If we are animating
then renders will happen naturally anyhow and we definitely
do not want extra renders as the make the apparent interaction
rate slower.

requestAnimation

Argument Type Required Description
requestor Yes

requestPointerLock

returnFromVRAnimation

rightButtonPressEvent

Argument Type Required Description
args Yes

rightButtonReleaseEvent

Argument Type Required Description
args Yes

rotateEvent

Argument Type Required Description
args Yes

setContainer

Add an HTMLElement as the new container for the interactor.
All events will be bound to this new container.
Any old container will be removed along with its listeners.

setCurrentRenderer

Manually sets the current renderer.

Argument Type Required Description
ren vtkRenderer Yes

setDesiredUpdateRate

Set the desired update rate.

Argument Type Required Description
desiredUpdateRate Yes

setInteractorStyle

External switching between joystick/trackball/new? modes.

Argument Type Required Description
style Yes

setLightFollowCamera

Turn on/off the automatic repositioning of lights as the camera moves.

Argument Type Required Description
lightFollowCamera Yes

setMouseScrollDebounceByPass

Allow system to bypass scrolling debounce. This function must be called to allow the debounce to be bypassed

Argument Type Required Description
mouseScrollDebounceByPass Yes

setPicker

Set the object used to perform pick operations.

Argument Type Required Description
picker Yes

setPreventDefaultOnPointerDown

Set whether preventDefault is called on pointer down.

Argument Type Required Description
preventDefault Boolean Yes

setPreventDefaultOnPointerUp

Set whether preventDefault is called on pointer up.

If pointerup occurs without a preceeding pointerdown, then
this does nothing.

Argument Type Required Description
preventDefault Boolean Yes

setRecognizeGestures

Argument Type Required Description
recognizeGestures Yes

setRenderWindow

Set/Get the rendering window being controlled by this object.

Argument Type Required Description
aren Yes

setStillUpdateRate

Set the desired update rate when movement has stopped.

Argument Type Required Description
stillUpdateRate Yes

setView

Argument Type Required Description
val Yes

start

Start the event loop.
This is provided so that you do not have to implement your own event loop.
You still can use your own event loop if you want.

startAnimationEvent

Argument Type Required Description
args Yes

startEventLoop

startInteractionEvent

Argument Type Required Description
args Yes

startMouseMoveEvent

Argument Type Required Description
args Yes

startMouseWheelEvent

Argument Type Required Description
args Yes

startPanEvent

Argument Type Required Description
args Yes

startPinchEvent

Argument Type Required Description
args Yes

startPointerLockEvent

Argument Type Required Description
args Yes

startRotateEvent

Argument Type Required Description
args Yes

switchToVRAnimation

updateGamepads

Argument Type Required Description
displayId Number Yes The ID of the display.

Source

Constants.d.ts
export declare enum Device {
Unknown = 0,
LeftController = 1,
RightController = 2,
}

export declare enum Input {
Unknown = 0,
Trigger = 1,
TrackPad = 2,
Grip = 3,
Thumbstick = 4,
A = 5,
B = 6,
ApplicationMenu = 7, // Not exposed in WebXR API
}

export declare enum Axis {
Unknown = 0,
TouchpadX = 1,
TouchpadY = 2,
ThumbstickX = 3,
ThumbstickY = 4,
}

declare const _default: {
Device: typeof Device;
Input: typeof Input;
Axis: typeof Axis;
};
export default _default;
Constants.js
export const Device = {
Unknown: 0,
LeftController: 1,
RightController: 2,
};

export const Input = {
Unknown: 0,
Trigger: 1,
TrackPad: 2,
Grip: 3,
Thumbstick: 4,
A: 5,
B: 6,
ApplicationMenu: 7, // Not exposed in WebXR API
};

export const Axis = {
Unknown: 0,
TouchpadX: 1,
TouchpadY: 2,
ThumbstickX: 3,
ThumbstickY: 4,
};

export default {
Device,
Input,
Axis,
};
index.d.ts
import { vtkObject, vtkSubscription } from "../../../interfaces";
import { Nullable } from "../../../types";
import vtkRenderer from "../Renderer";
import { Axis, Device, Input } from "./Constants";

declare enum handledEvents {
'StartAnimation',
'Animation',
'EndAnimation',
'MouseEnter',
'MouseLeave',
'StartMouseMove',
'MouseMove',
'EndMouseMove',
'LeftButtonPress',
'LeftButtonRelease',
'MiddleButtonPress',
'MiddleButtonRelease',
'RightButtonPress',
'RightButtonRelease',
'KeyPress',
'KeyDown',
'KeyUp',
'StartMouseWheel',
'MouseWheel',
'EndMouseWheel',
'StartPinch',
'Pinch',
'EndPinch',
'StartPan',
'Pan',
'EndPan',
'StartRotate',
'Rotate',
'EndRotate',
'Button3D',
'Move3D',
'StartPointerLock',
'EndPointerLock',
'StartInteraction',
'Interaction',
'EndInteraction',
'AnimationFrameRateUpdate',
}

/**
*
*/
export interface IRenderWindowInteractorInitialValues {
initialized?: boolean;
enabled?: boolean;
enableRender?: boolean;
lightFollowCamera?: boolean;
desiredUpdateRate?: number;
stillUpdateRate?: number;
recognizeGestures?: boolean;
currentGesture?: string;
lastFrameTime?: number;
wheelTimeoutID?: number;
moveTimeoutID?: number;
preventDefaultOnPointerDown?: boolean;
preventDefaultOnPointerUp?: boolean;
mouseScrollDebounceByPass?: boolean;
}

interface IPosition {
type: string;
}

export type InteractorEventCallback = (e: IRenderWindowInteractorEvent) => void

export type InteractorEventType = "StartInteractionEvent" | "InteractionEvent" | "EndInteractionEvent"

export interface IRenderWindowInteractorEvent {
altKey: boolean;
controlKey: boolean;
firstRenderer: vtkRenderer;
pokedRenderer: vtkRenderer;
position: { x: number; y: number; z: number };
shiftKey: boolean;
type: InteractorEventType;
}

export interface vtkRenderWindowInteractor extends vtkObject {

/**
*
* @default false
*/
getInitialized(): boolean;

/**
*
* @default null
*/
getContainer(): Nullable<HTMLElement>;

/**
*
* @default false
*/

getEnabled(): boolean;

/**
*
* @default true
*/
getEnableRender(): boolean;

/**
*
* @default null
*/
getInteractorStyle(): any;

/**
*
* @default 0.1
*/
getLastFrameTime(): number;

/**
*
* @default null
*/
getView(): any;

/**
*
* @default true
*/
getLightFollowCamera(): boolean;

/**
*
*/
getPicker(): any;

/**
*
* @default true
*/
getRecognizeGestures(): boolean;

/**
*
* @default 30.0
*/
getDesiredUpdateRate(): number;

/**
*
* @default 2.0
*/
getStillUpdateRate(): number;

/**
* @default false
*/
getPreventDefaultOnPointerDown(): boolean;

/**
* @default false
*/
getPreventDefaultOnPointerUp(): boolean;

/**
* @default false
*/
getMouseScrollDebounceByPass(): boolean;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeStartAnimation(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeAnimation(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeEndAnimation(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokePointerEnter(callData: IRenderWindowInteractorEvent): void;

/**
*
*/
invokePointerLeave(callData: IRenderWindowInteractorEvent): void;

/**
*
*/
invokeMouseEnter(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeMouseLeave(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeStartMouseMove(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeMouseMove(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeEndMouseMove(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeLeftButtonPress(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeLeftButtonRelease(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeMiddleButtonPress(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeMiddleButtonRelease(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeRightButtonPress(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeRightButtonRelease(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeKeyPress(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeKeyDown(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeKeyUp(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeStartMouseWheel(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeMouseWheel(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeEndMouseWheel(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeStartPinch(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokePinch(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeEndPinch(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeStartPan(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokePan(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeEndPan(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeStartRotate(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeRotate(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeEndRotate(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeButton3D(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeMove3D(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeStartPointerLock(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeEndPointerLock(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeStartInteractionEvent(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeInteractionEvent(callData: IRenderWindowInteractorEvent): void;

/**
*
* @param {IRenderWindowInteractorEvent} callData
*/
invokeEndInteractionEvent(callData: IRenderWindowInteractorEvent): void;

/**
*
*/
invokeRenderEvent(): void;

/**
*
* @param cb The callback to be called
*/
onStartAnimation(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onAnimation(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onEndAnimation(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onPointerEnter(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param cb The callback to be called
*/
onPointerLeave(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param cb The callback to be called
*/
onMouseEnter(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onMouseLeave(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onStartMouseMove(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onMouseMove(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onEndMouseMove(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onLeftButtonPress(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onLeftButtonRelease(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onMiddleButtonPress(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onMiddleButtonRelease(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onRightButtonPress(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onRightButtonRelease(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onKeyPress(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onKeyDown(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onKeyUp(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onStartMouseWheel(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onMouseWheel(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onEndMouseWheel(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onStartPinch(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onPinch(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onEndPinch(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onStartPan(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onPan(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onEndPan(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onStartRotate(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onRotate(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onEndRotate(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onButton3D(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onMove3D(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onStartPointerLock(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onEndPointerLock(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onStartInteractionEvent(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onInteractionEvent(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {InteractorEventCallback} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onEndInteractionEvent(cb: InteractorEventCallback, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param {Function} cb The callback to be called.
* @param {Number} [priority] The priority of the event.
*/
onRenderEvent(cb: () => void, priority?: number): Readonly<vtkSubscription>;

/**
*
* @param args
*/
animationEvent(args: any): any;

/**
*
* @param args
*/
button3DEvent(args: any): any;

/**
*
* @param args
*/
endAnimationEvent(args: any): any;

/**
*
* @param args
*/
endInteractionEvent(args: any): any;

/**
*
* @param args
*/
endMouseMoveEvent(args: any): any;

/**
*
* @param args
*/
endMouseWheelEvent(args: any): any;

/**
*
* @param args
*/
endPanEvent(args: any): any;

/**
*
* @param args
*/
endPinchEvent(args: any): any;

/**
*
* @param args
*/
endPointerLockEvent(args: any): any;

/**
*
* @param args
*/
endRotateEvent(args: any): any;

/**
*
* @param args
*/
interactionEvent(args: any): any;

/**
*
* @param args
*/
keyDownEvent(args: any): any;

/**
*
* @param args
*/
keyPressEvent(args: any): any;

/**
*
* @param args
*/
keyUpEvent(args: any): any;

/**
*
* @param args
*/
leftButtonPressEvent(args: any): any;

/**
*
* @param args
*/
leftButtonReleaseEvent(args: any): any;

/**
*
* @param args
*/
middleButtonPressEvent(args: any): any;

/**
*
* @param args
*/
middleButtonReleaseEvent(args: any): any;

/**
*
* @param args
*/
mouseEnterEvent(args: any): any;

/**
*
* @param args
*/
mouseLeaveEvent(args: any): any;

/**
*
* @param args
*/
mouseMoveEvent(args: any): any;

/**
*
* @param args
*/
mouseWheelEvent(args: any): any;

/**
*
* @param args
*/
move3DEvent(args: any): any;

/**
*
* @param args
*/
panEvent(args: any): any;

/**
*
* @param args
*/
pinchEvent(args: any): any;

/**
*
* @param args
*/
rightButtonPressEvent(args: any): any;

/**
*
* @param args
*/
rightButtonReleaseEvent(args: any): any;

/**
*
* @param args
*/
rotateEvent(args: any): any;

/**
* Add an HTMLElement as the new container for the interactor.
* All events will be bound to this new container.
* Any old container will be removed along with its listeners.
*/
setContainer(container: Nullable<HTMLElement>): boolean;

/**
* Turn on/off the automatic repositioning of lights as the camera moves.
* @param lightFollowCamera
*/
setLightFollowCamera(lightFollowCamera: boolean): boolean;

/**
* Set the object used to perform pick operations.
* @param picker
*/
setPicker(picker: any): boolean;

/**
* Set whether preventDefault is called on pointer down.
* @param {Boolean} preventDefault
*/
setPreventDefaultOnPointerDown(preventDefault: boolean): boolean;

/**
* Set whether preventDefault is called on pointer up.
*
* If pointerup occurs without a preceeding pointerdown, then
* this does nothing.
*
* @param {Boolean} preventDefault
*/
setPreventDefaultOnPointerUp(preventDefault: boolean): boolean;

/**
* Allow system to bypass scrolling debounce. This function must be called to allow the debounce to be bypassed
* @param mouseScrollDebounceByPass
*/

setMouseScrollDebounceByPass(mouseScrollDebounceByPass:boolean): boolean;

/**
*
* @param recognizeGestures
*/
setRecognizeGestures(recognizeGestures: boolean): boolean;

/**
* Set the desired update rate.
* @param desiredUpdateRate
*/
setDesiredUpdateRate(desiredUpdateRate: number): boolean;

/**
* Set the desired update rate when movement has stopped.
* @param stillUpdateRate
*/
setStillUpdateRate(stillUpdateRate: number): boolean;

/**
* Start the event loop.
* This is provided so that you do not have to implement your own event loop.
* You still can use your own event loop if you want.
*/
start(): void;

/**
*
* @param args
*/
startAnimationEvent(args: any): any;


/**
*
* @param args
*/
startInteractionEvent(args: any): any;


/**
*
* @param args
*/
startMouseMoveEvent(args: any): any;


/**
*
* @param args
*/
startMouseWheelEvent(args: any): any;


/**
*
* @param args
*/
startPanEvent(args: any): any;


/**
*
* @param args
*/
startPinchEvent(args: any): any;


/**
*
* @param args
*/
startPointerLockEvent(args: any): any;


/**
*
* @param args
*/
startRotateEvent(args: any): any;


/**
* Set/Get the rendering window being controlled by this object.
* @param aren
*/
setRenderWindow(aren: any): void;

/**
* External switching between joystick/trackball/new? modes.
* @param style
*/
setInteractorStyle(style: any): void;

/**
* ---------------------------------------------------------------------
*/
initialize(): void;

/**
* Enable/Disable interactions.
* By default interactors are enabled when initialized.
* Initialize() must be called prior to enabling/disabling interaction.
* These methods are used when a window/widget is being shared by multiple renderers and interactors.
* This allows a "modal" display where one interactor is active when its data is to be displayed and all other interactors associated with the widget are disabled when their data is not displayed.
*/
enable(): void;

/**
*
*/
disable(): void;

/**
*
*/
startEventLoop(): void;

/**
*
*/
getCurrentRenderer(): void;

/**
* Manually sets the current renderer.
* @param {vtkRenderer} ren
*/
setCurrentRenderer(ren: vtkRenderer): void;

/**
*
* @param container kept for backward compatibility.
* @deprecated please use vtkRenderWindowInteractor.setContainer(container: HTMLElement)
* which will also bind events.
*/
bindEvents(container: any): void;

/**
*
* @deprecated please use vtkRenderWindowInteractor.setContainer(null) instead.
*/
unbindEvents(): void;

/**
*
* @param {KeyboardEvent} event
*/
handleKeyPress(event: KeyboardEvent): void;

/**
*
* @param {KeyboardEvent} event
*/
handleKeyDown(event: KeyboardEvent): void;

/**
*
* @param {KeyboardEvent} event
*/
handleKeyUp(event: KeyboardEvent): void;

/**
*
* @param {PointerEvent} event
*/
handlePointerDown(event: PointerEvent): void;

/**
*
* @param {PointerEvent} event
*/
handlePointerUp(event: PointerEvent): void;

/**
*
* @param {PointerEvent} event
*/
handlePointerCancel(event: PointerEvent): void;

/**
*
* @param {PointerEvent} event
*/
handlePointerMove(event: PointerEvent): void;

/**
*
* @param {PointerEvent} event
*/
handleMouseDown(event: PointerEvent): void;

/**
*
*/
requestPointerLock(): void;

/**
*
*/
exitPointerLock(): void;

/**
*
*/
isPointerLocked(): boolean;

/**
*
*/
handlePointerLockChange(): void;

/**
*
* @param requestor
*/
requestAnimation(requestor: any): void;

/**
*
*/
isAnimating(): boolean;

/**
*
* @param requestor
* @param {Boolean} [skipWarning]
*/
cancelAnimation(requestor: any, skipWarning?: boolean): void;

/**
*
*/
switchToVRAnimation(): void;

/**
*
*/
returnFromVRAnimation(): void;

/**
*
* @param {Number} displayId The ID of the display.
*/
updateGamepads(displayId: number): void;

/**
*
* @param {PointerEvent} event
*/
handleMouseMove(event: PointerEvent): void;

/**
*
*/
handleAnimation(): void;

/**
*
* @param {MouseEvent} event
*/
handleWheel(event: MouseEvent): void;

/**
*
* @param {PointerEvent} event
*/
handlePointerEnter(event: PointerEvent): void;

/**
*
* @param {PointerEvent} event
*/
handlePointerLeave(event: PointerEvent): void;

/**
*
* @param {PointerEvent} event
*/
handleMouseUp(event: PointerEvent): void;

/**
*
* @param {PointerEvent} event
*/
handleTouchStart(event: PointerEvent): void;

/**
*
* @param {PointerEvent} event
*/
handleTouchMove(event: PointerEvent): void;

/**
*
* @param {PointerEvent} event
*/
handleTouchEnd(event: PointerEvent): void;

/**
*
* @param val
*/
setView(val: any): void;

/**
* @return first renderer to be used for camera manipulation
*/
getFirstRenderer(): vtkRenderer;

/**
*
* @param {Number} x
* @param {Number} y
*/
findPokedRenderer(x: number, y: number): Nullable<vtkRenderer>;

/**
* only render if we are not animating. If we are animating
* then renders will happen naturally anyhow and we definitely
* do not want extra renders as the make the apparent interaction
* rate slower.
*/
render(): void;

/**
* we know we are in multitouch now, so start recognizing
* @param event
* @param positions
*/
recognizeGesture(event: 'TouchStart' | 'TouchMove' | 'TouchEnd', positions: IPosition): void;

/**
*
*/
handleVisibilityChange(): void;

/**
* Stop animating if the renderWindowInteractor is deleted.
*/
delete(): void;
}


/**
* Method use to decorate a given object (publicAPI+model) with vtkRenderWindowInteractor characteristics.
*
* @param publicAPI object on which methods will be bounds (public)
* @param model object on which data structure will be bounds (protected)
* @param {IRenderWindowInteractorInitialValues} [initialValues] (default: {})
*/
export function extend(publicAPI: object, model: object, initialValues?: IRenderWindowInteractorInitialValues): void;

/**
* Method use to create a new instance of vtkRenderWindowInteractor
*/
export function newInstance(initialValues?: IRenderWindowInteractorInitialValues): vtkRenderWindowInteractor;

/**
* vtkRenderWindowInteractor provides an interaction mechanism for
* mouse/key/time events. It handles routing of mouse/key/timer messages to
* vtkInteractorObserver and its subclasses. vtkRenderWindowInteractor also
* provides controls for picking, rendering frame rate.
*
* vtkRenderWindowInteractor serves to hold user preferences and route messages
* to vtkInteractorStyle. Callbacks are available for many events. Platform
* specific subclasses should provide methods for manipulating timers,
* TerminateApp, and an event loop if required via
*
* Initialize/Start/Enable/Disable.
*
* ## Caveats
*
* vtkRenderWindowInteractor routes events through VTK’s command/observer design
* pattern. That is, when vtkRenderWindowInteractor (actually, one of its
* subclasses) sees an event, it translates it into a VTK event using the
* InvokeEvent() method. Afterward, any vtkInteractorObservers registered for
* that event are expected to respond appropriately.
*/
export declare const vtkRenderWindowInteractor: {
newInstance: typeof newInstance;
extend: typeof extend;
handledEvents: typeof handledEvents;
Device: typeof Device;
Input: typeof Input;
Axis: typeof Axis;
};
export default vtkRenderWindowInteractor;
index.js
import macro from 'vtk.js/Sources/macros';
import * as vtkMath from 'vtk.js/Sources/Common/Core/Math';

import Constants from 'vtk.js/Sources/Rendering/Core/RenderWindowInteractor/Constants';

const { Device, Input } = Constants;
const { vtkWarningMacro, vtkErrorMacro, normalizeWheel, vtkOnceErrorMacro } =
macro;

// ----------------------------------------------------------------------------
// Global methods
// ----------------------------------------------------------------------------

const EMPTY_MOUSE_EVENT = {
ctrlKey: false,
altKey: false,
shiftKey: false,
};

const deviceInputMap = {
'xr-standard': [
Input.Trigger,
Input.Grip,
Input.TrackPad,
Input.Thumbstick,
Input.A,
Input.B,
],
};

const handledEvents = [
'StartAnimation',
'Animation',
'EndAnimation',
'PointerEnter',
'PointerLeave',
'MouseEnter',
'MouseLeave',
'StartMouseMove',
'MouseMove',
'EndMouseMove',
'LeftButtonPress',
'LeftButtonRelease',
'MiddleButtonPress',
'MiddleButtonRelease',
'RightButtonPress',
'RightButtonRelease',
'KeyPress',
'KeyDown',
'KeyUp',
'StartMouseWheel',
'MouseWheel',
'EndMouseWheel',
'StartPinch',
'Pinch',
'EndPinch',
'StartPan',
'Pan',
'EndPan',
'StartRotate',
'Rotate',
'EndRotate',
'Button3D',
'Move3D',
'StartPointerLock',
'EndPointerLock',
'StartInteraction',
'Interaction',
'EndInteraction',
'AnimationFrameRateUpdate',
];

function preventDefault(event) {
if (event.cancelable) {
event.preventDefault();
}
}

function pointerCacheToPositions(cache) {
const positions = Object.create(null);
cache.forEach(({ pointerId, position }) => {
positions[pointerId] = position;
});
return positions;
}

// ----------------------------------------------------------------------------
// vtkRenderWindowInteractor methods
// ----------------------------------------------------------------------------

function vtkRenderWindowInteractor(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkRenderWindowInteractor');

// Capture "parentClass" api for internal use
const superClass = { ...publicAPI };

// Initialize list of requesters
const animationRequesters = new Set();

// map from pointerId to { pointerId: number, position: [x, y] }
const pointerCache = new Map();

// Factor to apply on wheel spin.
let wheelCoefficient = 1;

// Public API methods

//----------------------------------------------------------------------
publicAPI.start = () => {
// Let the compositing handle the event loop if it wants to.
// if (publicAPI.HasObserver(vtkCommand::StartEvent) && !publicAPI.HandleEventLoop) {
// publicAPI.invokeEvent({ type: 'StartEvent' });
// return;
// }

// As a convenience, initialize if we aren't initialized yet.
if (!model.initialized) {
publicAPI.initialize();
if (!model.initialized) {
return;
}
}
// Pass execution to the subclass which will run the event loop,
// this will not return until TerminateApp is called.
publicAPI.startEventLoop();
};

//----------------------------------------------------------------------
publicAPI.setRenderWindow = (aren) => {
vtkErrorMacro(
'you want to call setView(view) instead of setRenderWindow on a vtk.js interactor'
);
};

//----------------------------------------------------------------------
publicAPI.setInteractorStyle = (style) => {
if (model.interactorStyle !== style) {
if (model.interactorStyle != null) {
model.interactorStyle.setInteractor(null);
}
model.interactorStyle = style;
if (model.interactorStyle != null) {
if (model.interactorStyle.getInteractor() !== publicAPI) {
model.interactorStyle.setInteractor(publicAPI);
}
}
}
};

//---------------------------------------------------------------------
publicAPI.initialize = () => {
model.initialized = true;
publicAPI.enable();
publicAPI.render();
};

publicAPI.enable = () => publicAPI.setEnabled(true);

publicAPI.disable = () => publicAPI.setEnabled(false);

publicAPI.startEventLoop = () => vtkWarningMacro('empty event loop');

function updateCurrentRenderer(x, y) {
if (!model._forcedRenderer) {
model.currentRenderer = publicAPI.findPokedRenderer(x, y);
}
}

publicAPI.getCurrentRenderer = () => {
if (model.currentRenderer) {
return model.currentRenderer;
}
updateCurrentRenderer(0, 0);
return model.currentRenderer;
};

function _getScreenEventPositionFor(source) {
const canvas = model._view.getCanvas();
const bounds = canvas.getBoundingClientRect();
const scaleX = canvas.width / bounds.width;
const scaleY = canvas.height / bounds.height;
const position = {
x: scaleX * (source.clientX - bounds.left),
y: scaleY * (bounds.height - source.clientY + bounds.top),
z: 0,
};

// if multitouch, do not update the current renderer
if (pointerCache.size <= 1 || !model.currentRenderer) {
updateCurrentRenderer(position.x, position.y);
}
return position;
}
// Allow user to override it
const getScreenEventPositionFor =
model._getScreenEventPositionFor || _getScreenEventPositionFor;

function getModifierKeysFor(event) {
return {
controlKey: event.ctrlKey,
altKey: event.altKey,
shiftKey: event.shiftKey,
};
}

function getKeysFor(event) {
const modifierKeys = getModifierKeysFor(event);
const keys = {
key: event.key,
keyCode: event.charCode,
...modifierKeys,
};
return keys;
}

function getDeviceTypeFor(event) {
return event.pointerType || '';
}

const _bindEvents = () => {
if (model.container === null) {
return;
}

const { container } = model;
container.addEventListener('contextmenu', preventDefault);
container.addEventListener('wheel', publicAPI.handleWheel);
container.addEventListener('DOMMouseScroll', publicAPI.handleWheel);
container.addEventListener('pointerenter', publicAPI.handlePointerEnter);
container.addEventListener('pointerleave', publicAPI.handlePointerLeave);
container.addEventListener('pointermove', publicAPI.handlePointerMove, {
passive: false,
});
container.addEventListener('pointerdown', publicAPI.handlePointerDown, {
passive: false,
});
container.addEventListener('pointerup', publicAPI.handlePointerUp);
container.addEventListener('pointercancel', publicAPI.handlePointerCancel);
document.addEventListener('keypress', publicAPI.handleKeyPress);
document.addEventListener('keydown', publicAPI.handleKeyDown);
document.addEventListener('keyup', publicAPI.handleKeyUp);

document.addEventListener(
'pointerlockchange',
publicAPI.handlePointerLockChange
);

// using touchAction is more performant than preventDefault
// in a touchstart handler.
container.style.touchAction = 'none';
container.style.userSelect = 'none';
// disables tap highlight for when cursor is pointer
container.style.webkitTapHighlightColor = 'rgba(0,0,0,0)';
};

// For backward compatibility.
// Necessary for using unbind/bindEvent without calling setContainer.
publicAPI.bindEvents = (container) => {
if (container === null) {
return;
}
const res = superClass.setContainer(container);
if (res) {
_bindEvents();
}
};

const _unbindEvents = () => {
// Clear any previous timeouts and state variables that control mouse / touchpad behavior.
clearTimeout(model.moveTimeoutID);
clearTimeout(model.wheelTimeoutID);
model.moveTimeoutID = 0;
model.wheelTimeoutID = 0;
wheelCoefficient = 1.0;

const { container } = model;
if (container) {
container.removeEventListener('contextmenu', preventDefault);
container.removeEventListener('wheel', publicAPI.handleWheel);
container.removeEventListener('DOMMouseScroll', publicAPI.handleWheel);
container.removeEventListener(
'pointerenter',
publicAPI.handlePointerEnter
);
container.removeEventListener(
'pointerleave',
publicAPI.handlePointerLeave
);
container.removeEventListener(
'pointermove',
publicAPI.handlePointerMove,
{
passive: false,
}
);
container.removeEventListener(
'pointerdown',
publicAPI.handlePointerDown,
{
passive: false,
}
);
container.removeEventListener('pointerup', publicAPI.handlePointerUp);
container.removeEventListener(
'pointercancel',
publicAPI.handlePointerCancel
);
}
document.removeEventListener('keypress', publicAPI.handleKeyPress);
document.removeEventListener('keydown', publicAPI.handleKeyDown);
document.removeEventListener('keyup', publicAPI.handleKeyUp);
document.removeEventListener(
'pointerlockchange',
publicAPI.handlePointerLockChange
);
pointerCache.clear();
};

publicAPI.unbindEvents = () => {
_unbindEvents();
superClass.setContainer(null);
};

publicAPI.handleKeyPress = (event) => {
const data = getKeysFor(event);
publicAPI.keyPressEvent(data);
};

publicAPI.handleKeyDown = (event) => {
const data = getKeysFor(event);
publicAPI.keyDownEvent(data);
};

publicAPI.handleKeyUp = (event) => {
const data = getKeysFor(event);
publicAPI.keyUpEvent(data);
};

publicAPI.handlePointerEnter = (event) => {
const callData = {
...getModifierKeysFor(event),
position: getScreenEventPositionFor(event),
deviceType: getDeviceTypeFor(event),
};
publicAPI.pointerEnterEvent(callData);
if (callData.deviceType === 'mouse') {
publicAPI.mouseEnterEvent(callData);
}
};

publicAPI.handlePointerLeave = (event) => {
const callData = {
...getModifierKeysFor(event),
position: getScreenEventPositionFor(event),
deviceType: getDeviceTypeFor(event),
};
publicAPI.pointerLeaveEvent(callData);
if (callData.deviceType === 'mouse') {
publicAPI.mouseLeaveEvent(callData);
}
};

publicAPI.handlePointerDown = (event) => {
if (event.button > 2 || publicAPI.isPointerLocked()) {
// ignore events from extra mouse buttons such as `back` and `forward`
return;
}
if (model.preventDefaultOnPointerDown) {
preventDefault(event);
}

if (event.target.hasPointerCapture(event.pointerId)) {
event.target.releasePointerCapture(event.pointerId);
}
model.container.setPointerCapture(event.pointerId);

if (pointerCache.has(event.pointerId)) {
vtkWarningMacro('[RenderWindowInteractor] duplicate pointerId detected');
}

pointerCache.set(event.pointerId, {
pointerId: event.pointerId,
position: getScreenEventPositionFor(event),
});

switch (event.pointerType) {
case 'pen':
case 'touch':
publicAPI.handleTouchStart(event);
break;
case 'mouse':
default:
publicAPI.handleMouseDown(event);
break;
}
};

publicAPI.handlePointerUp = (event) => {
if (pointerCache.has(event.pointerId)) {
if (model.preventDefaultOnPointerUp) {
preventDefault(event);
}

pointerCache.delete(event.pointerId);
model.container.releasePointerCapture(event.pointerId);

switch (event.pointerType) {
case 'pen':
case 'touch':
publicAPI.handleTouchEnd(event);
break;
case 'mouse':
default:
publicAPI.handleMouseUp(event);
break;
}
}
};

publicAPI.handlePointerCancel = (event) => {
if (pointerCache.has(event.pointerId)) {
pointerCache.delete(event.pointerId);

switch (event.pointerType) {
case 'pen':
case 'touch':
publicAPI.handleTouchEnd(event);
break;
case 'mouse':
default:
publicAPI.handleMouseUp(event);
break;
}
}
};

publicAPI.handlePointerMove = (event) => {
if (pointerCache.has(event.pointerId)) {
const pointer = pointerCache.get(event.pointerId);
pointer.position = getScreenEventPositionFor(event);
}

switch (event.pointerType) {
case 'pen':
case 'touch':
publicAPI.handleTouchMove(event);
break;
case 'mouse':
default:
publicAPI.handleMouseMove(event);
break;
}
};

publicAPI.handleMouseDown = (event) => {
const callData = {
...getModifierKeysFor(event),
position: getScreenEventPositionFor(event),
deviceType: getDeviceTypeFor(event),
};
switch (event.button) {
case 0:
publicAPI.leftButtonPressEvent(callData);
break;
case 1:
publicAPI.middleButtonPressEvent(callData);
break;
case 2:
publicAPI.rightButtonPressEvent(callData);
break;
default:
vtkErrorMacro(`Unknown mouse button pressed: ${event.button}`);
break;
}
};

//----------------------------------------------------------------------
publicAPI.requestPointerLock = () => {
if (model.container) {
model.container.requestPointerLock();
}
};

//----------------------------------------------------------------------
publicAPI.exitPointerLock = () => document.exitPointerLock?.();

//----------------------------------------------------------------------
publicAPI.isPointerLocked = () =>
!!model.container && document.pointerLockElement === model.container;

//----------------------------------------------------------------------
publicAPI.handlePointerLockChange = () => {
if (publicAPI.isPointerLocked()) {
publicAPI.startPointerLockEvent();
} else {
publicAPI.endPointerLockEvent();
}
};

//----------------------------------------------------------------------
function forceRender() {
if (model._view && model.enabled && model.enableRender) {
model.inRender = true;
model._view.traverseAllPasses();
model.inRender = false;
}
// outside the above test so that third-party code can redirect
// the render to the appropriate class
publicAPI.invokeRenderEvent();
}

publicAPI.requestAnimation = (requestor) => {
if (requestor === undefined) {
vtkErrorMacro(`undefined requester, can not start animating`);
return;
}
if (animationRequesters.has(requestor)) {
vtkWarningMacro(`requester is already registered for animating`);
return;
}
animationRequesters.add(requestor);
if (
!model.animationRequest &&
animationRequesters.size === 1 &&
!model.xrAnimation
) {
model._animationStartTime = Date.now();
model._animationFrameCount = 0;
model.animationRequest = requestAnimationFrame(publicAPI.handleAnimation);
publicAPI.startAnimationEvent();
}
};

// continue animating for at least the specified duration of
// milliseconds.
publicAPI.extendAnimation = (duration) => {
const newEnd = Date.now() + duration;
model._animationExtendedEnd = Math.max(model._animationExtendedEnd, newEnd);
if (
!model.animationRequest &&
animationRequesters.size === 0 &&
!model.xrAnimation
) {
model._animationStartTime = Date.now();
model._animationFrameCount = 0;
model.animationRequest = requestAnimationFrame(publicAPI.handleAnimation);
publicAPI.startAnimationEvent();
}
};

publicAPI.isAnimating = () =>
model.xrAnimation || model.animationRequest !== null;

publicAPI.cancelAnimation = (requestor, skipWarning = false) => {
if (!animationRequesters.has(requestor)) {
if (!skipWarning) {
const requestStr =
requestor && requestor.getClassName
? requestor.getClassName()
: requestor;
vtkWarningMacro(`${requestStr} did not request an animation`);
}

return;
}
animationRequesters.delete(requestor);
if (
model.animationRequest &&
animationRequesters.size === 0 &&
Date.now() > model._animationExtendedEnd
) {
cancelAnimationFrame(model.animationRequest);
model.animationRequest = null;
publicAPI.endAnimationEvent();
publicAPI.render();
}
};

publicAPI.switchToXRAnimation = () => {
// cancel existing animation if any
if (model.animationRequest) {
cancelAnimationFrame(model.animationRequest);
model.animationRequest = null;
}
model.xrAnimation = true;
};

publicAPI.returnFromXRAnimation = () => {
model.xrAnimation = false;
if (animationRequesters.size !== 0) {
model.recentAnimationFrameRate = 10.0;
model.animationRequest = requestAnimationFrame(publicAPI.handleAnimation);
}
};

publicAPI.updateXRGamepads = (xrSession, xrFrame, xrRefSpace) => {
// watch for when buttons change state and fire events
xrSession.inputSources.forEach((inputSource) => {
const gripPose =
inputSource.gripSpace == null
? null
: xrFrame.getPose(inputSource.gripSpace, xrRefSpace);
const gp = inputSource.gamepad;
const hand = inputSource.handedness;
if (gp) {
if (!(gp.index in model.lastGamepadValues)) {
model.lastGamepadValues[gp.index] = {
left: { buttons: {} },
right: { buttons: {} },
none: { buttons: {} },
};
}
for (let b = 0; b < gp.buttons.length; ++b) {
if (!(b in model.lastGamepadValues[gp.index][hand].buttons)) {
model.lastGamepadValues[gp.index][hand].buttons[b] = false;
}
if (
model.lastGamepadValues[gp.index][hand].buttons[b] !==
gp.buttons[b].pressed &&
gripPose != null
) {
publicAPI.button3DEvent({
gamepad: gp,
position: gripPose.transform.position,
orientation: gripPose.transform.orientation,
pressed: gp.buttons[b].pressed,
device:
inputSource.handedness === 'left'
? Device.LeftController
: Device.RightController,
input:
deviceInputMap[gp.mapping] && deviceInputMap[gp.mapping][b]
? deviceInputMap[gp.mapping][b]
: Input.Trigger,
});
model.lastGamepadValues[gp.index][hand].buttons[b] =
gp.buttons[b].pressed;
}
if (
model.lastGamepadValues[gp.index][hand].buttons[b] &&
gripPose != null
) {
publicAPI.move3DEvent({
gamepad: gp,
position: gripPose.transform.position,
orientation: gripPose.transform.orientation,
device:
inputSource.handedness === 'left'
? Device.LeftController
: Device.RightController,
});
}
}
}
});
};

publicAPI.handleMouseMove = (event) => {
const callData = {
...getModifierKeysFor(event),
position: getScreenEventPositionFor(event),
deviceType: getDeviceTypeFor(event),
};

if (model.moveTimeoutID === 0) {
publicAPI.startMouseMoveEvent(callData);
} else {
publicAPI.mouseMoveEvent(callData);
clearTimeout(model.moveTimeoutID);
}

// start a timer to keep us animating while we get mouse move events
model.moveTimeoutID = setTimeout(() => {
publicAPI.endMouseMoveEvent();
model.moveTimeoutID = 0;
}, 200);
};

publicAPI.handleAnimation = () => {
const currTime = Date.now();
model._animationFrameCount++;
if (
currTime - model._animationStartTime > 1000.0 &&
model._animationFrameCount > 1
) {
model.recentAnimationFrameRate =
(1000.0 * (model._animationFrameCount - 1)) /
(currTime - model._animationStartTime);
model.lastFrameTime = 1.0 / model.recentAnimationFrameRate;
publicAPI.animationFrameRateUpdateEvent();
model._animationStartTime = currTime;
model._animationFrameCount = 1;
}
publicAPI.animationEvent();
forceRender();
if (
animationRequesters.size > 0 ||
Date.now() < model._animationExtendedEnd
) {
model.animationRequest = requestAnimationFrame(publicAPI.handleAnimation);
} else {
cancelAnimationFrame(model.animationRequest);
model.animationRequest = null;
publicAPI.endAnimationEvent();
publicAPI.render();
}
};

publicAPI.handleWheel = (event) => {
preventDefault(event);

/**
* wheel event values can vary significantly across browsers, platforms
* and devices [1]. `normalizeWheel` uses facebook's solution from their
* fixed-data-table repository [2].
*
* [1] https://developer.mozilla.org/en-US/docs/Web/Events/mousewheel
* [2] https://github.com/facebookarchive/fixed-data-table/blob/master/src/vendor_upstream/dom/normalizeWheel.js
*
* This code will return an object with properties:
*
* spinX -- normalized spin speed (use for zoom) - x plane
* spinY -- " - y plane
* pixelX -- normalized distance (to pixels) - x plane
* pixelY -- " - y plane
*
*/
const callData = {
...normalizeWheel(event),
...getModifierKeysFor(event),
position: getScreenEventPositionFor(event),
deviceType: getDeviceTypeFor(event),
};

// Wheel events are thought to scroll pages (i.e. multiple lines at once).
// See normalizeWheel() documentation for more context.
// While trackpad wheel events are many small (<1) wheel spins,
// mouse wheel events have absolute spin values higher than 1.
// Here the first spin value is "recorded", and used to normalize
// all the following mouse wheel events.
if (model.wheelTimeoutID === 0) {
// we attempt to distinguish between trackpads and mice
// .3 will be larger than the first trackpad event,
// but small enough to detect some common edge case mice
if (Math.abs(callData.spinY) >= 0.3) {
// Event is coming from mouse wheel
wheelCoefficient = Math.abs(callData.spinY);
} else {
// Event is coming from trackpad
wheelCoefficient = 1;
}
}
callData.spinY /= wheelCoefficient;

if (model.wheelTimeoutID === 0) {
publicAPI.startMouseWheelEvent(callData);
publicAPI.mouseWheelEvent(callData);
} else {
publicAPI.mouseWheelEvent(callData);
clearTimeout(model.wheelTimeoutID);
}

if (model.mouseScrollDebounceByPass) {
publicAPI.extendAnimation(600);
publicAPI.endMouseWheelEvent();
model.wheelTimeoutID = 0;
} else {
// start a timer to keep us animating while we get wheel events
model.wheelTimeoutID = setTimeout(() => {
publicAPI.extendAnimation(600);
publicAPI.endMouseWheelEvent();
model.wheelTimeoutID = 0;
}, 200);
}
};

publicAPI.handleMouseUp = (event) => {
const callData = {
...getModifierKeysFor(event),
position: getScreenEventPositionFor(event),
deviceType: getDeviceTypeFor(event),
};
switch (event.button) {
case 0:
publicAPI.leftButtonReleaseEvent(callData);
break;
case 1:
publicAPI.middleButtonReleaseEvent(callData);
break;
case 2:
publicAPI.rightButtonReleaseEvent(callData);
break;
default:
vtkErrorMacro(`Unknown mouse button released: ${event.button}`);
break;
}
};

publicAPI.handleTouchStart = (event) => {
const pointers = [...pointerCache.values()];
// If multitouch
if (model.recognizeGestures && pointers.length > 1) {
const positions = pointerCacheToPositions(pointerCache);
// did we just transition to multitouch?
if (pointers.length === 2) {
const callData = {
...getModifierKeysFor(EMPTY_MOUSE_EVENT),
position: pointers[0].position,
deviceType: getDeviceTypeFor(event),
};
publicAPI.leftButtonReleaseEvent(callData);
}
// handle the gesture
publicAPI.recognizeGesture('TouchStart', positions);
} else if (pointers.length === 1) {
const callData = {
...getModifierKeysFor(EMPTY_MOUSE_EVENT),
position: getScreenEventPositionFor(event),
deviceType: getDeviceTypeFor(event),
};
publicAPI.leftButtonPressEvent(callData);
}
};

publicAPI.handleTouchMove = (event) => {
const pointers = [...pointerCache.values()];
if (model.recognizeGestures && pointers.length > 1) {
const positions = pointerCacheToPositions(pointerCache);
publicAPI.recognizeGesture('TouchMove', positions);
} else if (pointers.length === 1) {
const callData = {
...getModifierKeysFor(EMPTY_MOUSE_EVENT),
position: pointers[0].position,
deviceType: getDeviceTypeFor(event),
};
publicAPI.mouseMoveEvent(callData);
}
};

publicAPI.handleTouchEnd = (event) => {
const pointers = [...pointerCache.values()];

if (model.recognizeGestures) {
// No more fingers down
if (pointers.length === 0) {
const callData = {
...getModifierKeysFor(EMPTY_MOUSE_EVENT),
position: getScreenEventPositionFor(event),
deviceType: getDeviceTypeFor(event),
};
publicAPI.leftButtonReleaseEvent(callData);
} else if (pointers.length === 1) {
// If one finger left, end touch and start button press
const positions = pointerCacheToPositions(pointerCache);
publicAPI.recognizeGesture('TouchEnd', positions);
const callData = {
...getModifierKeysFor(EMPTY_MOUSE_EVENT),
position: pointers[0].position,
deviceType: getDeviceTypeFor(event),
};
publicAPI.leftButtonPressEvent(callData);
} else {
// If more than one finger left, keep touch move
const positions = pointerCacheToPositions(pointerCache);
publicAPI.recognizeGesture('TouchMove', positions);
}
} else if (pointers.length === 1) {
const callData = {
...getModifierKeysFor(EMPTY_MOUSE_EVENT),
position: pointers[0].position,
deviceType: getDeviceTypeFor(event),
};
publicAPI.leftButtonReleaseEvent(callData);
}
};

publicAPI.setView = (val) => {
if (model._view === val) {
return;
}
model._view = val;
model._view.getRenderable().setInteractor(publicAPI);
publicAPI.modified();
};

publicAPI.getFirstRenderer = () =>
model._view?.getRenderable()?.getRenderersByReference()?.[0];

publicAPI.findPokedRenderer = (x = 0, y = 0) => {
if (!model._view) {
return null;
}
// The original order of renderers needs to remain as
// the first one is the one we want to manipulate the camera on.
const rc = model._view?.getRenderable()?.getRenderers();
if (!rc || rc.length === 0) {
return null;
}
rc.sort((a, b) => a.getLayer() - b.getLayer());
let interactiveren = null;
let viewportren = null;
let currentRenderer = null;

let count = rc.length;
while (count--) {
const aren = rc[count];
if (model._view.isInViewport(x, y, aren) && aren.getInteractive()) {
currentRenderer = aren;
break;
}

if (interactiveren === null && aren.getInteractive()) {
// Save this renderer in case we can't find one in the viewport that
// is interactive.
interactiveren = aren;
}
if (viewportren === null && model._view.isInViewport(x, y, aren)) {
// Save this renderer in case we can't find one in the viewport that
// is interactive.
viewportren = aren;
}
}

// We must have a value. If we found an interactive renderer before, that's
// better than a non-interactive renderer.
if (currentRenderer === null) {
currentRenderer = interactiveren;
}

// We must have a value. If we found a renderer that is in the viewport,
// that is better than any old viewport (but not as good as an interactive
// one).
if (currentRenderer === null) {
currentRenderer = viewportren;
}

// We must have a value - take anything.
if (currentRenderer == null) {
currentRenderer = rc[0];
}

return currentRenderer;
};

// only render if we are not animating. If we are animating
// then renders will happen naturally anyhow and we definitely
// do not want extra renders as the make the apparent interaction
// rate slower.
publicAPI.render = () => {
if (!publicAPI.isAnimating() && !model.inRender) {
forceRender();
}
};

// create the generic Event methods
handledEvents.forEach((eventName) => {
const lowerFirst = eventName.charAt(0).toLowerCase() + eventName.slice(1);
publicAPI[`${lowerFirst}Event`] = (arg) => {
// Check that interactor enabled
if (!model.enabled) {
return;
}

// Check that a poked renderer exists
const renderer = publicAPI.getCurrentRenderer();
if (!renderer) {
vtkOnceErrorMacro(`
Can not forward events without a current renderer on the interactor.
`);
return;
}

// Pass the eventName and the poked renderer
const callData = {
type: eventName,
pokedRenderer: model.currentRenderer,
firstRenderer: publicAPI.getFirstRenderer(),
// Add the arguments to the call data
...arg,
};

// Call invoke
publicAPI[`invoke${eventName}`](callData);
};
});

// we know we are in multitouch now, so start recognizing
publicAPI.recognizeGesture = (event, positions) => {
// more than two pointers we ignore
if (Object.keys(positions).length > 2) {
return;
}

if (!model.startingEventPositions) {
model.startingEventPositions = {};
}

// store the initial positions
if (event === 'TouchStart') {
Object.keys(positions).forEach((key) => {
model.startingEventPositions[key] = positions[key];
});
// we do not know what the gesture is yet
model.currentGesture = 'Start';
return;
}

// end the gesture if needed
if (event === 'TouchEnd') {
if (model.currentGesture === 'Pinch') {
publicAPI.render();
publicAPI.endPinchEvent();
}
if (model.currentGesture === 'Rotate') {
publicAPI.render();
publicAPI.endRotateEvent();
}
if (model.currentGesture === 'Pan') {
publicAPI.render();
publicAPI.endPanEvent();
}
model.currentGesture = 'Start';
model.startingEventPositions = {};
return;
}

// what are the two pointers we are working with
let count = 0;
const posVals = [];
const startVals = [];
Object.keys(positions).forEach((key) => {
posVals[count] = positions[key];
startVals[count] = model.startingEventPositions[key];
count++;
});

// The meat of the algorithm
// on move events we analyze them to determine what type
// of movement it is and then deal with it.
// calculate the distances
const originalDistance = Math.sqrt(
(startVals[0].x - startVals[1].x) * (startVals[0].x - startVals[1].x) +
(startVals[0].y - startVals[1].y) * (startVals[0].y - startVals[1].y)
);
const newDistance = Math.sqrt(
(posVals[0].x - posVals[1].x) * (posVals[0].x - posVals[1].x) +
(posVals[0].y - posVals[1].y) * (posVals[0].y - posVals[1].y)
);

// calculate rotations
let originalAngle = vtkMath.degreesFromRadians(
Math.atan2(
startVals[1].y - startVals[0].y,
startVals[1].x - startVals[0].x
)
);
let newAngle = vtkMath.degreesFromRadians(
Math.atan2(posVals[1].y - posVals[0].y, posVals[1].x - posVals[0].x)
);

// angles are cyclic so watch for that, 1 and 359 are only 2 apart :)
let angleDeviation = newAngle - originalAngle;
newAngle = newAngle + 180.0 >= 360.0 ? newAngle - 180.0 : newAngle + 180.0;
originalAngle =
originalAngle + 180.0 >= 360.0
? originalAngle - 180.0
: originalAngle + 180.0;
if (Math.abs(newAngle - originalAngle) < Math.abs(angleDeviation)) {
angleDeviation = newAngle - originalAngle;
}

// calculate the translations
const trans = [];
trans[0] =
(posVals[0].x - startVals[0].x + posVals[1].x - startVals[1].x) / 2.0;
trans[1] =
(posVals[0].y - startVals[0].y + posVals[1].y - startVals[1].y) / 2.0;

if (event === 'TouchMove') {
// OK we want to
// - immediately respond to the user
// - allow the user to zoom without panning (saves focal point)
// - allow the user to rotate without panning (saves focal point)

// do we know what gesture we are doing yet? If not
// see if we can figure it out
if (model.currentGesture === 'Start') {
// pinch is a move to/from the center point
// rotate is a move along the circumference
// pan is a move of the center point
// compute the distance along each of these axes in pixels
// the first to break thresh wins
let thresh =
0.01 *
Math.sqrt(
model.container.clientWidth * model.container.clientWidth +
model.container.clientHeight * model.container.clientHeight
);
if (thresh < 15.0) {
thresh = 15.0;
}
const pinchDistance = Math.abs(newDistance - originalDistance);
const rotateDistance =
(newDistance * 3.1415926 * Math.abs(angleDeviation)) / 360.0;
const panDistance = Math.sqrt(
trans[0] * trans[0] + trans[1] * trans[1]
);
if (
pinchDistance > thresh &&
pinchDistance > rotateDistance &&
pinchDistance > panDistance
) {
model.currentGesture = 'Pinch';
const callData = {
scale: 1.0,
touches: positions,
};
publicAPI.startPinchEvent(callData);
} else if (rotateDistance > thresh && rotateDistance > panDistance) {
model.currentGesture = 'Rotate';
const callData = {
rotation: 0.0,
touches: positions,
};
publicAPI.startRotateEvent(callData);
} else if (panDistance > thresh) {
model.currentGesture = 'Pan';
const callData = {
translation: [0, 0],
touches: positions,
};
publicAPI.startPanEvent(callData);
}
} else {
// if we have found a specific type of movement then
// handle it
if (model.currentGesture === 'Rotate') {
const callData = {
rotation: angleDeviation,
touches: positions,
};
publicAPI.rotateEvent(callData);
}

if (model.currentGesture === 'Pinch') {
const callData = {
scale: newDistance / originalDistance,
touches: positions,
};
publicAPI.pinchEvent(callData);
}

if (model.currentGesture === 'Pan') {
const callData = {
translation: trans,
touches: positions,
};
publicAPI.panEvent(callData);
}
}
}
};

publicAPI.handleVisibilityChange = () => {
model._animationStartTime = Date.now();
model._animationFrameCount = 0;
};

publicAPI.setCurrentRenderer = (r) => {
model._forcedRenderer = !!r;
model.currentRenderer = r;
};

publicAPI.setContainer = (container) => {
_unbindEvents();
const res = superClass.setContainer(container ?? null);
if (res) {
_bindEvents();
}
return res;
};

// Stop animating if the renderWindowInteractor is deleted.
publicAPI.delete = () => {
while (animationRequesters.size) {
publicAPI.cancelAnimation(animationRequesters.values().next().value);
}
if (typeof document.hidden !== 'undefined') {
document.removeEventListener(
'visibilitychange',
publicAPI.handleVisibilityChange
);
}
if (model.container) {
publicAPI.setContainer(null);
}
superClass.delete();
};

// Use the Page Visibility API to detect when we switch away from or back to
// this tab, and reset the animationFrameStart. When tabs are not active, browsers
// will stop calling requestAnimationFrame callbacks.
if (typeof document.hidden !== 'undefined') {
document.addEventListener(
'visibilitychange',
publicAPI.handleVisibilityChange,
false
);
}
}

// ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------

const DEFAULT_VALUES = {
renderWindow: null,
interactorStyle: null,
picker: null,
pickingManager: null,
initialized: false,
enabled: false,
enableRender: true,
currentRenderer: null,
lightFollowCamera: true,
desiredUpdateRate: 30.0,
stillUpdateRate: 2.0,
container: null,
// _view: null,
recognizeGestures: true,
currentGesture: 'Start',
animationRequest: null,
lastFrameTime: 0.1,
recentAnimationFrameRate: 10.0,
wheelTimeoutID: 0,
moveTimeoutID: 0,
lastGamepadValues: {},
preventDefaultOnPointerDown: false,
preventDefaultOnPointerUp: false,
mouseScrollDebounceByPass: false,
};

// ----------------------------------------------------------------------------

export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);

// Object methods
macro.obj(publicAPI, model);

// run animation at least until this time
model._animationExtendedEnd = 0;

macro.event(publicAPI, model, 'RenderEvent');
handledEvents.forEach((eventName) =>
macro.event(publicAPI, model, eventName)
);

// Create get-only macros
macro.get(publicAPI, model, [
'initialized',
'interactorStyle',
'lastFrameTime',
'recentAnimationFrameRate',
'_view',
]);

// Create get-set macros
macro.setGet(publicAPI, model, [
'container',
'lightFollowCamera',
'enabled',
'enableRender',
'recognizeGestures',
'desiredUpdateRate',
'stillUpdateRate',
'picker',
'preventDefaultOnPointerDown',
'preventDefaultOnPointerUp',
'mouseScrollDebounceByPass',
]);
macro.moveToProtected(publicAPI, model, ['view']);

// For more macro methods, see "Sources/macros.js"

// Object specific methods
vtkRenderWindowInteractor(publicAPI, model);
}

// ----------------------------------------------------------------------------

export const newInstance = macro.newInstance(
extend,
'vtkRenderWindowInteractor'
);

// ----------------------------------------------------------------------------

export default { newInstance, extend, handledEvents, ...Constants };