import macro from 'vtk.js/Sources/macros';
import vtkBoundingBox from 'vtk.js/Sources/Common/DataModel/BoundingBox'; import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray'; import vtkMath from 'vtk.js/Sources/Common/Core/Math/index'; import { AttributeTypes } from 'vtk.js/Sources/Common/DataModel/DataSetAttributes/Constants'; import vtkPoints from 'vtk.js/Sources/Common/Core/Points'; import vtkPolyData from 'vtk.js/Sources/Common/DataModel/PolyData'; import vtkTriangle from 'vtk.js/Sources/Common/DataModel/Triangle';
const VertexType = { VTK_SIMPLE_VERTEX: 0, VTK_FIXED_VERTEX: 1, VTK_FEATURE_EDGE_VERTEX: 2, VTK_BOUNDARY_EDGE_VERTEX: 3, };
function vtkWindowedSincPolyDataFilter(publicAPI, model) { model.classHierarchy.push('vtkWindowedSincPolyDataFilter');
publicAPI.vtkWindowedSincPolyDataFilterExecute = ( inPts, inputPolyData, output ) => { if (!inPts || model.numberOfIterations <= 0) { return inPts; } const inPtsData = inPts.getData();
const inVerts = inputPolyData.getVerts().getData(); const inLines = inputPolyData.getLines().getData(); const inPolys = inputPolyData.getPolys().getData(); const inStrips = inputPolyData.getStrips().getData();
const cosFeatureAngle = Math.cos( vtkMath.radiansFromDegrees(model.featureAngle) ); const cosEdgeAngle = Math.cos(vtkMath.radiansFromDegrees(model.edgeAngle));
const numPts = inPts.getNumberOfPoints();
const verts = new Array(numPts); for (let i = 0; i < numPts; ++i) { verts[i] = { type: VertexType.VTK_SIMPLE_VERTEX, edges: null, }; }
let npts = 0; for (let i = 0; i < inVerts.length; i += npts + 1) { npts = inVerts[i]; const pts = inVerts.slice(i + 1, i + 1 + npts); for (let j = 0; j < pts.length; ++j) { verts[pts[j]].type = VertexType.VTK_FIXED_VERTEX; } }
for (let i = 0; i < inLines.length; i += npts + 1) { npts = inLines[i]; const pts = inLines.slice(i + 1, i + 1 + npts);
const closedLoop = pts[0] === pts[npts - 1] && npts > 3;
for (let j = 0; j < npts; ++j) { if (verts[pts[j]].type === VertexType.VTK_SIMPLE_VERTEX) { if (j === 0) { if (!closedLoop) { verts[pts[0]].type = VertexType.VTK_FIXED_VERTEX; } else { verts[pts[0]].type = VertexType.VTK_FEATURE_EDGE_VERTEX; verts[pts[0]].edges = [pts[npts - 2], pts[1]]; } } else if (j === npts - 1 && !closedLoop) { verts[pts[j]].type = VertexType.VTK_FIXED_VERTEX; } else { verts[pts[j]].type = VertexType.VTK_FEATURE_EDGE_VERTEX; verts[pts[j]].edges = [ pts[j - 1], pts[closedLoop && j === npts - 2 ? 0 : j + 1], ]; } }
else if ( verts[pts[j]].type === VertexType.VTK_FEATURE_EDGE_VERTEX && !(closedLoop && j === npts - 1) ) { verts[pts[j]].type = VertexType.VTK_FIXED_VERTEX; verts[pts[j]].edges = null; } } }
const numPolys = inPolys.length; const numStrips = inStrips.length; if (numPolys > 0 || numStrips > 0) { const inMesh = vtkPolyData.newInstance(); inMesh.setPoints(inputPolyData.getPoints()); inMesh.setPolys(inputPolyData.getPolys()); const mesh = inMesh;
let neighbors = []; let nei = 0; const normal = []; const neiNormal = [];
mesh.buildLinks(); const polys = mesh.getPolys().getData();
let cellId = 0; for (let c = 0; c < polys.length; c += npts + 1, ++cellId) { npts = polys[c]; const pts = polys.slice(c + 1, c + 1 + npts);
for (let i = 0; i < npts; ++i) { const p1 = pts[i]; const p2 = pts[(i + 1) % npts];
if (verts[p1].edges === null) { verts[p1].edges = []; } if (verts[p2].edges == null) { verts[p2].edges = []; }
neighbors = mesh.getCellEdgeNeighbors(cellId, p1, p2); const numNei = neighbors.length;
let edge = VertexType.VTK_SIMPLE_VERTEX; if (numNei === 0) { edge = VertexType.VTK_BOUNDARY_EDGE_VERTEX; } else if (numNei >= 2) { if (!model.nonManifoldSmoothing) { let j = 0; for (; j < numNei; ++j) { if (neighbors[j] < cellId) { break; } } if (j >= numNei) { edge = VertexType.VTK_FEATURE_EDGE_VERTEX; } } } else if (numNei === 1 && (nei = neighbors[0]) > cellId) { if (model.featureEdgeSmoothing) { vtkTriangle.computeNormal( inPts.getPoint(pts[0]), inPts.getPoint(pts[1]), inPts.getPoint(pts[2]), normal ); const { cellPointIds } = mesh.getCellPoints(nei); vtkTriangle.computeNormal( inPts.getPoint(cellPointIds[0]), inPts.getPoint(cellPointIds[1]), inPts.getPoint(cellPointIds[2]), neiNormal );
if (vtkMath.dot(normal, neiNormal) <= cosFeatureAngle) { edge = VertexType.VTK_FEATURE_EDGE_VERTEX; } } } else { continue; }
if (edge && verts[p1].type === VertexType.VTK_SIMPLE_VERTEX) { verts[p1].edges = [p2]; verts[p1].type = edge; } else if ( (edge && verts[p1].type === VertexType.VTK_BOUNDARY_EDGE_VERTEX) || (edge && verts[p1].type === VertexType.VTK_FEATURE_EDGE_VERTEX) || (!edge && verts[p1].type === VertexType.VTK_SIMPLE_VERTEX) ) { verts[p1].edges.push(p2); if ( verts[p1].type && edge === VertexType.VTK_BOUNDARY_EDGE_VERTEX ) { verts[p1].type = VertexType.VTK_BOUNDARY_EDGE_VERTEX; } } if (edge && verts[p2].type === VertexType.VTK_SIMPLE_VERTEX) { verts[p2].edges = [p1]; verts[p2].type = edge; } else if ( (edge && verts[p2].type === VertexType.VTK_BOUNDARY_EDGE_VERTEX) || (edge && verts[p2].type === VertexType.VTK_FEATURE_EDGE_VERTEX) || (!edge && verts[p2].type === VertexType.VTK_SIMPLE_VERTEX) ) { verts[p2].edges.push(p1); if ( verts[p2].type && edge === VertexType.VTK_BOUNDARY_EDGE_VERTEX ) { verts[p2].type = VertexType.VTK_BOUNDARY_EDGE_VERTEX; } } } } }
let numSimple = 0; let numBEdges = 0; let numFixed = 0; let numFEdges = 0; for (let i = 0; i < numPts; ++i) { if (verts[i].type === VertexType.VTK_SIMPLE_VERTEX) { ++numSimple; } else if (verts[i].type === VertexType.VTK_FIXED_VERTEX) { ++numFixed; } else if ( verts[i].type === VertexType.VTK_FEATURE_EDGE_VERTEX || verts[i].type === VertexType.VTK_BOUNDARY_EDGE_VERTEX ) {
if ( !model.boundarySmoothing && verts[i].type === VertexType.VTK_BOUNDARY_EDGE_VERTEX ) { verts[i].type = VertexType.VTK_FIXED_VERTEX; ++numBEdges; } else if ((npts = verts[i].edges.length) !== 2) { verts[i].type = VertexType.VTK_FIXED_VERTEX; ++numFixed; } else { const x1 = inPts.getPoint(verts[i].edges[0]); const x2 = inPts.getPoint(i); const x3 = inPts.getPoint(verts[i].edges[1]);
const l1 = [0, 0, 0]; const l2 = [0, 0, 0]; for (let k = 0; k < 3; ++k) { l1[k] = x2[k] - x1[k]; l2[k] = x3[k] - x2[k]; } if ( vtkMath.normalize(l1) >= 0.0 && vtkMath.normalize(l2) >= 0.0 && vtkMath.dot(l1, l2) < cosEdgeAngle ) { ++numFixed; verts[i].type = VertexType.VTK_FIXED_VERTEX; } else if (verts[i].type === VertexType.VTK_FEATURE_EDGE_VERTEX) { ++numFEdges; } else { ++numBEdges; } } } }
let zero = 0; let one = 1; let two = 2; const three = 3;
const newPts = []; newPts.push(vtkPoints.newInstance()); newPts[zero].setNumberOfPoints(numPts); newPts.push(vtkPoints.newInstance()); newPts[one].setNumberOfPoints(numPts); newPts.push(vtkPoints.newInstance()); newPts[two].setNumberOfPoints(numPts); newPts.push(vtkPoints.newInstance()); newPts[three].setNumberOfPoints(numPts);
const inCenter = vtkBoundingBox.getCenter(inputPolyData.getBounds()); const inLength = vtkBoundingBox.getDiagonalLength( inputPolyData.getBounds() );
if (!model.normalizeCoordinates) { const copy = macro.newTypedArray(newPts[zero].getDataType(), inPtsData); newPts[zero].setData(copy, 3); } else { const normalizedPoint = [0, 0, 0]; for (let i = 0; i < numPts; ++i) { inPts.getPoint(i, normalizedPoint); normalizedPoint[0] = (normalizedPoint[0] - inCenter[0]) / inLength; normalizedPoint[1] = (normalizedPoint[1] - inCenter[1]) / inLength; normalizedPoint[2] = (normalizedPoint[2] - inCenter[2]) / inLength; newPts[zero].setPoint(i, ...normalizedPoint); } }
const kPb = model.passBand; const thetaPb = Math.acos(1.0 - 0.5 * kPb);
const w = new Array(model.numberOfIterations + 1); const c = new Array(model.numberOfIterations + 1); const cprime = new Array(model.numberOfIterations + 1);
const zerovector = [0, 0, 0];
for (let i = 0; i <= model.numberOfIterations; ++i) { w[i] = 0.54 + 0.46 * Math.cos((i * Math.PI) / (model.numberOfIterations + 1)); }
let fKpb = 0; let fPrimeKpb = 0; let done = false; let sigma = 0.0;
for (let j = 0; !done && j < 500; ++j) { c[0] = (w[0] * (thetaPb + sigma)) / Math.PI; for (let i = 1; i <= model.numberOfIterations; ++i) { c[i] = (2.0 * w[i] * Math.sin(i * (thetaPb + sigma))) / (i * Math.PI); }
cprime[model.numberOfIterations] = 0.0; cprime[model.numberOfIterations - 1] = 0.0; if (model.numberOfIterations > 1) { cprime[model.numberOfIterations - 2] = 2.0 * (model.numberOfIterations - 1) * c[model.numberOfIterations - 1]; } for (let i = model.numberOfIterations - 3; i >= 0; --i) { cprime[i] = cprime[i + 2] + 2.0 * (i + 1) * c[i + 1]; } fKpb = 0.0; fPrimeKpb = 0.0; fKpb += c[0]; fPrimeKpb += cprime[0]; for (let i = 1; i <= model.numberOfIterations; ++i) { if (i === 1) { fKpb += c[i] * (1.0 - 0.5 * kPb); fPrimeKpb += cprime[i] * (1.0 - 0.5 * kPb); } else { fKpb += c[i] * Math.cos(i * Math.acos(1.0 - 0.5 * kPb)); fPrimeKpb += cprime[i] * Math.cos(i * Math.acos(1.0 - 0.5 * kPb)); } } if (model.numberOfIterations > 1) { if (Math.abs(fKpb - 1.0) >= 1e-3) { sigma -= (fKpb - 1.0) / fPrimeKpb; } else { done = true; } } else { done = true; sigma = 0.0; } } if (Math.abs(fKpb - 1.0) >= 1e-3) { console.log( 'An optimal offset for the smoothing filter could not be found. Unpredictable smoothing/shrinkage may result.' ); }
const x = [0, 0, 0]; const y = [0, 0, 0]; const deltaX = [0, 0, 0]; const xNew = [0, 0, 0]; const x1 = [0, 0, 0]; const x2 = [0, 0, 0];
for (let i = 0; i < numPts; ++i) { if (verts[i].edges != null && (npts = verts[i].edges.length) > 0) { newPts[zero].getPoint(i, x); deltaX[0] = 0.0; deltaX[1] = 0.0; deltaX[2] = 0.0;
for (let j = 0; j < npts; ++j) { newPts[zero].getPoint(verts[i].edges[j], y); for (let k = 0; k < 3; ++k) { deltaX[k] += (x[k] - y[k]) / npts; } } for (let k = 0; k < 3; ++k) { deltaX[k] = x[k] - 0.5 * deltaX[k]; } newPts[one].setPoint(i, ...deltaX);
if (verts[i].type === VertexType.VTK_FIXED_VERTEX) { newPts[zero].getPoint(i, deltaX); } else { for (let k = 0; k < 3; ++k) { deltaX[k] = c[0] * x[k] + c[1] * deltaX[k]; } } newPts[three].setPoint(i, ...deltaX); } else { newPts[one].setPoint(i, ...zerovector); newPts[zero].getPoint(i, deltaX); newPts[three].setPoint(i, ...deltaX); } }
const pX0 = [0, 0, 0]; const pX1 = [0, 0, 0]; const pX3 = [0, 0, 0]; let iterationNumber = 2; for (; iterationNumber <= model.numberOfIterations; iterationNumber++) { if (iterationNumber && !(iterationNumber % 5)) { }
for (let i = 0; i < numPts; ++i) { npts = verts[i].edges != null ? verts[i].edges.length : 0; if (npts > 0) { newPts[zero].getPoint(i, pX0); newPts[one].getPoint(i, pX1);
deltaX[0] = 0.0; deltaX[1] = 0.0; deltaX[2] = 0.0;
for (let j = 0; j < npts; ++j) { newPts[one].getPoint(verts[i].edges[j], y); for (let k = 0; k < 3; ++k) { deltaX[k] += (pX1[k] - y[k]) / npts; } }
for (let k = 0; k < 3; ++k) { deltaX[k] = pX1[k] - pX0[k] + pX1[k] - deltaX[k]; } newPts[two].setPoint(i, ...deltaX);
newPts[three].getPoint(i, pX3); for (let k = 0; k < 3; ++k) { xNew[k] = pX3[k] + c[iterationNumber] * deltaX[k]; } if (verts[i].type !== VertexType.VTK_FIXED_VERTEX) { newPts[three].setPoint(i, ...xNew); } } else { newPts[one].setPoint(i, ...zerovector); newPts[two].setPoint(i, ...zerovector); } }
zero = (1 + zero) % 3; one = (1 + one) % 3; two = (1 + two) % 3; }
--iterationNumber;
zero = three;
if (model.normalizeCoordinates) { const repositionedPoint = [0, 0, 0]; for (let i = 0; i < numPts; ++i) { newPts[zero].getPoint(i, repositionedPoint); for (let j = 0; j < 3; ++j) { repositionedPoint[j] = repositionedPoint[j] * inLength + inCenter[j]; } newPts[zero].setPoint(i, ...repositionedPoint); } }
if (model.generateErrorScalars) { const newScalars = new Float32Array(numPts);
for (let i = 0; i < numPts; ++i) { inPts.getPoint(i, x1); newPts[zero].getPoint(i, x2); newScalars[i] = Math.sqrt(Math.distance2BetweenPoints(x1, x2)); }
const newScalarsArray = vtkDataArray.newInstance({ numberOfComponents: 1, values: newScalars, });
const idx = output.getPointData().addArray(newScalarsArray); output.getPointData().setActiveAttribute(idx, AttributeTypes.SCALARS); }
if (model.generateErrorVectors) { const newVectors = new Float32Array(3 * numPts); for (let i = 0; i < numPts; ++i) { inPts.getPoint(i, x1); newPts[zero].getPoint(i, x2); for (let j = 0; j < 3; ++j) { newVectors[3 * i + j] = x2[j] - x1[j]; } }
const newVectorsArray = vtkDataArray.newInstance({ numberOfComponents: 3, values: newVectors, });
output.getPointData().setVectors(newVectorsArray); }
return newPts[zero]; };
publicAPI.requestData = (inData, outData) => { const numberOfInputs = publicAPI.getNumberOfInputPorts();
if (!numberOfInputs) { return; }
const input = inData[0];
if (!input) { return; } const output = vtkPolyData.newInstance();
const outputPoints = publicAPI.vtkWindowedSincPolyDataFilterExecute( input.getPoints(), input, output );
output.setPointData(input.getPointData()); output.setCellData(input.getCellData()); output.setFieldData(input.getFieldData()); output.setPoints(outputPoints); output.setVerts(input.getVerts()); output.setLines(input.getLines()); output.setPolys(input.getPolys()); output.setStrips(input.getStrips());
outData[0] = output; }; }
const DEFAULT_VALUES = { numberOfIterations: 20, passBand: 0.1, featureAngle: 45.0, edgeAngle: 15.0, featureEdgeSmoothing: 0, boundarySmoothing: 1, nonManifoldSmoothing: 0, generateErrorScalars: 0, generateErrorVectors: 0, normalizeCoordinates: 0, };
export function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues);
macro.obj(publicAPI, model);
macro.algo(publicAPI, model, 1, 1);
macro.setGet(publicAPI, model, [ 'numberOfIterations', 'passBand', 'featureAngle', 'edgeAngle', 'featureEdgeSmoothing', 'boundarySmoothing', 'nonManifoldSmoothing', 'generateErrorScalars', 'generateErrorVectors', 'normalizeCoordinates', ]);
vtkWindowedSincPolyDataFilter(publicAPI, model); }
export const newInstance = macro.newInstance( extend, 'vtkWindowedSincPolyDataFilter' );
export default { newInstance, extend };
|