UNPKG

@kitware/vtk.js

Version:

Visualization Toolkit for the Web

305 lines (289 loc) 9.29 kB
import Constants from './ImageMapper/Constants.js'; import { m as macro } from '../../macros2.js'; import vtkAbstractImageMapper from './AbstractImageMapper.js'; import vtkBoundingBox from '../../Common/DataModel/BoundingBox.js'; import { intersectWithLineForPointPicking, intersectWithLineForCellPicking } from './AbstractImageMapper/helper.js'; import { F as clampValue, U as multiply3x3_vect3, I as createUninitializedBounds, V as getSparseOrthogonalMatrix } from '../../Common/Core/Math/index.js'; import CoincidentTopologyHelper from './Mapper/CoincidentTopologyHelper.js'; const { staticOffsetAPI, otherStaticMethods } = CoincidentTopologyHelper; const { SlicingMode } = Constants; // ---------------------------------------------------------------------------- // vtkImageMapper methods // ---------------------------------------------------------------------------- function vtkImageMapper(publicAPI, model) { // Set our className model.classHierarchy.push('vtkImageMapper'); publicAPI.getSliceAtPosition = pos => { const image = publicAPI.getCurrentImage(); let pos3; if (pos.length === 3) { pos3 = pos; } else if (Number.isFinite(pos)) { const bds = image.getBounds(); switch (model.slicingMode) { case SlicingMode.X: pos3 = [pos, (bds[3] + bds[2]) / 2, (bds[5] + bds[4]) / 2]; break; case SlicingMode.Y: pos3 = [(bds[1] + bds[0]) / 2, pos, (bds[5] + bds[4]) / 2]; break; case SlicingMode.Z: pos3 = [(bds[1] + bds[0]) / 2, (bds[3] + bds[2]) / 2, pos]; break; } } const ijk = [0, 0, 0]; image.worldToIndex(pos3, ijk); const ex = image.getExtent(); const { ijkMode } = publicAPI.getClosestIJKAxis(); let slice = 0; switch (ijkMode) { case SlicingMode.I: slice = clampValue(ijk[0], ex[0], ex[1]); break; case SlicingMode.J: slice = clampValue(ijk[1], ex[2], ex[3]); break; case SlicingMode.K: slice = clampValue(ijk[2], ex[4], ex[5]); break; default: return 0; } return slice; }; publicAPI.setSliceFromCamera = cam => { const fp = cam.getFocalPoint(); switch (model.slicingMode) { case SlicingMode.I: case SlicingMode.J: case SlicingMode.K: { const slice = publicAPI.getSliceAtPosition(fp); publicAPI.setSlice(slice); } break; case SlicingMode.X: publicAPI.setSlice(fp[0]); break; case SlicingMode.Y: publicAPI.setSlice(fp[1]); break; case SlicingMode.Z: publicAPI.setSlice(fp[2]); break; } }; publicAPI.setXSlice = id => { publicAPI.setSlicingMode(SlicingMode.X); publicAPI.setSlice(id); }; publicAPI.setYSlice = id => { publicAPI.setSlicingMode(SlicingMode.Y); publicAPI.setSlice(id); }; publicAPI.setZSlice = id => { publicAPI.setSlicingMode(SlicingMode.Z); publicAPI.setSlice(id); }; publicAPI.setISlice = id => { publicAPI.setSlicingMode(SlicingMode.I); publicAPI.setSlice(id); }; publicAPI.setJSlice = id => { publicAPI.setSlicingMode(SlicingMode.J); publicAPI.setSlice(id); }; publicAPI.setKSlice = id => { publicAPI.setSlicingMode(SlicingMode.K); publicAPI.setSlice(id); }; publicAPI.getSlicingModeNormal = () => { const out = [0, 0, 0]; const mat3 = publicAPI.getCurrentImage().getDirection(); switch (model.slicingMode) { case SlicingMode.X: out[0] = 1; break; case SlicingMode.Y: out[1] = 1; break; case SlicingMode.Z: out[2] = 1; break; case SlicingMode.I: multiply3x3_vect3(mat3, [1, 0, 0], out); break; case SlicingMode.J: multiply3x3_vect3(mat3, [0, 1, 0], out); break; case SlicingMode.K: multiply3x3_vect3(mat3, [0, 0, 1], out); break; } return out; }; function computeClosestIJKAxis() { let xyzMode; switch (model.slicingMode) { case SlicingMode.X: xyzMode = 0; break; case SlicingMode.Y: xyzMode = 1; break; case SlicingMode.Z: xyzMode = 2; break; default: model.closestIJKAxis = { ijkMode: model.slicingMode, flip: false }; return; } // The direction matrix in vtkImageData is the indexToWorld rotation matrix // with a column-major data layout since it is stored as a WebGL matrix. const direction = publicAPI.getCurrentImage().getDirection(); const newMatrix = getSparseOrthogonalMatrix(direction); // With {foo}Vector filled with 0s except at {foo}Mode position where it is 1 // We have xyzVector = (+/-) newMatrix * ijkVector let ijkMode = 0; for (; ijkMode < 3; ++ijkMode) { if (newMatrix[xyzMode + 3 * ijkMode] !== 0) { break; } } const flip = newMatrix[xyzMode + 3 * ijkMode] < 0; model.closestIJKAxis = { ijkMode, flip }; } publicAPI.setSlicingMode = mode => { if (model.slicingMode === mode) { return; } model.slicingMode = mode; if (publicAPI.getCurrentImage()) { computeClosestIJKAxis(); } publicAPI.modified(); }; publicAPI.getClosestIJKAxis = () => { if ((model.closestIJKAxis === undefined || model.closestIJKAxis.ijkMode === SlicingMode.NONE) && publicAPI.getCurrentImage()) { computeClosestIJKAxis(); } return model.closestIJKAxis; }; publicAPI.computeBounds = () => { const image = publicAPI.getCurrentImage(); if (!image) { vtkBoundingBox.reset(model.bounds); return; } if (!model.useCustomExtents) { vtkBoundingBox.setBounds(model.bounds, image.getBounds()); return; } const ex = model.customDisplayExtent.slice(); const { ijkMode } = publicAPI.getClosestIJKAxis(); let nSlice = model.slice; if (ijkMode !== model.slicingMode) { // If not IJK slicing, get the IJK slice from the XYZ position/slice nSlice = publicAPI.getSliceAtPosition(model.slice); } switch (ijkMode) { case SlicingMode.I: ex[0] = nSlice; ex[1] = nSlice; break; case SlicingMode.J: ex[2] = nSlice; ex[3] = nSlice; break; case SlicingMode.K: ex[4] = nSlice; ex[5] = nSlice; break; } vtkBoundingBox.setBounds(model.bounds, image.extentToBounds(ex)); }; publicAPI.getBoundsForSlice = (slice = model.slice, halfThickness = 0) => { const image = publicAPI.getCurrentImage(); if (!image) { return createUninitializedBounds(); } const extent = image.getSpatialExtent(); const { ijkMode } = publicAPI.getClosestIJKAxis(); let nSlice = slice; if (ijkMode !== model.slicingMode) { // If not IJK slicing, get the IJK slice from the XYZ position/slice nSlice = publicAPI.getSliceAtPosition(slice); } switch (ijkMode) { case SlicingMode.I: extent[0] = nSlice - halfThickness; extent[1] = nSlice + halfThickness; break; case SlicingMode.J: extent[2] = nSlice - halfThickness; extent[3] = nSlice + halfThickness; break; case SlicingMode.K: extent[4] = nSlice - halfThickness; extent[5] = nSlice + halfThickness; break; } return image.extentToBounds(extent); }; publicAPI.intersectWithLineForPointPicking = (p1, p2) => intersectWithLineForPointPicking(p1, p2, publicAPI); publicAPI.intersectWithLineForCellPicking = (p1, p2) => intersectWithLineForCellPicking(p1, p2, publicAPI); publicAPI.getCurrentImage = () => publicAPI.getInputData(); } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- const DEFAULT_VALUES = { slicingMode: SlicingMode.NONE, closestIJKAxis: { ijkMode: SlicingMode.NONE, flip: false }, renderToRectangle: false, sliceAtFocalPoint: false, preferSizeOverAccuracy: false // Whether to use halfFloat representation of float, when it is inaccurate }; // ---------------------------------------------------------------------------- function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues); // Build VTK API vtkAbstractImageMapper.extend(publicAPI, model, initialValues); macro.get(publicAPI, model, ['slicingMode']); macro.setGet(publicAPI, model, ['closestIJKAxis', 'renderToRectangle', 'sliceAtFocalPoint', 'preferSizeOverAccuracy']); CoincidentTopologyHelper.implementCoincidentTopologyMethods(publicAPI, model); // Object methods vtkImageMapper(publicAPI, model); } // ---------------------------------------------------------------------------- const newInstance = macro.newInstance(extend, 'vtkImageMapper'); // ---------------------------------------------------------------------------- var vtkImageMapper$1 = { newInstance, extend, ...staticOffsetAPI, ...otherStaticMethods, ...Constants }; export { vtkImageMapper$1 as default, extend, newInstance };