UNPKG

@kitware/vtk.js

Version:

Visualization Toolkit for the Web

253 lines (214 loc) 8.45 kB
import { m as macro } from '../../macros2.js'; import vtkBoundingBox from '../../Common/DataModel/BoundingBox.js'; import vtkCompositeKeyboardManipulator from './CompositeKeyboardManipulator.js'; import { k as add, l as normalize, j as cross } from '../../Common/Core/Math/index.js'; const { vtkErrorMacro } = macro; const ANIMATION_REQUESTER = 'vtkKeyboardCameraManipulator'; // ---------------------------------------------------------------------------- // vtkKeyboardCameraManipulator methods // ---------------------------------------------------------------------------- function vtkKeyboardCameraManipulator(publicAPI, model) { // Set our className model.classHierarchy.push('vtkKeyboardCameraManipulator'); const internal = { keysDown: [], direction: [0, 0, 0], skipUpdateDirection: false, animationSub: null, cameraModifiedSub: null }; //-------------------------------------------------------------------------- publicAPI.inMotion = () => internal.animationSub !== null; //-------------------------------------------------------------------------- publicAPI.resetMovementSpeed = () => { // Reset the movement speed to be proportional to the longest length // of the renderer's bounds. const { renderer } = model; const bounds = renderer.computeVisiblePropBounds(); // Just a number that seems to work okay for our examples... const divisor = 500; model.movementSpeed = vtkBoundingBox.getMaxLength(bounds) / divisor; }; //-------------------------------------------------------------------------- publicAPI.startMovement = () => { if (publicAPI.inMotion()) { vtkErrorMacro('Camera is already in motion!'); return; } if (model.movementSpeed === null) { publicAPI.resetMovementSpeed(); } const { interactor, renderer } = model; const move = () => { if (internal.keysDown.length === 0) { return; } // No need to update the direction when we move the camera here... internal.skipUpdateDirection = true; publicAPI.moveCamera(renderer.getActiveCamera(), internal.direction, model.movementSpeed); renderer.resetCameraClippingRange(); if (interactor.getLightFollowCamera()) { renderer.updateLightsGeometryToFollowCamera(); } internal.skipUpdateDirection = false; }; publicAPI.calculateCurrentDirection(); const camera = renderer.getActiveCamera(); // If the camera gets modified elsewhere, let's update the direction internal.cameraModifiedSub = camera.onModified(publicAPI.calculateCurrentDirection); interactor.requestAnimation(ANIMATION_REQUESTER); internal.animationSub = interactor.onAnimation(() => move()); }; //-------------------------------------------------------------------------- publicAPI.endMovement = () => { if (internal.animationSub) { internal.animationSub.unsubscribe(); internal.animationSub = null; } model._interactor.cancelAnimation(ANIMATION_REQUESTER); if (internal.cameraModifiedSub) { internal.cameraModifiedSub.unsubscribe(); internal.cameraModifiedSub = null; } }; //-------------------------------------------------------------------------- publicAPI.calculateCurrentDirection = () => { if (internal.skipUpdateDirection) { return; } // Reset internal.direction = [0, 0, 0]; const { renderer } = model; if (!renderer) { return; } const camera = renderer.getActiveCamera(); if (!camera) { return; } if (internal.keysDown.length === 0) { return; } let directions = internal.keysDown.map(key => publicAPI.getDirectionFromKey(key, camera)); directions = directions.filter(item => item); if (directions.length === 0) { return; } const netDirection = directions.reduce((a, b) => { add(a, b, b); return b; }); normalize(netDirection); internal.direction = netDirection; }; //-------------------------------------------------------------------------- publicAPI.getDirectionFromKey = (key, camera) => { let direction; if (model.moveForwardKeys.includes(key)) { // Move forward direction = camera.getDirectionOfProjection(); } else if (model.moveLeftKeys.includes(key)) { // Move left const dirProj = camera.getDirectionOfProjection(); direction = [0, 0, 0]; cross(camera.getViewUp(), dirProj, direction); } else if (model.moveBackwardKeys.includes(key)) { // Move backward direction = camera.getDirectionOfProjection().map(e => -e); } else if (model.moveRightKeys.includes(key)) { // Move right const dirProj = camera.getDirectionOfProjection(); direction = [0, 0, 0]; cross(dirProj, camera.getViewUp(), direction); } else if (model.moveUpKeys.includes(key)) { // Move up direction = camera.getViewUp(); } else if (model.moveDownKeys.includes(key)) { // Move down direction = camera.getViewUp().map(e => -e); } else { return undefined; } normalize(direction); return direction; }; //-------------------------------------------------------------------------- publicAPI.moveCamera = (camera, direction, speed) => { const position = camera.getPosition(); const focalPoint = camera.getFocalPoint(); camera.setFocalPoint(focalPoint[0] + direction[0] * speed, focalPoint[1] + direction[1] * speed, focalPoint[2] + direction[2] * speed); camera.setPosition(position[0] + direction[0] * speed, position[1] + direction[1] * speed, position[2] + direction[2] * speed); }; //-------------------------------------------------------------------------- publicAPI.onKeyPress = (interactor, renderer, key) => {}; //-------------------------------------------------------------------------- publicAPI.onKeyDown = (interactor, renderer, key) => { if (!internal.keysDown.includes(key)) { internal.keysDown.push(key); publicAPI.calculateCurrentDirection(); } if (!publicAPI.inMotion()) { Object.assign(model, { interactor, renderer }); publicAPI.startMovement(); } }; //-------------------------------------------------------------------------- publicAPI.onKeyUp = (interactor, renderer, key) => { // The following is case insensitive for when the user // presses/releases the shift key while this key is down. internal.keysDown = internal.keysDown.filter(item => item.toUpperCase() !== key.toUpperCase()); publicAPI.calculateCurrentDirection(); if (internal.keysDown.length === 0) { publicAPI.endMovement(); } }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- const DEFAULT_VALUES = { // The movementSpeed is the magnitude of the camera translation // for each animation frame (which occur each 1/60 second) // If null, publicAPI.resetMovementSpeed() will be called when // movement starts. movementSpeed: null, moveForwardKeys: ['w', 'W', 'ArrowUp'], moveLeftKeys: ['a', 'A', 'ArrowLeft'], moveBackwardKeys: ['s', 'S', 'ArrowDown'], moveRightKeys: ['d', 'D', 'ArrowRight'], moveUpKeys: [' '], moveDownKeys: ['Shift'], interactor: null, renderer: null }; // ---------------------------------------------------------------------------- function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues); // Inheritance macro.obj(publicAPI, model); vtkCompositeKeyboardManipulator.extend(publicAPI, model, initialValues); // Create get-set macros macro.setGet(publicAPI, model, ['movementSpeed', 'moveForwardKeys', 'moveLeftKeys', 'moveBackwardKeys', 'moveRightKeys', 'moveUpKeys', 'moveDownKeys', 'interactor', 'renderer']); // Object specific methods vtkKeyboardCameraManipulator(publicAPI, model); } // ---------------------------------------------------------------------------- const newInstance = macro.newInstance(extend, 'vtkKeyboardCameraManipulator'); // ---------------------------------------------------------------------------- var vtkKeyboardCameraManipulator$1 = { newInstance, extend }; export { vtkKeyboardCameraManipulator$1 as default, extend, newInstance };