@kitware/vtk.js
Version:
Visualization Toolkit for the Web
277 lines (222 loc) • 8.3 kB
JavaScript
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 };