vtk.js-custom-jhonny
Version:
Visualization Toolkit for the Web
296 lines (259 loc) • 9.95 kB
JavaScript
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 };