UNPKG

@thewtex/vtk.js-esm

Version:

Visualization Toolkit for the Web

668 lines (541 loc) 22.9 kB
import _defineProperty from '@babel/runtime/helpers/defineProperty'; import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray'; import _slicedToArray from '@babel/runtime/helpers/slicedToArray'; import { capitalize } from '../../../macro.js'; import vtkActor from '../../Core/Actor.js'; import vtkCamera from '../../Core/Camera.js'; import vtkColorTransferFunction from '../../Core/ColorTransferFunction.js'; import vtkDataArray from '../../../Common/Core/DataArray.js'; import vtkPoints from '../../../Common/Core/Points.js'; import vtkCellArray from '../../../Common/Core/CellArray.js'; import vtkGlyph3DMapper from '../../Core/Glyph3DMapper.js'; import vtkLight from '../../Core/Light.js'; import vtkLookupTable from '../../../Common/Core/LookupTable.js'; import vtkMapper from '../../Core/Mapper.js'; import vtkPolyData from '../../../Common/DataModel/PolyData.js'; import vtkImageData from '../../../Common/DataModel/ImageData.js'; import vtkProperty from '../../Core/Property.js'; import vtkRenderer from '../../Core/Renderer.js'; import vtkRenderWindow from '../../Core/RenderWindow.js'; import vtkTexture from '../../Core/Texture.js'; import vtkVolume from '../../Core/Volume.js'; import vtkVolumeMapper from '../../Core/VolumeMapper.js'; import vtkVolumeProperty from '../../Core/VolumeProperty.js'; import vtkImageSlice from '../../Core/ImageSlice.js'; import vtkImageMapper from '../../Core/ImageMapper.js'; import vtkImageProperty from '../../Core/ImageProperty.js'; import vtkPiecewiseFunction from '../../../Common/DataModel/PiecewiseFunction.js'; function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } // Some internal, module-level variables and methods // ---------------------------------------------------------------------------- var TYPE_HANDLERS = {}; var WRAPPED_ID_RE = /instance:\${([^}]+)}/; var WRAP_ID = function WRAP_ID(id) { return "instance:${".concat(id, "}"); }; var ONE_TIME_INSTANCE_TRACKERS = {}; var SKIPPED_INSTANCE_IDS = []; var EXCLUDE_INSTANCE_MAP = {}; var DATA_ARRAY_MAPPER = { vtkPoints: vtkPoints, vtkCellArray: vtkCellArray, vtkDataArray: vtkDataArray }; // ---------------------------------------------------------------------------- function extractCallArgs(synchronizerContext, argList) { return argList.map(function (arg) { var m = WRAPPED_ID_RE.exec(arg); if (m) { return synchronizerContext.getInstance(m[1]); } return arg; }); } // ---------------------------------------------------------------------------- function extractInstanceIds(argList) { return argList.map(function (arg) { return WRAPPED_ID_RE.exec(arg); }).filter(function (m) { return m; }).map(function (m) { return m[1]; }); } // ---------------------------------------------------------------------------- function extractDependencyIds(state) { var depList = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; if (state.dependencies) { state.dependencies.forEach(function (childState) { depList.push(childState.id); extractDependencyIds(childState, depList); }); } return depList; } // ---------------------------------------------------------------------------- function bindArrays(arraysToBind) { while (arraysToBind.length) { var _arraysToBind$shift = arraysToBind.shift(), _arraysToBind$shift2 = _slicedToArray(_arraysToBind$shift, 2), fn = _arraysToBind$shift2[0], args = _arraysToBind$shift2[1]; fn.apply(void 0, _toConsumableArray(args)); } } // ---------------------------------------------------------------------------- function createNewArrayHandler(instance, arrayMetadata, arraysToBind) { return function (values) { var vtkClass = arrayMetadata.vtkClass ? arrayMetadata.vtkClass : 'vtkDataArray'; var array = DATA_ARRAY_MAPPER[vtkClass].newInstance(_objectSpread(_objectSpread({}, arrayMetadata), {}, { values: values })); var regMethod = arrayMetadata.registration ? arrayMetadata.registration : 'addArray'; var location = arrayMetadata.location ? instance.getReferenceByName(arrayMetadata.location) : instance; arraysToBind.push([location[regMethod], [array]]); return array; }; } // ---------------------------------------------------------------------------- // Static methods for export // ---------------------------------------------------------------------------- function update(type, instance, props, context) { if (!instance) { return; } var handler = TYPE_HANDLERS[type]; if (handler && handler.update) { handler.update(instance, props, context); } else { console.log('no updater for', type); } } // ---------------------------------------------------------------------------- function build(type) { var initialProps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var handler = TYPE_HANDLERS[type]; if (handler && handler.build) { // DEBUG console.log(`new ${type} - ${initialProps.managedInstanceId}`); return handler.build(initialProps); } console.log('No builder for', type); return null; } // ---------------------------------------------------------------------------- function excludeInstance(type, propertyName, propertyValue) { EXCLUDE_INSTANCE_MAP[type] = { key: propertyName, value: propertyValue }; } // ---------------------------------------------------------------------------- function getSupportedTypes() { return Object.keys(TYPE_HANDLERS); } // ---------------------------------------------------------------------------- function clearTypeMapping() { Object.keys(TYPE_HANDLERS).forEach(function (key) { delete TYPE_HANDLERS[key]; }); } // ---------------------------------------------------------------------------- function updateRenderWindow(instance, props, context) { return update('vtkRenderWindow', instance, props, context); } // ---------------------------------------------------------------------------- function clearAllOneTimeUpdaters() { Object.keys(ONE_TIME_INSTANCE_TRACKERS).forEach(function (key) { delete ONE_TIME_INSTANCE_TRACKERS[key]; }); } // ---------------------------------------------------------------------------- function clearOneTimeUpdaters() { for (var _len = arguments.length, ids = new Array(_len), _key = 0; _key < _len; _key++) { ids[_key] = arguments[_key]; } if (ids.length === 0) { return clearAllOneTimeUpdaters(); } var array = ids; // allow an array passed as a single arg. if (array.length === 1 && Array.isArray(array[0])) { array = array[0]; } array.forEach(function (instanceId) { delete ONE_TIME_INSTANCE_TRACKERS[instanceId]; }); return array; } // ---------------------------------------------------------------------------- function notSkippedInstance(call) { if (call[1].length === 1) { return SKIPPED_INSTANCE_IDS.indexOf(call[1][0]) === -1; } var keep = false; for (var i = 0; i < call[1].length; i++) { keep = keep || SKIPPED_INSTANCE_IDS.indexOf(call[1][i]) === -1; } return keep; } // ---------------------------------------------------------------------------- // Updater functions // ---------------------------------------------------------------------------- function genericUpdater(instance, state, context) { context.start(); // -> start(generic-updater) // First update our own properties instance.set(state.properties); // Now handle dependencies if (state.dependencies) { state.dependencies.forEach(function (childState) { var id = childState.id, type = childState.type; if (EXCLUDE_INSTANCE_MAP[type]) { var _EXCLUDE_INSTANCE_MAP = EXCLUDE_INSTANCE_MAP[type], key = _EXCLUDE_INSTANCE_MAP.key, value = _EXCLUDE_INSTANCE_MAP.value; if (!key || childState.properties[key] === value) { SKIPPED_INSTANCE_IDS.push(WRAP_ID(id)); return; } } var childInstance = context.getInstance(id); if (!childInstance) { childInstance = build(type, { managedInstanceId: id }); context.registerInstance(id, childInstance); } update(type, childInstance, childState, context); }); } if (state.calls) { state.calls.filter(notSkippedInstance).forEach(function (call) { // DEBUG console.log('==>', call[0], extractCallArgs(context, call[1])); instance[call[0]].apply(null, extractCallArgs(context, call[1])); }); } // if some arrays need to be be fetch if (state.arrays) { var arraysToBind = []; var promises = state.arrays.map(function (arrayMetadata) { context.start(); // -> start(arrays) return context.getArray(arrayMetadata.hash, arrayMetadata.dataType, context).then(createNewArrayHandler(instance, arrayMetadata, arraysToBind)).catch(function (error) { console.log('Error fetching array', JSON.stringify(arrayMetadata), error); }).finally(context.end); // -> end(arrays) }); context.start(); // -> start(arraysToBind) Promise.all(promises).then(function () { bindArrays(arraysToBind); }).catch(function (error) { console.error('Error in array handling for state', JSON.stringify(state), error); }).finally(context.end); // -> end(arraysToBind) } context.end(); // -> end(generic-updater) } // ---------------------------------------------------------------------------- function oneTimeGenericUpdater(instance, state, context) { if (!ONE_TIME_INSTANCE_TRACKERS[state.id]) { genericUpdater(instance, state, context); } ONE_TIME_INSTANCE_TRACKERS[state.id] = true; } // ---------------------------------------------------------------------------- function rendererUpdater(instance, state, context) { // Don't do start/end on the context here because we don't need to hold up // rendering for the book-keeping we do after the genericUpdater finishes. // First allow generic update process to happen as usual genericUpdater(instance, state, context); // Any view props that were removed in the previous phase, genericUpdater(...), // may have left orphaned children in our instance cache. Below is where those // refs can be tracked in the first place, and then later removed as necessary // to allow garbage collection. // In some cases, seemingly with 'vtkColorTransferFunction', the server side // object id may be conserved even though the actor and mapper containing or // using it were deleted. In this case we must not unregister an instance // which is depended upon by an incoming actor just because it was also // depended upon by an outgoing one. var allActorsDeps = new Set(); // Here we gather the list of dependencies (instance ids) for each view prop and // store them on the instance, in case that view prop is later removed. if (state.dependencies) { state.dependencies.forEach(function (childState) { var viewPropInstance = context.getInstance(childState.id); if (viewPropInstance) { var flattenedDepIds = extractDependencyIds(childState); viewPropInstance.set({ flattenedDepIds: flattenedDepIds }, true); flattenedDepIds.forEach(function (depId) { return allActorsDeps.add(depId); }); } }); } // Look for 'removeViewProp' calls and clean up references to dependencies of // those view props. var unregisterCandidates = new Set(); if (state.calls) { state.calls.filter(notSkippedInstance).filter(function (call) { return call[0] === 'removeViewProp'; }).forEach(function (call) { // extract any ids associated with a 'removeViewProp' call (though really there // should just be a single one), and use them to build a flat list of all // representation dependency ids which we can then use our synchronizer context // to unregister extractInstanceIds(call[1]).forEach(function (vpId) { var deps = context.getInstance(vpId).get('flattenedDepIds').flattenedDepIds; if (deps) { // Consider each dependency for un-registering deps.forEach(function (depId) { return unregisterCandidates.add(depId); }); } // Consider the viewProp itself for un-registering unregisterCandidates.add(vpId); }); }); } // Now unregister any instances that are no longer needed var idsToUnregister = _toConsumableArray(unregisterCandidates).filter(function (depId) { return !allActorsDeps.has(depId); }); idsToUnregister.forEach(function (depId) { return context.unregisterInstance(depId); }); } // ---------------------------------------------------------------------------- function vtkRenderWindowUpdater(instance, state, context) { // For each renderer we may be removing from this render window, we should first // remove all of the renderer's view props, then have the render window re-render // itself. This will clear the screen, at which point we can go about the normal // updater process. if (state.calls) { state.calls.filter(notSkippedInstance).filter(function (call) { return call[0] === 'removeRenderer'; }).forEach(function (call) { extractInstanceIds(call[1]).forEach(function (renId) { var renderer = context.getInstance(renId); // Take brief detour through the view props to unregister the dependencies // of each one var viewProps = renderer.getViewProps(); viewProps.forEach(function (viewProp) { var deps = viewProp.get('flattenedDepIds').flattenedDepIds; if (deps) { deps.forEach(function (depId) { return context.unregisterInstance(depId); }); } context.unregisterInstance(context.getInstanceId(viewProp)); }); // Now just remove all the view props renderer.removeAllViewProps(); }); }); } instance.render(); // Now just do normal update process genericUpdater(instance, state, context); } // ---------------------------------------------------------------------------- function colorTransferFunctionUpdater(instance, state, context) { context.start(); // -> start(colorTransferFunctionUpdater) var nodes = state.properties.nodes.map(function (_ref) { var _ref2 = _slicedToArray(_ref, 6), x = _ref2[0], r = _ref2[1], g = _ref2[2], b = _ref2[3], midpoint = _ref2[4], sharpness = _ref2[5]; return { x: x, r: r, g: g, b: b, midpoint: midpoint, sharpness: sharpness }; }); instance.set(_objectSpread(_objectSpread({}, state.properties), {}, { nodes: nodes }), true); instance.sortAndUpdateRange(); instance.modified(); context.end(); // -> end(colorTransferFunctionUpdater) } function piecewiseFunctionUpdater(instance, state, context) { context.start(); // -> start(piecewiseFunctionUpdater) var nodes = state.properties.nodes.map(function (_ref3) { var _ref4 = _slicedToArray(_ref3, 4), x = _ref4[0], y = _ref4[1], midpoint = _ref4[2], sharpness = _ref4[3]; return { x: x, y: y, midpoint: midpoint, sharpness: sharpness }; }); instance.set(_objectSpread(_objectSpread({}, state.properties), {}, { nodes: nodes }), true); instance.sortAndUpdateRange(); instance.modified(); context.end(); // -> end(piecewiseFunctionUpdater) } // ---------------------------------------------------------------------------- function createDataSetUpdate() { var piecesToFetch = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; return function (instance, state, context) { var _state$arrays; context.start(); // -> start(dataset-update) // Make sure we provide container for std arrays if (!state.arrays) { state.arrays = []; } // Array members // => convert old format to generic state.arrays piecesToFetch.forEach(function (key) { if (state.properties[key]) { var arrayMeta = state.properties[key]; arrayMeta.registration = "set".concat(capitalize(key)); state.arrays.push(arrayMeta); delete state.properties[key]; } }); // Extract dataset fields var fieldsArrays = state.properties.fields || []; (_state$arrays = state.arrays).push.apply(_state$arrays, _toConsumableArray(fieldsArrays)); delete state.properties.fields; // Reset any pre-existing fields array instance.getPointData().removeAllArrays(); instance.getCellData().removeAllArrays(); // Generic handling genericUpdater(instance, state, context); // Finish what we started context.end(); // -> end(dataset-update) }; } var polydataUpdater = createDataSetUpdate(['points', 'polys', 'verts', 'lines', 'strips']); var imageDataUpdater = createDataSetUpdate([]); // ---------------------------------------------------------------------------- // Construct the type mapping // ---------------------------------------------------------------------------- function setTypeMapping(type) { var buildFn = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; var updateFn = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : genericUpdater; if (!build && !update) { delete TYPE_HANDLERS[type]; return; } TYPE_HANDLERS[type] = { build: buildFn, update: updateFn }; } // ---------------------------------------------------------------------------- var DEFAULT_ALIASES = { vtkMapper: ['vtkOpenGLPolyDataMapper', 'vtkCompositePolyDataMapper2', 'vtkDataSetMapper'], vtkProperty: ['vtkOpenGLProperty'], vtkRenderer: ['vtkOpenGLRenderer'], vtkCamera: ['vtkOpenGLCamera'], vtkColorTransferFunction: ['vtkPVDiscretizableColorTransferFunction'], vtkActor: ['vtkOpenGLActor', 'vtkPVLODActor'], vtkLight: ['vtkOpenGLLight', 'vtkPVLight'], vtkTexture: ['vtkOpenGLTexture'], vtkImageMapper: ['vtkOpenGLImageSliceMapper'], vtkVolumeMapper: ['vtkFixedPointVolumeRayCastMapper'] }; // ---------------------------------------------------------------------------- var DEFAULT_MAPPING = { vtkRenderWindow: { build: vtkRenderWindow.newInstance, update: vtkRenderWindowUpdater }, vtkRenderer: { build: vtkRenderer.newInstance, update: rendererUpdater }, vtkLookupTable: { build: vtkLookupTable.newInstance, update: genericUpdater }, vtkCamera: { build: vtkCamera.newInstance, update: oneTimeGenericUpdater }, vtkPolyData: { build: vtkPolyData.newInstance, update: polydataUpdater }, vtkImageData: { build: vtkImageData.newInstance, update: imageDataUpdater }, vtkMapper: { build: vtkMapper.newInstance, update: genericUpdater }, vtkGlyph3DMapper: { build: vtkGlyph3DMapper.newInstance, update: genericUpdater }, vtkProperty: { build: vtkProperty.newInstance, update: genericUpdater }, vtkActor: { build: vtkActor.newInstance, update: genericUpdater }, vtkLight: { build: vtkLight.newInstance, update: genericUpdater }, vtkColorTransferFunction: { build: vtkColorTransferFunction.newInstance, update: colorTransferFunctionUpdater }, vtkTexture: { build: vtkTexture.newInstance, update: genericUpdater }, vtkVolume: { build: vtkVolume.newInstance, update: genericUpdater }, vtkVolumeMapper: { build: vtkVolumeMapper.newInstance, update: genericUpdater }, vtkVolumeProperty: { build: vtkVolumeProperty.newInstance, update: genericUpdater }, vtkImageSlice: { build: vtkImageSlice.newInstance, update: genericUpdater }, vtkImageMapper: { build: vtkImageMapper.newInstance, update: genericUpdater }, vtkImageProperty: { build: vtkImageProperty.newInstance, update: genericUpdater }, vtkPiecewiseFunction: { build: vtkPiecewiseFunction.newInstance, update: piecewiseFunctionUpdater } }; // ---------------------------------------------------------------------------- function setDefaultMapping() { var reset = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; if (reset) { clearTypeMapping(); } Object.keys(DEFAULT_MAPPING).forEach(function (type) { var mapping = DEFAULT_MAPPING[type]; setTypeMapping(type, mapping.build, mapping.update); }); } // ---------------------------------------------------------------------------- function applyDefaultAliases() { // Add aliases Object.keys(DEFAULT_ALIASES).forEach(function (name) { var aliases = DEFAULT_ALIASES[name]; aliases.forEach(function (alias) { TYPE_HANDLERS[alias] = TYPE_HANDLERS[name]; }); }); } // ---------------------------------------------------------------------------- function alwaysUpdateCamera() { setTypeMapping('vtkCamera', vtkCamera.newInstance); applyDefaultAliases(); } // ---------------------------------------------------------------------------- setDefaultMapping(); applyDefaultAliases(); // ---------------------------------------------------------------------------- // Avoid handling any lights at the moment EXCLUDE_INSTANCE_MAP.vtkOpenGLLight = {}; EXCLUDE_INSTANCE_MAP.vtkPVLight = {}; EXCLUDE_INSTANCE_MAP.vtkLight = {}; // ---------------------------------------------------------------------------- // Publicly exposed methods // ---------------------------------------------------------------------------- var vtkObjectManager = { build: build, update: update, genericUpdater: genericUpdater, oneTimeGenericUpdater: oneTimeGenericUpdater, setTypeMapping: setTypeMapping, clearTypeMapping: clearTypeMapping, getSupportedTypes: getSupportedTypes, clearOneTimeUpdaters: clearOneTimeUpdaters, updateRenderWindow: updateRenderWindow, excludeInstance: excludeInstance, setDefaultMapping: setDefaultMapping, applyDefaultAliases: applyDefaultAliases, alwaysUpdateCamera: alwaysUpdateCamera }; export default vtkObjectManager;