import * as macro from 'vtk.js/Sources/macros'; import vtkPolyData from 'vtk.js/Sources/Common/DataModel/PolyData';
const { vtkErrorMacro } = macro;
function initPolyIterator(pd) { const polys = pd.getPolys().getData(); const strips = pd.getStrips().getData(); const it = { cellSize: 0, cell: [], done: false, polyIdx: 0, stripIdx: 0, remainingStripLength: 0,
next() { if (it.polyIdx < polys.length) { it.cellSize = polys[it.polyIdx]; const start = it.polyIdx + 1; const end = start + it.cellSize; it.polyIdx = end; let p = 0; for (let i = start; i < end; ++i) { it.cell[p++] = polys[i]; } } else if (it.stripIdx < strips.length) { it.cellSize = 3; if (it.remainingStripLength === 0) { it.remainingStripLength = strips[it.stripIdx] - 2; it.stripIdx += 3; } const start = it.stripIdx - 2; const end = it.stripIdx + 1; it.stripIdx++; it.remainingStripLength--; let p = 0; for (let i = start; i < end; ++i) { it.cell[p++] = strips[i]; } } else if (!it.done) { it.done = true; } else { throw new Error('Iterator is done'); } }, }; it.next(); return it; }
function vtkCutter(publicAPI, model) { model.classHierarchy.push('vtkCutter');
const superClass = { ...publicAPI };
publicAPI.getMTime = () => { let mTime = superClass.getMTime(); if (!model.cutFunction) { return mTime; }
mTime = Math.max(mTime, model.cutFunction.getMTime()); return mTime; };
function dataSetCutter(input, output) { const points = input.getPoints(); const pointsData = points.getData(); const numPts = points.getNumberOfPoints(); const newPointsData = []; const newLinesData = []; const newPolysData = [];
if (!model.cutScalars || model.cutScalars.length < numPts) { model.cutScalars = new Float32Array(numPts); }
let inOffset = 0; let outOffset = 0; while (inOffset < pointsData.length) { model.cutScalars[outOffset++] = model.cutFunction.evaluateFunction( pointsData[inOffset++], pointsData[inOffset++], pointsData[inOffset++] ); }
const crossedEdges = []; const x1 = new Array(3); const x2 = new Array(3); const cellPointsScalars = [];
for (const it = initPolyIterator(input); !it.done; it.next()) {
if (it.cellSize <= 2) { continue; }
for (let i = 0; i < it.cellSize; ) { cellPointsScalars[i] = model.cutScalars[it.cell[i++]]; }
const sideFirstPoint = cellPointsScalars[0] > 0; let allPointsSameSide = true; for (let i = 1; i < it.cell.length; i++) { const sideCurrentPoint = cellPointsScalars[i] > 0; if (sideCurrentPoint !== sideFirstPoint) { allPointsSameSide = false; break; } }
if (allPointsSameSide) { continue; }
const intersectedEdgesList = []; for (let i = 0; i < it.cellSize; i++) { const idNext = i + 1 === it.cellSize ? 0 : i + 1;
const signPoint0 = cellPointsScalars[i] > 0; const signPoint1 = cellPointsScalars[idNext] > 0; if (signPoint1 === signPoint0) { continue; }
let e1 = i; let e2 = idNext; let deltaScalar = cellPointsScalars[e2] - cellPointsScalars[e1]; if (deltaScalar <= 0) { e1 = idNext; e2 = i; deltaScalar *= -1; }
let t = 0.0; if (deltaScalar !== 0.0) { t = (model.cutValue - cellPointsScalars[e1]) / deltaScalar; }
const pointID1 = it.cell[e1]; const pointID2 = it.cell[e2]; x1[0] = pointsData[pointID1 * 3]; x1[1] = pointsData[pointID1 * 3 + 1]; x1[2] = pointsData[pointID1 * 3 + 2]; x2[0] = pointsData[pointID2 * 3]; x2[1] = pointsData[pointID2 * 3 + 1]; x2[2] = pointsData[pointID2 * 3 + 2];
const computedIntersectedPoint = [ x1[0] + t * (x2[0] - x1[0]), x1[1] + t * (x2[1] - x1[1]), x1[2] + t * (x2[2] - x1[2]), ];
intersectedEdgesList.push({ pointEdge1: pointID1, pointEdge2: pointID2, intersectedPoint: computedIntersectedPoint, newPointID: -1, }); }
for (let i = 0; i < intersectedEdgesList.length; i++) { const intersectedEdge = intersectedEdgesList[i]; let alreadyAdded = false; for (let j = 0; j < crossedEdges.length; j++) { const crossedEdge = crossedEdges[j]; const sameEdge = intersectedEdge.pointEdge1 === crossedEdge.pointEdge1 && intersectedEdge.pointEdge2 === crossedEdge.pointEdge2; const samePoint = intersectedEdge.intersectedPoint[0] === crossedEdge.intersectedPoint[0] && intersectedEdge.intersectedPoint[1] === crossedEdge.intersectedPoint[1] && intersectedEdge.intersectedPoint[2] === crossedEdge.intersectedPoint[2]; if (sameEdge || samePoint) { alreadyAdded = true; intersectedEdgesList[i].newPointID = crossedEdges[j].newPointID; break; } } if (!alreadyAdded) { newPointsData.push(intersectedEdge.intersectedPoint[0]); newPointsData.push(intersectedEdge.intersectedPoint[1]); newPointsData.push(intersectedEdge.intersectedPoint[2]); intersectedEdgesList[i].newPointID = newPointsData.length / 3 - 1; crossedEdges.push(intersectedEdgesList[i]); } }
const cellSize = intersectedEdgesList.length; if (cellSize === 2) { newLinesData.push( cellSize, intersectedEdgesList[0].newPointID, intersectedEdgesList[1].newPointID ); } else if (cellSize > 2) { newPolysData.push(cellSize); intersectedEdgesList.forEach((edge) => { newPolysData.push(edge.newPointID); }); } }
const outputPoints = output.getPoints(); outputPoints.setData( macro.newTypedArrayFrom(points.getDataType(), newPointsData), 3 );
if (newLinesData.length !== 0) { output.getLines().setData(Uint16Array.from(newLinesData)); }
if (newPolysData.length !== 0) { output.getPolys().setData(Uint16Array.from(newPolysData)); } }
publicAPI.requestData = (inData, outData) => { const input = inData[0];
if (!input) { vtkErrorMacro('Invalid or missing input'); return; }
if (!model.cutFunction) { vtkErrorMacro('Missing cut function'); return; }
const output = vtkPolyData.newInstance();
dataSetCutter(input, output);
outData[0] = output; }; }
const DEFAULT_VALUES = { cutFunction: null, cutScalars: null, cutValue: 0.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, ['cutFunction', 'cutValue']);
vtkCutter(publicAPI, model); }
export const newInstance = macro.newInstance(extend, 'vtkCutter');
export default { newInstance, extend };
|