UNPKG

@kitware/vtk.js

Version:

Visualization Toolkit for the Web

242 lines (220 loc) 9.13 kB
import { m as macro } from '../../macros2.js'; import vtkPolyData from '../../Common/DataModel/PolyData.js'; import vtkCellArray from '../../Common/Core/CellArray.js'; import vtkPolygon from '../../Common/DataModel/Polygon.js'; import vtkSphere from '../../Common/DataModel/Sphere.js'; import vtkTriangleStrip from '../../Common/DataModel/TriangleStrip.js'; // ---------------------------------------------------------------------------- // vtkFillHolesFilter methods // ---------------------------------------------------------------------------- function vtkFillHolesFilter(publicAPI, model) { // Set our className model.classHierarchy.push('vtkFillHolesFilter'); function getSingleLineNeighbor(linesPolyData, currentCellId, pointId) { const pointCells = linesPolyData.getPointCells(pointId); let neighborId = -1; let neighborCount = 0; for (let i = 0; i < pointCells.length; i++) { const cellId = pointCells[i]; if (cellId !== currentCellId) { neighborId = cellId; neighborCount += 1; if (neighborCount > 1) { return -2; } } } return neighborId; } function mapTriangulatedPointToLocalId(pointToLocalIds, x, y, z) { const key = `${x},${y},${z}`; const ids = pointToLocalIds.get(key); if (!ids || ids.length === 0) { return -1; } return ids[0]; } publicAPI.requestData = (inData, outData) => { const input = inData[0]; const output = outData[0]?.initialize() || vtkPolyData.newInstance(); if (!input) { outData[0] = output; return; } const inPts = input.getPoints(); const inPolys = input.getPolys(); const numPts = inPts?.getNumberOfPoints(); const numPolys = inPolys?.getNumberOfCells(); const inStrips = input.getStrips(); const numStrips = inStrips?.getNumberOfCells(); if (numPts < 1 || !inPts || numPolys < 1 && numStrips < 1) { outData[0] = input; return; } const mesh = vtkPolyData.newInstance(); mesh.setPoints(inPts); let polysForConnectivity = inPolys; if (numStrips > 0) { const decomposedPolys = vtkCellArray.newInstance(); if (numPolys > 0) { decomposedPolys.deepCopy(inPolys); } const stripValues = inStrips.getData(); const stripPtIds = []; let offset = 0; while (offset < stripValues.length) { const npts = stripValues[offset]; stripPtIds.length = npts; for (let i = 0; i < npts; i++) { stripPtIds[i] = stripValues[offset + 1 + i]; } vtkTriangleStrip.decomposeStrip(stripPtIds, decomposedPolys); offset += npts + 1; } polysForConnectivity = decomposedPolys; } mesh.setPolys(polysForConnectivity); mesh.buildLinks(); // Allocate storage for lines/points (arbitrary allocation sizes). const newLines = vtkCellArray.newInstance(); const polyValues = polysForConnectivity.getData(); const edgePtIds = [0, 0]; let polyOffset = 0; let cellId = 0; // Grab all free edges and place them into a temporary polydata. while (polyOffset < polyValues.length) { const npts = polyValues[polyOffset]; const polyPtsOffset = polyOffset + 1; for (let i = 0; i < npts; i++) { const p1 = polyValues[polyPtsOffset + i]; const p2 = polyValues[polyPtsOffset + (i + 1) % npts]; const neighbors = mesh.getCellEdgeNeighbors(cellId, p1, p2); if (neighbors.length < 1) { edgePtIds[0] = p1; edgePtIds[1] = p2; newLines.insertNextCell(edgePtIds); } } polyOffset += npts + 1; cellId++; } const numLines = newLines.getNumberOfCells(); let newCells = null; // Track all free edges and see whether polygons can be built from them. // For each polygon of appropriate HoleSize, triangulate the hole and // add to the output list of cells. if (numLines >= 3) { newCells = vtkCellArray.newInstance(); newCells.deepCopy(inPolys); const linesPolyData = vtkPolyData.newInstance(); linesPolyData.setPoints(inPts); linesPolyData.setLines(newLines); linesPolyData.buildLinks(); const visited = new Uint8Array(numLines); for (let lineCellId = 0; lineCellId < numLines; lineCellId++) { if (visited[lineCellId] === 0) { visited[lineCellId] = 1; // Setup the polygon. const linePts = linesPolyData.getCellPoints(lineCellId).cellPointIds; const startId = linePts[0]; const polygonPtIds = [startId]; const polygonPts = [inPts.getPoint(startId)]; let endId = linePts[1]; let currentCellId = lineCellId; let valid = true; // Work around the loop and terminate when the loop ends. while (startId !== endId && valid) { polygonPtIds.push(endId); polygonPts.push(inPts.getPoint(endId)); const neighborId = getSingleLineNeighbor(linesPolyData, currentCellId, endId); if (neighborId === -1) { valid = false; } else if (neighborId === -2) { // Have to logically split this vertex. valid = false; } else { visited[neighborId] = 1; const neiPts = linesPolyData.getCellPoints(neighborId).cellPointIds; endId = neiPts[0] !== endId ? neiPts[0] : neiPts[1]; currentCellId = neighborId; } } // Evaluate the size of the loop and see if it is small enough. if (valid) { const flatPts = polygonPts.flat(); const sphere = vtkSphere.computeBoundingSphere(flatPts); const holeSize = Number.isFinite(model.holeSize) ? Math.max(0, model.holeSize) : 0; if (sphere[3] <= holeSize) { // Now triangulate the loop and pass to the output. const polygon = vtkPolygon.newInstance(); polygon.setPoints(polygonPts); if (polygon.triangulate()) { const triangles = polygon.getPointArray(); const pointToLocalIds = new Map(); for (let i = 0; i < polygonPts.length; i++) { const pt = polygonPts[i]; const key = `${pt[0]},${pt[1]},${pt[2]}`; if (!pointToLocalIds.has(key)) { pointToLocalIds.set(key, []); } pointToLocalIds.get(key).push(i); } const trianglePtIds = [0, 0, 0]; for (let i = 0; i < triangles.length; i += 9) { const localId0 = mapTriangulatedPointToLocalId(pointToLocalIds, triangles[i], triangles[i + 1], triangles[i + 2]); const localId1 = mapTriangulatedPointToLocalId(pointToLocalIds, triangles[i + 3], triangles[i + 4], triangles[i + 5]); const localId2 = mapTriangulatedPointToLocalId(pointToLocalIds, triangles[i + 6], triangles[i + 7], triangles[i + 8]); if (localId0 >= 0 && localId1 >= 0 && localId2 >= 0 && localId0 !== localId1 && localId1 !== localId2 && localId2 !== localId0) { trianglePtIds[0] = polygonPtIds[localId0]; trianglePtIds[1] = polygonPtIds[localId1]; trianglePtIds[2] = polygonPtIds[localId2]; newCells.insertNextCell(trianglePtIds); } } } } } } } } // No new points are created, so the points and point data can be passed // through to the output. output.setPoints(inPts); output.getPointData().passData(input.getPointData()); // New cells are created, so we do not pass cell data. output.setVerts(input.getVerts()); output.setLines(input.getLines()); if (newCells) { output.setPolys(newCells); } else { output.setPolys(inPolys); } output.setStrips(input.getStrips()); outData[0] = output; }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- const DEFAULT_VALUES = { holeSize: 1.0 }; // ---------------------------------------------------------------------------- function extend(publicAPI, model, initialValues = {}) { 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, ['holeSize']); // Object specific methods vtkFillHolesFilter(publicAPI, model); } // ---------------------------------------------------------------------------- const newInstance = macro.newInstance(extend, 'vtkFillHolesFilter'); // ---------------------------------------------------------------------------- var vtkFillHolesFilter$1 = { newInstance, extend }; export { vtkFillHolesFilter$1 as default, extend, newInstance };