UNPKG

@kitware/vtk.js

Version:

Visualization Toolkit for the Web

327 lines (302 loc) 12.3 kB
import { quat, vec3, mat4 } from 'gl-matrix'; import CoincidentTopologyHelper from './Mapper/CoincidentTopologyHelper.js'; import vtkAbstractImageMapper from './AbstractImageMapper.js'; import { m as macro } from '../../macros2.js'; import vtkPoints from '../../Common/Core/Points.js'; import vtkPolyLine from '../../Common/DataModel/PolyLine.js'; import { ProjectionMode } from './ImageCPRMapper/Constants.js'; const { vtkErrorMacro } = macro; const { staticOffsetAPI, otherStaticMethods } = CoincidentTopologyHelper; // ---------------------------------------------------------------------------- // vtkImageCPRMapper methods // ---------------------------------------------------------------------------- function vtkImageCPRMapper(publicAPI, model) { // Set our className model.classHierarchy.push('vtkImageCPRMapper'); const superClass = { ...publicAPI }; /** * Public methods */ publicAPI.getBounds = () => { const imageWidth = publicAPI.getWidth(); const imageHeight = publicAPI.getHeight(); return [0, imageWidth, 0, imageHeight, 0, 0]; }; publicAPI.getOrientationDataArray = () => { const pointData = publicAPI.getInputData(1)?.getPointData(); if (!pointData) { return null; } if (model.orientationArrayName !== null) { return pointData.getArrayByName(model.orientationArrayName) || null; } return pointData.getArrayByName('Orientation') || pointData.getArrayByName('Direction') || pointData.getVectors() || pointData.getTensors() || pointData.getNormals() || null; }; publicAPI.getOrientedCenterline = () => { const inputPolydata = publicAPI.getInputData(1); if (!inputPolydata) { // No polydata: return previous centerline // Don't reset centerline as it could have been set using setOrientedCenterline return model._orientedCenterline; } // Get dependencies of centerline const orientationDataArray = publicAPI.getOrientationDataArray(); const linesDataArray = inputPolydata.getLines(); const pointsDataArray = inputPolydata.getPoints(); if (!model.useUniformOrientation && !orientationDataArray) { vtkErrorMacro('Failed to create oriented centerline from polydata: no orientation'); publicAPI._resetOrientedCenterline(); return model._orientedCenterline; } // If centerline didn't change, don't recompute const centerlineTime = model._orientedCenterline.getMTime(); if (centerlineTime >= publicAPI.getMTime() && centerlineTime > linesDataArray.getMTime() && centerlineTime > pointsDataArray.getMTime() && (model.useUniformOrientation || centerlineTime > orientationDataArray.getMTime())) { return model._orientedCenterline; } // Get points of the centerline const linesData = linesDataArray.getData(); if (linesData.length <= 0) { // No polyline publicAPI._resetOrientedCenterline(); return model._orientedCenterline; } const nPoints = linesData[0]; if (nPoints <= 1) { // Empty centerline publicAPI._resetOrientedCenterline(); return model._orientedCenterline; } const pointIndices = linesData.subarray(1, 1 + nPoints); // Get orientations of the centerline const orientations = new Array(nPoints); // Function to convert from mat4, mat3, quat or vec3 to quaternion let convert = () => null; const numComps = model.useUniformOrientation ? model.uniformOrientation.length : orientationDataArray.getNumberOfComponents(); switch (numComps) { case 16: convert = (outQuat, inMat) => { mat4.getRotation(outQuat, inMat); quat.normalize(outQuat, outQuat); }; break; case 9: convert = (outQuat, inMat) => { quat.fromMat3(outQuat, inMat); quat.normalize(outQuat, outQuat); }; break; case 4: convert = quat.copy; break; case 3: convert = (a, b) => quat.rotationTo(a, model.tangentDirection, b); break; default: vtkErrorMacro('Orientation doesnt match mat4, mat3, quat or vec3'); publicAPI._resetOrientedCenterline(); return model._orientedCenterline; } // Function to get orientation from point index let getOrientation = () => null; if (model.useUniformOrientation) { const outQuat = new Float64Array(4); convert(outQuat, model.uniformOrientation); getOrientation = () => outQuat; } else { const temp = new Float64Array(16); getOrientation = i => { const outQuat = new Float64Array(4); orientationDataArray.getTuple(i, temp); convert(outQuat, temp); return outQuat; }; } // Fill the orientation array for (let i = 0; i < nPoints; ++i) { const pointIdx = pointIndices[i]; orientations[i] = getOrientation(pointIdx); } // Done recomputing model._orientedCenterline.initialize(pointsDataArray, pointIndices); model._orientedCenterline.setOrientations(orientations); return model._orientedCenterline; }; publicAPI.setOrientedCenterline = centerline => { if (model._orientedCenterline !== centerline) { model._orientedCenterline = centerline; return true; } return false; }; publicAPI._resetOrientedCenterline = () => { model._orientedCenterline.initialize(vtkPoints.newInstance()); model._orientedCenterline.setOrientations([]); }; publicAPI.getMTime = () => { let mTime = superClass.getMTime(); if (!model._orientedCenterline) { return mTime; } mTime = Math.max(mTime, model._orientedCenterline.getMTime()); return mTime; }; publicAPI.getHeight = () => { const accHeights = publicAPI.getOrientedCenterline().getDistancesToFirstPoint(); if (accHeights.length === 0) { return 0; } return accHeights[accHeights.length - 1]; }; publicAPI.getCenterlinePositionAndOrientation = distance => { const centerline = publicAPI.getOrientedCenterline(); const subId = centerline.findPointIdAtDistanceFromFirstPoint(distance); if (subId < 0) { return {}; } const distances = centerline.getDistancesToFirstPoint(); const pcoords = [(distance - distances[subId]) / (distances[subId + 1] - distances[subId])]; const weights = new Array(2); const position = new Array(3); centerline.evaluateLocation(subId, pcoords, position, weights); const orientation = new Array(4); if (!centerline.evaluateOrientation(subId, pcoords, orientation, weights)) { // No orientation return { position }; } return { position, orientation }; }; publicAPI.getCenterlineTangentDirections = () => { const centerline = publicAPI.getOrientedCenterline(); const directionsTime = model._centerlineTangentDirectionsTime.getMTime(); if (directionsTime < centerline.getMTime()) { const orientations = centerline.getOrientations(); model._centerlineTangentDirections = new Float32Array(3 * orientations.length); const localDirection = new Array(3); for (let i = 0; i < orientations.length; ++i) { vec3.transformQuat(localDirection, model.tangentDirection, orientations[i]); model._centerlineTangentDirections.set(localDirection, 3 * i); } model._centerlineTangentDirectionsTime.modified(); } return model._centerlineTangentDirections; }; publicAPI.getUniformDirection = () => vec3.transformQuat(new Array(3), model.tangentDirection, model.uniformOrientation); publicAPI.getDirectionMatrix = () => { const tangent = model.tangentDirection; const bitangent = model.bitangentDirection; const normal = model.normalDirection; return new Float64Array([tangent[0], tangent[1], tangent[2], bitangent[0], bitangent[1], bitangent[2], normal[0], normal[1], normal[2]]); }; publicAPI.setDirectionMatrix = mat => { if (mat4.equals(mat, publicAPI.getDirectionMatrix())) { return false; } model.tangentDirection = [mat[0], mat[1], mat[2]]; model.bitangentDirection = [mat[3], mat[4], mat[5]]; model.normalDirection = [mat[6], mat[7], mat[8]]; publicAPI.modified(); return true; }; // Check if the rendering can occur publicAPI.preRenderCheck = () => { if (!publicAPI.getInputData(0)) { vtkErrorMacro('No image data input'); return false; } return true; }; publicAPI.useStraightenedMode = () => { publicAPI.setCenterPoint(null); publicAPI.setUseUniformOrientation(false); publicAPI.getOrientedCenterline().setDistanceFunction(vec3.dist); }; publicAPI.useStretchedMode = centerPoint => { const centerline = publicAPI.getOrientedCenterline(); // Set center point if (!centerPoint) { // Get the first point of the centerline if there is one const centerlinePoints = centerline.getPoints(); const newCenterPoint = centerlinePoints.getNumberOfTuples() > 0 ? centerlinePoints.getPoint(0) : [0, 0, 0]; publicAPI.setCenterPoint(newCenterPoint); } else { publicAPI.setCenterPoint(centerPoint); } // Enable uniform orientation publicAPI.setUseUniformOrientation(true); // Change distance function centerline.setDistanceFunction((a, b) => { const direction = publicAPI.getUniformDirection(); const vec = vec3.subtract([], a, b); const d2 = vec3.squaredLength(vec); const x = vec3.dot(direction, vec); return Math.sqrt(d2 - x * x); }); }; publicAPI.isProjectionEnabled = () => model.projectionSlabNumberOfSamples > 1; publicAPI.setCenterlineData = centerlineData => publicAPI.setInputData(centerlineData, 1); publicAPI.setCenterlineConnection = centerlineConnection => publicAPI.setInputConnection(centerlineConnection, 1); publicAPI.setImageData = imageData => publicAPI.setInputData(imageData, 0); publicAPI.setImageConnection = imageData => publicAPI.setInputConnection(imageData, 0); publicAPI.getIsOpaque = () => true; // One can also call setOrientedCenterline and not provide a polydata centerline to input 1 model._orientedCenterline = vtkPolyLine.newInstance(); publicAPI._resetOrientedCenterline(); } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- const defaultValues = initialValues => ({ width: 10, uniformOrientation: [0, 0, 0, 1], useUniformOrientation: false, centerPoint: null, preferSizeOverAccuracy: false, orientationArrayName: null, tangentDirection: [1, 0, 0], bitangentDirection: [0, 1, 0], normalDirection: [0, 0, 1], projectionSlabThickness: 1, projectionSlabNumberOfSamples: 1, projectionMode: ProjectionMode.MAX, ...initialValues }); // ---------------------------------------------------------------------------- function extend(publicAPI, model) { let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; Object.assign(model, defaultValues(initialValues)); // Inheritance vtkAbstractImageMapper.extend(publicAPI, model, initialValues); // Two inputs: one for the ImageData and one for the PolyData (centerline) macro.algo(publicAPI, model, 2, 0); model._centerlineTangentDirectionsTime = {}; macro.obj(model._centerlineTangentDirectionsTime, { mtime: 0 }); // Setters and getters macro.setGet(publicAPI, model, ['width', 'uniformOrientation', 'useUniformOrientation', 'centerPoint', 'preferSizeOverAccuracy', 'orientationArrayName', 'tangentDirection', 'bitangentDirection', 'normalDirection', 'projectionSlabThickness', 'projectionSlabNumberOfSamples', 'projectionMode']); CoincidentTopologyHelper.implementCoincidentTopologyMethods(publicAPI, model); // Object methods vtkImageCPRMapper(publicAPI, model); } // ---------------------------------------------------------------------------- const newInstance = macro.newInstance(extend, 'vtkImageCPRMapper'); // ---------------------------------------------------------------------------- var index = { newInstance, extend, ...staticOffsetAPI, ...otherStaticMethods }; export { index as default, extend, newInstance };