UNPKG

@thewtex/vtk.js-esm

Version:

Visualization Toolkit for the Web

996 lines (801 loc) 34.4 kB
import _defineProperty from '@babel/runtime/helpers/defineProperty'; import macro from '../../macro.js'; import { y as degreesFromRadians } from '../../Common/Core/Math/index.js'; import Constants from './RenderWindowInteractor/Constants.js'; function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } var Device = Constants.Device, Input = Constants.Input; var vtkWarningMacro = macro.vtkWarningMacro, vtkErrorMacro = macro.vtkErrorMacro, normalizeWheel = macro.normalizeWheel, vtkOnceErrorMacro = macro.vtkOnceErrorMacro; // ---------------------------------------------------------------------------- // Global methods // ---------------------------------------------------------------------------- var deviceInputMap = { 'OpenVR Gamepad': [Input.TrackPad, Input.Trigger, Input.Grip, Input.ApplicationMenu] }; var 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']; function preventDefault(event) { if (event.cancelable) { event.stopPropagation(); event.preventDefault(); } return false; } // ---------------------------------------------------------------------------- // vtkRenderWindowInteractor methods // ---------------------------------------------------------------------------- function vtkRenderWindowInteractor(publicAPI, model) { // Set our className model.classHierarchy.push('vtkRenderWindowInteractor'); // Initialize list of requesters var animationRequesters = new Set(); // track active event listeners to handle simultaneous button tracking var activeListenerCount = 0; // Public API methods //---------------------------------------------------------------------- publicAPI.start = function () { // 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 = function (aren) { vtkErrorMacro('you want to call setView(view) instead of setRenderWindow on a vtk.js interactor'); }; //---------------------------------------------------------------------- publicAPI.setInteractorStyle = function (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 = function () { model.initialized = true; publicAPI.enable(); publicAPI.render(); }; publicAPI.enable = function () { return publicAPI.setEnabled(true); }; publicAPI.disable = function () { return publicAPI.setEnabled(false); }; publicAPI.startEventLoop = function () { return vtkWarningMacro('empty event loop'); }; function updateCurrentRenderer(x, y) { if (!model._forcedRenderer) { model.currentRenderer = publicAPI.findPokedRenderer(x, y); } } publicAPI.getCurrentRenderer = function () { if (model.currentRenderer) { return model.currentRenderer; } updateCurrentRenderer(0, 0); return model.currentRenderer; }; function getScreenEventPositionFor(source) { var bounds = model.container.getBoundingClientRect(); var canvas = model.view.getCanvas(); var scaleX = canvas.width / bounds.width; var scaleY = canvas.height / bounds.height; var position = { x: scaleX * (source.clientX - bounds.left), y: scaleY * (bounds.height - source.clientY + bounds.top), z: 0 }; updateCurrentRenderer(position.x, position.y); return position; } function getTouchEventPositionsFor(touches) { var positions = {}; for (var i = 0; i < touches.length; i++) { var touch = touches[i]; positions[touch.identifier] = getScreenEventPositionFor(touch); } return positions; } function getModifierKeysFor(event) { return { controlKey: event.ctrlKey, altKey: event.altKey, shiftKey: event.shiftKey }; } function getKeysFor(event) { var modifierKeys = getModifierKeysFor(event); var keys = _objectSpread({ key: event.key, keyCode: event.charCode }, modifierKeys); return keys; } function interactionRegistration(addListeners) { var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var rootElm = document; var method = addListeners ? 'addEventListener' : 'removeEventListener'; var invMethod = addListeners ? 'removeEventListener' : 'addEventListener'; if (!force && !addListeners && activeListenerCount > 0) { --activeListenerCount; } // only add/remove listeners when there are no registered listeners if (!activeListenerCount || force) { activeListenerCount = 0; if (model.container) { model.container[invMethod]('mousemove', publicAPI.handleMouseMove); } rootElm[method]('mouseup', publicAPI.handleMouseUp); rootElm[method]('mousemove', publicAPI.handleMouseMove); rootElm[method]('touchend', publicAPI.handleTouchEnd, false); rootElm[method]('touchcancel', publicAPI.handleTouchEnd, false); rootElm[method]('touchmove', publicAPI.handleTouchMove, false); } if (!force && addListeners) { ++activeListenerCount; } } publicAPI.bindEvents = function (container) { model.container = container; container.addEventListener('contextmenu', preventDefault); // container.addEventListener('click', preventDefault); // Avoid stopping event propagation container.addEventListener('wheel', publicAPI.handleWheel); container.addEventListener('DOMMouseScroll', publicAPI.handleWheel); container.addEventListener('mouseenter', publicAPI.handleMouseEnter); container.addEventListener('mouseleave', publicAPI.handleMouseLeave); container.addEventListener('mousemove', publicAPI.handleMouseMove); container.addEventListener('mousedown', publicAPI.handleMouseDown); document.addEventListener('keypress', publicAPI.handleKeyPress); document.addEventListener('keydown', publicAPI.handleKeyDown); document.addEventListener('keyup', publicAPI.handleKeyUp); document.addEventListener('pointerlockchange', publicAPI.handlePointerLockChange); container.addEventListener('touchstart', publicAPI.handleTouchStart, false); }; publicAPI.unbindEvents = function () { // force unbinding listeners interactionRegistration(false, true); model.container.removeEventListener('contextmenu', preventDefault); // model.container.removeEventListener('click', preventDefault); // Avoid stopping event propagation model.container.removeEventListener('wheel', publicAPI.handleWheel); model.container.removeEventListener('DOMMouseScroll', publicAPI.handleWheel); model.container.removeEventListener('mouseenter', publicAPI.handleMouseEnter); model.container.removeEventListener('mouseleave', publicAPI.handleMouseLeave); model.container.removeEventListener('mousemove', publicAPI.handleMouseMove); model.container.removeEventListener('mousedown', publicAPI.handleMouseDown); document.removeEventListener('keypress', publicAPI.handleKeyPress); document.removeEventListener('keydown', publicAPI.handleKeyDown); document.removeEventListener('keyup', publicAPI.handleKeyUp); document.removeEventListener('pointerlockchange', publicAPI.handlePointerLockChange); model.container.removeEventListener('touchstart', publicAPI.handleTouchStart); model.container = null; }; publicAPI.handleKeyPress = function (event) { var data = getKeysFor(event); publicAPI.keyPressEvent(data); }; publicAPI.handleKeyDown = function (event) { var data = getKeysFor(event); publicAPI.keyDownEvent(data); }; publicAPI.handleKeyUp = function (event) { var data = getKeysFor(event); publicAPI.keyUpEvent(data); }; publicAPI.handleMouseDown = function (event) { if (event.button > 2) { // ignore events from extra mouse buttons such as `back` and `forward` return; } interactionRegistration(true); preventDefault(event); var callData = _objectSpread(_objectSpread({}, getModifierKeysFor(event)), {}, { position: getScreenEventPositionFor(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: ".concat(event.button)); break; } }; //---------------------------------------------------------------------- publicAPI.requestPointerLock = function () { var canvas = publicAPI.getView().getCanvas(); canvas.requestPointerLock(); }; //---------------------------------------------------------------------- publicAPI.exitPointerLock = function () { return document.exitPointerLock(); }; //---------------------------------------------------------------------- publicAPI.isPointerLocked = function () { return !!document.pointerLockElement; }; //---------------------------------------------------------------------- publicAPI.handlePointerLockChange = function () { 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 = function (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 (animationRequesters.size === 1) { model.lastFrameTime = 0.1; model.lastFrameStart = Date.now(); model.animationRequest = requestAnimationFrame(publicAPI.handleAnimation); publicAPI.startAnimationEvent(); } }; publicAPI.isAnimating = function () { return model.vrAnimation || model.animationRequest !== null; }; publicAPI.cancelAnimation = function (requestor) { var skipWarning = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; if (!animationRequesters.has(requestor)) { if (!skipWarning) { var requestStr = requestor && requestor.getClassName ? requestor.getClassName() : requestor; vtkWarningMacro("".concat(requestStr, " did not request an animation")); } return; } animationRequesters.delete(requestor); if (model.animationRequest && animationRequesters.size === 0) { cancelAnimationFrame(model.animationRequest); model.animationRequest = null; publicAPI.endAnimationEvent(); publicAPI.render(); } }; publicAPI.switchToVRAnimation = function () { // cancel existing animation if any if (model.animationRequest) { cancelAnimationFrame(model.animationRequest); model.animationRequest = null; } model.vrAnimation = true; }; publicAPI.returnFromVRAnimation = function () { model.vrAnimation = false; if (animationRequesters.size !== 0) { model.FrameTime = -1; model.animationRequest = requestAnimationFrame(publicAPI.handleAnimation); } }; publicAPI.updateGamepads = function (displayId) { var gamepads = navigator.getGamepads(); // watch for when buttons change state and fire events for (var i = 0; i < gamepads.length; ++i) { var gp = gamepads[i]; if (gp && gp.displayId === displayId) { if (!(gp.index in model.lastGamepadValues)) { model.lastGamepadValues[gp.index] = { buttons: {} }; } for (var b = 0; b < gp.buttons.length; ++b) { if (!(b in model.lastGamepadValues[gp.index].buttons)) { model.lastGamepadValues[gp.index].buttons[b] = false; } if (model.lastGamepadValues[gp.index].buttons[b] !== gp.buttons[b].pressed) { publicAPI.button3DEvent({ gamepad: gp, position: gp.pose.position, orientation: gp.pose.orientation, pressed: gp.buttons[b].pressed, device: gp.hand === 'left' ? Device.LeftController : Device.RightController, input: deviceInputMap[gp.id] && deviceInputMap[gp.id][b] ? deviceInputMap[gp.id][b] : Input.Trigger }); model.lastGamepadValues[gp.index].buttons[b] = gp.buttons[b].pressed; } if (model.lastGamepadValues[gp.index].buttons[b]) { publicAPI.move3DEvent({ gamepad: gp, position: gp.pose.position, orientation: gp.pose.orientation, device: gp.hand === 'left' ? Device.LeftController : Device.RightController }); } } } } }; publicAPI.handleMouseMove = function (event) { // Do not consume event for move // preventDefault(event); var callData = _objectSpread(_objectSpread({}, getModifierKeysFor(event)), {}, { position: getScreenEventPositionFor(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(function () { publicAPI.endMouseMoveEvent(); model.moveTimeoutID = 0; }, 200); }; publicAPI.handleAnimation = function () { var currTime = Date.now(); if (model.FrameTime === -1.0) { model.lastFrameTime = 0.1; } else { model.lastFrameTime = (currTime - model.lastFrameStart) / 1000.0; } model.lastFrameTime = Math.max(0.01, model.lastFrameTime); model.lastFrameStart = currTime; publicAPI.animationEvent(); forceRender(); model.animationRequest = requestAnimationFrame(publicAPI.handleAnimation); }; publicAPI.handleWheel = function (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 * */ var callData = _objectSpread(_objectSpread(_objectSpread({}, normalizeWheel(event)), getModifierKeysFor(event)), {}, { position: getScreenEventPositionFor(event) }); if (model.wheelTimeoutID === 0) { publicAPI.startMouseWheelEvent(callData); } else { publicAPI.mouseWheelEvent(callData); clearTimeout(model.wheelTimeoutID); } // start a timer to keep us animating while we get wheel events model.wheelTimeoutID = setTimeout(function () { publicAPI.endMouseWheelEvent(); model.wheelTimeoutID = 0; }, 200); }; publicAPI.handleMouseEnter = function (event) { var callData = _objectSpread(_objectSpread({}, getModifierKeysFor(event)), {}, { position: getScreenEventPositionFor(event) }); publicAPI.mouseEnterEvent(callData); }; publicAPI.handleMouseLeave = function (event) { var callData = _objectSpread(_objectSpread({}, getModifierKeysFor(event)), {}, { position: getScreenEventPositionFor(event) }); publicAPI.mouseLeaveEvent(callData); }; publicAPI.handleMouseUp = function (event) { interactionRegistration(false); preventDefault(event); var callData = _objectSpread(_objectSpread({}, getModifierKeysFor(event)), {}, { position: getScreenEventPositionFor(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: ".concat(event.button)); break; } }; publicAPI.handleTouchStart = function (event) { interactionRegistration(true); preventDefault(event); // If multitouch if (model.recognizeGestures && event.touches.length > 1) { var positions = getTouchEventPositionsFor(event.touches); // did we just transition to multitouch? if (event.touches.length === 2) { var touch = event.touches[0]; var callData = { position: getScreenEventPositionFor(touch), shiftKey: false, altKey: false, controlKey: false }; publicAPI.leftButtonReleaseEvent(callData); } // handle the gesture publicAPI.recognizeGesture('TouchStart', positions); } else { var _touch = event.touches[0]; var _callData = { position: getScreenEventPositionFor(_touch), shiftKey: false, altKey: false, controlKey: false }; publicAPI.leftButtonPressEvent(_callData); } }; publicAPI.handleTouchMove = function (event) { preventDefault(event); if (model.recognizeGestures && event.touches.length > 1) { var positions = getTouchEventPositionsFor(event.touches); publicAPI.recognizeGesture('TouchMove', positions); } else { var touch = event.touches[0]; var callData = { position: getScreenEventPositionFor(touch), shiftKey: false, altKey: false, controlKey: false }; publicAPI.mouseMoveEvent(callData); } }; publicAPI.handleTouchEnd = function (event) { preventDefault(event); if (model.recognizeGestures) { // No more fingers down if (event.touches.length === 0) { // If just one finger released, consider as left button if (event.changedTouches.length === 1) { var touch = event.changedTouches[0]; var callData = { position: getScreenEventPositionFor(touch), shiftKey: false, altKey: false, controlKey: false }; publicAPI.leftButtonReleaseEvent(callData); interactionRegistration(false); } else { // If more than one finger released, recognize touchend var positions = getTouchEventPositionsFor(event.changedTouches); publicAPI.recognizeGesture('TouchEnd', positions); interactionRegistration(false); } } else if (event.touches.length === 1) { // If one finger left, end touch and start button press var _positions = getTouchEventPositionsFor(event.changedTouches); publicAPI.recognizeGesture('TouchEnd', _positions); var _touch2 = event.touches[0]; var _callData2 = { position: getScreenEventPositionFor(_touch2), shiftKey: false, altKey: false, controlKey: false }; publicAPI.leftButtonPressEvent(_callData2); } else { // If more than one finger left, keep touch move var _positions2 = getTouchEventPositionsFor(event.touches); publicAPI.recognizeGesture('TouchMove', _positions2); } } else { var _touch3 = event.changedTouches[0]; var _callData3 = { position: getScreenEventPositionFor(_touch3), shiftKey: false, altKey: false, controlKey: false }; publicAPI.leftButtonReleaseEvent(_callData3); interactionRegistration(false); } }; publicAPI.setView = function (val) { if (model.view === val) { return; } model.view = val; model.view.getRenderable().setInteractor(publicAPI); publicAPI.modified(); }; publicAPI.getFirstRenderer = function () { return model.view.getRenderable().getRenderersByReference()[0]; }; publicAPI.findPokedRenderer = function () { var x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 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. var rc = model.view.getRenderable().getRenderers(); rc.sort(function (a, b) { return a.getLayer() - b.getLayer(); }); var interactiveren = null; var viewportren = null; var currentRenderer = null; var count = rc.length; while (count--) { var 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 = function () { if (model.animationRequest === null && !model.inRender) { forceRender(); } }; // create the generic Event methods handledEvents.forEach(function (eventName) { var lowerFirst = eventName.charAt(0).toLowerCase() + eventName.slice(1); publicAPI["".concat(lowerFirst, "Event")] = function (arg) { // Check that interactor enabled if (!model.enabled) { return; } // Check that a poked renderer exists var renderer = publicAPI.getCurrentRenderer(); if (!renderer) { vtkOnceErrorMacro("\n Can not forward events without a current renderer on the interactor.\n "); return; } // Pass the eventName and the poked renderer var callData = _objectSpread({ type: eventName, pokedRenderer: model.currentRenderer, firstRenderer: publicAPI.getFirstRenderer() }, arg); // Call invoke publicAPI["invoke".concat(eventName)](callData); }; }); // we know we are in multitouch now, so start recognizing publicAPI.recognizeGesture = function (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(function (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 var count = 0; var posVals = []; var startVals = []; Object.keys(positions).forEach(function (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 var 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)); var 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 var originalAngle = degreesFromRadians(Math.atan2(startVals[1].y - startVals[0].y, startVals[1].x - startVals[0].x)); var newAngle = 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 :) var 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 var 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 var thresh = 0.01 * Math.sqrt(model.container.clientWidth * model.container.clientWidth + model.container.clientHeight * model.container.clientHeight); if (thresh < 15.0) { thresh = 15.0; } var pinchDistance = Math.abs(newDistance - originalDistance); var rotateDistance = newDistance * 3.1415926 * Math.abs(angleDeviation) / 360.0; var panDistance = Math.sqrt(trans[0] * trans[0] + trans[1] * trans[1]); if (pinchDistance > thresh && pinchDistance > rotateDistance && pinchDistance > panDistance) { model.currentGesture = 'Pinch'; var callData = { scale: 1.0, touches: positions }; publicAPI.startPinchEvent(callData); } else if (rotateDistance > thresh && rotateDistance > panDistance) { model.currentGesture = 'Rotate'; var _callData4 = { rotation: 0.0, touches: positions }; publicAPI.startRotateEvent(_callData4); } else if (panDistance > thresh) { model.currentGesture = 'Pan'; var _callData5 = { translation: [0, 0], touches: positions }; publicAPI.startPanEvent(_callData5); } } else { // if we have found a specific type of movement then // handle it if (model.currentGesture === 'Rotate') { var _callData6 = { rotation: angleDeviation, touches: positions }; publicAPI.rotateEvent(_callData6); } if (model.currentGesture === 'Pinch') { var _callData7 = { scale: newDistance / originalDistance, touches: positions }; publicAPI.pinchEvent(_callData7); } if (model.currentGesture === 'Pan') { var _callData8 = { translation: trans, touches: positions }; publicAPI.panEvent(_callData8); } } } }; publicAPI.handleVisibilityChange = function () { model.lastFrameStart = Date.now(); }; publicAPI.setCurrentRenderer = function (r) { model._forcedRenderer = !!r; model.currentRenderer = r; }; // Stop animating if the renderWindowInteractor is deleted. var superDelete = publicAPI.delete; publicAPI.delete = function () { while (animationRequesters.size) { publicAPI.cancelAnimation(animationRequesters.values().next().value); } if (typeof document.hidden !== 'undefined') { document.removeEventListener('visibilitychange', publicAPI.handleVisibilityChange); } superDelete(); }; // Use the Page Visibility API to detect when we switch away from or back to // this tab, and reset the lastFrameStart. When tabs are not active, browsers // will stop calling requestAnimationFrame callbacks. if (typeof document.hidden !== 'undefined') { document.addEventListener('visibilitychange', publicAPI.handleVisibilityChange, false); } } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- var 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, wheelTimeoutID: 0, moveTimeoutID: 0, lastGamepadValues: {} }; // ---------------------------------------------------------------------------- function extend(publicAPI, model) { var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; Object.assign(model, DEFAULT_VALUES, initialValues); // Object methods macro.obj(publicAPI, model); macro.event(publicAPI, model, 'RenderEvent'); handledEvents.forEach(function (eventName) { return macro.event(publicAPI, model, eventName); }); // Create get-only macros macro.get(publicAPI, model, ['initialized', 'container', 'interactorStyle', 'lastFrameTime', 'view']); // Create get-set macros macro.setGet(publicAPI, model, ['lightFollowCamera', 'enabled', 'enableRender', 'recognizeGestures', 'desiredUpdateRate', 'stillUpdateRate', 'picker']); // For more macro methods, see "Sources/macro.js" // Object specific methods vtkRenderWindowInteractor(publicAPI, model); } // ---------------------------------------------------------------------------- var newInstance = macro.newInstance(extend, 'vtkRenderWindowInteractor'); // ---------------------------------------------------------------------------- var vtkRenderWindowInteractor$1 = _objectSpread({ newInstance: newInstance, extend: extend, handledEvents: handledEvents }, Constants); export default vtkRenderWindowInteractor$1; export { extend, newInstance };