UNPKG

@thewtex/vtk.js-esm

Version:

Visualization Toolkit for the Web

574 lines (451 loc) 24 kB
import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray'; import _defineProperty from '@babel/runtime/helpers/defineProperty'; import _objectWithoutProperties from '@babel/runtime/helpers/objectWithoutProperties'; import _slicedToArray from '@babel/runtime/helpers/slicedToArray'; import { obj, setGet, get, event, proxy, proxyPropertyMapping, newInstance as newInstance$1, chain } from '../../macro.js'; import vtkAnnotatedCubeActor from '../../Rendering/Core/AnnotatedCubeActor.js'; import vtkAxesActor from '../../Rendering/Core/AxesActor.js'; import vtkCornerAnnotation from '../../Interaction/UI/CornerAnnotation.js'; import vtkInteractorStyleManipulator from '../../Interaction/Style/InteractorStyleManipulator.js'; import vtkMatrixBuilder from '../../Common/Core/MatrixBuilder.js'; import '../../Rendering/OpenGL/RenderWindow.js'; import '../../Rendering/WebGPU/RenderWindow.js'; import vtkOrientationMarkerWidget from '../../Interaction/Widgets/OrientationMarkerWidget.js'; import vtkRenderer from '../../Rendering/Core/Renderer.js'; import vtkRenderWindow from '../../Rendering/Core/RenderWindow.js'; import vtkRenderWindowInteractor from '../../Rendering/Core/RenderWindowInteractor.js'; import InteractionPresets from '../../Interaction/Style/InteractorStyleManipulator/Presets.js'; import AnnotatedCubePresets from '../../Rendering/Core/AnnotatedCubeActor/Presets.js'; var EPSILON = 0.000001; // ---------------------------------------------------------------------------- // vtkViewProxy methods // ---------------------------------------------------------------------------- function vtkViewProxy(publicAPI, model) { // Set our className model.classHierarchy.push('vtkViewProxy'); // Private -------------------------------------------------------------------- function updateAnnotationColor() { var _model$renderer$getBa = model.renderer.getBackground(), _model$renderer$getBa2 = _slicedToArray(_model$renderer$getBa, 3), r = _model$renderer$getBa2[0], g = _model$renderer$getBa2[1], b = _model$renderer$getBa2[2]; model.cornerAnnotation.getAnnotationContainer().style.color = r + g + b > 1.5 ? 'black' : 'white'; } // Setup -------------------------------------------------------------------- model.renderWindow = vtkRenderWindow.newInstance(); model.renderer = vtkRenderer.newInstance({ background: [0, 0, 0] }); model.renderWindow.addRenderer(model.renderer); model.openglRenderWindow = model.renderWindow.newAPISpecificView(); model.renderWindow.addView(model.openglRenderWindow); model.interactor = vtkRenderWindowInteractor.newInstance(); model.interactor.setView(model.openglRenderWindow); model.interactorStyle3D = vtkInteractorStyleManipulator.newInstance(); model.interactorStyle2D = vtkInteractorStyleManipulator.newInstance(); // Apply default interaction styles InteractionPresets.applyPreset('3D', model.interactorStyle3D); InteractionPresets.applyPreset('2D', model.interactorStyle2D); model.cornerAnnotation = vtkCornerAnnotation.newInstance(); // Setup interaction model.interactor.setInteractorStyle(model.useParallelRendering ? model.interactorStyle2D : model.interactorStyle3D); model.camera = model.renderer.getActiveCamera(); model.camera.setParallelProjection(!!model.useParallelRendering); // Orientation axis setup ------------------------------------------------- model.orientationAxesArrow = vtkAxesActor.newInstance(); model.orientationAxesCube = vtkAnnotatedCubeActor.newInstance(); AnnotatedCubePresets.applyPreset('default', model.orientationAxesCube); AnnotatedCubePresets.applyPreset('lps', model.orientationAxesCube); model.orientationAxesMap = { arrow: model.orientationAxesArrow, cube: model.orientationAxesCube }; model.orientationWidget = vtkOrientationMarkerWidget.newInstance({ actor: model.orientationAxesArrow, interactor: model.renderWindow.getInteractor() }); model.orientationWidget.setEnabled(true); model.orientationWidget.setViewportCorner(vtkOrientationMarkerWidget.Corners.BOTTOM_LEFT); model.orientationWidget.setViewportSize(0.1); // API ---------------------------------------------------------------------- publicAPI.setPresetToInteractor3D = function (nameOrDefinitions) { if (Array.isArray(nameOrDefinitions)) { return InteractionPresets.applyDefinitions(nameOrDefinitions, model.interactorStyle3D); } return InteractionPresets.applyPreset(nameOrDefinitions, model.interactorStyle3D); }; // -------------------------------------------------------------------------- publicAPI.setPresetToInteractor2D = function (nameOrDefinitions) { if (Array.isArray(nameOrDefinitions)) { return InteractionPresets.applyDefinitions(nameOrDefinitions, model.interactorStyle2D); } return InteractionPresets.applyPreset(nameOrDefinitions, model.interactorStyle2D); }; // -------------------------------------------------------------------------- publicAPI.setOrientationAxesType = function (type) { var actor = model.orientationAxesMap[type]; if (actor) { model.orientationAxesType = type; model.orientationWidget.setActor(actor); publicAPI.renderLater(); } }; // -------------------------------------------------------------------------- publicAPI.registerOrientationAxis = function (name, actor) { model.orientationAxesMap[name] = actor; }; // -------------------------------------------------------------------------- publicAPI.unregisterOrientationAxis = function (name) { delete model.orientationAxesMap[name]; }; // -------------------------------------------------------------------------- publicAPI.listOrientationAxis = function () { return Object.keys(model.orientationAxesMap); }; // -------------------------------------------------------------------------- publicAPI.setPresetToOrientationAxes = function (nameOrDefinitions) { var changeDetected = false; if (typeof nameOrDefinitions === 'string') { if (model.presetToOrientationAxes !== nameOrDefinitions) { model.presetToOrientationAxes = nameOrDefinitions; changeDetected = AnnotatedCubePresets.applyPreset(nameOrDefinitions, model.orientationAxesCube); publicAPI.modified(); } return changeDetected; } model.presetToOrientationAxes = 'Custom'; changeDetected = AnnotatedCubePresets.applyDefinitions(nameOrDefinitions, model.orientationAxesCube); publicAPI.modified(); return changeDetected; }; // -------------------------------------------------------------------------- publicAPI.setContainer = function (container) { if (model.container) { model.interactor.unbindEvents(model.container); model.openglRenderWindow.setContainer(null); model.cornerAnnotation.setContainer(null); } model.container = container; if (container) { model.openglRenderWindow.setContainer(container); model.cornerAnnotation.setContainer(container); model.interactor.initialize(); model.interactor.bindEvents(container); } }; // -------------------------------------------------------------------------- publicAPI.resize = function () { if (model.container) { var dims = model.container.getBoundingClientRect(); if (dims.width === dims.height && dims.width === 0) { return; } var devicePixelRatio = window.devicePixelRatio || 1; var width = Math.max(10, Math.floor(devicePixelRatio * dims.width)); var height = Math.max(10, Math.floor(devicePixelRatio * dims.height)); model.openglRenderWindow.setSize(width, height); publicAPI.invokeResize({ width: width, height: height }); publicAPI.renderLater(); } }; // -------------------------------------------------------------------------- publicAPI.renderLater = function () { publicAPI.render(false); }; // -------------------------------------------------------------------------- publicAPI.render = function () { var blocking = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; if (model.representations.length > 0 && model.resetCameraOnFirstRender) { model.resetCameraOnFirstRender = false; publicAPI.resetCamera(); } model.orientationWidget.updateMarkerOrientation(); model.renderer.resetCameraClippingRange(); if (blocking) { model.renderWindow.render(); } else { setTimeout(model.renderWindow.render, 0); } }; // -------------------------------------------------------------------------- publicAPI.addRepresentation = function (representation) { if (!representation) { return; } if (model.representations.indexOf(representation) === -1) { model.representations.push(representation); model.renderer.addViewProp(representation); } }; // -------------------------------------------------------------------------- publicAPI.removeRepresentation = function (representation) { if (!representation) { return; } if (model.representations.indexOf(representation) !== -1) { model.representations = model.representations.filter(function (r) { return r !== representation; }); model.renderer.removeViewProp(representation); } if (model.representations.length === 0) { model.resetCameraOnFirstRender = true; } }; // -------------------------------------------------------------------------- publicAPI.resetCamera = function () { model.renderer.resetCamera(); model.renderer.resetCameraClippingRange(); model.interactorStyle2D.setCenterOfRotation(model.camera.getFocalPoint()); model.interactorStyle3D.setCenterOfRotation(model.camera.getFocalPoint()); publicAPI.renderLater(); }; // -------------------------------------------------------------------------- publicAPI.captureImage = function () { var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, _ref$format = _ref.format, format = _ref$format === void 0 ? 'image/png' : _ref$format, opts = _objectWithoutProperties(_ref, ["format"]); return model.renderWindow.captureImages(format, opts)[0]; }; // -------------------------------------------------------------------------- publicAPI.openCaptureImage = function () { var target = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '_blank'; var image = new Image(); return publicAPI.captureImage().then(function (imageURL) { image.src = imageURL; var w = window.open('', target); w.document.write(image.outerHTML); w.document.title = 'vtk.js Image Capture'; window.focus(); }); }; // -------------------------------------------------------------------------- publicAPI.setCornerAnnotation = function (corner, templateString) { model.cornerAnnotation.updateTemplates(_defineProperty({}, corner, function (meta) { return vtkCornerAnnotation.applyTemplate(templateString, meta); })); }; // -------------------------------------------------------------------------- publicAPI.setCornerAnnotations = function (annotations) { var useTemplateString = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; if (useTemplateString) { Object.keys(annotations).forEach(function (key) { publicAPI.setCornerAnnotation(key, annotations[key]); }); } else { model.cornerAnnotation.updateTemplates(annotations); } }; // -------------------------------------------------------------------------- publicAPI.updateCornerAnnotation = function (meta) { return model.cornerAnnotation.updateMetadata(meta); }; // -------------------------------------------------------------------------- publicAPI.setAnnotationOpacity = function (opacity) { if (model.annotationOpacity !== Number(opacity)) { model.annotationOpacity = Number(opacity); model.cornerAnnotation.getAnnotationContainer().style.opacity = opacity; publicAPI.modified(); } }; // -------------------------------------------------------------------------- publicAPI.setBackground = chain(model.renderer.setBackground, updateAnnotationColor); // -------------------------------------------------------------------------- publicAPI.getBackground = model.renderer.getBackground; // -------------------------------------------------------------------------- publicAPI.setAnimation = function (enable) { var requester = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : publicAPI; if (model.disableAnimation && enable) { return; } if (enable) { model.renderWindow.getInteractor().requestAnimation(requester); } else { var skipWarning = requester === publicAPI || "".concat(requester).indexOf('ViewProxy.moveCamera.') === 0; model.renderWindow.getInteractor().cancelAnimation(requester, skipWarning); } }; // -------------------------------------------------------------------------- publicAPI.updateOrientation = function (axisIndex, orientation, viewUp) { var _model$camera, _model$camera2, _model$camera3, _model$camera4, _model$camera5; var animateSteps = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; if (axisIndex === undefined) { return Promise.resolve(); } var originalPosition = model.camera.getPosition(); var originalViewUp = model.camera.getViewUp(); var originalFocalPoint = model.camera.getFocalPoint(); model.axis = axisIndex; model.orientation = orientation; model.viewUp = viewUp; var position = model.camera.getFocalPoint(); position[model.axis] += model.orientation; (_model$camera = model.camera).setPosition.apply(_model$camera, _toConsumableArray(position)); (_model$camera2 = model.camera).setViewUp.apply(_model$camera2, _toConsumableArray(viewUp)); model.renderer.resetCamera(); var destFocalPoint = model.camera.getFocalPoint(); var destPosition = model.camera.getPosition(); var destViewUp = model.camera.getViewUp(); // Reset to original to prevent initial render flash (_model$camera3 = model.camera).setFocalPoint.apply(_model$camera3, _toConsumableArray(originalFocalPoint)); (_model$camera4 = model.camera).setPosition.apply(_model$camera4, _toConsumableArray(originalPosition)); (_model$camera5 = model.camera).setViewUp.apply(_model$camera5, _toConsumableArray(originalViewUp)); return publicAPI.moveCamera(destFocalPoint, destPosition, destViewUp, animateSteps); }; // -------------------------------------------------------------------------- publicAPI.moveCamera = function (focalPoint, position, viewUp) { var animateSteps = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; var originalFocalPoint = model.camera.getFocalPoint(); var originalPosition = model.camera.getPosition(); var originalViewUp = model.camera.getViewUp(); var animationStack = [{ focalPoint: focalPoint, position: position, viewUp: viewUp }]; if (animateSteps) { var deltaFocalPoint = [(originalFocalPoint[0] - focalPoint[0]) / animateSteps, (originalFocalPoint[1] - focalPoint[1]) / animateSteps, (originalFocalPoint[2] - focalPoint[2]) / animateSteps]; var deltaPosition = [(originalPosition[0] - position[0]) / animateSteps, (originalPosition[1] - position[1]) / animateSteps, (originalPosition[2] - position[2]) / animateSteps]; var deltaViewUp = [(originalViewUp[0] - viewUp[0]) / animateSteps, (originalViewUp[1] - viewUp[1]) / animateSteps, (originalViewUp[2] - viewUp[2]) / animateSteps]; var needSteps = deltaFocalPoint[0] || deltaFocalPoint[1] || deltaFocalPoint[2] || deltaPosition[0] || deltaPosition[1] || deltaPosition[2] || deltaViewUp[0] || deltaViewUp[1] || deltaViewUp[2]; var focalPointDeltaAxisCount = deltaFocalPoint.map(function (i) { return Math.abs(i) < EPSILON ? 0 : 1; }).reduce(function (a, b) { return a + b; }, 0); var positionDeltaAxisCount = deltaPosition.map(function (i) { return Math.abs(i) < EPSILON ? 0 : 1; }).reduce(function (a, b) { return a + b; }, 0); var viewUpDeltaAxisCount = deltaViewUp.map(function (i) { return Math.abs(i) < EPSILON ? 0 : 1; }).reduce(function (a, b) { return a + b; }, 0); var rotation180Only = viewUpDeltaAxisCount === 1 && positionDeltaAxisCount === 0 && focalPointDeltaAxisCount === 0; if (needSteps) { if (rotation180Only) { var availableAxes = originalFocalPoint.map(function (fp, i) { return Math.abs(originalPosition[i] - fp) < EPSILON ? i : null; }).filter(function (i) { return i !== null; }); var axisCorrectionIndex = availableAxes.find(function (v) { return Math.abs(deltaViewUp[v]) < EPSILON; }); for (var i = 0; i < animateSteps; i++) { var newViewUp = [viewUp[0] + (i + 1) * deltaViewUp[0], viewUp[1] + (i + 1) * deltaViewUp[1], viewUp[2] + (i + 1) * deltaViewUp[2]]; newViewUp[axisCorrectionIndex] = Math.sin(Math.PI * i / (animateSteps - 1)); animationStack.push({ focalPoint: focalPoint, position: position, viewUp: newViewUp }); } } else { for (var _i = 0; _i < animateSteps; _i++) { animationStack.push({ focalPoint: [focalPoint[0] + (_i + 1) * deltaFocalPoint[0], focalPoint[1] + (_i + 1) * deltaFocalPoint[1], focalPoint[2] + (_i + 1) * deltaFocalPoint[2]], position: [position[0] + (_i + 1) * deltaPosition[0], position[1] + (_i + 1) * deltaPosition[1], position[2] + (_i + 1) * deltaPosition[2]], viewUp: [viewUp[0] + (_i + 1) * deltaViewUp[0], viewUp[1] + (_i + 1) * deltaViewUp[1], viewUp[2] + (_i + 1) * deltaViewUp[2]] }); } } } } if (animationStack.length === 1) { // update camera directly model.camera.set(animationStack.pop()); model.renderer.resetCameraClippingRange(); if (model.interactor.getLightFollowCamera()) { model.renderer.updateLightsGeometryToFollowCamera(); } return Promise.resolve(); } return new Promise(function (resolve, reject) { var now = performance.now().toString(); var animationRequester = "ViewProxy.moveCamera.".concat(now); publicAPI.setAnimation(true, animationRequester); var intervalId = null; var consumeAnimationStack = function consumeAnimationStack() { if (animationStack.length) { var _model$camera6, _model$camera7, _model$camera8; var _animationStack$pop = animationStack.pop(), cameraFocalPoint = _animationStack$pop.focalPoint, cameraPosition = _animationStack$pop.position, cameraViewUp = _animationStack$pop.viewUp; (_model$camera6 = model.camera).setFocalPoint.apply(_model$camera6, _toConsumableArray(cameraFocalPoint)); (_model$camera7 = model.camera).setPosition.apply(_model$camera7, _toConsumableArray(cameraPosition)); (_model$camera8 = model.camera).setViewUp.apply(_model$camera8, _toConsumableArray(cameraViewUp)); model.renderer.resetCameraClippingRange(); if (model.interactor.getLightFollowCamera()) { model.renderer.updateLightsGeometryToFollowCamera(); } } else { clearInterval(intervalId); publicAPI.setAnimation(false, animationRequester); resolve(); } }; intervalId = setInterval(consumeAnimationStack, 1); }); }; // -------------------------------------------------------------------------- publicAPI.resetOrientation = function () { var animateSteps = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; return publicAPI.updateOrientation(model.axis, model.orientation, model.viewUp, animateSteps); }; // -------------------------------------------------------------------------- publicAPI.rotate = function (angle) { var _model$camera9; var _model$camera$get = model.camera.get('viewUp', 'focalPoint', 'position'), viewUp = _model$camera$get.viewUp, focalPoint = _model$camera$get.focalPoint, position = _model$camera$get.position; var axis = [focalPoint[0] - position[0], focalPoint[1] - position[1], focalPoint[2] - position[2]]; vtkMatrixBuilder.buildFromDegree().rotate(Number.isNaN(angle) ? 90 : angle, axis).apply(viewUp); (_model$camera9 = model.camera).setViewUp.apply(_model$camera9, _toConsumableArray(viewUp)); model.camera.modified(); model.orientationWidget.updateMarkerOrientation(); model.renderWindow.render(); }; // -------------------------------------------------------------------------- publicAPI.focusTo = chain(model.camera.setFocalPoint, model.interactorStyle2D.setCenterOfRotation, model.interactorStyle3D.setCenterOfRotation); // -------------------------------------------------------------------------- publicAPI.delete = chain(publicAPI.setContainer, model.openglRenderWindow.delete, publicAPI.delete); // -------------------------------------------------------------------------- // Initialization from state or input // -------------------------------------------------------------------------- publicAPI.resetOrientation(); updateAnnotationColor(); } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- var DEFAULT_VALUES = { representations: [], sectionName: 'view', annotationOpacity: 1, resetCameraOnFirstRender: true, presetToOrientationAxes: 'lps', orientationAxesType: 'arrow', disableAnimation: false, axis: 1, orientation: 0, viewUp: [0, 0, 1] }; // ---------------------------------------------------------------------------- function extend(publicAPI, model) { var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; Object.assign(model, DEFAULT_VALUES, initialValues); obj(publicAPI, model); setGet(publicAPI, model, ['name', 'disableAnimation']); get(publicAPI, model, ['annotationOpacity', 'camera', 'container', 'cornerAnnotation', 'interactor', 'interactorStyle2D', 'interactorStyle3D', 'openglRenderWindow', // todo breaking? convert to apiSpecificWindow 'orientationAxesType', 'presetToOrientationAxes', 'renderer', 'renderWindow', 'representations', 'useParallelRendering']); event(publicAPI, model, 'Resize'); // Object specific methods vtkViewProxy(publicAPI, model); // Proxy handling proxy(publicAPI, model); proxyPropertyMapping(publicAPI, model, { orientationAxesVisibility: { modelKey: 'orientationWidget', property: 'enabled' }, orientationAxesCorner: { modelKey: 'orientationWidget', property: 'viewportCorner' }, orientationAxesSize: { modelKey: 'orientationWidget', property: 'viewportSize' }, cameraViewUp: { modelKey: 'camera', property: 'viewUp', modified: false }, cameraPosition: { modelKey: 'camera', property: 'position', modified: false }, cameraFocalPoint: { modelKey: 'camera', property: 'focalPoint', modified: false } }); } // ---------------------------------------------------------------------------- var newInstance = newInstance$1(extend, 'vtkViewProxy'); // ---------------------------------------------------------------------------- var vtkViewProxy$1 = { newInstance: newInstance, extend: extend }; export default vtkViewProxy$1; export { newInstance };