UNPKG

@kitware/vtk.js

Version:

Visualization Toolkit for the Web

277 lines (222 loc) 8.3 kB
import macro from '../../macros.js'; import vtkPolyData from '../../Common/DataModel/PolyData.js'; var vtkErrorMacro = macro.vtkErrorMacro; // ---------------------------------------------------------------------------- // vtkImageStreamline methods // ---------------------------------------------------------------------------- function vtkImageStreamline(publicAPI, model) { // Set our className model.classHierarchy.push('vtkImageStreamline'); var indices = new Int32Array(3); var paramCoords = new Float32Array(3); var weights = new Float32Array(8); var voxelIndices = new Uint32Array(8); var dimensions = new Uint32Array(3); var velAt = new Float32Array(3); var xtmp = new Float32Array(3); publicAPI.interpolationFunctions = function (pcoords, sf) { var r = pcoords[0]; var s = pcoords[1]; var t = pcoords[2]; var rm = 1.0 - r; var sm = 1.0 - s; var 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 = function (x, ijk, pcoords, extent, spacing, origin, bounds) { // tolerance is needed for 2D data (this is squared tolerance) var tol2 = 1e-12; // // Compute the ijk location // var isInBounds = true; for (var i = 0; i < 3; i++) { var d = x[i] - origin[i]; var doubleLoc = d / spacing[i]; // Floor for negative indexes. ijk[i] = Math.floor(doubleLoc); pcoords[i] = doubleLoc - ijk[i]; var tmpInBounds = false; var minExt = extent[i * 2]; var maxExt = extent[i * 2 + 1]; // check if data is one pixel thick if (minExt === maxExt) { var 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]) { // make sure index is within the allowed cell index range pcoords[i] = 1.0; ijk[i] = maxExt - 1; tmpInBounds = true; } } else { tmpInBounds = true; } // clear isInBounds if out of bounds for this dimension isInBounds = isInBounds && tmpInBounds; } return isInBounds; }; publicAPI.getVoxelIndices = function (ijk, dims, ids) { ids[0] = ijk[2] * dims[0] * dims[1] + ijk[1] * dims[0] + ijk[0]; ids[1] = ids[0] + 1; // i+1, j, k ids[2] = ids[0] + dims[0]; // i, j+1, k ids[3] = ids[2] + 1; // i+1, j+1, k ids[4] = ids[0] + dims[0] * dims[1]; // i, j, k+1 ids[5] = ids[4] + 1; // i+1, j, k+1 ids[6] = ids[4] + dims[0]; // i, j+1, k+1 ids[7] = ids[6] + 1; // i+1, j+1, k+1 }; publicAPI.vectorAt = function (xyz, velArray, image, velAtArg) { if (!publicAPI.computeStructuredCoordinates(xyz, indices, paramCoords, image.getExtent(), image.getSpacing(), image.getOrigin(), image.getBounds())) { return false; } publicAPI.interpolationFunctions(paramCoords, weights); var 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; var vel = new Array(3); for (var i = 0; i < 8; i++) { velArray.getTuple(voxelIndices[i], vel); for (var j = 0; j < 3; j++) { velAtArg[j] += weights[i] * vel[j]; } } return true; }; publicAPI.computeNextStep = function (velArray, image, delT, xyz) { // This does Runge-Kutta 2 // Start with evaluating velocity @ initial point if (!publicAPI.vectorAt(xyz, velArray, image, velAt)) { return false; } // Now find the mid point for (var i = 0; i < 3; i++) { xtmp[i] = xyz[i] + delT / 2.0 * velAt[i]; } // Use the velocity @ that point to project if (!publicAPI.vectorAt(xtmp, velArray, image, velAt)) { return false; } for (var _i = 0; _i < 3; _i++) { xyz[_i] += delT * velAt[_i]; } if (!publicAPI.vectorAt(xyz, velArray, image, velAt)) { return false; } return true; }; publicAPI.streamIntegrate = function (velArray, image, seed, offset) { var retVal = []; var maxSteps = model.maximumNumberOfSteps; var delT = model.integrationStep; var xyz = new Float32Array(3); xyz[0] = seed[0]; xyz[1] = seed[1]; xyz[2] = seed[2]; var pointsBuffer = []; var step = 0; for (step = 0; step < maxSteps; step++) { if (!publicAPI.computeNextStep(velArray, image, delT, xyz)) { break; } for (var i = 0; i < 3; i++) { pointsBuffer[3 * step + i] = xyz[i]; } } var pd = vtkPolyData.newInstance(); var points = new Float32Array(pointsBuffer); retVal[0] = points; pd.getPoints().setData(points, 3); var npts = points.length / 3; var line = new Uint32Array(npts + 1); line[0] = npts; for (var _i2 = 0; _i2 < npts; _i2++) { line[_i2 + 1] = _i2 + offset; } retVal[1] = line; pd.getLines().setData(line); return retVal; }; publicAPI.requestData = function (inData, outData) { // implement requestData var input = inData[0]; var seeds = inData[1]; if (!input) { vtkErrorMacro('Invalid or missing input'); return; } if (!seeds) { vtkErrorMacro('Invalid or missing seeds'); return; } var seedPts = seeds.getPoints(); var nSeeds = seedPts.getNumberOfPoints(); var offset = 0; var datas = []; var vectors = input.getPointData().getVectors(); var point = []; for (var i = 0; i < nSeeds; i++) { seedPts.getTuple(i, point); var retVal = publicAPI.streamIntegrate(vectors, input, point, offset); offset += retVal[0].length / 3; datas.push(retVal); } var cellArrayLength = 0; var pointArrayLength = 0; datas.forEach(function (data) { cellArrayLength += data[1].length; pointArrayLength += data[0].length; }); offset = 0; var offset2 = 0; var cellArray = new Uint32Array(cellArrayLength); var pointArray = new Float32Array(pointArrayLength); datas.forEach(function (data) { cellArray.set(data[1], offset); offset += data[1].length; pointArray.set(data[0], offset2); offset2 += data[0].length; }); var output = vtkPolyData.newInstance(); output.getPoints().setData(pointArray, 3); output.getLines().setData(cellArray); outData[0] = output; }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- var DEFAULT_VALUES = { integrationStep: 1, maximumNumberOfSteps: 1000 }; // ---------------------------------------------------------------------------- function extend(publicAPI, model) { var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; Object.assign(model, DEFAULT_VALUES, initialValues); // Make this a VTK object macro.obj(publicAPI, model); // Also make it an algorithm with one input and one output macro.algo(publicAPI, model, 2, 1); // Generate macros for properties macro.setGet(publicAPI, model, ['integrationStep', 'maximumNumberOfSteps']); // Object specific methods vtkImageStreamline(publicAPI, model); } // ---------------------------------------------------------------------------- var newInstance = macro.newInstance(extend, 'vtkImageStreamline'); // ---------------------------------------------------------------------------- var vtkImageStreamline$1 = { newInstance: newInstance, extend: extend }; export { vtkImageStreamline$1 as default, extend, newInstance };