UNPKG

@thewtex/vtk.js-esm

Version:

Visualization Toolkit for the Web

262 lines (207 loc) 9.96 kB
import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray'; import macro from '../../macro.js'; import { l as normalize, f as distance2BetweenPoints } from '../../Common/Core/Math/index.js'; import vtkMatrixBuilder from '../../Common/Core/MatrixBuilder.js'; import vtkInteractorStyleManipulator from './InteractorStyleManipulator.js'; import vtkMouseCameraTrackballRotateManipulator from '../Manipulators/MouseCameraTrackballRotateManipulator.js'; import vtkMouseCameraTrackballPanManipulator from '../Manipulators/MouseCameraTrackballPanManipulator.js'; import vtkMouseCameraTrackballZoomManipulator from '../Manipulators/MouseCameraTrackballZoomManipulator.js'; import vtkMouseRangeManipulator from '../Manipulators/MouseRangeManipulator.js'; // Global methods // ---------------------------------------------------------------------------- function boundsToCorners(bounds) { return [[bounds[0], bounds[2], bounds[4]], [bounds[0], bounds[2], bounds[5]], [bounds[0], bounds[3], bounds[4]], [bounds[0], bounds[3], bounds[5]], [bounds[1], bounds[2], bounds[4]], [bounds[1], bounds[2], bounds[5]], [bounds[1], bounds[3], bounds[4]], [bounds[1], bounds[3], bounds[5]]]; } // ---------------------------------------------------------------------------- function clamp(value, min, max) { if (value < min) { return min; } if (value > max) { return max; } return value; } // ---------------------------------------------------------------------------- // vtkInteractorStyleMPRSlice methods // ---------------------------------------------------------------------------- function vtkInteractorStyleMPRSlice(publicAPI, model) { // Set our className model.classHierarchy.push('vtkInteractorStyleMPRSlice'); model.trackballManipulator = vtkMouseCameraTrackballRotateManipulator.newInstance({ button: 1 }); model.panManipulator = vtkMouseCameraTrackballPanManipulator.newInstance({ button: 1, shift: true }); model.zoomManipulator = vtkMouseCameraTrackballZoomManipulator.newInstance({ button: 3 }); model.scrollManipulator = vtkMouseRangeManipulator.newInstance({ scrollEnabled: true, dragEnabled: false }); // cache for sliceRange var cache = { sliceNormal: [0, 0, 0], sliceRange: [0, 0] }; var cameraSub = null; function updateScrollManipulator() { var range = publicAPI.getSliceRange(); model.scrollManipulator.removeScrollListener(); model.scrollManipulator.setScrollListener(range[0], range[1], 1, publicAPI.getSlice, publicAPI.setSlice); } function setManipulators() { publicAPI.removeAllMouseManipulators(); publicAPI.addMouseManipulator(model.trackballManipulator); publicAPI.addMouseManipulator(model.panManipulator); publicAPI.addMouseManipulator(model.zoomManipulator); publicAPI.addMouseManipulator(model.scrollManipulator); updateScrollManipulator(); } var superSetInteractor = publicAPI.setInteractor; publicAPI.setInteractor = function (interactor) { superSetInteractor(interactor); if (cameraSub) { cameraSub.unsubscribe(); cameraSub = null; } if (interactor) { var renderer = interactor.getCurrentRenderer(); var camera = renderer.getActiveCamera(); cameraSub = camera.onModified(function () { updateScrollManipulator(); publicAPI.modified(); }); } }; publicAPI.handleMouseMove = macro.chain(publicAPI.handleMouseMove, function () { var renderer = model.interactor.getCurrentRenderer(); var camera = renderer.getActiveCamera(); var dist = camera.getDistance(); camera.setClippingRange(dist, dist + 0.1); }); var superSetVolumeMapper = publicAPI.setVolumeMapper; publicAPI.setVolumeMapper = function (mapper) { if (superSetVolumeMapper(mapper)) { var renderer = model.interactor.getCurrentRenderer(); var camera = renderer.getActiveCamera(); if (mapper) { // prevent zoom manipulator from messing with our focal point camera.setFreezeFocalPoint(true); publicAPI.setSliceNormal.apply(publicAPI, _toConsumableArray(publicAPI.getSliceNormal())); } else { camera.setFreezeFocalPoint(false); } } }; publicAPI.getSlice = function () { var renderer = model.interactor.getCurrentRenderer(); var camera = renderer.getActiveCamera(); var sliceNormal = publicAPI.getSliceNormal(); // Get rotation matrix from normal to +X (since bounds is aligned to XYZ) var transform = vtkMatrixBuilder.buildFromDegree().identity().rotateFromDirections(sliceNormal, [1, 0, 0]); var fp = camera.getFocalPoint(); transform.apply(fp); return fp[0]; }; publicAPI.setSlice = function (slice) { var renderer = model.interactor.getCurrentRenderer(); var camera = renderer.getActiveCamera(); if (model.volumeMapper) { var range = publicAPI.getSliceRange(); var bounds = model.volumeMapper.getBounds(); var clampedSlice = clamp.apply(void 0, [slice].concat(_toConsumableArray(range))); var center = [(bounds[0] + bounds[1]) / 2.0, (bounds[2] + bounds[3]) / 2.0, (bounds[4] + bounds[5]) / 2.0]; var distance = camera.getDistance(); var dop = camera.getDirectionOfProjection(); normalize(dop); var midPoint = (range[1] + range[0]) / 2.0; var zeroPoint = [center[0] - dop[0] * midPoint, center[1] - dop[1] * midPoint, center[2] - dop[2] * midPoint]; var slicePoint = [zeroPoint[0] + dop[0] * clampedSlice, zeroPoint[1] + dop[1] * clampedSlice, zeroPoint[2] + dop[2] * clampedSlice]; var newPos = [slicePoint[0] - dop[0] * distance, slicePoint[1] - dop[1] * distance, slicePoint[2] - dop[2] * distance]; camera.setPosition.apply(camera, newPos); camera.setFocalPoint.apply(camera, slicePoint); } }; publicAPI.getSliceRange = function () { if (model.volumeMapper) { var sliceNormal = publicAPI.getSliceNormal(); if (sliceNormal[0] === cache.sliceNormal[0] && sliceNormal[1] === cache.sliceNormal[1] && sliceNormal[2] === cache.sliceNormal[2]) { return cache.sliceRange; } var bounds = model.volumeMapper.getBounds(); var points = boundsToCorners(bounds); // Get rotation matrix from normal to +X (since bounds is aligned to XYZ) var transform = vtkMatrixBuilder.buildFromDegree().identity().rotateFromDirections(sliceNormal, [1, 0, 0]); points.forEach(function (pt) { return transform.apply(pt); }); // range is now maximum X distance var minX = Infinity; var maxX = -Infinity; for (var i = 0; i < 8; i++) { var x = points[i][0]; if (x > maxX) { maxX = x; } if (x < minX) { minX = x; } } cache.sliceNormal = sliceNormal; cache.sliceRange = [minX, maxX]; return cache.sliceRange; } return [0, 0]; }; // Slice normal is just camera DOP publicAPI.getSliceNormal = function () { if (model.volumeMapper) { var renderer = model.interactor.getCurrentRenderer(); var camera = renderer.getActiveCamera(); return camera.getDirectionOfProjection(); } return [0, 0, 0]; }; // in world space publicAPI.setSliceNormal = function () { var renderer = model.interactor.getCurrentRenderer(); var camera = renderer.getActiveCamera(); for (var _len = arguments.length, normal = new Array(_len), _key = 0; _key < _len; _key++) { normal[_key] = arguments[_key]; } normalize(normal); if (model.volumeMapper) { var bounds = model.volumeMapper.getBounds(); // diagonal will be used as "width" of camera scene var diagonal = Math.sqrt(distance2BetweenPoints([bounds[0], bounds[2], bounds[4]], [bounds[1], bounds[3], bounds[5]])); // center will be used as initial focal point var center = [(bounds[0] + bounds[1]) / 2.0, (bounds[2] + bounds[3]) / 2.0, (bounds[4] + bounds[5]) / 2.0]; var angle = 90; // distance from camera to focal point var dist = diagonal / (2 * Math.tan(angle / 360 * Math.PI)); var cameraPos = [center[0] - normal[0] * dist, center[1] - normal[1] * dist, center[2] - normal[2] * dist]; // set viewUp based on DOP rotation var oldDop = camera.getDirectionOfProjection(); var transform = vtkMatrixBuilder.buildFromDegree().identity().rotateFromDirections(oldDop, normal); var viewUp = [0, 1, 0]; transform.apply(viewUp); camera.setPosition.apply(camera, cameraPos); camera.setDistance(dist); // should be set after pos and distance camera.setDirectionOfProjection.apply(camera, normal); camera.setViewUp.apply(camera, viewUp); camera.setViewAngle(angle); camera.setClippingRange(dist, dist + 0.1); publicAPI.setCenterOfRotation(center); } }; setManipulators(); } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- var DEFAULT_VALUES = {}; // ---------------------------------------------------------------------------- function extend(publicAPI, model) { var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; Object.assign(model, DEFAULT_VALUES, initialValues); // Inheritance vtkInteractorStyleManipulator.extend(publicAPI, model, initialValues); macro.setGet(publicAPI, model, ['volumeMapper']); // Object specific methods vtkInteractorStyleMPRSlice(publicAPI, model); } // ---------------------------------------------------------------------------- var newInstance = macro.newInstance(extend, 'vtkInteractorStyleMPRSlice'); // ---------------------------------------------------------------------------- var vtkInteractorStyleMPRSlice$1 = { newInstance: newInstance, extend: extend }; export default vtkInteractorStyleMPRSlice$1; export { extend, newInstance };