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;
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; }
function vtkRenderWindowInteractor(publicAPI, model) { model.classHierarchy.push('vtkRenderWindowInteractor');
const superClass = { ...publicAPI };
const animationRequesters = new Set();
const pointerCache = new Map();
let wheelCoefficient = 1;
publicAPI.start = () => {
if (!model.initialized) { publicAPI.initialize(); if (!model.initialized) { return; } } 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 (pointerCache.size <= 1 || !model.currentRenderer) { updateCurrentRenderer(position.x, position.y); } return position; } 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 );
container.style.touchAction = 'none'; container.style.userSelect = 'none'; container.style.webkitTapHighlightColor = 'rgba(0,0,0,0)'; };
publicAPI.bindEvents = (container) => { if (container === null) { return; } const res = superClass.setContainer(container); if (res) { _bindEvents(); } };
const _unbindEvents = () => { 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()) { 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; } 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(); } };
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 = () => { 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) => { xrSession.inputSources.forEach((inputSource) => { const gripPose = inputSource.gripSpace == null ? null : xrFrame.getPose(inputSource.gripSpace, xrRefSpace);
const targetRayPose = inputSource.gripSpace == null ? null : xrFrame.getPose(inputSource.targetRaySpace, xrRefSpace);
const gamepad = inputSource.gamepad; const hand = inputSource.handedness;
if (!gamepad) { return; }
if (!(gamepad.index in model.lastGamepadValues)) { model.lastGamepadValues[gamepad.index] = { left: { buttons: {} }, right: { buttons: {} }, none: { buttons: {} }, }; }
for (let buttonIdx = 0; buttonIdx < gamepad.buttons.length; ++buttonIdx) { if ( !(buttonIdx in model.lastGamepadValues[gamepad.index][hand].buttons) ) { model.lastGamepadValues[gamepad.index][hand].buttons[ buttonIdx ] = false; } if ( model.lastGamepadValues[gamepad.index][hand].buttons[buttonIdx] !== gamepad.buttons[buttonIdx].pressed && gripPose != null ) { publicAPI.button3DEvent({ gamepad, position: gripPose.transform.position, orientation: gripPose.transform.orientation, targetPosition: targetRayPose.transform.position, targetOrientation: targetRayPose.transform.orientation, pressed: gamepad.buttons[buttonIdx].pressed, device: inputSource.handedness === 'left' ? Device.LeftController : Device.RightController, input: deviceInputMap[gamepad.mapping] && deviceInputMap[gamepad.mapping][buttonIdx] ? deviceInputMap[gamepad.mapping][buttonIdx] : Input.Trigger, }); model.lastGamepadValues[gamepad.index][hand].buttons[buttonIdx] = gamepad.buttons[buttonIdx].pressed; } if ( model.lastGamepadValues[gamepad.index][hand].buttons[buttonIdx] && gripPose != null ) { publicAPI.move3DEvent({ gamepad, position: gripPose.transform.position, orientation: gripPose.transform.orientation, targetPosition: targetRayPose.transform.position, targetOrientation: targetRayPose.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); }
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);
const callData = { ...normalizeWheel(event), ...getModifierKeysFor(event), position: getScreenEventPositionFor(event), deviceType: getDeviceTypeFor(event), };
if (model.wheelTimeoutID === 0) { if (Math.abs(callData.spinY) >= 0.3) { wheelCoefficient = Math.abs(callData.spinY); } else { 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 { 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 (model.recognizeGestures && pointers.length > 1) { const positions = pointerCacheToPositions(pointerCache); if (pointers.length === 2) { const callData = { ...getModifierKeysFor(EMPTY_MOUSE_EVENT), position: pointers[0].position, deviceType: getDeviceTypeFor(event), }; publicAPI.leftButtonReleaseEvent(callData); } 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) { if (pointers.length === 0) { const callData = { ...getModifierKeysFor(EMPTY_MOUSE_EVENT), position: getScreenEventPositionFor(event), deviceType: getDeviceTypeFor(event), }; publicAPI.leftButtonReleaseEvent(callData); } else if (pointers.length === 1) { 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 { 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; } 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()) { interactiveren = aren; } if (viewportren === null && model._view.isInViewport(x, y, aren)) { viewportren = aren; } }
if (currentRenderer === null) { currentRenderer = interactiveren; }
if (currentRenderer === null) { currentRenderer = viewportren; }
if (currentRenderer == null) { currentRenderer = rc[0]; }
return currentRenderer; };
publicAPI.render = () => { if (!publicAPI.isAnimating() && !model.inRender) { forceRender(); } };
handledEvents.forEach((eventName) => { const lowerFirst = eventName.charAt(0).toLowerCase() + eventName.slice(1); publicAPI[`${lowerFirst}Event`] = (arg) => { if (!model.enabled) { return; }
const renderer = publicAPI.getCurrentRenderer(); if (!renderer) { vtkOnceErrorMacro(` Can not forward events without a current renderer on the interactor. `); return; }
const callData = { type: eventName, pokedRenderer: model.currentRenderer, firstRenderer: publicAPI.getFirstRenderer(), ...arg, };
publicAPI[`invoke${eventName}`](callData); }; });
publicAPI.recognizeGesture = (event, positions) => { if (Object.keys(positions).length > 2) { return; }
if (!model.startingEventPositions) { model.startingEventPositions = {}; }
if (event === 'TouchStart') { Object.keys(positions).forEach((key) => { model.startingEventPositions[key] = positions[key]; }); model.currentGesture = 'Start'; return; }
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; }
let count = 0; const posVals = []; const startVals = []; Object.keys(positions).forEach((key) => { posVals[count] = positions[key]; startVals[count] = model.startingEventPositions[key]; count++; });
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) );
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) );
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; }
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') {
if (model.currentGesture === 'Start') { 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 (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; };
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(); };
if (typeof document.hidden !== 'undefined') { document.addEventListener( 'visibilitychange', publicAPI.handleVisibilityChange, false ); } }
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, 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);
macro.obj(publicAPI, model);
model._animationExtendedEnd = 0;
macro.event(publicAPI, model, 'RenderEvent'); handledEvents.forEach((eventName) => macro.event(publicAPI, model, eventName) );
macro.get(publicAPI, model, [ 'initialized', 'interactorStyle', 'lastFrameTime', 'recentAnimationFrameRate', '_view', ]);
macro.setGet(publicAPI, model, [ 'container', 'lightFollowCamera', 'enabled', 'enableRender', 'recognizeGestures', 'desiredUpdateRate', 'stillUpdateRate', 'picker', 'preventDefaultOnPointerDown', 'preventDefaultOnPointerUp', 'mouseScrollDebounceByPass', ]); macro.moveToProtected(publicAPI, model, ['view']);
vtkRenderWindowInteractor(publicAPI, model); }
export const newInstance = macro.newInstance( extend, 'vtkRenderWindowInteractor' );
export default { newInstance, extend, handledEvents, ...Constants };
|