UNPKG

@kitware/vtk.js

Version:

Visualization Toolkit for the Web

325 lines (258 loc) 11.2 kB
import macro from '../../macros.js'; import vtkDataArray from '../../Common/Core/DataArray.js'; import vtkEdgeLocator from '../../Common/DataModel/EdgeLocator.js'; import vtkPolyData from '../../Common/DataModel/PolyData.js'; import { l as normalize } from '../../Common/Core/Math/index.js'; import vtkCaseTable from './ImageMarchingCubes/caseTable.js'; var vtkErrorMacro = macro.vtkErrorMacro, vtkDebugMacro = macro.vtkDebugMacro; // ---------------------------------------------------------------------------- // vtkImageMarchingCubes methods // ---------------------------------------------------------------------------- function vtkImageMarchingCubes(publicAPI, model) { // Set our className model.classHierarchy.push('vtkImageMarchingCubes'); var ids = []; var voxelScalars = []; var voxelGradients = []; var voxelPts = []; var edgeLocator = vtkEdgeLocator.newInstance(); // Retrieve scalars and voxel coordinates. i-j-k is origin of voxel. publicAPI.getVoxelScalars = function (i, j, k, slice, dims, origin, spacing, s) { // First get the indices for the voxel ids[0] = k * slice + j * dims[0] + i; // i, j, k 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] + slice; // 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 // Now retrieve the scalars for (var ii = 0; ii < 8; ++ii) { voxelScalars[ii] = s[ids[ii]]; } }; // Retrieve voxel coordinates. i-j-k is origin of voxel. publicAPI.getVoxelPoints = function (i, j, k, origin, spacing) { // (i,i+1),(j,j+1),(k,k+1) - i varies fastest; then j; then k voxelPts[0] = origin[0] + i * spacing[0]; // 0 voxelPts[1] = origin[1] + j * spacing[1]; voxelPts[2] = origin[2] + k * spacing[2]; voxelPts[3] = voxelPts[0] + spacing[0]; // 1 voxelPts[4] = voxelPts[1]; voxelPts[5] = voxelPts[2]; voxelPts[6] = voxelPts[0]; // 2 voxelPts[7] = voxelPts[1] + spacing[1]; voxelPts[8] = voxelPts[2]; voxelPts[9] = voxelPts[3]; // 3 voxelPts[10] = voxelPts[7]; voxelPts[11] = voxelPts[2]; voxelPts[12] = voxelPts[0]; // 4 voxelPts[13] = voxelPts[1]; voxelPts[14] = voxelPts[2] + spacing[2]; voxelPts[15] = voxelPts[3]; // 5 voxelPts[16] = voxelPts[1]; voxelPts[17] = voxelPts[14]; voxelPts[18] = voxelPts[0]; // 6 voxelPts[19] = voxelPts[7]; voxelPts[20] = voxelPts[14]; voxelPts[21] = voxelPts[3]; // 7 voxelPts[22] = voxelPts[7]; voxelPts[23] = voxelPts[14]; }; // Compute point gradient at i-j-k location publicAPI.getPointGradient = function (i, j, k, dims, slice, spacing, s, g) { var sp; var sm; // x-direction if (i === 0) { sp = s[i + 1 + j * dims[0] + k * slice]; sm = s[i + j * dims[0] + k * slice]; g[0] = (sm - sp) / spacing[0]; } else if (i === dims[0] - 1) { sp = s[i + j * dims[0] + k * slice]; sm = s[i - 1 + j * dims[0] + k * slice]; g[0] = (sm - sp) / spacing[0]; } else { sp = s[i + 1 + j * dims[0] + k * slice]; sm = s[i - 1 + j * dims[0] + k * slice]; g[0] = 0.5 * (sm - sp) / spacing[0]; } // y-direction if (j === 0) { sp = s[i + (j + 1) * dims[0] + k * slice]; sm = s[i + j * dims[0] + k * slice]; g[1] = (sm - sp) / spacing[1]; } else if (j === dims[1] - 1) { sp = s[i + j * dims[0] + k * slice]; sm = s[i + (j - 1) * dims[0] + k * slice]; g[1] = (sm - sp) / spacing[1]; } else { sp = s[i + (j + 1) * dims[0] + k * slice]; sm = s[i + (j - 1) * dims[0] + k * slice]; g[1] = 0.5 * (sm - sp) / spacing[1]; } // z-direction if (k === 0) { sp = s[i + j * dims[0] + (k + 1) * slice]; sm = s[i + j * dims[0] + k * slice]; g[2] = (sm - sp) / spacing[2]; } else if (k === dims[2] - 1) { sp = s[i + j * dims[0] + k * slice]; sm = s[i + j * dims[0] + (k - 1) * slice]; g[2] = (sm - sp) / spacing[2]; } else { sp = s[i + j * dims[0] + (k + 1) * slice]; sm = s[i + j * dims[0] + (k - 1) * slice]; g[2] = 0.5 * (sm - sp) / spacing[2]; } }; // Compute voxel gradient values. I-j-k is origin point of voxel. publicAPI.getVoxelGradients = function (i, j, k, dims, slice, spacing, scalars) { var g = []; publicAPI.getPointGradient(i, j, k, dims, slice, spacing, scalars, g); voxelGradients[0] = g[0]; voxelGradients[1] = g[1]; voxelGradients[2] = g[2]; publicAPI.getPointGradient(i + 1, j, k, dims, slice, spacing, scalars, g); voxelGradients[3] = g[0]; voxelGradients[4] = g[1]; voxelGradients[5] = g[2]; publicAPI.getPointGradient(i, j + 1, k, dims, slice, spacing, scalars, g); voxelGradients[6] = g[0]; voxelGradients[7] = g[1]; voxelGradients[8] = g[2]; publicAPI.getPointGradient(i + 1, j + 1, k, dims, slice, spacing, scalars, g); voxelGradients[9] = g[0]; voxelGradients[10] = g[1]; voxelGradients[11] = g[2]; publicAPI.getPointGradient(i, j, k + 1, dims, slice, spacing, scalars, g); voxelGradients[12] = g[0]; voxelGradients[13] = g[1]; voxelGradients[14] = g[2]; publicAPI.getPointGradient(i + 1, j, k + 1, dims, slice, spacing, scalars, g); voxelGradients[15] = g[0]; voxelGradients[16] = g[1]; voxelGradients[17] = g[2]; publicAPI.getPointGradient(i, j + 1, k + 1, dims, slice, spacing, scalars, g); voxelGradients[18] = g[0]; voxelGradients[19] = g[1]; voxelGradients[20] = g[2]; publicAPI.getPointGradient(i + 1, j + 1, k + 1, dims, slice, spacing, scalars, g); voxelGradients[21] = g[0]; voxelGradients[22] = g[1]; voxelGradients[23] = g[2]; }; publicAPI.produceTriangles = function (cVal, i, j, k, extent, slice, dims, origin, spacing, scalars, points, tris, normals) { var CASE_MASK = [1, 2, 4, 8, 16, 32, 64, 128]; var VERT_MAP = [0, 1, 3, 2, 4, 5, 7, 6]; var xyz = []; var n = []; var pId; publicAPI.getVoxelScalars(i, j, k, slice, dims, origin, spacing, scalars); var index = 0; for (var idx = 0; idx < 8; idx++) { if (voxelScalars[VERT_MAP[idx]] >= cVal) { index |= CASE_MASK[idx]; // eslint-disable-line no-bitwise } } var voxelTris = vtkCaseTable.getCase(index); if (voxelTris[0] < 0) { return; // don't get the voxel coordinates, nothing to do } publicAPI.getVoxelPoints(i + extent[0], j + extent[2], k + extent[4], origin, spacing); if (model.computeNormals) { publicAPI.getVoxelGradients(i, j, k, dims, slice, spacing, scalars); } for (var _idx = 0; voxelTris[_idx] >= 0; _idx += 3) { tris.push(3); for (var eid = 0; eid < 3; eid++) { var edgeVerts = vtkCaseTable.getEdge(voxelTris[_idx + eid]); pId = undefined; if (model.mergePoints) { var _edgeLocator$isInsert; pId = (_edgeLocator$isInsert = edgeLocator.isInsertedEdge(ids[edgeVerts[0]], ids[edgeVerts[1]])) === null || _edgeLocator$isInsert === void 0 ? void 0 : _edgeLocator$isInsert.value; } if (pId === undefined) { var t = (cVal - voxelScalars[edgeVerts[0]]) / (voxelScalars[edgeVerts[1]] - voxelScalars[edgeVerts[0]]); var x0 = voxelPts.slice(edgeVerts[0] * 3, (edgeVerts[0] + 1) * 3); var x1 = voxelPts.slice(edgeVerts[1] * 3, (edgeVerts[1] + 1) * 3); xyz[0] = x0[0] + t * (x1[0] - x0[0]); xyz[1] = x0[1] + t * (x1[1] - x0[1]); xyz[2] = x0[2] + t * (x1[2] - x0[2]); pId = points.length / 3; points.push(xyz[0], xyz[1], xyz[2]); if (model.computeNormals) { var n0 = voxelGradients.slice(edgeVerts[0] * 3, (edgeVerts[0] + 1) * 3); var n1 = voxelGradients.slice(edgeVerts[1] * 3, (edgeVerts[1] + 1) * 3); n[0] = n0[0] + t * (n1[0] - n0[0]); n[1] = n0[1] + t * (n1[1] - n0[1]); n[2] = n0[2] + t * (n1[2] - n0[2]); normalize(n); normals.push(n[0], n[1], n[2]); } if (model.mergePoints) { edgeLocator.insertEdge(ids[edgeVerts[0]], ids[edgeVerts[1]], pId); } } tris.push(pId); } } }; publicAPI.requestData = function (inData, outData) { // implement requestData var input = inData[0]; if (!input) { vtkErrorMacro('Invalid or missing input'); return; } console.time('mcubes'); // Retrieve output and volume data var origin = input.getOrigin(); var spacing = input.getSpacing(); var dims = input.getDimensions(); var s = input.getPointData().getScalars().getData(); // Points - dynamic array var pBuffer = []; // Cells - dynamic array var tBuffer = []; // Normals var nBuffer = []; // Loop over all voxels, determine case and process var extent = input.getExtent(); var slice = dims[0] * dims[1]; for (var k = 0; k < dims[2] - 1; ++k) { for (var j = 0; j < dims[1] - 1; ++j) { for (var i = 0; i < dims[0] - 1; ++i) { publicAPI.produceTriangles(model.contourValue, i, j, k, extent, slice, dims, origin, spacing, s, pBuffer, tBuffer, nBuffer); } } } edgeLocator.initialize(); // Update output var polydata = vtkPolyData.newInstance(); polydata.getPoints().setData(new Float32Array(pBuffer), 3); polydata.getPolys().setData(new Uint32Array(tBuffer)); if (model.computeNormals) { var nData = new Float32Array(nBuffer); var normals = vtkDataArray.newInstance({ numberOfComponents: 3, values: nData, name: 'Normals' }); polydata.getPointData().setNormals(normals); } outData[0] = polydata; vtkDebugMacro('Produced output'); console.timeEnd('mcubes'); }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- var DEFAULT_VALUES = { contourValue: 0, computeNormals: false, mergePoints: false }; // ---------------------------------------------------------------------------- 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, 1, 1); macro.setGet(publicAPI, model, ['contourValue', 'computeNormals', 'mergePoints']); // Object specific methods macro.algo(publicAPI, model, 1, 1); vtkImageMarchingCubes(publicAPI, model); } // ---------------------------------------------------------------------------- var newInstance = macro.newInstance(extend, 'vtkImageMarchingCubes'); // ---------------------------------------------------------------------------- var vtkImageMarchingCubes$1 = { newInstance: newInstance, extend: extend }; export { vtkImageMarchingCubes$1 as default, extend, newInstance };