UNPKG

@kitware/vtk.js

Version:

Visualization Toolkit for the Web

284 lines (251 loc) 9.77 kB
import { m as macro } from '../../macros2.js'; import vtkInteractorStyleTrackballCamera from './InteractorStyleTrackballCamera.js'; import { r as radiansFromDegrees } from '../../Common/Core/Math/index.js'; import { States } from '../../Rendering/Core/InteractorStyle/Constants.js'; // ---------------------------------------------------------------------------- // vtkInteractorStyleImage methods // ---------------------------------------------------------------------------- function vtkInteractorStyleImage(publicAPI, model) { // Set our className model.classHierarchy.push('vtkInteractorStyleImage'); // Public API methods publicAPI.superHandleMouseMove = publicAPI.handleMouseMove; publicAPI.handleMouseMove = callData => { const pos = callData.position; const renderer = model.getRenderer(callData); switch (model.state) { case States.IS_WINDOW_LEVEL: publicAPI.windowLevel(renderer, pos); publicAPI.invokeInteractionEvent({ type: 'InteractionEvent' }); break; case States.IS_SLICE: publicAPI.slice(renderer, pos); publicAPI.invokeInteractionEvent({ type: 'InteractionEvent' }); break; } publicAPI.superHandleMouseMove(callData); }; //---------------------------------------------------------------------------- publicAPI.superHandleLeftButtonPress = publicAPI.handleLeftButtonPress; publicAPI.handleLeftButtonPress = callData => { const pos = callData.position; if (!callData.shiftKey && !callData.controlKey) { model.windowLevelStartPosition[0] = pos.x; model.windowLevelStartPosition[1] = pos.y; // Get the last (the topmost) image publicAPI.setCurrentImageNumber(model.currentImageNumber); const property = model.currentImageProperty; if (property) { model.windowLevelInitial[0] = property.getColorWindow(); model.windowLevelInitial[1] = property.getColorLevel(); } publicAPI.startWindowLevel(); } else if (model.interactionMode === 'IMAGE3D' && callData.shiftKey) { // If ctrl+shift or alt+shift is held down, dolly the camera if (callData.controlKey || callData.altKey) { publicAPI.startDolly(); } else { // If shift is held down, rotate publicAPI.startRotate(); } } else if (model.interactionMode === 'IMAGE_SLICING' && callData.controlKey) { // If ctrl is held down in slicing mode, slice the image model.lastSlicePosition = pos.y; publicAPI.startSlice(); } else { // The rest of the button + key combinations remain the same publicAPI.superHandleLeftButtonPress(callData); } }; //-------------------------------------------------------------------------- publicAPI.superHandleLeftButtonRelease = publicAPI.handleLeftButtonRelease; publicAPI.handleLeftButtonRelease = () => { switch (model.state) { case States.IS_WINDOW_LEVEL: publicAPI.endWindowLevel(); break; case States.IS_SLICE: publicAPI.endSlice(); break; default: publicAPI.superHandleLeftButtonRelease(); break; } }; //-------------------------------------------------------------------------- publicAPI.handleStartMouseWheel = () => { publicAPI.startSlice(); }; //-------------------------------------------------------------------------- publicAPI.handleEndMouseWheel = () => { publicAPI.endSlice(); }; //-------------------------------------------------------------------------- publicAPI.handleMouseWheel = callData => { const camera = model.getRenderer(callData).getActiveCamera(); let distance = camera.getDistance(); distance += callData.spinY; // clamp the distance to the clipping range const range = camera.getClippingRange(); if (distance < range[0]) { distance = range[0]; } if (distance > range[1]) { distance = range[1]; } camera.setDistance(distance); const props = model.getRenderer(callData).getViewProps().filter(prop => prop.isA('vtkImageSlice')); props.forEach(prop => { if (prop.getMapper().isA('vtkImageResliceMapper')) { const p = prop.getMapper().getSlicePlane(); if (p) { p.push(callData.spinY); p.modified(); prop.getMapper().modified(); } } }); }; //---------------------------------------------------------------------------- publicAPI.windowLevel = (renderer, position) => { model.windowLevelCurrentPosition[0] = position.x; model.windowLevelCurrentPosition[1] = position.y; const rwi = model._interactor; if (model.currentImageProperty) { const size = rwi.getView().getViewportSize(renderer); const mWindow = model.windowLevelInitial[0]; const level = model.windowLevelInitial[1]; // Compute normalized delta let dx = (model.windowLevelCurrentPosition[0] - model.windowLevelStartPosition[0]) * 4.0 / size[0]; let dy = (model.windowLevelStartPosition[1] - model.windowLevelCurrentPosition[1]) * 4.0 / size[1]; // Scale by current values if (Math.abs(mWindow) > 0.01) { dx *= mWindow; } else { dx *= mWindow < 0 ? -0.01 : 0.01; } if (Math.abs(level) > 0.01) { dy *= level; } else { dy *= level < 0 ? -0.01 : 0.01; } // Abs so that direction does not flip if (mWindow < 0.0) { dx *= -1; } if (level < 0.0) { dy *= -1; } // Compute new mWindow level let newWindow = dx + mWindow; const newLevel = level - dy; if (newWindow < 0.01) { newWindow = 0.01; } model.currentImageProperty.setColorWindow(newWindow); model.currentImageProperty.setColorLevel(newLevel); } }; //---------------------------------------------------------------------------- publicAPI.slice = (renderer, position) => { const rwi = model._interactor; const dy = position.y - model.lastSlicePosition; const camera = renderer.getActiveCamera(); const range = camera.getClippingRange(); let distance = camera.getDistance(); // scale the interaction by the height of the viewport let viewportHeight = 0.0; if (camera.getParallelProjection()) { viewportHeight = 2.0 * camera.getParallelScale(); } else { const angle = radiansFromDegrees(camera.getViewAngle()); viewportHeight = 2.0 * distance * Math.tan(0.5 * angle); } const size = rwi.getView().getSize(); const delta = dy * viewportHeight / size[1]; distance += delta; // clamp the distance to the clipping range if (distance < range[0]) { distance = range[0] + viewportHeight * 1e-3; } if (distance > range[1]) { distance = range[1] - viewportHeight * 1e-3; } camera.setDistance(distance); model.lastSlicePosition = position.y; }; //---------------------------------------------------------------------------- // This is a way of dealing with images as if they were layers. // It looks through the renderer's list of props and sets the // interactor ivars from the Nth image that it finds. You can // also use negative numbers, i.e. -1 will return the last image, // -2 will return the second-to-last image, etc. publicAPI.setCurrentImageNumber = i => { if (i === null) { return; } const renderer = model._interactor.getCurrentRenderer(); if (!renderer) { return; } model.currentImageNumber = i; function propMatch(j, prop, targetIndex) { return j === targetIndex && prop.getNestedPickable(); } const props = renderer.getViewProps().filter(prop => prop.isA('vtkImageSlice')); let targetIndex = i; if (i < 0) { targetIndex += props.length; } const imageProp = props.find((prop, index) => propMatch(index, prop, targetIndex)); if (imageProp) { publicAPI.setCurrentImageProperty(imageProp.getProperty()); } }; //---------------------------------------------------------------------------- publicAPI.setCurrentImageProperty = imageProperty => { model.currentImageProperty = imageProperty; }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- const DEFAULT_VALUES = { windowLevelStartPosition: [0, 0], windowLevelCurrentPosition: [0, 0], lastSlicePosition: 0, windowLevelInitial: [1.0, 0.5], // currentImageProperty: null, currentImageNumber: -1, interactionMode: 'IMAGE2D', xViewRightVector: [0, 1, 0], xViewUpVector: [0, 0, -1], yViewRightVector: [1, 0, 0], yViewUpVector: [0, 0, -1], zViewRightVector: [1, 0, 0], zViewUpVector: [0, 1, 0] }; // ---------------------------------------------------------------------------- function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues); // Inheritance vtkInteractorStyleTrackballCamera.extend(publicAPI, model, initialValues); // Create get-set macros macro.setGet(publicAPI, model, ['interactionMode']); macro.get(publicAPI, model, ['currentImageProperty']); // For more macro methods, see "Sources/macros.js" // Object specific methods vtkInteractorStyleImage(publicAPI, model); } // ---------------------------------------------------------------------------- const newInstance = macro.newInstance(extend, 'vtkInteractorStyleImage'); // ---------------------------------------------------------------------------- var vtkInteractorStyleImage$1 = { newInstance, extend }; export { vtkInteractorStyleImage$1 as default, extend, newInstance };