@kitware/vtk.js
Version:
Visualization Toolkit for the Web
382 lines (342 loc) • 14 kB
JavaScript
import { m as macro } from '../../macros2.js';
import vtkPolyData from '../../Common/DataModel/PolyData.js';
import vtkMatrixBuilder from '../../Common/Core/MatrixBuilder.js';
import vtkDataArray from '../../Common/Core/DataArray.js';
// ----------------------------------------------------------------------------
// vtkConcentricCylinderSource methods
// ----------------------------------------------------------------------------
function vtkConcentricCylinderSource(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkConcentricCylinderSource');
// Internal private function
function validateCellFields() {
while (model.cellFields.length < model.radius.length) {
model.cellFields.push(model.cellFields.length);
}
}
publicAPI.clearRadius = () => {
model.radius = [];
model.cellFields = [];
publicAPI.modified();
};
publicAPI.addRadius = (radius, cellField) => {
model.radius.push(radius);
if (cellField !== undefined) {
model.cellFields.push(cellField);
}
validateCellFields();
publicAPI.modified();
};
publicAPI.getNumberOfRadius = () => model.radius.length;
publicAPI.getRadius = (index = 0) => model.radius[index];
publicAPI.setRadius = (index, radius) => {
model.radius[index] = radius;
publicAPI.modified();
};
publicAPI.setCellField = (index, field) => {
model.cellFields[index] = field;
publicAPI.modified();
};
publicAPI.removeMask = () => {
model.mask = null;
publicAPI.modified();
};
publicAPI.setMaskLayer = (index, hidden) => {
let changeDetected = false;
if (!model.mask && hidden) {
changeDetected = true;
model.mask = [];
}
if (model.mask) {
if (!model.mask[index] !== !hidden) {
changeDetected = true;
}
model.mask[index] = hidden;
}
if (changeDetected) {
publicAPI.modified();
}
};
publicAPI.getMaskLayer = index => index === undefined ? model.mask : model.mask[index];
publicAPI.requestData = (inData, outData) => {
if (!model.radius.length) {
macro.vtkErrorMacro('No radius defined');
return;
}
// Make sure we have consistency
validateCellFields();
const numLayers = model.radius.length;
const zRef = model.height / 2.0;
// Compute cell count
let cellArraySize = 0;
let numCells = 0;
let startTheta = model.startTheta < model.endTheta ? model.startTheta : model.endTheta;
startTheta *= Math.PI / 180.0;
let endTheta = model.endTheta > model.startTheta ? model.endTheta : model.startTheta;
endTheta *= Math.PI / 180.0;
let thetaResolution = model.resolution;
let partialDisk = false;
if (endTheta >= startTheta + 2 * Math.PI) {
// complete, closed cylinder
endTheta = startTheta + 2 * Math.PI;
} else {
// We add an extra point at endTheta, since the cylinder isn't closed.
// We cap the sides of the partial cylinder, so set the partialDisk flag.
++thetaResolution;
partialDisk = true;
}
const deltaTheta = (endTheta - startTheta) / model.resolution;
const numberOfPoints = thetaResolution * numLayers * 2 + 2;
// 5 entries per poly, 4 polys (top, bottom, in, out) per resolution
if (!model.skipInnerFaces && !model.mask) {
// We keep everything
cellArraySize = 2 * (thetaResolution + 1) + 5 * thetaResolution + (numLayers - 1) * thetaResolution * 20 + (partialDisk ? 10 * numLayers : 0);
numCells = 2 + thetaResolution + (numLayers - 1) * 4 * thetaResolution + (partialDisk ? 2 * numLayers : 0);
} else if (!model.skipInnerFaces && model.mask) {
// We skip some cylinders
// Handle core
if (!model.mask[0]) {
cellArraySize += 2 * (thetaResolution + 1) + 5 * thetaResolution + (partialDisk ? 10 : 0);
numCells += 2 + thetaResolution + (partialDisk ? 2 : 0);
}
// Handle inside cylinders
for (let layer = 1; layer < numLayers; layer++) {
if (!model.mask[layer]) {
// Add inside cylinder count
cellArraySize += thetaResolution * 20 + (partialDisk ? 10 : 0);
numCells += 4 * thetaResolution + (partialDisk ? 2 : 0);
}
}
} else {
// We skip cylinders and internal faces
if (!model.skipInnerFaces || !model.mask || !model.mask[0]) {
// core handling
cellArraySize += 2 * (thetaResolution + 1) + (partialDisk ? 10 : 0);
numCells += 2 + (partialDisk ? 2 : 0);
if (model.radius.length === 1 || !model.skipInnerFaces || model.mask && model.mask[1]) {
// add side faces
cellArraySize += 5 * thetaResolution;
numCells += thetaResolution;
}
}
// Handle inside cylinders
for (let layer = 1; layer < numLayers; layer++) {
if (!model.skipInnerFaces || !model.mask || !model.mask[layer]) {
const lastLayer = numLayers - 1 === layer;
// Add inside cylinder
cellArraySize += thetaResolution * 10 + (partialDisk ? 10 : 0);
numCells += thetaResolution * 2 + (partialDisk ? 2 : 0); // top + bottom + side caps
// Do we add innerFaces
if (!model.skipInnerFaces || model.mask && model.mask[layer - 1]) {
cellArraySize += thetaResolution * 5;
numCells += thetaResolution;
}
// Do we add outerFaces
if (lastLayer || !model.skipInnerFaces || model.mask && model.mask[layer + 1]) {
cellArraySize += thetaResolution * 5;
numCells += thetaResolution;
}
}
}
}
// Points
let pointIdx = 0;
const points = macro.newTypedArray(model.pointType, numberOfPoints * 3);
// Cells
let cellLocation = 0;
const polys = new Uint32Array(cellArraySize);
// CellFields
let fieldLocation = 0;
const field = new Float32Array(numCells);
// Create points
// First two are centered, top and bottom. Used only if partialDisk is true.
points[pointIdx * 3 + 0] = 0;
points[pointIdx * 3 + 1] = 0;
points[pointIdx * 3 + 2] = zRef;
pointIdx++;
points[pointIdx * 3 + 0] = 0;
points[pointIdx * 3 + 1] = 0;
points[pointIdx * 3 + 2] = -zRef;
pointIdx++;
for (let layer = 0; layer < numLayers; layer++) {
const radius = model.radius[layer];
// Create top
for (let i = 0; i < thetaResolution; i++) {
const theta = startTheta + i * deltaTheta;
points[pointIdx * 3 + 0] = radius * Math.cos(theta);
points[pointIdx * 3 + 1] = radius * Math.sin(theta);
points[pointIdx * 3 + 2] = zRef;
pointIdx++;
}
// Create bottom
for (let i = 0; i < thetaResolution; i++) {
const theta = startTheta + i * deltaTheta;
points[pointIdx * 3 + 0] = radius * Math.cos(theta);
points[pointIdx * 3 + 1] = radius * Math.sin(theta);
points[pointIdx * 3 + 2] = -zRef;
pointIdx++;
}
}
// Create cells for the core
let currentField = model.cellFields[0];
// Core: filtering
if (!model.mask || !model.mask[0]) {
// Core: Top disk
field[fieldLocation++] = currentField;
// partial adds the center point and the last point.
polys[cellLocation++] = thetaResolution + (partialDisk ? 1 : 0);
if (partialDisk) polys[cellLocation++] = 0;
for (let i = 0; i < thetaResolution; i++) {
polys[cellLocation++] = i + 2;
}
// Core: Bottom disk
field[fieldLocation++] = currentField;
polys[cellLocation++] = thetaResolution + (partialDisk ? 1 : 0);
if (partialDisk) polys[cellLocation++] = 1;
for (let i = 0; i < thetaResolution; i++) {
polys[cellLocation++] = 2 * thetaResolution - i - 1 + 2;
}
// Core: sides
if (!model.skipInnerFaces || model.mask && model.mask[1] || numLayers === 1) {
for (let i = 0; i < model.resolution; i++) {
polys[cellLocation++] = 4;
polys[cellLocation++] = (i + 1) % thetaResolution + 2;
polys[cellLocation++] = i + 2;
polys[cellLocation++] = i + thetaResolution + 2;
polys[cellLocation++] = (i + 1) % thetaResolution + thetaResolution + 2;
field[fieldLocation++] = currentField;
}
}
if (partialDisk) {
polys[cellLocation++] = 4;
polys[cellLocation++] = 2;
polys[cellLocation++] = 0;
polys[cellLocation++] = 1;
polys[cellLocation++] = thetaResolution + 2;
field[fieldLocation++] = currentField;
polys[cellLocation++] = 4;
polys[cellLocation++] = 0;
polys[cellLocation++] = thetaResolution + 1;
polys[cellLocation++] = 2 * thetaResolution + 1;
polys[cellLocation++] = 1;
field[fieldLocation++] = currentField;
}
}
// Create cells for the layers
for (let layer = 1; layer < numLayers; layer++) {
// Skip layer if masked
if (model.mask && model.mask[layer]) {
/* eslint-disable no-continue */
continue;
/* eslint-enable no-continue */
}
// two for center points, then skip previous layer's points
const offset = thetaResolution * 2 * (layer - 1) + 2;
const a = offset + 2 * thetaResolution; // next layer offset
const lastLayer = numLayers - 1 === layer;
currentField = model.cellFields[layer];
// Create top
for (let i = 0; i < model.resolution; i++) {
polys[cellLocation++] = 4;
polys[cellLocation++] = i + offset;
polys[cellLocation++] = (i + 1) % thetaResolution + offset;
polys[cellLocation++] = (i + 1) % thetaResolution + a;
polys[cellLocation++] = i + a;
field[fieldLocation++] = currentField;
}
// Create bottom
for (let i = 0; i < model.resolution; i++) {
polys[cellLocation++] = 4;
polys[cellLocation++] = (i + 1) % thetaResolution + offset + thetaResolution;
polys[cellLocation++] = i + offset + thetaResolution;
polys[cellLocation++] = i + a + thetaResolution;
polys[cellLocation++] = (i + 1) % thetaResolution + a + thetaResolution;
field[fieldLocation++] = currentField;
}
// Create inner
if (!model.skipInnerFaces || model.mask && model.mask[layer - 1]) {
for (let i = 0; i < model.resolution; i++) {
polys[cellLocation++] = 4;
polys[cellLocation++] = i + offset;
polys[cellLocation++] = (i + 1) % thetaResolution + offset;
polys[cellLocation++] = (i + 1) % thetaResolution + thetaResolution + offset;
polys[cellLocation++] = i + thetaResolution + offset;
field[fieldLocation++] = currentField;
}
}
// Create outer
if (!model.skipInnerFaces || lastLayer || model.mask && (model.mask[layer + 1] || lastLayer)) {
for (let i = 0; i < model.resolution; i++) {
polys[cellLocation++] = 4;
polys[cellLocation++] = (i + 1) % thetaResolution + a;
polys[cellLocation++] = i + a;
polys[cellLocation++] = i + thetaResolution + a;
polys[cellLocation++] = (i + 1) % thetaResolution + thetaResolution + a;
field[fieldLocation++] = currentField;
}
}
// create caps
if (partialDisk) {
polys[cellLocation++] = 4;
polys[cellLocation++] = a; // from first outer
polys[cellLocation++] = offset; // first inner
polys[cellLocation++] = thetaResolution + offset; // first inner
polys[cellLocation++] = thetaResolution + a; // first outer
field[fieldLocation++] = currentField;
polys[cellLocation++] = 4;
polys[cellLocation++] = model.resolution + a; // last outer
polys[cellLocation++] = model.resolution + offset; // last inner
polys[cellLocation++] = model.resolution + thetaResolution + offset; // last inner
polys[cellLocation++] = model.resolution + thetaResolution + a; // last outer
field[fieldLocation++] = currentField;
}
}
// Apply transformation to the point coordinates
vtkMatrixBuilder.buildFromRadian().translate(...model.center).rotateFromDirections([0, 0, 1], model.direction).apply(points);
const dataset = outData[0]?.initialize() || vtkPolyData.newInstance();
dataset.getPoints().setData(points, 3);
dataset.getPolys().setData(polys, 1);
dataset.getCellData().setScalars(vtkDataArray.newInstance({
name: 'layer',
values: field
}));
// Update output
outData[0] = dataset;
};
}
// ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------
const DEFAULT_VALUES = {
height: 1.0,
radius: [0.5],
cellFields: [1],
resolution: 6,
startTheta: 0.0,
endTheta: 360.0,
center: [0, 0, 0],
direction: [0.0, 0.0, 1.0],
skipInnerFaces: true,
mask: null,
// If present, array to know if a layer should be skipped(=true)
pointType: 'Float64Array'
};
// ----------------------------------------------------------------------------
function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);
// Build VTK API
macro.obj(publicAPI, model);
macro.setGet(publicAPI, model, ['height', 'resolution', 'startTheta', 'endTheta', 'skipInnerFaces']);
macro.setGetArray(publicAPI, model, ['center', 'direction'], 3);
macro.getArray(publicAPI, model, ['cellFields']);
macro.algo(publicAPI, model, 0, 1);
vtkConcentricCylinderSource(publicAPI, model);
}
// ----------------------------------------------------------------------------
const newInstance = macro.newInstance(extend, 'vtkConcentricCylinderSource');
// ----------------------------------------------------------------------------
var vtkConcentricCylinderSource$1 = {
newInstance,
extend
};
export { vtkConcentricCylinderSource$1 as default, extend, newInstance };