UNPKG

@kitware/vtk.js

Version:

Visualization Toolkit for the Web

318 lines (288 loc) 11.7 kB
import { m as macro } from '../../macros2.js'; import vtkCellArray from '../../Common/Core/CellArray.js'; import vtkPolyData from '../../Common/DataModel/PolyData.js'; import vtkPoints from '../../Common/Core/Points.js'; import vtkPolygon from '../../Common/DataModel/Polygon.js'; const { vtkErrorMacro } = macro; // ---------------------------------------------------------------------------- // vtkShrinkPolyData methods // ---------------------------------------------------------------------------- function vtkShrinkPolyData(publicAPI, model) { // Set our className model.classHierarchy.push('vtkShrinkPolyData'); /** * Shrink a point towards a given center by a shrink factor. * @param {Vector3} point - The [x, y, z] coordinates of the point to shrink * @param {Vector3} center - The [x, y, z] coordinates of the center * @param {number} shrinkFactor - The shrink factor (0.0 to 1.0) * @param {Vector3} [shrunkPoint] - Optional array to store the shrunk point * @returns {Vector3} The shrunk point [x, y, z] coordinates */ function shrinkTowardsPoint(point, center, shrinkFactor) { let shrunkPoint = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : []; shrunkPoint[0] = center[0] + shrinkFactor * (point[0] - center[0]); shrunkPoint[1] = center[1] + shrinkFactor * (point[1] - center[1]); shrunkPoint[2] = center[2] + shrinkFactor * (point[2] - center[2]); return shrunkPoint; } /** * Shrinks a cell towards its center by a shrink factor. * @param {number[]} cellPointIds - Array of point indices that define the cell * @param {vtkPoints} inPoints - Input points * @param {number} shrinkFactor - The shrink factor (0.0 to 1.0) * @param {Float32Array} newPointsData - Output array to store new point coordinates * @param {number} outCount - Current index in the output points array * @returns {Object} Object containing newPointIds array and updated outCount */ function shrinkCell(cellPointIds, inPoints, shrinkFactor, newPointsData, outCount) { const inPts = inPoints.getData(); const center = [0, 0, 0]; const newPointIds = []; const shrunkPoint = [0, 0, 0]; const currentPoint = [0, 0, 0]; let nextOutCount = outCount; const numPoints = cellPointIds.length; if (numPoints === 0) { return { newPointIds, outCount: nextOutCount }; } if (numPoints === 1) { // vertex - no shrinking needed, just copy the point const ptId = cellPointIds[0]; newPointsData[nextOutCount * 3] = inPts[ptId * 3]; newPointsData[nextOutCount * 3 + 1] = inPts[ptId * 3 + 1]; newPointsData[nextOutCount * 3 + 2] = inPts[ptId * 3 + 2]; newPointIds.push(nextOutCount); nextOutCount++; } else if (numPoints === 2) { // line - shrink towards midpoint // Calculate midpoint as center vtkPolygon.computeCentroid(cellPointIds, inPoints, center); // Shrink both points towards center for (let i = 0; i < 2; i++) { const ptId = cellPointIds[i]; currentPoint[0] = inPts[ptId * 3]; currentPoint[1] = inPts[ptId * 3 + 1]; currentPoint[2] = inPts[ptId * 3 + 2]; shrinkTowardsPoint(currentPoint, center, shrinkFactor, shrunkPoint); newPointsData[nextOutCount * 3] = shrunkPoint[0]; newPointsData[nextOutCount * 3 + 1] = shrunkPoint[1]; newPointsData[nextOutCount * 3 + 2] = shrunkPoint[2]; newPointIds.push(nextOutCount); nextOutCount++; } } else { // polygon/triangle - shrink towards centroid vtkPolygon.computeCentroid(cellPointIds, inPoints, center); // Shrink each point towards centroid for (let i = 0; i < numPoints; i++) { const ptId = cellPointIds[i]; currentPoint[0] = inPts[ptId * 3]; currentPoint[1] = inPts[ptId * 3 + 1]; currentPoint[2] = inPts[ptId * 3 + 2]; shrinkTowardsPoint(currentPoint, center, shrinkFactor, shrunkPoint); newPointsData[nextOutCount * 3] = shrunkPoint[0]; newPointsData[nextOutCount * 3 + 1] = shrunkPoint[1]; newPointsData[nextOutCount * 3 + 2] = shrunkPoint[2]; newPointIds.push(nextOutCount); nextOutCount++; } } return { newPointIds, outCount: nextOutCount }; } // Internal method to process the shrinking function shrinkData(input, output) { const inPoints = input.getPoints(); const inVerts = input.getVerts(); const inLines = input.getLines(); const inPolys = input.getPolys(); const inStrips = input.getStrips(); const shrinkFactor = model.shrinkFactor; let numNewPts = 0; if (inVerts) { const cellSizes = inVerts.getCellSizes(); for (let i = 0; i < cellSizes.length; i++) { numNewPts += cellSizes[i]; } } if (inLines) { const cellSizes = inLines.getCellSizes(); for (let i = 0; i < cellSizes.length; i++) { numNewPts += (cellSizes[i] - 1) * 2; } } if (inPolys) { const cellSizes = inPolys.getCellSizes(); for (let i = 0; i < cellSizes.length; i++) { numNewPts += cellSizes[i]; } } if (inStrips) { const cellSizes = inStrips.getCellSizes(); for (let i = 0; i < cellSizes.length; i++) { numNewPts += (cellSizes[i] - 2) * 3; } } const newPointsData = new Float32Array(numNewPts * 3); const newPoints = vtkPoints.newInstance(); newPoints.setData(newPointsData, 3); const newVerts = vtkCellArray.newInstance(); const newLines = vtkCellArray.newInstance(); const newPolys = vtkCellArray.newInstance(); let outCount = 0; // Process vertices if (inVerts) { const vertData = inVerts.getData(); const newVertData = []; const cellPointIds = []; for (let i = 0; i < vertData.length;) { cellPointIds.length = 0; // Clear previous point IDs const npts = vertData[i]; for (let j = 1; j <= npts; j++) { cellPointIds.push(vertData[i + j]); } const result = shrinkCell(cellPointIds, inPoints, shrinkFactor, newPointsData, outCount); outCount = result.outCount; newVertData.push(npts); newVertData.push(...result.newPointIds); i += npts + 1; } newVerts.setData(new Uint32Array(newVertData)); } // Process lines if (inLines) { const lineData = inLines.getData(); const newLineData = []; for (let i = 0; i < lineData.length;) { const npts = lineData[i]; // Process each line segment for (let j = 0; j < npts - 1; j++) { const cellPointIds = [lineData[i + j + 1], lineData[i + j + 2]]; const result = shrinkCell(cellPointIds, inPoints, shrinkFactor, newPointsData, outCount); outCount = result.outCount; newLineData.push(2, result.newPointIds[0], result.newPointIds[1]); } i += npts + 1; } newLines.setData(new Uint32Array(newLineData)); } // Process polygons if (inPolys) { const polyData = inPolys.getData(); const newPolyData = []; const cellPointIds = []; for (let i = 0; i < polyData.length;) { cellPointIds.length = 0; // Clear previous point IDs const npts = polyData[i]; for (let j = 1; j <= npts; j++) { cellPointIds.push(polyData[i + j]); } const result = shrinkCell(cellPointIds, inPoints, shrinkFactor, newPointsData, outCount); outCount = result.outCount; newPolyData.push(npts); newPolyData.push(...result.newPointIds); i += npts + 1; } newPolys.setData(new Uint32Array(newPolyData)); } // Process triangle strips (convert to triangles and shrink) if (inStrips) { const stripData = inStrips.getData(); const newPolyData = []; for (let i = 0; i < stripData.length;) { const npts = stripData[i]; for (let j = 0; j < npts - 2; j++) { const cellPointIds = [stripData[i + j + 1], stripData[i + j + 2], stripData[i + j + 3]]; const result = shrinkCell(cellPointIds, inPoints, shrinkFactor, newPointsData, outCount); outCount = result.outCount; // Triangle strips alternate the winding order of each triangle as you // move along the strip. This means that the orientation // (clockwise/counter-clockwise) flips for every new triangle. To // ensure consistent face orientation (so normals and rendering are // correct), we reverse the vertex order for every odd triangle. // Example strip with vertices [0,1,2,3,4,5] produces these triangles: // // 0───2───4 Triangle 0: (0,1,2) [CCW] // │ ╱ │ ╱ │ Triangle 1: (1,3,2) [CW -> reversed to (2,3,1) for CCW] // │╱ │╱ │ Triangle 2: (2,3,4) [CCW] // 1───3───5 Triangle 3: (3,5,4) [CW -> reversed to (4,5,3) for CCW] const newIds = [...result.newPointIds]; if (j % 2) { const tmp = newIds[0]; newIds[0] = newIds[2]; newIds[2] = tmp; } newPolyData.push(3, newIds[0], newIds[1], newIds[2]); } i += npts + 1; } if (newPolyData.length > 0) { const existingPolyData = newPolys.getData(); const combinedPolyData = new Uint32Array(existingPolyData.length + newPolyData.length); combinedPolyData.set(existingPolyData); combinedPolyData.set(newPolyData, existingPolyData.length); newPolys.setData(combinedPolyData); } } // Set output output.setPoints(newPoints); output.setVerts(newVerts); output.setLines(newLines); output.setPolys(newPolys); // Copy cell data output.getCellData().passData(input.getCellData()); } publicAPI.requestData = (inData, outData) => { const input = inData[0]; const output = outData[0]?.initialize() || vtkPolyData.newInstance(); if (!input) { vtkErrorMacro('No input!'); return; } if (!input.getPoints()) { vtkErrorMacro('Input has no points!'); return; } shrinkData(input, output); outData[0] = output; }; // Set the shrink factor publicAPI.setShrinkFactor = shrinkFactor => { if (shrinkFactor !== model.shrinkFactor) { model.shrinkFactor = Math.max(0.0, Math.min(1.0, shrinkFactor)); publicAPI.modified(); } }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- const DEFAULT_VALUES = { shrinkFactor: 0.5 }; // ---------------------------------------------------------------------------- function extend(publicAPI, model) { let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; Object.assign(model, DEFAULT_VALUES, initialValues); // Build VTK API 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, ['shrinkFactor']); vtkShrinkPolyData(publicAPI, model); } // ---------------------------------------------------------------------------- const newInstance = macro.newInstance(extend, 'vtkShrinkPolyData'); // ---------------------------------------------------------------------------- var vtkShrinkPolyData$1 = { newInstance, extend }; export { vtkShrinkPolyData$1 as default, extend, newInstance };