@kitware/vtk.js
Version:
Visualization Toolkit for the Web
384 lines (360 loc) • 13.9 kB
JavaScript
import { vec3 } from 'gl-matrix';
import { m as macro } from '../../macros2.js';
import vtkBufferObject from './BufferObject.js';
import { ObjectType } from './BufferObject/Constants.js';
import { Representation } from '../Core/Property/Constants.js';
import { computeCoordShiftAndScale, computeInverseShiftAndScaleMatrix } from './CellArrayBufferObject/helpers.js';
const {
vtkErrorMacro
} = macro;
// ----------------------------------------------------------------------------
// Static functions
// ----------------------------------------------------------------------------
function shouldApplyCoordShiftAndScale(coordShift, coordScale) {
if (coordShift === null || coordScale === null) {
return false;
}
return !(vec3.exactEquals(coordShift, [0, 0, 0]) && vec3.exactEquals(coordScale, [1, 1, 1]));
}
// ----------------------------------------------------------------------------
// vtkOpenGLCellArrayBufferObject methods
// ----------------------------------------------------------------------------
function vtkOpenGLCellArrayBufferObject(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkOpenGLCellArrayBufferObject');
publicAPI.setType(ObjectType.ARRAY_BUFFER);
publicAPI.createVBO = (cellArray, inRep, outRep, options, selectionMaps = null) => {
if (!cellArray.getData() || !cellArray.getData().length) {
model.elementCount = 0;
return 0;
}
// Figure out how big each block will be, currently 6 or 7 floats.
model.blockSize = 3;
model.vertexOffset = 0;
model.normalOffset = 0;
model.tCoordOffset = 0;
model.tCoordComponents = 0;
model.colorComponents = 0;
model.colorOffset = 0;
model.customData = [];
const pointData = options.points.getData();
let normalData = null;
let tcoordData = null;
let colorData = null;
const colorComponents = options.colors ? options.colors.getNumberOfComponents() : 0;
const textureComponents = options.tcoords ? options.tcoords.getNumberOfComponents() : 0;
// the values of 4 below are because floats are 4 bytes
if (options.normals) {
model.normalOffset = 4 * model.blockSize;
model.blockSize += 3;
normalData = options.normals.getData();
}
if (options.customAttributes) {
options.customAttributes.forEach(a => {
if (a) {
model.customData.push({
data: a.getData(),
offset: 4 * model.blockSize,
components: a.getNumberOfComponents(),
name: a.getName()
});
model.blockSize += a.getNumberOfComponents();
}
});
}
if (options.tcoords) {
model.tCoordOffset = 4 * model.blockSize;
model.tCoordComponents = textureComponents;
model.blockSize += textureComponents;
tcoordData = options.tcoords.getData();
}
if (options.colors) {
model.colorComponents = options.colors.getNumberOfComponents();
model.colorOffset = 0;
colorData = options.colors.getData();
if (!model.colorBO) {
model.colorBO = vtkBufferObject.newInstance();
}
model.colorBO.setOpenGLRenderWindow(model._openGLRenderWindow);
} else {
model.colorBO = null;
}
model.stride = 4 * model.blockSize;
let pointIdx = 0;
let normalIdx = 0;
let tcoordIdx = 0;
let colorIdx = 0;
let custIdx = 0;
let cellCount = 0;
let addAPoint;
const cellBuilders = {
// easy, every input point becomes an output point
anythingToPoints(numPoints, cellPts, offset, cellId) {
for (let i = 0; i < numPoints; ++i) {
addAPoint(cellPts[offset + i], cellId);
}
},
linesToWireframe(numPoints, cellPts, offset, cellIdx) {
// for lines we add a bunch of segments
for (let i = 0; i < numPoints - 1; ++i) {
addAPoint(cellPts[offset + i], cellIdx);
addAPoint(cellPts[offset + i + 1], cellIdx);
}
},
polysToWireframe(numPoints, cellPts, offset, cellIdx) {
// for polys we add a bunch of segments and close it
if (numPoints > 2) {
for (let i = 0; i < numPoints; ++i) {
addAPoint(cellPts[offset + i], cellIdx);
addAPoint(cellPts[offset + (i + 1) % numPoints], cellIdx);
}
}
},
stripsToWireframe(numPoints, cellPts, offset, cellIdx) {
if (numPoints > 2) {
// for strips we add a bunch of segments and close it
for (let i = 0; i < numPoints - 1; ++i) {
addAPoint(cellPts[offset + i], cellIdx);
addAPoint(cellPts[offset + i + 1], cellIdx);
}
for (let i = 0; i < numPoints - 2; i++) {
addAPoint(cellPts[offset + i], cellIdx);
addAPoint(cellPts[offset + i + 2], cellIdx);
}
}
},
polysToSurface(npts, cellPts, offset, cellIdx) {
for (let i = 0; i < npts - 2; i++) {
addAPoint(cellPts[offset + 0], cellIdx);
addAPoint(cellPts[offset + i + 1], cellIdx);
addAPoint(cellPts[offset + i + 2], cellIdx);
}
},
stripsToSurface(npts, cellPts, offset, cellIdx) {
for (let i = 0; i < npts - 2; i++) {
addAPoint(cellPts[offset + i], cellIdx);
addAPoint(cellPts[offset + i + 1 + i % 2], cellIdx);
addAPoint(cellPts[offset + i + 1 + (i + 1) % 2], cellIdx);
}
}
};
const cellCounters = {
// easy, every input point becomes an output point
anythingToPoints(numPoints, cellPts) {
return numPoints;
},
linesToWireframe(numPoints, cellPts) {
if (numPoints > 1) {
return (numPoints - 1) * 2;
}
return 0;
},
polysToWireframe(numPoints, cellPts) {
if (numPoints > 2) {
return numPoints * 2;
}
return 0;
},
stripsToWireframe(numPoints, cellPts) {
if (numPoints > 2) {
return numPoints * 4 - 6;
}
return 0;
},
polysToSurface(npts, cellPts) {
if (npts > 2) {
return (npts - 2) * 3;
}
return 0;
},
stripsToSurface(npts, cellPts, offset) {
if (npts > 2) {
return (npts - 2) * 3;
}
return 0;
}
};
let func = null;
let countFunc = null;
if (outRep === Representation.POINTS || inRep === 'verts') {
func = cellBuilders.anythingToPoints;
countFunc = cellCounters.anythingToPoints;
} else if (outRep === Representation.WIREFRAME || inRep === 'lines') {
func = cellBuilders[`${inRep}ToWireframe`];
countFunc = cellCounters[`${inRep}ToWireframe`];
} else {
func = cellBuilders[`${inRep}ToSurface`];
countFunc = cellCounters[`${inRep}ToSurface`];
}
const array = cellArray.getData();
const size = array.length;
let caboCount = 0;
for (let index = 0; index < size;) {
caboCount += countFunc(array[index], array);
index += array[index] + 1;
}
let packedUCVBO = null;
const packedVBO = new Float32Array(caboCount * model.blockSize);
if (colorData) {
packedUCVBO = new Uint8Array(caboCount * 4);
}
let vboidx = 0;
let ucidx = 0;
// Find out if shift scale should be used
const {
useShiftAndScale,
coordShift,
coordScale
} = computeCoordShiftAndScale(options.points);
if (useShiftAndScale) {
publicAPI.setCoordShiftAndScale(coordShift, coordScale);
} else if (model.coordShiftAndScaleEnabled === true) {
// Make sure to reset
publicAPI.setCoordShiftAndScale(null, null);
}
// Initialize the structures used to keep track of point ids and cell ids for selectors
if (selectionMaps) {
if (!selectionMaps.points && !selectionMaps.cells) {
selectionMaps.points = new Int32Array(caboCount);
selectionMaps.cells = new Int32Array(caboCount);
} else {
const newPoints = new Int32Array(caboCount + selectionMaps.points.length);
newPoints.set(selectionMaps.points);
selectionMaps.points = newPoints;
const newCells = new Int32Array(caboCount + selectionMaps.cells.length);
newCells.set(selectionMaps.cells);
selectionMaps.cells = newCells;
}
}
let pointCount = options.vertexOffset;
addAPoint = function addAPointFunc(pointId, cellId) {
// Keep track of original point and cell ids, for selection
if (selectionMaps) {
selectionMaps.points[pointCount] = pointId;
selectionMaps.cells[pointCount] = cellCount + options.cellOffset;
}
++pointCount;
// Vertices
pointIdx = pointId * 3;
if (!model.coordShiftAndScaleEnabled) {
packedVBO[vboidx++] = pointData[pointIdx++];
packedVBO[vboidx++] = pointData[pointIdx++];
packedVBO[vboidx++] = pointData[pointIdx++];
} else {
// Apply shift and scale
packedVBO[vboidx++] = (pointData[pointIdx++] - model.coordShift[0]) * model.coordScale[0];
packedVBO[vboidx++] = (pointData[pointIdx++] - model.coordShift[1]) * model.coordScale[1];
packedVBO[vboidx++] = (pointData[pointIdx++] - model.coordShift[2]) * model.coordScale[2];
}
if (normalData !== null) {
if (options.haveCellNormals) {
normalIdx = (cellCount + options.cellOffset) * 3;
} else {
normalIdx = pointId * 3;
}
packedVBO[vboidx++] = normalData[normalIdx++];
packedVBO[vboidx++] = normalData[normalIdx++];
packedVBO[vboidx++] = normalData[normalIdx++];
}
model.customData.forEach(attr => {
custIdx = pointId * attr.components;
for (let j = 0; j < attr.components; ++j) {
packedVBO[vboidx++] = attr.data[custIdx++];
}
});
if (tcoordData !== null) {
if (options.useTCoordsPerCell) {
tcoordIdx = cellId * textureComponents;
} else {
tcoordIdx = pointId * textureComponents;
}
for (let j = 0; j < textureComponents; ++j) {
packedVBO[vboidx++] = tcoordData[tcoordIdx++];
}
}
if (colorData !== null) {
if (options.haveCellScalars) {
colorIdx = (cellCount + options.cellOffset) * colorComponents;
} else {
colorIdx = pointId * colorComponents;
}
packedUCVBO[ucidx++] = colorData[colorIdx++];
packedUCVBO[ucidx++] = colorData[colorIdx++];
packedUCVBO[ucidx++] = colorData[colorIdx++];
packedUCVBO[ucidx++] = colorComponents === 4 ? colorData[colorIdx++] : 255;
}
};
// Browse the cell array: the index is at the beginning of a cell
// The value of 'array' at the position 'index' is the number of points in the cell
for (let index = 0; index < size; index += array[index] + 1, cellCount++) {
func(array[index], array, index + 1, cellCount + options.cellOffset);
}
model.elementCount = caboCount;
publicAPI.upload(packedVBO, ObjectType.ARRAY_BUFFER);
if (model.colorBO) {
model.colorBOStride = 4;
model.colorBO.upload(packedUCVBO, ObjectType.ARRAY_BUFFER);
}
return cellCount;
};
publicAPI.setCoordShiftAndScale = (coordShift, coordScale) => {
if (coordShift !== null && (coordShift.constructor !== Float64Array || coordShift.length !== 3)) {
vtkErrorMacro('Wrong type for coordShift, expected vec3 or null');
return;
}
if (coordScale !== null && (coordScale.constructor !== Float64Array || coordScale.length !== 3)) {
vtkErrorMacro('Wrong type for coordScale, expected vec3 or null');
return;
}
if (model.coordShift === null || coordShift === null || !vec3.equals(coordShift, model.coordShift)) {
model.coordShift = coordShift;
}
if (model.coordScale === null || coordScale === null || !vec3.equals(coordScale, model.coordScale)) {
model.coordScale = coordScale;
}
model.coordShiftAndScaleEnabled = shouldApplyCoordShiftAndScale(model.coordShift, model.coordScale);
if (model.coordShiftAndScaleEnabled) {
model.inverseShiftAndScaleMatrix = computeInverseShiftAndScaleMatrix(model.coordShift, model.coordScale);
} else {
model.inverseShiftAndScaleMatrix = null;
}
};
}
// ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------
const DEFAULT_VALUES = {
elementCount: 0,
stride: 0,
colorBOStride: 0,
vertexOffset: 0,
normalOffset: 0,
tCoordOffset: 0,
tCoordComponents: 0,
colorOffset: 0,
colorComponents: 0,
tcoordBO: null,
customData: [],
coordShift: null,
coordScale: null,
coordShiftAndScaleEnabled: false,
inverseShiftAndScaleMatrix: null
};
// ----------------------------------------------------------------------------
function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);
// Inheritance
vtkBufferObject.extend(publicAPI, model, initialValues);
macro.setGet(publicAPI, model, ['colorBO', 'elementCount', 'stride', 'colorBOStride', 'vertexOffset', 'normalOffset', 'tCoordOffset', 'tCoordComponents', 'colorOffset', 'colorComponents', 'customData']);
macro.get(publicAPI, model, ['coordShift', 'coordScale', 'coordShiftAndScaleEnabled', 'inverseShiftAndScaleMatrix']);
// Object specific methods
vtkOpenGLCellArrayBufferObject(publicAPI, model);
}
// ----------------------------------------------------------------------------
const newInstance = macro.newInstance(extend);
// ----------------------------------------------------------------------------
var vtkCellArrayBufferObject = {
newInstance,
extend
};
export { vtkCellArrayBufferObject as default, extend, newInstance };