import macro from 'vtk.js/Sources/macros'; import vtkPolyData from 'vtk.js/Sources/Common/DataModel/PolyData';
const { vtkErrorMacro } = macro;
function vtkImageStreamline(publicAPI, model) { model.classHierarchy.push('vtkImageStreamline');
const indices = new Int32Array(3); const paramCoords = new Float32Array(3); const weights = new Float32Array(8); const voxelIndices = new Uint32Array(8); const dimensions = new Uint32Array(3); const velAt = new Float32Array(3); const xtmp = new Float32Array(3);
publicAPI.interpolationFunctions = (pcoords, sf) => { const r = pcoords[0]; const s = pcoords[1]; const t = pcoords[2];
const rm = 1.0 - r; const sm = 1.0 - s; const tm = 1.0 - t;
sf[0] = rm * sm * tm; sf[1] = r * sm * tm; sf[2] = rm * s * tm; sf[3] = r * s * tm; sf[4] = rm * sm * t; sf[5] = r * sm * t; sf[6] = rm * s * t; sf[7] = r * s * t; };
publicAPI.computeStructuredCoordinates = ( x, ijk, pcoords, extent, spacing, origin, bounds ) => { const tol2 = 1e-12; let isInBounds = true; for (let i = 0; i < 3; i++) { const d = x[i] - origin[i]; const doubleLoc = d / spacing[i]; ijk[i] = Math.floor(doubleLoc); pcoords[i] = doubleLoc - ijk[i];
let tmpInBounds = false; const minExt = extent[i * 2]; const maxExt = extent[i * 2 + 1];
if (minExt === maxExt) { const dist = x[i] - bounds[2 * i]; if (dist * dist <= spacing[i] * spacing[i] * tol2) { pcoords[i] = 0.0; ijk[i] = minExt; tmpInBounds = true; } } else if (ijk[i] < minExt) { if ( (spacing[i] >= 0 && x[i] >= bounds[i * 2]) || (spacing[i] < 0 && x[i] <= bounds[i * 2 + 1]) ) { pcoords[i] = 0.0; ijk[i] = minExt; tmpInBounds = true; } } else if (ijk[i] >= maxExt) { if ( (spacing[i] >= 0 && x[i] <= bounds[i * 2 + 1]) || (spacing[i] < 0 && x[i] >= bounds[i * 2]) ) { pcoords[i] = 1.0; ijk[i] = maxExt - 1; tmpInBounds = true; } } else { tmpInBounds = true; }
isInBounds = isInBounds && tmpInBounds; }
return isInBounds; };
publicAPI.getVoxelIndices = (ijk, dims, ids) => { ids[0] = ijk[2] * dims[0] * dims[1] + ijk[1] * dims[0] + ijk[0]; ids[1] = ids[0] + 1; ids[2] = ids[0] + dims[0]; ids[3] = ids[2] + 1; ids[4] = ids[0] + dims[0] * dims[1]; ids[5] = ids[4] + 1; ids[6] = ids[4] + dims[0]; ids[7] = ids[6] + 1; };
publicAPI.vectorAt = (xyz, velArray, image, velAtArg) => { if ( !publicAPI.computeStructuredCoordinates( xyz, indices, paramCoords, image.getExtent(), image.getSpacing(), image.getOrigin(), image.getBounds() ) ) { return false; }
publicAPI.interpolationFunctions(paramCoords, weights); const extent = image.getExtent(); dimensions[0] = extent[1] - extent[0] + 1; dimensions[1] = extent[3] - extent[2] + 1; dimensions[2] = extent[5] - extent[4] + 1; publicAPI.getVoxelIndices(indices, dimensions, voxelIndices); velAtArg[0] = 0.0; velAtArg[1] = 0.0; velAtArg[2] = 0.0; const vel = new Array(3); for (let i = 0; i < 8; i++) { velArray.getTuple(voxelIndices[i], vel); for (let j = 0; j < 3; j++) { velAtArg[j] += weights[i] * vel[j]; } }
return true; };
publicAPI.computeNextStep = (velArray, image, delT, xyz) => {
if (!publicAPI.vectorAt(xyz, velArray, image, velAt)) { return false; } for (let i = 0; i < 3; i++) { xtmp[i] = xyz[i] + (delT / 2.0) * velAt[i]; } if (!publicAPI.vectorAt(xtmp, velArray, image, velAt)) { return false; } for (let i = 0; i < 3; i++) { xyz[i] += delT * velAt[i]; }
if (!publicAPI.vectorAt(xyz, velArray, image, velAt)) { return false; }
return true; };
publicAPI.streamIntegrate = (velArray, image, seed, offset) => { const retVal = [];
const maxSteps = model.maximumNumberOfSteps; const delT = model.integrationStep; const xyz = new Float32Array(3); xyz[0] = seed[0]; xyz[1] = seed[1]; xyz[2] = seed[2];
const pointsBuffer = [];
let step = 0; for (step = 0; step < maxSteps; step++) { if (!publicAPI.computeNextStep(velArray, image, delT, xyz)) { break; } for (let i = 0; i < 3; i++) { pointsBuffer[3 * step + i] = xyz[i]; } }
const pd = vtkPolyData.newInstance();
const points = new Float32Array(pointsBuffer); retVal[0] = points;
pd.getPoints().setData(points, 3);
const npts = points.length / 3; const line = new Uint32Array(npts + 1); line[0] = npts; for (let i = 0; i < npts; i++) { line[i + 1] = i + offset; } retVal[1] = line;
pd.getLines().setData(line);
return retVal; };
publicAPI.requestData = (inData, outData) => { const input = inData[0]; const seeds = inData[1];
if (!input) { vtkErrorMacro('Invalid or missing input'); return; }
if (!seeds) { vtkErrorMacro('Invalid or missing seeds'); return; }
const seedPts = seeds.getPoints(); const nSeeds = seedPts.getNumberOfPoints();
let offset = 0; const datas = []; const vectors = input.getPointData().getVectors(); const point = []; for (let i = 0; i < nSeeds; i++) { seedPts.getTuple(i, point); const retVal = publicAPI.streamIntegrate(vectors, input, point, offset); offset += retVal[0].length / 3; datas.push(retVal); }
let cellArrayLength = 0; let pointArrayLength = 0; datas.forEach((data) => { cellArrayLength += data[1].length; pointArrayLength += data[0].length; }); offset = 0; let offset2 = 0; const cellArray = new Uint32Array(cellArrayLength); const pointArray = new Float32Array(pointArrayLength); datas.forEach((data) => { cellArray.set(data[1], offset); offset += data[1].length; pointArray.set(data[0], offset2); offset2 += data[0].length; });
const output = vtkPolyData.newInstance(); output.getPoints().setData(pointArray, 3); output.getLines().setData(cellArray);
outData[0] = output; }; }
const DEFAULT_VALUES = { integrationStep: 1, maximumNumberOfSteps: 1000, };
export function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues);
macro.obj(publicAPI, model);
macro.algo(publicAPI, model, 2, 1);
macro.setGet(publicAPI, model, ['integrationStep', 'maximumNumberOfSteps']);
vtkImageStreamline(publicAPI, model); }
export const newInstance = macro.newInstance(extend, 'vtkImageStreamline');
export default { newInstance, extend };
|