@thewtex/vtk.js-esm
Version:
Visualization Toolkit for the Web
574 lines (451 loc) • 24 kB
JavaScript
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 };