UNPKG

paraview-glance

Version:

Web application for Visualizing Scientific and Medical datasets

367 lines (318 loc) 11.7 kB
import 'vtk.js/Sources/IO/Core/DataAccessHelper/JSZipDataAccessHelper'; import DataAccessHelper from 'vtk.js/Sources/IO/Core/DataAccessHelper'; import macro from 'vtk.js/Sources/macro'; import vtkHttpSceneLoader from 'vtk.js/Sources/IO/Core/HttpSceneLoader'; import vtkMapper from 'vtk.js/Sources/Rendering/Core/Mapper'; // Can't import both "Mode" objects directly. import LookupTableProxyConstants from 'vtk.js/Sources/Proxy/Core/LookupTableProxy/Constants'; import PiecewiseFunctionProxyConstants from 'vtk.js/Sources/Proxy/Core/PiecewiseFunctionProxy/Constants'; // ---------------------------------------------------------------------------- // vtkGlanceVtkJsReader methods // ---------------------------------------------------------------------------- function loadZip(zipContent) { return new Promise((resolve) => { let dsCount = 0; const dataAccessHelper = DataAccessHelper.get('zip', { zipContent, callback() { const sceneImporter = vtkHttpSceneLoader.newInstance({ dataAccessHelper, startLODLoaders: false, }); sceneImporter.setUrl('index.json'); sceneImporter.onReady(() => { // We need to wait for all DataSet to finish loading if (++dsCount === sceneImporter.getScene().length) { const response = { ...sceneImporter.getMetadata() }; response.scene = sceneImporter.getScene(); response.animationHandler = sceneImporter.getAnimationHandler(); resolve(response); } }); }, }); }); } // ---------------------------------------------------------------------------- /* eslint-disable no-param-reassign */ // ---------------------------------------------------------------------------- function updateRanges(container, name, dataRange) { if (!container[name]) { container[name] = dataRange.slice(); } else { container[name][0] = Math.min(container[name][0], dataRange[0]); container[name][1] = Math.max(container[name][1], dataRange[1]); } return container; } // ---------------------------------------------------------------------------- function gatherRanges(container, dataset) { const fn = (array) => updateRanges(container, array.getName(), array.getRange(-1)); dataset .getPointData() .getArrays() .forEach(fn); dataset .getCellData() .getArrays() .forEach(fn); } // ---------------------------------------------------------------------------- function vtkGlanceVtkJsReader(publicAPI, model) { // Set our className model.classHierarchy.push('vtkGlanceVtkJsReader'); // Returns a promise to signal when image is ready publicAPI.parseAsArrayBuffer = (arrayBuffer) => { if (!arrayBuffer || arrayBuffer === model.rawDataBuffer) { return Promise.resolve(model.appState); } model.rawDataBuffer = arrayBuffer; return loadZip(arrayBuffer).then( ({ scene, camera, background, lookupTables, cameraViewPoints, animationHandler, }) => { model.scene = scene; model.camera = camera; model.background = background; model.lookupTables = lookupTables; model.cameraViewPoints = cameraViewPoints; model.animationHandler = animationHandler; publicAPI.modified(); return model.scene; } ); }; publicAPI.requestData = () => { publicAPI.parseAsArrayBuffer(model.rawDataBuffer); }; publicAPI.setProxyManager = (proxyManager) => { const allViews = proxyManager.getViews(); const allDataRanges = {}; model.scene.forEach((sceneItem) => { const { source, mapper, actor, volume, name, volumeComponents, } = sceneItem; const actorState = actor ? actor.get('origin', 'scale', 'position') : {}; const volumeState = volume ? volume.get('origin', 'scale', 'position') : {}; const propState = actor ? actor .getProperty() .get( 'representation', 'edgeVisibility', 'diffuseColor', 'pointSize', 'opacity' ) : {}; const volumePropState = volume ? volume .getProperty() .get('interpolationType', 'independantComponents', 'shade') : {}; const mapperState = mapper.get( 'colorByArrayName', 'colorMode', 'scalarMode' ); // Create various sources const sourceProxy = proxyManager.createProxy( 'Sources', 'TrivialProducer', { name, } ); sourceProxy.setInputAlgorithm( source, source.getOutputData().getClassName() ); sourceProxy.activate(); // Gather the range for each fields gatherRanges(allDataRanges, source.getOutputData()); for (let i = 0; i < allViews.length; i++) { const view = allViews[i]; const rep = proxyManager.getRepresentation(sourceProxy, allViews[i]); rep.setRescaleOnColorBy(false); if (actor) { const actorFromRep = rep.getActors()[0]; if (actorFromRep) { actorFromRep.set(actorState); actorFromRep.getProperty().set(propState); actorFromRep.getMapper().set(mapperState); // Add textures back actor.getTextures().forEach((t) => { actorFromRep.addTexture(t); }); } } if (volume) { const volumeFromRep = rep.getVolumes()[0]; if (volumeFromRep) { volumeFromRep.set(volumeState); volumeFromRep.getProperty().set(volumePropState); } } // Use representation API to set active array let arrayLocation = 'pointData'; if ( mapperState.scalarMode === vtkMapper.ScalarMode.USE_CELL_FIELD_DATA ) { arrayLocation = 'cellData'; } rep.setColorBy(mapperState.colorByArrayName, arrayLocation); // Update camera if 3d view if (view.getName() === 'default') { view.getCamera().set(model.camera); if (model.animationHandler) { model.animationHandler.addRenderer(view.getRenderer()); } } if (volumeComponents) { const [colorByName] = rep.getColorBy(); const [{ rgbTransferFunction, scalarOpacity }] = volumeComponents; const lutProxy = rep.getLookupTableProxy(colorByName); const pwfProxy = rep.getPiecewiseFunctionProxy(colorByName); if (rgbTransferFunction) { // Push state data into lookup table lutProxy.setPresetName('-'); const [min, max] = rgbTransferFunction.getMappingRange(); lutProxy.setDataRange(min, max); lutProxy.setMode(LookupTableProxyConstants.Mode.Nodes); lutProxy .getLookupTable() .setColorSpace(rgbTransferFunction.getColorSpace()); lutProxy.setNodes(rgbTransferFunction.get('nodes').nodes); } if (scalarOpacity) { const pwf = pwfProxy.getPiecewiseFunction(); pwf.setClamping(scalarOpacity.getClamping()); const nodes = []; const range = scalarOpacity.getRange(); pwfProxy.setDataRange(...range); const width = range[1] - range[0]; for ( let nodeIdx = 0; nodeIdx < scalarOpacity.getSize(); ++nodeIdx ) { const node = []; scalarOpacity.getNodeValue(nodeIdx, node); const [x, y, midpoint, sharpness] = node; // x needs to be normalized nodes.push({ x: (x - range[0]) / width, y, midpoint, sharpness }); } pwfProxy.setMode(PiecewiseFunctionProxyConstants.Mode.Nodes); pwfProxy.setNodes(nodes); } } } const { textureLODsDownloader } = sceneItem; if (textureLODsDownloader) { // Trigger re-renders when new textures are downloaded textureLODsDownloader.setStepFinishedCallback( proxyManager.renderAllViews ); const maxTextureLODSize = proxyManager.getReferenceByName('$store') .state.views.maxTextureLODSize; textureLODsDownloader.setMaxTextureLODSize(maxTextureLODSize); // Start the downloads textureLODsDownloader.startDownloads(); } const { dataSetLODsLoader } = sceneItem; if (dataSetLODsLoader) { const callback = () => { // We must set the new source on the proxy to get paraview // glance to update. const newSource = dataSetLODsLoader.getCurrentSource(); sourceProxy.setInputAlgorithm( newSource, newSource.getOutputData().getClassName() ); }; dataSetLODsLoader.setStepFinishedCallback(callback); // Start the downloads dataSetLODsLoader.startDownloads(); } if (model.animationHandler) { if (proxyManager.getProxyInGroup('AnimationManager').length) { // Find an existing animation manager const animationManager = proxyManager.getProxyInGroup( 'AnimationManager' )[0]; const animation = proxyManager.createProxy( 'Animations', 'TimeStepAnimation' ); animation.setInputAnimationHandler(model.animationHandler); animationManager.addAnimation(animation); animationManager.onCurrentFrameChanged(() => proxyManager.renderAllViews() ); } } }); // Create LookupTable for each field with max range Object.keys(allDataRanges).forEach((fieldName) => { const lutProxy = proxyManager.getLookupTable(fieldName); lutProxy.setDataRange(...allDataRanges[fieldName]); }); // Update LookupTables with PV ranges Object.keys(model.lookupTables).forEach((fieldName) => { const lutState = model.lookupTables[fieldName]; const lutProxy = proxyManager.getLookupTable(fieldName); const lut = lutProxy.getLookupTable(); // Push state data into lookup table const min = lutState.nodes[0][0]; const max = lutState.nodes[lutState.nodes.length - 1][0]; lutProxy.setPresetName('-'); lutProxy.setDataRange(min, max); lutProxy.setMode(3); // use nodes lut.setColorSpace(lutState.colorSpace); lutProxy.setNodes( lutState.nodes.map(([x, r, g, b, midpoint, sharpness]) => ({ x, r, g, b, midpoint, sharpness, })) ); }); proxyManager.renderAllViews(); }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- export function extend(publicAPI, model, initialValues = {}) { Object.assign(model, initialValues); // Build VTK API macro.obj(publicAPI, model); macro.get(publicAPI, model, [ 'scene', 'camera', 'background', 'cameraViewPoints', ]); // vtkGlanceStateReader methods vtkGlanceVtkJsReader(publicAPI, model); } // ---------------------------------------------------------------------------- export const newInstance = macro.newInstance(extend, 'vtkGlanceVtkJsReader'); // ---------------------------------------------------------------------------- export default { newInstance, extend };