UNPKG

@kitware/vtk.js

Version:

Visualization Toolkit for the Web

175 lines (146 loc) 6.14 kB
import { m as macro } from '../../macros2.js'; import vtkCompositeCameraManipulator from './CompositeCameraManipulator.js'; import vtkCompositeMouseManipulator from './CompositeMouseManipulator.js'; const ANIMATION_REQUESTER = 'vtkMouseCameraTrackballFirstPersonManipulator'; // ---------------------------------------------------------------------------- // vtkMouseCameraTrackballFirstPersonManipulator methods // ---------------------------------------------------------------------------- function vtkMouseCameraTrackballFirstPersonManipulator(publicAPI, model) { // Set our className model.classHierarchy.push('vtkMouseCameraTrackballFirstPersonManipulator'); const internal = { interactor: null, renderer: null, previousPosition: null }; //-------------------------------------------------------------------------- publicAPI.onButtonDown = (interactor, renderer, position) => { internal.previousPosition = position; if (model.usePointerLock && !interactor.isPointerLocked()) { Object.assign(internal, { interactor, renderer }); interactor.requestPointerLock(); publicAPI.startPointerLockInteraction(); } }; //-------------------------------------------------------------------------- publicAPI.startPointerLockInteraction = () => { const { interactor } = internal; // TODO: at some point, this should perhaps be done in // RenderWindowInteractor instead of here. // We need to hook into mousemove directly for two reasons: // 1. We need to keep receiving mouse move events after the mouse button // is released. This is currently not possible with // vtkInteractorStyleManipulator. // 2. Since the mouse is stationary in pointer lock mode, we need the // event.movementX and event.movementY info, which are not currently // passed via interactor.onMouseMove. document.addEventListener('mousemove', publicAPI.onPointerLockMove); let subscription = null; const endInteraction = () => { document.removeEventListener('mousemove', publicAPI.onPointerLockMove); subscription.unsubscribe(); }; subscription = interactor.onEndPointerLock(endInteraction); }; //-------------------------------------------------------------------------- publicAPI.onPointerLockMove = e => { const sensitivity = model.sensitivity; const yaw = -1 * e.movementX * sensitivity; const pitch = -1 * e.movementY * sensitivity; publicAPI.moveCamera(yaw, pitch); }; //-------------------------------------------------------------------------- publicAPI.onMouseMove = (interactor, renderer, position) => { // This is currently only being called for non pointer lock mode if (!position) { return; } const { previousPosition } = internal; const sensitivity = model.sensitivity; const yaw = (previousPosition.x - position.x) * sensitivity; const pitch = (position.y - previousPosition.y) * sensitivity; Object.assign(internal, { interactor, renderer }); publicAPI.moveCamera(yaw, pitch); internal.previousPosition = position; }; //-------------------------------------------------------------------------- publicAPI.moveCamera = (yaw, pitch) => { const { renderer, interactor } = internal; const camera = renderer.getActiveCamera(); // We need to pick a number of steps here that is not too few // (or the camera will be jittery) and not too many (or the // animations will take too long). // Perhaps this should be calculated? const numSteps = model.numAnimationSteps; const yawStep = yaw / numSteps; const pitchStep = pitch / numSteps; const now = performance.now().toString(); const animationRequester = `${ANIMATION_REQUESTER}.${now}`; let curStep = 0; let animationSub = null; const performStep = () => { camera.yaw(yawStep); camera.pitch(pitchStep); camera.orthogonalizeViewUp(); curStep += 1; if (curStep === numSteps) { animationSub.unsubscribe(); renderer.resetCameraClippingRange(); if (interactor.getLightFollowCamera()) { renderer.updateLightsGeometryToFollowCamera(); } // This needs to be posted to the event loop so it isn't called // in the `handleAnimation` stack, or else the animation will // not be canceled. const cancelRequest = () => { internal.interactor.cancelAnimation(animationRequester); }; setTimeout(cancelRequest, 0); } }; interactor.requestAnimation(animationRequester); animationSub = interactor.onAnimation(() => performStep()); }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- const DEFAULT_VALUES = { numAnimationSteps: 5, sensitivity: 0.05, usePointerLock: true }; // ---------------------------------------------------------------------------- function extend(publicAPI, model) { let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; Object.assign(model, DEFAULT_VALUES, initialValues); // Inheritance macro.obj(publicAPI, model); vtkCompositeCameraManipulator.extend(publicAPI, model, initialValues); vtkCompositeMouseManipulator.extend(publicAPI, model, initialValues); // Create get-set macros macro.setGet(publicAPI, model, ['numAnimationSteps', 'sensitivity', 'usePointerLock']); // Object specific methods vtkMouseCameraTrackballFirstPersonManipulator(publicAPI, model); } // ---------------------------------------------------------------------------- const newInstance = macro.newInstance(extend, 'vtkMouseCameraTrackballFirstPersonManipulator'); // ---------------------------------------------------------------------------- var vtkMouseCameraTrackballFirstPersonManipulator$1 = { newInstance, extend }; export { vtkMouseCameraTrackballFirstPersonManipulator$1 as default, extend, newInstance };