import macro from 'vtk.js/Sources/macros'; import vtkImageSlice from 'vtk.js/Sources/Rendering/Core/ImageSlice'; import vtkImageMapper from 'vtk.js/Sources/Rendering/Core/ImageMapper';
import vtkAbstractRepresentationProxy from 'vtk.js/Sources/Proxy/Core/AbstractRepresentationProxy';
function sum(a, b) { return a + b; }
function mean(...array) { return array.reduce(sum, 0) / array.length; }
function updateDomains(dataset, dataArray, model, updateProp) { const dataRange = dataArray.getRange(); const spacing = dataset.getSpacing(); const bounds = dataset.getBounds(); const extent = dataset.getExtent();
let sliceMin; let sliceMax; let stepVal; let axisIndex; const sliceMode = model.mapper.getSlicingMode(); const sliceModeLabel = 'IJKXYZ'[sliceMode]; switch (sliceMode) { case vtkImageMapper.SlicingMode.I: case vtkImageMapper.SlicingMode.J: case vtkImageMapper.SlicingMode.K: axisIndex = 'IJK'.indexOf(sliceModeLabel); sliceMin = extent[axisIndex * 2]; sliceMax = extent[axisIndex * 2 + 1]; stepVal = 1; break; case vtkImageMapper.SlicingMode.X: case vtkImageMapper.SlicingMode.Y: case vtkImageMapper.SlicingMode.Z: { axisIndex = 'XYZ'.indexOf(sliceModeLabel); sliceMin = bounds[axisIndex * 2]; sliceMax = bounds[axisIndex * 2 + 1]; const { ijkMode } = model.mapper.getClosestIJKAxis(); stepVal = spacing[ijkMode]; } break; default: break; }
const propToUpdate = { slice: { domain: { min: sliceMin, max: sliceMax, step: stepVal, }, }, windowWidth: { domain: { min: 0, max: dataRange[1] - dataRange[0], step: 'any', }, }, windowLevel: { domain: { min: dataRange[0], max: dataRange[1], step: 'any', }, }, };
updateProp('slice', propToUpdate.slice); updateProp('windowWidth', propToUpdate.windowWidth); updateProp('windowLevel', propToUpdate.windowLevel);
return { slice: mean(propToUpdate.slice.domain.min, propToUpdate.slice.domain.max), windowWidth: propToUpdate.windowWidth.domain.max, windowLevel: Math.floor( mean( propToUpdate.windowLevel.domain.min, propToUpdate.windowLevel.domain.max ) ), }; }
function vtkSliceRepresentationProxy(publicAPI, model) { model.classHierarchy.push('vtkSliceRepresentationProxy');
model.mapper = vtkImageMapper.newInstance(); model.actor = vtkImageSlice.newInstance(); model.property = model.actor.getProperty();
model.actor.setMapper(model.mapper); model.actors.push(model.actor);
function setInputData(inputDataset) { const state = updateDomains( inputDataset, publicAPI.getDataArray(), model, publicAPI.updateProxyProperty ); publicAPI.set(state);
const bounds = inputDataset.getBounds(); const extent = inputDataset.getExtent(); switch (model.mapper.getSlicingMode()) { case vtkImageMapper.SlicingMode.I: publicAPI.setSlice(Math.floor(mean(extent[0], extent[1]))); break; case vtkImageMapper.SlicingMode.J: publicAPI.setSlice(Math.floor(mean(extent[2], extent[3]))); break; case vtkImageMapper.SlicingMode.K: publicAPI.setSlice(Math.floor(mean(extent[4], extent[5]))); break; case vtkImageMapper.SlicingMode.X: publicAPI.setSlice(mean(bounds[0], bounds[1])); break; case vtkImageMapper.SlicingMode.Y: publicAPI.setSlice(mean(bounds[2], bounds[3])); break; case vtkImageMapper.SlicingMode.Z: publicAPI.setSlice(mean(bounds[4], bounds[5])); break; default: break; } }
model.sourceDependencies.push(model.mapper); model.sourceDependencies.push({ setInputData });
function updateSlicingMode(mode) { model.mapper.setSlicingMode(vtkImageMapper.SlicingMode[mode]);
const modelKey = `${mode.toLowerCase()}Slice`; if (modelKey in model && model[modelKey] !== undefined) { model.mapper.setSlice(model[modelKey]); }
if (model.input) { const state = updateDomains( publicAPI.getInputDataSet(), publicAPI.getDataArray(), model, publicAPI.updateProxyProperty ); publicAPI.set(state); } publicAPI.modified(); }
publicAPI.setSlicingMode = (mode) => { if (!mode) { return false; } if (model.slicingMode !== mode) { model.slicingMode = mode; updateSlicingMode(mode); return true; } return false; };
publicAPI.getSliceIndex = () => { if ('XYZ'.indexOf(model.slicingMode) !== -1) { return model.mapper.getSliceAtPosition(model.mapper.getSlice()); } return model.mapper.getSlice(); };
publicAPI.getAnnotations = () => { const dynamicAddOn = {}; const sliceIndex = publicAPI.getSliceIndex(); const sliceBounds = model.mapper.getBoundsForSlice(); const sliceNormal = model.mapper.getSlicingModeNormal(); const { ijkMode } = model.mapper.getClosestIJKAxis(); const sliceOrigin = [ (sliceBounds[0] + sliceBounds[1]) * 0.5, (sliceBounds[2] + sliceBounds[3]) * 0.5, (sliceBounds[4] + sliceBounds[5]) * 0.5, ]; let slicePosition = 0; if (sliceBounds[1] - sliceBounds[0] < Number.EPSILON) { slicePosition = sliceBounds[0]; } if (sliceBounds[3] - sliceBounds[2] < Number.EPSILON) { slicePosition = sliceBounds[2]; } if (sliceBounds[5] - sliceBounds[4] < Number.EPSILON) { slicePosition = sliceBounds[4]; }
const imageData = model.mapper.getInputData(); if (imageData) { dynamicAddOn.sliceSpacing = imageData.getSpacing()[ijkMode]; dynamicAddOn.dimensions = imageData.getDimensions(); dynamicAddOn.sliceCount = imageData.getDimensions()[ijkMode]; const ijkOrientation = []; for (let i = 0; i < 3; i++) { const extent = [0, 0, 0, 0, 0, 0]; extent[i * 2 + 1] = 1; const tmpBounds = imageData.extentToBounds(extent); if (tmpBounds[1] - tmpBounds[0] > Number.EPSILON) { ijkOrientation[0] = 'IJK'[i]; } if (tmpBounds[3] - tmpBounds[2] > Number.EPSILON) { ijkOrientation[1] = 'IJK'[i]; } if (tmpBounds[5] - tmpBounds[4] > Number.EPSILON) { ijkOrientation[2] = 'IJK'[i]; } } dynamicAddOn.ijkOrientation = ijkOrientation.join(''); }
return { ijkMode, sliceBounds, sliceIndex, sliceNormal, sliceOrigin, slicePosition, ...dynamicAddOn, }; };
const parentSetColorBy = publicAPI.setColorBy; publicAPI.setColorBy = (arrayName, arrayLocation, componentIndex = -1) => { if (arrayName === null) { model.property.setRGBTransferFunction(null); model.property.setPiecewiseFunction(null); } else { parentSetColorBy(arrayName, arrayLocation, componentIndex); const lutProxy = publicAPI.getLookupTableProxy(arrayName); const pwfProxy = publicAPI.getPiecewiseFunctionProxy(arrayName); model.property.setRGBTransferFunction(lutProxy.getLookupTable()); model.property.setPiecewiseFunction(pwfProxy.getPiecewiseFunction()); } };
updateSlicingMode(model.slicingMode || 'X'); }
const DEFAULT_VALUES = {};
export function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues);
vtkAbstractRepresentationProxy.extend(publicAPI, model, initialValues); macro.get(publicAPI, model, ['slicingMode']);
vtkSliceRepresentationProxy(publicAPI, model);
macro.proxyPropertyMapping(publicAPI, model, { visibility: { modelKey: 'actor', property: 'visibility' }, windowWidth: { modelKey: 'property', property: 'colorWindow' }, windowLevel: { modelKey: 'property', property: 'colorLevel' }, interpolationType: { modelKey: 'property', property: 'interpolationType' }, slice: { modelKey: 'mapper', property: 'slice' }, }); }
export const newInstance = macro.newInstance( extend, 'vtkSliceRepresentationProxy' );
export default { newInstance, extend };
|