UNPKG

@acransac/vtk.js

Version:

Visualization Toolkit for the Web

752 lines (661 loc) 24.9 kB
import macro from 'vtk.js/Sources/macro'; import vtkInteractorStyle from 'vtk.js/Sources/Rendering/Core/InteractorStyle'; const { vtkDebugMacro } = macro; const { States } = vtkInteractorStyle; // ---------------------------------------------------------------------------- // Event Types // ---------------------------------------------------------------------------- const START_INTERACTION_EVENT = { type: 'StartInteractionEvent' }; const INTERACTION_EVENT = { type: 'InteractionEvent' }; const END_INTERACTION_EVENT = { type: 'EndInteractionEvent' }; // ---------------------------------------------------------------------------- // Global methods // ---------------------------------------------------------------------------- function translateCamera(renderer, rwi, toX, toY, fromX, fromY) { const cam = renderer.getActiveCamera(); let viewFocus = cam.getFocalPoint(); viewFocus = rwi .getInteractorStyle() .computeWorldToDisplay(renderer, viewFocus[0], viewFocus[1], viewFocus[2]); const focalDepth = viewFocus[2]; const newPickPoint = rwi .getInteractorStyle() .computeDisplayToWorld(renderer, toX, toY, focalDepth); const oldPickPoint = rwi .getInteractorStyle() .computeDisplayToWorld(renderer, fromX, fromY, focalDepth); // camera motion is reversed const motionVector = [ oldPickPoint[0] - newPickPoint[0], oldPickPoint[1] - newPickPoint[1], oldPickPoint[2] - newPickPoint[2], ]; viewFocus = cam.getFocalPoint(); const viewPoint = cam.getPosition(); cam.setFocalPoint( motionVector[0] + viewFocus[0], motionVector[1] + viewFocus[1], motionVector[2] + viewFocus[2] ); cam.setPosition( motionVector[0] + viewPoint[0], motionVector[1] + viewPoint[1], motionVector[2] + viewPoint[2] ); } function dollyToPosition(fact, position, renderer, rwi) { const cam = renderer.getActiveCamera(); if (cam.getParallelProjection()) { // Zoom relatively to the cursor const aSize = rwi.getView().getSize(); const w = aSize[0]; const h = aSize[1]; const x0 = w / 2; const y0 = h / 2; const x1 = position.x; const y1 = position.y; translateCamera(renderer, rwi, x0, y0, x1, y1); cam.setParallelScale(cam.getParallelScale() / fact); translateCamera(renderer, rwi, x1, y1, x0, y0); } else { // Zoom relatively to the cursor position // Move focal point to cursor position let viewFocus = cam.getFocalPoint(); const norm = cam.getViewPlaneNormal(); viewFocus = rwi .getInteractorStyle() .computeWorldToDisplay( renderer, viewFocus[0], viewFocus[1], viewFocus[2] ); const newFp = rwi .getInteractorStyle() .computeDisplayToWorld(renderer, position.x, position.y, viewFocus[2]); cam.setFocalPoint(newFp[0], newFp[1], newFp[2]); // Move camera in/out along projection direction cam.dolly(fact); renderer.resetCameraClippingRange(); // Find new focal point const newCameraPos = cam.getPosition(); viewFocus = cam.getFocalPoint(); const newPoint = [0, 0, 0]; let t = norm[0] * (viewFocus[0] - newCameraPos[0]) + norm[1] * (viewFocus[1] - newCameraPos[1]) + norm[2] * (viewFocus[2] - newCameraPos[2]); t /= norm[0] ** 2 + norm[1] ** 2 + norm[2] ** 2; newPoint[0] = newCameraPos[0] + norm[0] * t; newPoint[1] = newCameraPos[1] + norm[1] * t; newPoint[2] = newCameraPos[2] + norm[2] * t; cam.setFocalPoint(newPoint[0], newPoint[1], newPoint[2]); renderer.resetCameraClippingRange(); } } function dollyByFactor(interactor, renderer, factor) { if (Number.isNaN(factor)) { return; } const camera = renderer.getActiveCamera(); if (camera.getParallelProjection()) { camera.setParallelScale(camera.getParallelScale() / factor); } else { camera.dolly(factor); renderer.resetCameraClippingRange(); } if (interactor.getLightFollowCamera()) { renderer.updateLightsGeometryToFollowCamera(); } } // ---------------------------------------------------------------------------- // Static API // ---------------------------------------------------------------------------- export const STATIC = { dollyToPosition, translateCamera, dollyByFactor, }; // ---------------------------------------------------------------------------- // vtkInteractorStyleManipulator methods // ---------------------------------------------------------------------------- function vtkInteractorStyleManipulator(publicAPI, model) { // Set our className model.classHierarchy.push('vtkInteractorStyleManipulator'); model.mouseManipulators = []; model.keyboardManipulators = []; model.vrManipulators = []; model.gestureManipulators = []; model.currentManipulator = null; model.currentWheelManipulator = null; model.centerOfRotation = [0, 0, 0]; model.rotationFactor = 1; //------------------------------------------------------------------------- publicAPI.removeAllManipulators = () => { publicAPI.removeAllMouseManipulators(); publicAPI.removeAllKeyboardManipulators(); publicAPI.removeAllVRManipulators(); publicAPI.removeAllGestureManipulators(); }; //------------------------------------------------------------------------- publicAPI.removeAllMouseManipulators = () => { model.mouseManipulators = []; }; //------------------------------------------------------------------------- publicAPI.removeAllKeyboardManipulators = () => { model.keyboardManipulators = []; }; //------------------------------------------------------------------------- publicAPI.removeAllVRManipulators = () => { model.vrManipulators = []; }; //------------------------------------------------------------------------- publicAPI.removeAllGestureManipulators = () => { model.gestureManipulators = []; }; //------------------------------------------------------------------------- const removeManipulator = (manipulator, list) => { const index = list.indexOf(manipulator); if (index === -1) { return false; } list.splice(index, 1); publicAPI.modified(); return true; }; //------------------------------------------------------------------------- publicAPI.removeMouseManipulator = (manipulator) => removeManipulator(manipulator, model.mouseManipulators); //------------------------------------------------------------------------- publicAPI.removeKeyboardManipulator = (manipulator) => removeManipulator(manipulator, model.keyboardManipulators); //------------------------------------------------------------------------- publicAPI.removeVRManipulator = (manipulator) => removeManipulator(manipulator, model.vrManipulators); //------------------------------------------------------------------------- publicAPI.removeGestureManipulator = (manipulator) => removeManipulator(manipulator, model.gestureManipulators); //------------------------------------------------------------------------- const addManipulator = (manipulator, list) => { const index = list.indexOf(manipulator); if (index !== -1) { return false; } list.push(manipulator); publicAPI.modified(); return true; }; //------------------------------------------------------------------------- publicAPI.addMouseManipulator = (manipulator) => addManipulator(manipulator, model.mouseManipulators); //------------------------------------------------------------------------- publicAPI.addKeyboardManipulator = (manipulator) => addManipulator(manipulator, model.keyboardManipulators); //------------------------------------------------------------------------- publicAPI.addVRManipulator = (manipulator) => addManipulator(manipulator, model.vrManipulators); //------------------------------------------------------------------------- publicAPI.addGestureManipulator = (manipulator) => addManipulator(manipulator, model.gestureManipulators); //------------------------------------------------------------------------- publicAPI.getNumberOfMouseManipulators = () => model.mouseManipulators.length; //------------------------------------------------------------------------- publicAPI.getNumberOfKeyboardManipulators = () => model.keyboardManipulators.length; //------------------------------------------------------------------------- publicAPI.getNumberOfVRManipulators = () => model.vrManipulators.length; //------------------------------------------------------------------------- publicAPI.getNumberOfGestureManipulators = () => model.gestureManipulators.length; //------------------------------------------------------------------------- publicAPI.resetCurrentManipulator = () => { model.currentManipulator = null; model.currentWheelManipulator = null; }; //------------------------------------------------------------------------- // Mouse //------------------------------------------------------------------------- publicAPI.handleLeftButtonPress = (callData) => { model.previousPosition = callData.position; publicAPI.onButtonDown(1, callData); }; //------------------------------------------------------------------------- publicAPI.handleMiddleButtonPress = (callData) => { model.previousPosition = callData.position; publicAPI.onButtonDown(2, callData); }; //------------------------------------------------------------------------- publicAPI.handleRightButtonPress = (callData) => { model.previousPosition = callData.position; publicAPI.onButtonDown(3, callData); }; //------------------------------------------------------------------------- publicAPI.handleButton3D = (ed) => { if (!ed) { return; } // Look for a matching 3D camera interactor. model.currentManipulator = publicAPI.findVRManipulator( ed.device, ed.input, ed.pressed ); if (model.currentManipulator) { model.currentManipulator.onButton3D( publicAPI, ed.pokedRenderer, model.state, ed.device, ed.input, ed.pressed ); if (ed.pressed) { publicAPI.startCameraPose(); } else { publicAPI.endCameraPose(); } } else { vtkDebugMacro('No manipulator found'); } }; //------------------------------------------------------------------------- publicAPI.handleMove3D = (ed) => { if (model.currentManipulator && model.state === States.IS_CAMERA_POSE) { model.currentManipulator.onMove3D( publicAPI, ed.pokedRenderer, model.state, ed ); } }; //------------------------------------------------------------------------- publicAPI.onButtonDown = (button, callData) => { // Must not be processing an interaction to start another. if (model.currentManipulator) { return; } // Look for a matching camera interactor. model.currentManipulator = publicAPI.findMouseManipulator( button, callData.shiftKey, callData.controlKey, callData.altKey ); if (model.currentManipulator) { if (model.currentManipulator.setCenter) { model.currentManipulator.setCenter(model.centerOfRotation); } if (model.currentManipulator.setRotationFactor) { model.currentManipulator.setRotationFactor(model.rotationFactor); } model.currentManipulator.startInteraction(); model.currentManipulator.onButtonDown( model.interactor, callData.pokedRenderer, callData.position ); model.interactor.requestAnimation(publicAPI.onButtonDown); publicAPI.invokeStartInteractionEvent(START_INTERACTION_EVENT); } else { vtkDebugMacro('No manipulator found'); } }; //------------------------------------------------------------------------- publicAPI.findMouseManipulator = (button, shift, control, alt) => { // Look for a matching camera manipulator let manipulator = null; let count = model.mouseManipulators.length; while (count--) { const manip = model.mouseManipulators[count]; if ( manip && manip.getButton() === button && manip.getShift() === shift && manip.getControl() === control && manip.getAlt() === alt && manip.isDragEnabled() ) { manipulator = manip; } } return manipulator; }; //------------------------------------------------------------------------- publicAPI.findVRManipulator = (device, input) => { // Look for a matching camera manipulator let manipulator = null; let count = model.vrManipulators.length; while (count--) { const manip = model.vrManipulators[count]; if (manip && manip.getDevice() === device && manip.getInput() === input) { manipulator = manip; } } return manipulator; }; //------------------------------------------------------------------------- publicAPI.handleLeftButtonRelease = () => { publicAPI.onButtonUp(1); }; //------------------------------------------------------------------------- publicAPI.handleMiddleButtonRelease = () => { publicAPI.onButtonUp(2); }; //------------------------------------------------------------------------- publicAPI.handleRightButtonRelease = () => { publicAPI.onButtonUp(3); }; //------------------------------------------------------------------------- publicAPI.onButtonUp = (button) => { if (!model.currentManipulator) { return; } if ( model.currentManipulator.getButton && model.currentManipulator.getButton() === button ) { model.currentManipulator.onButtonUp(model.interactor); model.currentManipulator.endInteraction(); model.currentManipulator = null; model.interactor.cancelAnimation(publicAPI.onButtonDown); publicAPI.invokeEndInteractionEvent(END_INTERACTION_EVENT); } }; //------------------------------------------------------------------------- publicAPI.handleStartMouseWheel = (callData) => { // Must not be processing a wheel interaction to start another. if (model.currentWheelManipulator) { return; } let manipulator = null; let count = model.mouseManipulators.length; while (count--) { const manip = model.mouseManipulators[count]; if ( manip && manip.isScrollEnabled() && manip.getShift() === callData.shiftKey && manip.getControl() === callData.controlKey && manip.getAlt() === callData.altKey ) { manipulator = manip; } } if (manipulator) { model.currentWheelManipulator = manipulator; model.currentWheelManipulator.onStartScroll( model.interactor, callData.pokedRenderer, callData.spinY ); model.currentWheelManipulator.startInteraction(); model.interactor.requestAnimation(publicAPI.handleStartMouseWheel); publicAPI.invokeStartInteractionEvent(START_INTERACTION_EVENT); } else { vtkDebugMacro('No manipulator found'); } }; //------------------------------------------------------------------------- publicAPI.handleEndMouseWheel = () => { if (!model.currentWheelManipulator) { return; } if (model.currentWheelManipulator.onEndScroll) { model.currentWheelManipulator.onEndScroll(model.interactor); model.currentWheelManipulator.endInteraction(); model.currentWheelManipulator = null; model.interactor.cancelAnimation(publicAPI.handleStartMouseWheel); publicAPI.invokeEndInteractionEvent(END_INTERACTION_EVENT); } }; //------------------------------------------------------------------------- publicAPI.handleMouseWheel = (callData) => { if ( model.currentWheelManipulator && model.currentWheelManipulator.onScroll ) { model.currentWheelManipulator.onScroll( model.interactor, callData.pokedRenderer, callData.spinY, model.cachedMousePosition ); publicAPI.invokeInteractionEvent(INTERACTION_EVENT); } }; //------------------------------------------------------------------------- publicAPI.handleMouseMove = (callData) => { model.cachedMousePosition = callData.position; if (model.currentManipulator && model.currentManipulator.onMouseMove) { model.currentManipulator.onMouseMove( model.interactor, callData.pokedRenderer, callData.position ); publicAPI.invokeInteractionEvent(INTERACTION_EVENT); } }; //------------------------------------------------------------------------- // Keyboard //------------------------------------------------------------------------- publicAPI.handleKeyPress = (callData) => { model.keyboardManipulators .filter((m) => m.onKeyPress) .forEach((manipulator) => { manipulator.onKeyPress( model.interactor, callData.pokedRenderer, callData.key ); publicAPI.invokeInteractionEvent(INTERACTION_EVENT); }); }; //------------------------------------------------------------------------- publicAPI.handleKeyDown = (callData) => { model.keyboardManipulators .filter((m) => m.onKeyDown) .forEach((manipulator) => { manipulator.onKeyDown( model.interactor, callData.pokedRenderer, callData.key ); publicAPI.invokeInteractionEvent(INTERACTION_EVENT); }); }; //------------------------------------------------------------------------- publicAPI.handleKeyUp = (callData) => { model.keyboardManipulators .filter((m) => m.onKeyUp) .forEach((manipulator) => { manipulator.onKeyUp( model.interactor, callData.pokedRenderer, callData.key ); publicAPI.invokeInteractionEvent(INTERACTION_EVENT); }); }; //------------------------------------------------------------------------- // Gesture //------------------------------------------------------------------------- publicAPI.handleStartPinch = (callData) => { publicAPI.startDolly(); let count = model.gestureManipulators.length; while (count--) { const manipulator = model.gestureManipulators[count]; if (manipulator && manipulator.isPinchEnabled()) { manipulator.onStartPinch(model.interactor, callData.scale); manipulator.startInteraction(); } } model.interactor.requestAnimation(publicAPI.handleStartPinch); publicAPI.invokeStartInteractionEvent(START_INTERACTION_EVENT); }; //-------------------------------------------------------------------------- publicAPI.handleEndPinch = () => { publicAPI.endDolly(); let count = model.gestureManipulators.length; while (count--) { const manipulator = model.gestureManipulators[count]; if (manipulator && manipulator.isPinchEnabled()) { manipulator.onEndPinch(model.interactor); manipulator.endInteraction(); } } model.interactor.cancelAnimation(publicAPI.handleStartPinch); publicAPI.invokeEndInteractionEvent(END_INTERACTION_EVENT); }; //---------------------------------------------------------------------------- publicAPI.handleStartRotate = (callData) => { publicAPI.startRotate(); let count = model.gestureManipulators.length; while (count--) { const manipulator = model.gestureManipulators[count]; if (manipulator && manipulator.isRotateEnabled()) { manipulator.onStartRotate(model.interactor, callData.rotation); manipulator.startInteraction(); } } model.interactor.requestAnimation(publicAPI.handleStartRotate); publicAPI.invokeStartInteractionEvent(START_INTERACTION_EVENT); }; //-------------------------------------------------------------------------- publicAPI.handleEndRotate = () => { publicAPI.endRotate(); let count = model.gestureManipulators.length; while (count--) { const manipulator = model.gestureManipulators[count]; if (manipulator && manipulator.isRotateEnabled()) { manipulator.onEndRotate(model.interactor); manipulator.endInteraction(); } } model.interactor.cancelAnimation(publicAPI.handleStartRotate); publicAPI.invokeEndInteractionEvent(END_INTERACTION_EVENT); }; //---------------------------------------------------------------------------- publicAPI.handleStartPan = (callData) => { publicAPI.startPan(); let count = model.gestureManipulators.length; while (count--) { const manipulator = model.gestureManipulators[count]; if (manipulator && manipulator.isPanEnabled()) { manipulator.onStartPan(model.interactor, callData.translation); manipulator.startInteraction(); } } model.interactor.requestAnimation(publicAPI.handleStartPan); publicAPI.invokeStartInteractionEvent(START_INTERACTION_EVENT); }; //-------------------------------------------------------------------------- publicAPI.handleEndPan = () => { publicAPI.endPan(); let count = model.gestureManipulators.length; while (count--) { const manipulator = model.gestureManipulators[count]; if (manipulator && manipulator.isPanEnabled()) { manipulator.onEndPan(model.interactor); manipulator.endInteraction(); } } model.interactor.cancelAnimation(publicAPI.handleStartPan); publicAPI.invokeEndInteractionEvent(END_INTERACTION_EVENT); }; //---------------------------------------------------------------------------- publicAPI.handlePinch = (callData) => { let count = model.gestureManipulators.length; let actionCount = 0; while (count--) { const manipulator = model.gestureManipulators[count]; if (manipulator && manipulator.isPinchEnabled()) { manipulator.onPinch( model.interactor, callData.pokedRenderer, callData.scale ); actionCount++; } } if (actionCount) { publicAPI.invokeInteractionEvent(INTERACTION_EVENT); } }; //---------------------------------------------------------------------------- publicAPI.handlePan = (callData) => { let count = model.gestureManipulators.length; let actionCount = 0; while (count--) { const manipulator = model.gestureManipulators[count]; if (manipulator && manipulator.isPanEnabled()) { manipulator.onPan( model.interactor, callData.pokedRenderer, callData.translation ); actionCount++; } } if (actionCount) { publicAPI.invokeInteractionEvent(INTERACTION_EVENT); } }; //---------------------------------------------------------------------------- publicAPI.handleRotate = (callData) => { let count = model.gestureManipulators.length; let actionCount = 0; while (count--) { const manipulator = model.gestureManipulators[count]; if (manipulator && manipulator.isRotateEnabled()) { manipulator.onRotate( model.interactor, callData.pokedRenderer, callData.rotation ); actionCount++; } } if (actionCount) { publicAPI.invokeInteractionEvent(INTERACTION_EVENT); } }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- const DEFAULT_VALUES = { cachedMousePosition: null, currentManipulator: null, currentWheelManipulator: null, // mouseManipulators: null, // keyboardManipulators: null, // vrManipulators: null, // gestureManipulators: null, centerOfRotation: [0, 0, 0], rotationFactor: 1, }; // ---------------------------------------------------------------------------- export function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues); // Inheritance vtkInteractorStyle.extend(publicAPI, model, initialValues); // Create get-set macros macro.setGet(publicAPI, model, ['rotationFactor']); macro.get(publicAPI, model, [ 'mouseManipulators', 'keyboardManipulators', 'vrManipulators', 'gestureManipulators', ]); macro.setGetArray(publicAPI, model, ['centerOfRotation'], 3); // Object specific methods vtkInteractorStyleManipulator(publicAPI, model); } // ---------------------------------------------------------------------------- export const newInstance = macro.newInstance( extend, 'vtkInteractorStyleManipulator' ); // ---------------------------------------------------------------------------- export default { newInstance, extend, ...STATIC };