UNPKG

@thewtex/vtk.js-esm

Version:

Visualization Toolkit for the Web

295 lines (221 loc) 9.39 kB
import macro from '../../macro.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 = function (callData) { var pos = callData.position; var renderer = callData.pokedRenderer; 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 = function (callData) { var 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); var 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 shift is held down, do a rotation 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 = function () { switch (model.state) { case States.IS_WINDOW_LEVEL: publicAPI.endWindowLevel(); break; case States.IS_SLICE: publicAPI.endSlice(); break; default: publicAPI.superHandleLeftButtonRelease(); break; } }; //-------------------------------------------------------------------------- publicAPI.handleStartMouseWheel = function (callData) { publicAPI.startSlice(); publicAPI.handleMouseWheel(callData); }; //-------------------------------------------------------------------------- publicAPI.handleEndMouseWheel = function () { publicAPI.endSlice(); }; //-------------------------------------------------------------------------- publicAPI.handleMouseWheel = function (callData) { var camera = callData.pokedRenderer.getActiveCamera(); var distance = camera.getDistance(); distance += callData.spinY; // clamp the distance to the clipping range var range = camera.getClippingRange(); if (distance < range[0]) { distance = range[0]; } if (distance > range[1]) { distance = range[1]; } camera.setDistance(distance); }; //---------------------------------------------------------------------------- publicAPI.windowLevel = function (renderer, position) { model.windowLevelCurrentPosition[0] = position.x; model.windowLevelCurrentPosition[1] = position.y; var rwi = model.interactor; if (model.currentImageProperty) { var size = rwi.getView().getViewportSize(renderer); var mWindow = model.windowLevelInitial[0]; var level = model.windowLevelInitial[1]; // Compute normalized delta var dx = (model.windowLevelCurrentPosition[0] - model.windowLevelStartPosition[0]) * 4.0 / size[0]; var 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 var newWindow = dx + mWindow; var newLevel = level - dy; if (newWindow < 0.01) { newWindow = 0.01; } model.currentImageProperty.setColorWindow(newWindow); model.currentImageProperty.setColorLevel(newLevel); } }; //---------------------------------------------------------------------------- publicAPI.slice = function (renderer, position) { var rwi = model.interactor; var dy = position.y - model.lastSlicePosition; var camera = renderer.getActiveCamera(); var range = camera.getClippingRange(); var distance = camera.getDistance(); // scale the interaction by the height of the viewport var viewportHeight = 0.0; if (camera.getParallelProjection()) { viewportHeight = camera.getParallelScale(); } else { var angle = radiansFromDegrees(camera.getViewAngle()); viewportHeight = 2.0 * distance * Math.tan(0.5 * angle); } var size = rwi.getView().getViewportSize(renderer); var 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 = function (i) { if (i === null) { return; } var renderer = model.interactor.getCurrentRenderer(); if (!renderer) { return; } model.currentImageNumber = i; function propMatch(j, prop, targetIndex) { if (prop.isA('vtkImageSlice') && j === targetIndex && prop.getPickable()) { return true; } return false; } var props = renderer.getViewProps(); var targetIndex = i; if (i < 0) { targetIndex += props.length; } var imageProp = null; var foundImageProp = false; for (var j = 0; j < props.length && !foundImageProp; j++) { if (propMatch(j, props[j], targetIndex)) { foundImageProp = true; imageProp = props[j]; } } if (imageProp) { publicAPI.setCurrentImageProperty(imageProp.getProperty()); } }; //---------------------------------------------------------------------------- publicAPI.setCurrentImageProperty = function (imageProperty) { model.currentImageProperty = imageProperty; }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- var DEFAULT_VALUES = { windowLevelStartPosition: [0, 0], windowLevelCurrentPosition: [0, 0], lastSlicePosition: 0, windowLevelInitial: [1.0, 0.5], currentImageProperty: 0, 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) { var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; Object.assign(model, DEFAULT_VALUES, initialValues); // Inheritance vtkInteractorStyleTrackballCamera.extend(publicAPI, model, initialValues); // Create get-set macros macro.setGet(publicAPI, model, ['interactionMode']); // For more macro methods, see "Sources/macro.js" // Object specific methods vtkInteractorStyleImage(publicAPI, model); } // ---------------------------------------------------------------------------- var newInstance = macro.newInstance(extend, 'vtkInteractorStyleImage'); // ---------------------------------------------------------------------------- var vtkInteractorStyleImage$1 = { newInstance: newInstance, extend: extend }; export default vtkInteractorStyleImage$1; export { extend, newInstance };