import Monologue from 'monologue.js';
import 'vtk.js/Sources/Rendering/OpenGL/Profiles/All';
import vtkOpenGLRenderWindow from 'vtk.js/Sources/Rendering/OpenGL/RenderWindow'; import vtkRenderer from 'vtk.js/Sources/Rendering/Core/Renderer'; import vtkRenderWindow from 'vtk.js/Sources/Rendering/Core/RenderWindow'; import vtkRenderWindowInteractor from 'vtk.js/Sources/Rendering/Core/RenderWindowInteractor';
import vtkColorTransferFunction from 'vtk.js/Sources/Rendering/Core/ColorTransferFunction'; import vtkPiecewiseFunction from 'vtk.js/Sources/Common/DataModel/PiecewiseFunction'; import vtkVolume from 'vtk.js/Sources/Rendering/Core/Volume'; import vtkVolumeMapper from 'vtk.js/Sources/Rendering/Core/VolumeMapper';
const IMAGE_READY_TOPIC = 'image-ready'; const DEFAULT_ARRAY_NAME = 'Scalars';
export default class VTKVolumeBuilder { constructor(lutMgr, imageDataModel, queryDataModel) { this.lookupTableManager = lutMgr; this.imageDataModel = imageDataModel; this.queryDataModel = queryDataModel; this.pipeline = {}; this.initActions = [];
this.queryDataModel.onDataChange((data, envelope) => { if (data.scene) { this.imageDataModel.loadScene(data.scene.data); } if (data.clusters) { this.imageDataModel.loadClusters(this.queryDataModel, data.clusters); } });
this.pipeline.range = [0, 255]; if ( this.queryDataModel.originalData.metadata && this.queryDataModel.originalData.metadata.piecewise ) { const pwArray = this.queryDataModel.originalData.metadata.piecewise; this.pipeline.range[1] = pwArray[pwArray.length - 1][0]; } const arrayNames = Object.keys( this.queryDataModel.originalData.LookupTables || {} ); this.arrayName = arrayNames.length ? arrayNames[0] : DEFAULT_ARRAY_NAME; this.lookupTableManager.addFields( { [this.arrayName]: this.pipeline.range }, this.queryDataModel.originalData.LookupTables ); this.lookupTableManager.updateActiveLookupTable(this.arrayName);
this.lookupTableManager.onChange((data, envelope) => { this.updateColoring(data.change, data.lut); });
this.renderWindow = vtkRenderWindow.newInstance(); this.renderer = vtkRenderer.newInstance(); this.renderWindow.addRenderer(this.renderer);
const color = this.queryDataModel.originalData.metadata && this.queryDataModel.originalData.metadata.backgroundColor ? this.queryDataModel.originalData.metadata.backgroundColor : '#000000'; if (color.length === 7) { const bgColor = [ color.slice(1, 3), color.slice(3, 5), color.slice(5, 7), ].map((v) => parseInt(v, 16) / 255); this.renderer.setBackground(bgColor); }
this.imageBuilderSubscription = this.imageDataModel.onGeometryReady( (data, envelope) => { this.updateGeometry(data); } ); }
destroy() { if (this.imageBuilderSubscription) { this.imageBuilderSubscription.unsubscribe(); this.imageBuilderSubscription = null; } }
configureRenderer(canvas) { this.openGlRenderWindow = vtkOpenGLRenderWindow.newInstance(); this.openGlRenderWindow.setCanvas(canvas); this.renderWindow.addView(this.openGlRenderWindow);
this.interactor = vtkRenderWindowInteractor.newInstance(); this.interactor.setView(this.openGlRenderWindow); this.interactor.initialize(); this.interactor.bindEvents(canvas);
this.render = this.renderWindow.render;
this.queryDataModel.fetchData(); }
updateColoring() { const lookupTable = this.getLookupTable(); this.pipeline.ctfun.removeAllPoints(); lookupTable.controlPoints.forEach(({ x, r, g, b }) => { this.pipeline.ctfun.addRGBPoint(x, r, g, b); }); this.pipeline.ctfun.setMappingRange(...lookupTable.getScalarRange()); this.renderWindow.render(); }
getLookupTable() { return this.lookupTableManager.getLookupTable(this.arrayName); }
getColorFunction() { return this.pipeline.ctfun; }
getPiecewiseFunction() { return this.pipeline.ofun; }
getDataRange() { return this.pipeline.range; }
getImageData() { return this.pipeline.source; }
getActor() { return this.pipeline.actor; }
getMapper() { return this.pipeline.mapper; }
getActiveCamera() { return this.renderer.getActiveCamera(); }
updateGeometry(imageData) { let firstTime = false; if (!this.pipeline.actor) { firstTime = true;
this.pipeline.actor = vtkVolume.newInstance(); this.pipeline.mapper = vtkVolumeMapper.newInstance({ sampleDistance: 0.7, }); this.pipeline.actor.setMapper(this.pipeline.mapper);
this.pipeline.ctfun = vtkColorTransferFunction.newInstance(); this.pipeline.ctfun.addRGBPoint(this.pipeline.range[0], 85 / 255.0, 0, 0); this.pipeline.ctfun.addRGBPoint( 0.37 * this.pipeline.range[1], 1.0, 1.0, 1.0 ); this.pipeline.ctfun.addRGBPoint( 0.88 * this.pipeline.range[1], 0.66, 0.66, 0.5 ); this.pipeline.ctfun.addRGBPoint(this.pipeline.range[1], 0.3, 1.0, 0.5);
this.pipeline.ofun = vtkPiecewiseFunction.newInstance(); this.pipeline.ofun.addPoint(this.pipeline.range[0], 0.0); this.pipeline.ofun.addPoint(this.pipeline.range[1], 1.0);
this.pipeline.actor .getProperty() .setRGBTransferFunction(0, this.pipeline.ctfun); this.pipeline.actor.getProperty().setScalarOpacity(0, this.pipeline.ofun); this.pipeline.actor.getProperty().setScalarOpacityUnitDistance(0, 3.0); this.pipeline.actor.getProperty().setInterpolationTypeToLinear();
this.pipeline.actor.getProperty().setGradientOpacityMinimumValue(0, 15); this.pipeline.actor .getProperty() .setGradientOpacityMinimumOpacity(0, 0.0); this.pipeline.actor.getProperty().setGradientOpacityMaximumValue(0, 100); this.pipeline.actor .getProperty() .setGradientOpacityMaximumOpacity(0, 1.0); this.pipeline.actor.getProperty().setShade(true); this.pipeline.actor.getProperty().setAmbient(0.2); this.pipeline.actor.getProperty().setDiffuse(0.7); this.pipeline.actor.getProperty().setSpecular(0.3); this.pipeline.actor.getProperty().setSpecularPower(8.0); }
if (this.pipeline.source !== imageData) { this.pipeline.source = imageData; this.pipeline.range = imageData.getPointData().getScalars().getRange(); this.pipeline.mapper.setInputData(this.pipeline.source);
this.emit(IMAGE_READY_TOPIC, firstTime); }
if (firstTime) { this.renderer.addVolume(this.pipeline.actor); this.renderer.resetCamera(); this.renderer.updateLightsGeometryToFollowCamera(); this.initActions.forEach((cb) => cb()); }
this.renderWindow.render(); }
getRenderer() { return this.renderer; }
getRenderWindow() { return this.renderWindow; }
resetCamera() { this.renderer.resetCamera(); this.renderWindow.render(); }
addInitializationAction(initCallback) { this.initActions.push(initCallback); }
clearInitializationActions() { this.initActions = []; }
render() { this.renderer.resetCameraClippingRange(); this.renderWindow.render(); }
updateSize(width, height) { this.openGlRenderWindow.setSize(width, height); this.renderWindow.render(); }
onImageReady(callback) { return this.on(IMAGE_READY_TOPIC, callback); } }
Monologue.mixInto(VTKVolumeBuilder);
|