UNPKG

vtk.js-custom-jhonny

Version:

Visualization Toolkit for the Web

296 lines (259 loc) 9.95 kB
import { n as newInstance$1, o as obj, d as algo, e as setGet, c as macro, b as newTypedArrayFrom } from '../../macros2.js'; import vtkPolyData from '../../Common/DataModel/PolyData.js'; 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, // returns a single poly cell 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; // sliding window of 3 points // stripIdx points to the last point in a triangle 3-tuple 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; } // ---------------------------------------------------------------------------- // vtkCutter methods // ---------------------------------------------------------------------------- function vtkCutter(publicAPI, model) { // Set our className model.classHierarchy.push('vtkCutter'); // Capture "parentClass" api for internal use 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); } // Loop over all points evaluating scalar function at each point 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 = []; // Loop over all cells; get scalar values for all cell points // and process each cell. /* eslint-disable no-continue */ for (const it = initPolyIterator(input); !it.done; it.next()) { // cell contains the point IDs/indices // Check that cells have at least 3 points if (it.cellSize <= 2) { continue; } // Get associated scalar of points that constitute the current cell for (let i = 0; i < it.cellSize;) { cellPointsScalars[i] = model.cutScalars[it.cell[i++]]; } // Check if all cell points are on same side (same side == cell not crossed by cut function) // TODO: won't work if one point scalar is = 0 ? 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; } } // Go to next cell if cell is not crossed by cut function if (allPointsSameSide) { continue; } // Find and compute edges which intersect cells const intersectedEdgesList = []; for (let i = 0; i < it.cellSize; i++) { const idNext = i + 1 === it.cellSize ? 0 : i + 1; // Go to next edge if edge is not crossed // TODO: in most come cases, (numberOfPointsInCell - 1) or 0 edges of the cell // will be crossed, but if it crosses right at a point, it could be intersecting // with (numberOfPoints) or 1 edge(s). Do we account for that? const signPoint0 = cellPointsScalars[i] > 0; const signPoint1 = cellPointsScalars[idNext] > 0; if (signPoint1 === signPoint0) { continue; } // Compute preferred interpolation direction let e1 = i; let e2 = idNext; let deltaScalar = cellPointsScalars[e2] - cellPointsScalars[e1]; if (deltaScalar <= 0) { e1 = idNext; e2 = i; deltaScalar *= -1; } // linear interpolation let t = 0.0; if (deltaScalar !== 0.0) { t = (model.cutValue - cellPointsScalars[e1]) / deltaScalar; } // points position 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]; // Compute the intersected point on edge const computedIntersectedPoint = [x1[0] + t * (x2[0] - x1[0]), x1[1] + t * (x2[1] - x1[1]), x1[2] + t * (x2[2] - x1[2])]; // Keep track of it intersectedEdgesList.push({ pointEdge1: pointID1, // id of one point of the edge pointEdge2: pointID2, // id of one point of the edge intersectedPoint: computedIntersectedPoint, // 3D coordinate of points that intersected edge newPointID: -1 // id of the intersected point when it will be added into vtkPoints }); } // Add points into newPointList for (let i = 0; i < intersectedEdgesList.length; i++) { const intersectedEdge = intersectedEdgesList[i]; let alreadyAdded = false; // Check if point/edge already added 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]); } } // Store cells 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); }); } } // Set points const outputPoints = output.getPoints(); outputPoints.setData(newTypedArrayFrom(points.getDataType(), newPointsData), 3); // Set lines if (newLinesData.length !== 0) { output.getLines().setData(Uint16Array.from(newLinesData)); } // Set polys if (newPolysData.length !== 0) { output.getPolys().setData(Uint16Array.from(newPolysData)); } } // expose requestData publicAPI.requestData = (inData, outData) => { // implement requestData 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; }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- const DEFAULT_VALUES = { cutFunction: null, // support method with evaluateFunction method cutScalars: null, cutValue: 0.0 }; // ---------------------------------------------------------------------------- function extend(publicAPI, model) { let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; Object.assign(model, DEFAULT_VALUES, initialValues); // Make this a VTK object obj(publicAPI, model); // Also make it an algorithm with one input and one output algo(publicAPI, model, 1, 1); // Set implicit function use to cut the input data (is vtkPlane) setGet(publicAPI, model, ['cutFunction', 'cutValue']); // Object specific methods vtkCutter(publicAPI, model); } // ---------------------------------------------------------------------------- const newInstance = newInstance$1(extend, 'vtkCutter'); // ---------------------------------------------------------------------------- var vtkCutter$1 = { newInstance, extend }; export { vtkCutter$1 as default, extend, newInstance };